Demo 1:HHL 2×2¶
可视化:单子图 —— QPE 提本征相位 + 重建 x = A⁻¹|b⟩
对应教程:第 6 章 量子科学计算 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
流水线:构造 A = [[1, 0.5], [0.5, 1]] (kappa=3) -> uniqc 跑 QPE 抽取本 征值 (1 system + 3 precision) -> 经典反演 |x> = A^-1|b>。 PNG 同时画出 A、kappa、QPE 相位直方图与解向量对比。
完整源码¶
demos/ch06-scientific-computing/01_hhl_2x2.py¶
1"""
2ch06 演示 1:HHL 解 2x2 线性系统
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch06-scientific-computing/hhl-algorithm-tutorial.html
5依赖:pip install unified-quantum[visualization]
6运行:python demos/ch06-scientific-computing/01_hhl_2x2.py
7
8流水线:构造 A = [[1, 0.5], [0.5, 1]] (kappa=3) -> uniqc 跑 QPE 抽取本
9征值 (1 system + 3 precision) -> 经典反演 |x> = A^-1|b>。 PNG 同时画出
10A、kappa、QPE 相位直方图与解向量对比。
11"""
12from __future__ import annotations
13
14from pathlib import Path
15
16import matplotlib
17matplotlib.use("Agg")
18import matplotlib.pyplot as plt
19import numpy as np
20
21from uniqc import Circuit, submit_task, wait_for_result
22from uniqc.algorithms.core.circuits import qpe_circuit
23
24OUT = Path(__file__).parent
25
26# --------------------------------------------------------------------------
27# 1) 经典参考:HHL 解 A x = b 的解析解
28# --------------------------------------------------------------------------
29A = np.array([[1.0, 0.5], [0.5, 1.0]])
30b = np.array([1.0, 0.0])
31evals, evecs = np.linalg.eigh(A)
32kappa = float(evals.max() / evals.min())
33x_classical = np.linalg.solve(A, b)
34x_norm = x_classical / np.linalg.norm(x_classical)
35probs_classical = x_norm ** 2 # P(q[0]=0), P(q[0]=1)
36
37# --------------------------------------------------------------------------
38# 2) uniqc QPE:U = exp(i*pi*A) = -i*X = H RZ(pi) H
39# --------------------------------------------------------------------------
40t = float(np.pi)
41u = Circuit()
42u.h(0)
43u.rz(0, t)
44u.h(0)
45
46n_precision = 3
47qpe = qpe_circuit(n_precision=n_precision, unitary_circuit=u, measure=True)
48task_id = submit_task(circuit=qpe, backend="dummy:local:simulator", shots=4000)
49result = wait_for_result(task_id)
50counts = result.counts
51total = int(sum(counts.values()))
52
53# counts['bs'] positional: bs[i] = q[i]. Precision register at q[1], q[2], q[3]
54# with q[1] = MSB of m.
55phase_bins = np.arange(2 ** n_precision)
56phase_hits = np.zeros(2 ** n_precision, dtype=int)
57for bs, c in counts.items():
58 q1 = int(bs[1]); q2 = int(bs[2]); q3 = int(bs[3])
59 m = (q1 << 2) | (q2 << 1) | q3
60 phase_hits[m] += c
61phase_emp = phase_hits / max(phase_hits.sum(), 1)
62ideal_phases = evals * t / (2 * np.pi)
63
64# --------------------------------------------------------------------------
65# 3) HHL 经典重建:从 QPE 抽取的相位反演本征值 -> 重建 |x> = A^-1|b>
66# --------------------------------------------------------------------------
67# QPE peaks identify eigenphases; mapping phi -> lambda via lambda = 2*pi*phi/t
68# Choose C = 1 (no rescaling). HHL output state:
69# |x> ~ sum_j (C/lambda_j) * <u_j|b> * |u_j>
70# For b = |0>, c_j = <u_j|0>. |x> = A^-1|0> normalized.
71modes = []
72for m in range(2 ** n_precision):
73 phi = m / (2 ** n_precision)
74 lam = 2 * np.pi * phi / t
75 if lam < 1e-6:
76 continue
77 weight = phase_hits[m] / max(total, 1)
78 modes.append((phi, lam, weight))
79
80x_reconstructed = np.zeros(2, dtype=complex)
81C = 1.0
82for phi, lam, w in modes:
83 if w < 1e-9:
84 continue
85 idx = int(np.argmin(np.abs(evals - lam)))
86 u_j = evecs[:, idx]
87 c_j = np.vdot(u_j, b)
88 x_reconstructed += (C / lam) * c_j * u_j
89if np.linalg.norm(x_reconstructed) > 1e-12:
90 x_reconstructed /= np.linalg.norm(x_reconstructed)
91probs_hhl = np.abs(x_reconstructed) ** 2
92
93# --------------------------------------------------------------------------
94# 4) 可视化
95# --------------------------------------------------------------------------
96fig, axes = plt.subplots(1, 3, figsize=(15, 4.5))
97
98ax = axes[0]
99im = ax.imshow(A, cmap="viridis", vmin=0, vmax=A.max())
100for i in range(2):
101 for j in range(2):
102 ax.text(j, i, f"{A[i, j]:.2f}", ha="center", va="center",
103 color="white" if A[i, j] < A.max() / 2 else "black", fontsize=12)
104ax.set_title(f"A = [[1, 0.5], [0.5, 1]]\n"
105 f"eigs = ({evals[0]:.2f}, {evals[1]:.2f}), kappa = {kappa:.2f}")
106ax.set_xticks([0, 1]); ax.set_yticks([0, 1])
107plt.colorbar(im, ax=ax, fraction=0.046)
108
109ax = axes[1]
110ax.bar(phase_bins / (2 ** n_precision), phase_emp,
111 width=1.0 / (2 ** n_precision) * 0.85,
112 color="steelblue", edgecolor="navy")
113for phi in ideal_phases:
114 ax.axvline(phi, color="red", linestyle="--", linewidth=1.5)
115ax.text(0.25, phase_emp.max() * 0.95, "phi=0.25\n(lambda=0.5)",
116 ha="center", fontsize=8, color="red")
117ax.text(0.75, phase_emp.max() * 0.95, "phi=0.75\n(lambda=1.5)",
118 ha="center", fontsize=8, color="red")
119ax.set_xlabel("phase phi")
120ax.set_ylabel("probability")
121ax.set_xticks(np.arange(0, 1.01, 0.25))
122ax.set_title(f"QPE histogram ({n_precision} precision qubits)\n"
123 f"uniqc backend = dummy:local:simulator")
124ax.grid(True, alpha=0.3)
125
126ax = axes[2]
127x_pos = np.arange(2)
128w = 0.32
129ax.bar(x_pos - w / 2, probs_classical, w, label="classical A^-1|b>",
130 color="seagreen", alpha=0.85)
131ax.bar(x_pos + w / 2, probs_hhl, w, label="HHL reconstructed from QPE",
132 color="coral", alpha=0.85)
133ax.set_xticks(x_pos)
134ax.set_xticklabels(["q[0]=0", "q[0]=1"])
135ax.set_ylabel("probability")
136ax.set_ylim(0, 1)
137ax.set_title(f"Solution: P(0)={probs_hhl[0]:.3f}, P(1)={probs_hhl[1]:.3f}\n"
138 f"classical: P(0)={probs_classical[0]:.3f}, P(1)={probs_classical[1]:.3f}")
139ax.legend(fontsize=9, loc="upper right")
140ax.grid(True, alpha=0.3, axis="y")
141
142plt.tight_layout()
143plt.savefig(OUT / "01_hhl_2x2.png", dpi=120, bbox_inches="tight")
144plt.close()
145
146# --------------------------------------------------------------------------
147# 5) stdout 关键统计
148# --------------------------------------------------------------------------
149print("ch06-demo-1: HHL on A=[[1,0.5],[0.5,1]], b=|0>")
150print(f" A eigenvalues: {evals[0]:.3f}, {evals[1]:.3f}")
151print(f" condition number kappa = {kappa:.3f}")
152print(f" QPE shots (uniqc): {total}")
153top = np.argsort(phase_hits)[::-1][:2]
154for m in top:
155 phi = m / (2 ** n_precision)
156 lam = 2 * np.pi * phi / t
157 print(f" phase m={m} (phi={phi:.3f}): {phase_hits[m]} hits "
158 f"(reconstructed lambda={lam:.3f})")
159print(f" HHL reconstructed P(0)={probs_hhl[0]:.4f} vs classical {probs_classical[0]:.4f}")
160print(f" HHL reconstructed P(1)={probs_hhl[1]:.4f} vs classical {probs_classical[1]:.4f}")
运行结果¶

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