Demo 2:配分函数

第 13 章 拓扑、统计物理与采样 · 本章演示 · 代码演示总索引

可视化:单子图(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}")

运行结果

Demo 2:配分函数

运行方式

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

← 本章演示 · ↑ 代码演示总索引 · ⌂ 教程门户