Demo 1:VQE H₂¶
可视化:双子图(收敛+vs FCI) —— UCCSD + COBYLA 估 H₂ 基态 ≈ -2.016 Ha
对应教程:第 8 章 量子机器学习 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
核心:用 2-比特 Bravyi-Kitaev 映射的 H2 哈密顿量 H = -1.0523732 I + 0.3979374 Z0 - 0.3979374 Z1 + 0.08972914 Z0Z1 + 0.1809313 X0X1 + 0.1809313 Y0Y1 FCI 由 numpy.linalg.eigh 解 H 矩阵直接给出(≈ -2.0164 Ha)。 配合 4 参数硬件高效拟设 U(θ) = [Ry(θ2) ⊗ Ry(θ3)] · CNOT · [Ry(θ0) ⊗ Ry(θ1)] 在 scipy.optimize (COBYLA) 下做经典变分优化, 每步分三个测量基(Z、Y、X)估计 5 条泡利字符串, 绘制能量收敛曲线 + 末态能量 vs FCI 对比柱图。
完整源码¶
demos/ch08-qml/01_vqe_h2.py¶
1"""
2ch08-qml demo 1: VQE for the H2 molecule ground state
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch08-qml/vqe-tutorial.html
5核心:用 2-比特 Bravyi-Kitaev 映射的 H2 哈密顿量
6 H = -1.0523732 I + 0.3979374 Z0 - 0.3979374 Z1
7 + 0.08972914 Z0Z1 + 0.1809313 X0X1 + 0.1809313 Y0Y1
8 FCI 由 numpy.linalg.eigh 解 H 矩阵直接给出(≈ -2.0164 Ha)。
9 配合 4 参数硬件高效拟设
10 U(θ) = [Ry(θ2) ⊗ Ry(θ3)] · CNOT · [Ry(θ0) ⊗ Ry(θ1)]
11 在 scipy.optimize (COBYLA) 下做经典变分优化,
12 每步分三个测量基(Z、Y、X)估计 5 条泡利字符串,
13 绘制能量收敛曲线 + 末态能量 vs FCI 对比柱图。
14
15依赖:pip install unified-quantum[visualization] scipy
16运行:python demos/ch08-qml/01_vqe_h2.py
17"""
18from __future__ import annotations
19
20from pathlib import Path
21
22import matplotlib
23matplotlib.use("Agg")
24import matplotlib.pyplot as plt
25import numpy as np
26
27from scipy.optimize import minimize
28
29from uniqc import (
30 Circuit, submit_task, wait_for_result,
31 calculate_expectation,
32)
33
34OUT = Path(__file__).parent
35
36# H2 / STO-3G in the Bravyi-Kitaev 2-qubit mapping
37# Reference: O'Malley et al. (2016), qiskit nature tutorials.
38H_CONST, C_Z0, C_Z1, C_Z0Z1, C_XX, C_YY = (
39 -1.0523732, 0.3979374, -0.3979374, 0.08972914, 0.1809313, 0.1809313,
40)
41I2 = np.eye(2, dtype=complex); Z = np.array([[1, 0], [0, -1]], dtype=complex)
42X = np.array([[0, 1], [1, 0]], dtype=complex); Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
43H_MAT = (
44 H_CONST * np.kron(I2, I2) + C_Z0 * np.kron(Z, I2) + C_Z1 * np.kron(I2, Z)
45 + C_Z0Z1 * np.kron(Z, Z) + C_XX * np.kron(X, X) + C_YY * np.kron(Y, Y)
46)
47H_FCI = float(np.linalg.eigvalsh(H_MAT)[0].real)
48
49SHOTS = 600
50
51
52def ansatz(theta0: float, theta1: float, theta2: float, theta3: float) -> Circuit:
53 """2-layer hardware-efficient ansatz: 4 free parameters."""
54 c = Circuit()
55 c.ry(0, float(theta0))
56 c.ry(1, float(theta1))
57 c.cnot(0, 1)
58 c.ry(0, float(theta2))
59 c.ry(1, float(theta3))
60 return c
61
62
63def measure_in_basis(circuit: Circuit, basis: str) -> dict:
64 """Clone the state-prep circuit, append basis rotation, measure."""
65 c = Circuit()
66 c.add_circuit(circuit)
67 if basis == "Z":
68 pass # measure directly
69 elif basis == "X":
70 c.h(0); c.h(1)
71 elif basis == "Y":
72 c.sdg(0); c.h(0)
73 c.sdg(1); c.h(1)
74 else:
75 raise ValueError(basis)
76 c.measure(0)
77 c.measure(1)
78 r = wait_for_result(submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS))
79 return r.probabilities
80
81
82def energy_of(theta) -> float:
83 """Estimate ⟨ψ(θ)|H|ψ(θ)⟩ over the 5 non-constant Pauli strings."""
84 state_prep = ansatz(*theta)
85
86 z_probs = measure_in_basis(state_prep, "Z")
87 x_probs = measure_in_basis(state_prep, "X")
88 y_probs = measure_in_basis(state_prep, "Y")
89 e_z0 = calculate_expectation(z_probs, "ZI")
90 e_z1 = calculate_expectation(z_probs, "IZ")
91 e_z0z1 = calculate_expectation(z_probs, "ZZ")
92 e_xx = calculate_expectation(x_probs, "ZZ") # X⊗X after H⊗H
93 e_yy = calculate_expectation(y_probs, "ZZ") # Y⊗Y after (S†H)⊗(S†H)
94 return (
95 H_CONST
96 + C_Z0 * e_z0
97 + C_Z1 * e_z1
98 + C_Z0Z1 * e_z0z1
99 + C_XX * e_xx
100 + C_YY * e_yy
101 )
102
103
104# === 1. warm-up: evaluate energy at a few initial points to scope the search ===
105print("ch08-qml-demo-1: VQE on H2 (STO-3G, BK 2-qubit reduction)")
106print(f" FCI reference energy: {H_FCI:.6f} Hartree")
107print(f" shots per basis measurement: {SHOTS}")
108
109# === 2. COBYLA optimization with a callback recording the trajectory ===
110trajectory: list[tuple[int, float, np.ndarray]] = []
111
112
113def cb(xk: np.ndarray) -> None:
114 e = energy_of(xk)
115 trajectory.append((len(trajectory), e, xk.copy()))
116
117
118rng = np.random.default_rng(seed=0)
119x0 = rng.uniform(-np.pi, np.pi, size=4)
120
121# initial evaluation
122trajectory.append((0, energy_of(x0), x0.copy()))
123
124result = minimize(
125 fun=lambda th: energy_of(th),
126 x0=x0,
127 method="COBYLA",
128 options={"maxiter": 60, "rhobeg": 0.6, "disp": False},
129 callback=cb,
130)
131theta_opt = np.asarray(result.x, dtype=float)
132energy_opt = float(result.fun)
133
134iters = np.array([t[0] for t in trajectory])
135energies = np.array([t[1] for t in trajectory])
136
137# === 3. matplotlib 可视化 ===
138fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12.5, 4.6))
139
140# Left: energy convergence
141ax1.plot(iters, energies, "-o", color="#d62728", ms=4, lw=1.4,
142 label=r"VQE $\langle H\rangle$ trajectory")
143ax1.axhline(H_FCI, color="#1f77b4", ls="--", lw=2.0,
144 label=fr"FCI reference $E_0 = {H_FCI:.4f}$ Ha")
145ax1.fill_between(iters, H_FCI, energies,
146 where=(energies > H_FCI), color="#d62728", alpha=0.12,
147 label="variational gap")
148ax1.set_xlabel("COBYLA iteration")
149ax1.set_ylabel(r"energy $\langle H\rangle$ (Hartree)")
150ax1.set_title("VQE convergence on H2/STO-3G (2-qubit BK)")
151ax1.grid(alpha=0.3)
152ax1.legend(loc="upper right", fontsize=9)
153
154# Right: bar chart of final energy vs FCI
155bar_x = np.array([0, 1])
156bar_y = np.array([energy_opt, H_FCI])
157colors = ["#d62728", "#1f77b4"]
158labels = [fr"VQE $E(\theta^*) = {energy_opt:.4f}$", fr"FCI $E_0 = {H_FCI:.4f}$"]
159bars = ax2.bar(bar_x, bar_y, color=colors, ec="black", lw=0.6, width=0.6)
160for b, y in zip(bars, bar_y):
161 ax2.text(b.get_x() + b.get_width() / 2, y + 0.002, f"{y:.4f}",
162 ha="center", va="bottom", fontsize=10, fontweight="bold")
163err = abs(energy_opt - H_FCI)
164ax2.set_xticks(bar_x)
165ax2.set_xticklabels(labels, fontsize=9)
166ax2.set_ylabel(r"energy (Hartree)")
167ax2.set_title(fr"final energy gap = ${err:.2e}$ Ha ($\approx {err*627.5:.1f}$ kcal/mol)")
168ax2.set_ylim(min(energy_opt, H_FCI) - 0.01, max(energy_opt, H_FCI) + 0.025)
169ax2.grid(alpha=0.3, axis="y")
170ax2.axhline(H_FCI, color="#1f77b4", ls=":", lw=1.0, alpha=0.6)
171
172plt.suptitle("ch08-qml demo 1: VQE on the H2 molecule ground state", y=1.02)
173plt.tight_layout()
174out_path = OUT / "01_vqe_h2.png"
175plt.savefig(out_path, dpi=120, bbox_inches="tight")
176plt.close()
177
178# === 4. stdout 关键统计 ===
179print(f" total circuit evaluations: {len(trajectory)}")
180print(f" initial energy : {energies[0]:.6f} Ha")
181print(f" final energy E(theta*) : {energy_opt:.6f} Ha")
182print(f" FCI energy : {H_FCI:.6f} Ha")
183print(f" absolute error : {abs(energy_opt - H_FCI):.3e} Ha")
184print(f" theta* (rad) : {theta_opt.tolist()}")
185print(f" PNG -> {out_path}")
运行结果¶

运行方式¶
# 仓库根目录执行;输出 PNG 到 demos/ch08-qml/01_vqe_h2.png,关键统计到 stdout
.venv/bin/python3 demos/ch08-qml/01_vqe_h2.py