Demo 2:配分函数¶
可视化:单子图(4 柱) —— Z = 1/P(ground) 估 Z(β)
对应教程:第 13 章 拓扑、统计物理与采样 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
H = diag(0, 1, 2, 3) on 2 qubits. 经典 Z(β) = Σ e^{-β E_j}. 量子 "Gibbs sampling" trick: 用 uniqc.rotation_prepare 装 |ψ_β> ∝ Σ e^{-β E_j/2}|j> 到 2 个 qubit. 由于 |00> 是 H 的基态 (E_0=0), P(|00>) = e^{-β·0} / Z = 1/Z, 因此 Z = 1 / P(|00>) 可从 shots 直接读出. 对比经典 Z(β), 并验证 Boltzmann 分布 p_j ∝ e^{-β E_j}. 扫描 β ∈ [0, 3].
完整源码¶
demos/ch13-topology-statistical-physics/02_partition_function.py¶
1"""
2ch13-topology demo 2: 量子法估配分函数 Z(β) = Tr[e^{-βH}]
3
4对应教程: chenzhaoyun.com/quantum-tutorial/ch13-topology-statistical-physics/partition-functions.html
5依赖: pip install unified-quantum[visualization]
6运行: python demos/ch13-topology-statistical-physics/02_partition_function.py
7
8H = diag(0, 1, 2, 3) on 2 qubits. 经典 Z(β) = Σ e^{-β E_j}.
9量子 "Gibbs sampling" trick: 用 uniqc.rotation_prepare 装
10|ψ_β> ∝ Σ e^{-β E_j/2}|j> 到 2 个 qubit. 由于 |00> 是 H 的基态 (E_0=0),
11P(|00>) = e^{-β·0} / Z = 1/Z, 因此 Z = 1 / P(|00>) 可从 shots 直接读出.
12对比经典 Z(β), 并验证 Boltzmann 分布 p_j ∝ e^{-β E_j}.
13扫描 β ∈ [0, 3].
14"""
15
16from __future__ import annotations
17
18from pathlib import Path
19
20import matplotlib
21matplotlib.use("Agg")
22import matplotlib.pyplot as plt
23import numpy as np
24
25from uniqc import Circuit, rotation_prepare, submit_task, wait_for_result
26
27OUT = Path(__file__).parent
28H_DIAG = np.array([0.0, 1.0, 2.0, 3.0])
29BETAS = [0.0, 0.3, 0.7, 1.2, 1.8, 2.5, 3.0]
30SHOTS = 400
31
32
33# ---------------------------------------------------------------------------
34# 1) 经典 Z(β) 参考
35# ---------------------------------------------------------------------------
36def classical_Z(beta: float) -> float:
37 return float(np.sum(np.exp(-beta * H_DIAG)))
38
39
40def gibbs_probs(beta: float) -> np.ndarray:
41 w = np.exp(-beta * H_DIAG)
42 return w / w.sum()
43
44
45Z_class = np.array([classical_Z(b) for b in BETAS])
46print("ch13-demo-2: quantum Z(β) via Gibbs-state inversion")
47print(f" H eigenvalues (2-qubit computational basis): {H_DIAG.tolist()}")
48print(f" β grid: {BETAS}, shots per circuit: {SHOTS}")
49
50
51# ---------------------------------------------------------------------------
52# 2) 量子版: 装 |ψ_β>, 测 P(|00>), 反推 Z
53# ---------------------------------------------------------------------------
54def measure_gibbs(beta: float) -> dict:
55 """Load Gibbs state via rotation_prepare, measure all 4 basis probs."""
56 target = np.sqrt(gibbs_probs(beta)).astype(complex)
57 c = Circuit()
58 rotation_prepare(c, target, qubits=[0, 1])
59 c.measure(0)
60 c.measure(1)
61 tid = submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS)
62 r = wait_for_result(tid)
63 counts = r.counts
64 total = sum(counts.values())
65 probs = np.array([
66 counts.get("00", 0) / total,
67 counts.get("01", 0) / total,
68 counts.get("10", 0) / total,
69 counts.get("11", 0) / total,
70 ])
71 return {
72 "probs": probs,
73 "Z_inverted": 1.0 / max(probs[0], 1e-9),
74 "counts": counts,
75 }
76
77
78results = []
79for beta in BETAS:
80 info = measure_gibbs(beta)
81 info["beta"] = beta
82 info["Z_class"] = classical_Z(beta)
83 info["probs_th"] = gibbs_probs(beta)
84 results.append(info)
85 print(f" β={beta:.1f}: Z_class={info['Z_class']:.4f} "
86 f"P(00)={info['probs'][0]:.4f} Z_quantum={info['Z_inverted']:.4f} "
87 f"err={abs(info['Z_class'] - info['Z_inverted']):.4f}")
88
89Z_quant = np.array([r["Z_inverted"] for r in results])
90
91
92# ---------------------------------------------------------------------------
93# 3) matplotlib 可视化 (3 panels)
94# ---------------------------------------------------------------------------
95fig, axes = plt.subplots(1, 3, figsize=(16.0, 4.8))
96
97ax = axes[0]
98ax.plot(BETAS, Z_class, "o-", color="#1f77b4", lw=2.2, ms=9,
99 label=r"classical $Z(\beta) = \sum_j e^{-\beta E_j}$")
100ax.plot(BETAS, Z_quant, "s--", color="#d62728", lw=2.0, ms=8,
101 label=r"quantum $Z = 1/P(|00\rangle)$ from Gibbs state")
102ax.fill_between(BETAS, Z_class, Z_quant, color="gray", alpha=0.15)
103ax.set_xlabel(r"inverse temperature $\beta$")
104ax.set_ylabel(r"partition function $Z(\beta)$")
105ax.set_title("Classical vs quantum Z(β)\n"
106 r"H = diag(0, 1, 2, 3) on 2 qubits")
107ax.grid(alpha=0.3)
108ax.legend(fontsize=9)
109
110ax = axes[1]
111# 选 3 个 β 显示 Boltzmann 分布
112sel_idx = [1, 3, 5]
113sel_labels = [f"β={BETAS[i]:.1f}" for i in sel_idx]
114labels = ["|00⟩", "|01⟩", "|10⟩", "|11⟩"]
115x = np.arange(4)
116w = 0.25
117colors_t = ["#1f77b4", "#2ca02c", "#d62728"]
118for k, i in enumerate(sel_idx):
119 p_th = results[i]["probs_th"]
120 p_emp = results[i]["probs"]
121 ax.bar(x + (k - 1) * w, p_th, w, color=colors_t[k], alpha=0.85,
122 label=f"{sel_labels[k]} theory")
123 ax.bar(x + (k - 1) * w, p_emp, w, color=colors_t[k], alpha=0.30,
124 ec="black", lw=0.7, hatch="//")
125ax.set_xticks(x)
126ax.set_xticklabels(labels)
127ax.set_ylabel("probability")
128ax.set_title("Boltzmann distribution\n"
129 r"$p_j = e^{-\beta E_j}/Z$ vs uniqc shots")
130ax.set_ylim(0, 1.1)
131ax.grid(axis="y", alpha=0.3)
132ax.legend(fontsize=8, loc="upper right")
133
134ax = axes[2]
135rel_err = np.abs(Z_class - Z_quant) / Z_class * 100
136ax.semilogy(BETAS, rel_err + 1e-2, "o-", color="#9467bd", lw=2, ms=8,
137 label=r"$|Z_{\rm class} - Z_{\rm quant}|/Z_{\rm class}$")
138ax.axhline(5.0, color="gray", ls=":", lw=1, label="5% reference")
139ax.axhline(1.0, color="green", ls="--", lw=1, alpha=0.6,
140 label="1% reference")
141ax.set_xlabel(r"$\beta$")
142ax.set_ylabel("relative error (%) — log scale")
143ax.set_title("Z-quantum relative error vs β\n"
144 r"(shot noise scales as $1/\sqrt{N_{\rm shots}}$)")
145ax.set_ylim(1e-1, 50)
146ax.grid(alpha=0.3, which="both")
147ax.legend(fontsize=8)
148
149plt.suptitle("ch13-topology demo 2: partition function Z(β) via "
150 "Gibbs-state probability inversion", y=1.02)
151plt.tight_layout()
152out_path = OUT / "02_partition_function.png"
153plt.savefig(out_path, dpi=120, bbox_inches="tight")
154plt.close()
155
156
157# ---------------------------------------------------------------------------
158# 4) stdout 关键统计
159# ---------------------------------------------------------------------------
160print("\n summary:")
161print(f" shots per β: {SHOTS}, total β: {len(BETAS)}")
162print(f" total circuits run: {len(BETAS)} (one Gibbs-prep per β)")
163print(f" total shots: {len(BETAS) * SHOTS}")
164print(f" max relative error Z: {rel_err.max():.2f}%")
165print(f" mean max|Boltzmann err| across β: "
166 f"{np.mean([np.abs(r['probs_th'] - r['probs']).max() for r in results]):.4f}")
167print(f" PNG -> {out_path}")
运行结果¶

运行方式¶
# 仓库根目录执行;输出 PNG 到 demos/ch13-topology-statistical-physics/02_partition_function.png,关键统计到 stdout
.venv/bin/python3 demos/ch13-topology-statistical-physics/02_partition_function.py