Demo 1:HHL 2×2

第 6 章 量子科学计算 · 本章演示 · 代码演示总索引

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

运行结果

Demo 1:HHL 2×2

运行方式

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

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