Demo 2:块编码

第 5 章 哈密顿量模拟与 QSP · 本章演示 · 代码演示总索引

可视化:3 子图(U_A+A+A/α+测量) —— 2×2 厄米矩阵 A 的 4-qubit 块编码 U_A 验证

对应教程:第 5 章 哈密顿量模拟与 QSP 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"

实现要点

模型: A = [[1, 0.5], [0.5, 1]] (Hermitian, 对称正定) 操作数范数: alpha = ||A||op = 1.5, 归一化后 A/alpha = [[2/3, 1/3], [1/3, 2/3]] 特征值 (A/alpha): 1, 1/3; 特征向量 (descending): v+ = (1, 1)/sqrt(2), v_- = (1, -1)/sqrt(2) 特征基旋转: U_eig = H (按 descending eigvec 列排列)

uniqc 不支持 add_gate('U', matrix=...) 自定义矩阵入口. 采用「数学构造 + 物理电路 + get_matrix 校验」三步法:

  1. 用 numpy 显式构造 4x4 块编码酉阵 U_A = (U_eig ⊗ I_a) · U_D · (U_eig^dag ⊗ I_a), 其中 U_D 是 multiplexed Ry 块编码 diag(cos theta_0, cos theta_1), theta_s = arccos(eigval_s).

  2. 用 H (本征基旋转) + CR_y (对角块编码) 分解得到电路.

  3. 比对 c.get_matrix() 与理论 U_A; 在模拟器上验证 P(ancilla=0|s) = |(A/alpha)_{s,s}|^2.

完整源码

demos/ch05-hamiltonian-qsp/02_block_encoding_hermitian.py
  1"""
  2ch05 演示 2: 2x2 厄米矩阵的块编码 (Block Encoding) 演示.
  3
  4模型: A = [[1, 0.5], [0.5, 1]] (Hermitian, 对称正定)
  5操作数范数: alpha = ||A||_op = 1.5, 归一化后 A/alpha = [[2/3, 1/3], [1/3, 2/3]]
  6特征值 (A/alpha): 1, 1/3; 特征向量 (descending): v_+ = (1, 1)/sqrt(2), v_- = (1, -1)/sqrt(2)
  7特征基旋转: U_eig = H (按 descending eigvec 列排列)
  8
  9uniqc 不支持 add_gate('U', matrix=...) 自定义矩阵入口.
 10采用「数学构造 + 物理电路 + get_matrix 校验」三步法:
 11  1. 用 numpy 显式构造 4x4 块编码酉阵 U_A = (U_eig ⊗ I_a) · U_D · (U_eig^dag ⊗ I_a),
 12     其中 U_D 是 multiplexed Ry 块编码 diag(cos theta_0, cos theta_1), theta_s = arccos(eigval_s).
 13  2. 用 H (本征基旋转) + CR_y (对角块编码) 分解得到电路.
 14  3. 比对 c.get_matrix() 与理论 U_A; 在模拟器上验证 P(ancilla=0|s) = |(A/alpha)_{s,s}|^2.
 15
 16对应教程: chenzhaoyun.com/quantum-tutorial/ch05-hamiltonian-qsp/block-encoding-tutorial.html
 17依赖: pip install unified-quantum[visualization]
 18运行: python demos/ch05-hamiltonian-qsp/02_block_encoding_hermitian.py
 19"""
 20from uniqc import Circuit, submit_task, wait_for_result
 21import matplotlib
 22matplotlib.use("Agg")
 23import matplotlib.pyplot as plt
 24import numpy as np
 25from pathlib import Path
 26
 27OUT = Path(__file__).parent
 28SHOTS = 4000
 29
 30A = np.array([[1.0, 0.5], [0.5, 1.0]], dtype=complex)
 31ALPHA = float(np.linalg.norm(A, ord=2))
 32A_NORM = A / ALPHA
 33
 34eigvals_N, eigvecs_N = np.linalg.eigh(A_NORM)
 35order = np.argsort(eigvals_N)[::-1]
 36EIGVALS = eigvals_N[order]
 37U_EIG = eigvecs_N[:, order]
 38THETA = np.array([float(np.arccos(max(-1.0, min(1.0, v)))) for v in EIGVALS])
 39
 40N_A = 2
 41DIM = 4
 42I2 = np.eye(2, dtype=complex)
 43
 44
 45def build_block_encoding_unitary() -> np.ndarray:
 46    """Construct 4x4 U_A explicitly via eigenbasis-rotation + multiplexed-Ry.
 47
 48    Returns a 4x4 matrix matching uniqc's convention: q[0] is the LSB and
 49    the inner (right) tensor factor; q[1] is the MSB and the outer (left) factor.
 50    The block-encoding upper-left 2x2 block (projecting q[0]=q_a=0) equals A/alpha.
 51    """
 52    U_D = np.zeros((DIM, DIM), dtype=complex)
 53    for s in range(N_A):
 54        c, si = np.cos(THETA[s]), np.sin(THETA[s])
 55        ry_block = np.array([[c, -si], [si, c]], dtype=complex)
 56        base = 2 * s
 57        U_D[base:base + 2, base:base + 2] = ry_block
 58    left = np.kron(U_EIG, I2)
 59    right = np.kron(U_EIG.conj().T, I2)
 60    return left @ U_D @ right
 61
 62
 63U_A_theory = build_block_encoding_unitary()
 64upper_left_theory = U_A_theory[::2, ::2]
 65
 66
 67def build_block_encoding_circuit() -> Circuit:
 68    """Eigenbasis rotation H + multiplexed-Ry on q_a=LSB controlled by q_s=MSB.
 69
 70    Note: c.h(1) is the Hadamard gate (different from c.ry(1, pi/2) which is R_y(pi/2)).
 71    """
 72    c = Circuit()
 73    c.h(1)
 74    c.ry(0, 2 * THETA[0])
 75    c.ry(0, THETA[1] - THETA[0])
 76    c.cnot(1, 0)
 77    c.ry(0, -(THETA[1] - THETA[0]))
 78    c.cnot(1, 0)
 79    c.h(1)
 80    return c
 81
 82
 83c_block = build_block_encoding_circuit()
 84U_A_circuit = c_block.get_matrix()
 85upper_left_circuit = U_A_circuit[::2, ::2]
 86match_err = float(np.linalg.norm(U_A_circuit - U_A_theory, ord="fro"))
 87be_err = float(np.linalg.norm(upper_left_circuit - A_NORM, ord="fro"))
 88
 89
 90def measure_block_encoding(input_q_s: int) -> dict:
 91    """Run block-encoding circuit initialized with |input_q_s⟩ and return
 92    joint probability table over (q_s, q_a). LE bitstring 'ab' has a=q_s, b=q_a.
 93    """
 94    c = build_block_encoding_circuit()
 95    if input_q_s == 1:
 96        c.x(1)
 97    c.measure(0)
 98    c.measure(1)
 99    res = wait_for_result(submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS))
100    counts = res.counts
101    joint = {(s, a): 0.0 for s in range(2) for a in range(2)}
102    for bs, cnt in counts.items():
103        q_s_val = int(bs[0])
104        q_a_val = int(bs[1])
105        joint[(q_s_val, q_a_val)] = cnt / SHOTS
106    return joint
107
108
109joint_s0 = measure_block_encoding(0)
110joint_s1 = measure_block_encoding(1)
111p0_s0 = joint_s0[(0, 0)] + joint_s0[(1, 0)]
112p0_s1 = joint_s1[(0, 0)] + joint_s1[(1, 0)]
113theory_p0_s0_row = [float(abs(A_NORM[s, 0]) ** 2) for s in range(2)]
114theory_p0_s1_row = [float(abs(A_NORM[s, 1]) ** 2) for s in range(2)]
115
116
117fig, axes = plt.subplots(1, 3, figsize=(16.0, 5.2))
118
119ax = axes[0]
120vmax = float(np.max(np.abs(U_A_theory)))
121im = ax.imshow(np.abs(U_A_theory), cmap="viridis", vmin=0, vmax=vmax)
122ax.set_title(f"Block-encoding U_A (4x4)\nupper-left 2x2 = A/alpha  (alpha = {ALPHA:.3f})")
123ax.set_xticks(range(4))
124ax.set_yticks(range(4))
125labels_idx = ["|00>", "|01>", "|10>", "|11>"]
126ax.set_xticklabels(labels_idx, fontsize=10)
127ax.set_yticklabels(labels_idx, fontsize=10)
128ax.set_xlabel("col index (LE: q_a q_s)")
129ax.set_ylabel("row index (LE: q_a q_s)")
130for i in range(4):
131    for j in range(4):
132        v = U_A_theory[i, j]
133        if abs(v) > 0.05:
134            ax.text(j, i, f"{abs(v):.2f}", ha="center", va="center",
135                    fontsize=8, color="white" if abs(v) > 0.4 else "black")
136plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
137
138ax = axes[1]
139panels = [
140    ("A\n(original 2x2)", np.abs(A)),
141    ("A/alpha\n(normalized)", np.abs(A_NORM)),
142    ("circuit block\n(q_a=0)", np.abs(upper_left_circuit)),
143    ("theory block\n(q_a=0)", np.abs(upper_left_theory)),
144]
145combined = np.zeros((2, 2 * len(panels)), dtype=float)
146for k, (_, mat) in enumerate(panels):
147    combined[:, 2 * k:2 * (k + 1)] = mat
148im2 = ax.imshow(combined, cmap="Blues", vmin=0, vmax=1, aspect="auto")
149ax.set_title("2x2 blocks compared (right two should match A/alpha)")
150ax.set_yticks([0, 1])
151ax.set_yticklabels(["row 0", "row 1"], fontsize=9)
152ax.set_xticks([0.5 + 2 * k for k in range(len(panels))])
153ax.set_xticklabels([p[0] for p in panels], fontsize=8)
154for k, (_, mat) in enumerate(panels):
155    for i in range(2):
156        for j in range(2):
157            v = mat[i, j]
158            if v > 0.01:
159                ax.text(2 * k + j, i, f"{v:.3f}", ha="center", va="center",
160                        fontsize=8, color="white" if v > 0.5 else "black")
161plt.colorbar(im2, ax=ax, fraction=0.046, pad=0.04)
162
163ax = axes[2]
164system_states = ["|0_s>", "|1_s>"]
165x = np.arange(2)
166w = 0.18
167for j, q_s_target in enumerate([0, 1]):
168    measured_anc0 = [joint_s0[(q_s_target, 0)], joint_s1[(q_s_target, 0)]]
169    theory_anc0 = [theory_p0_s0_row[q_s_target], theory_p0_s1_row[q_s_target]]
170    offset = (j - 0.5) * w
171    ax.bar(x + offset, measured_anc0, width=w * 0.9,
172           color="#1f77b4" if j == 0 else "#aec7e8",
173           edgecolor="black", label=f"measured q_s={q_s_target}")
174    ax.bar(x + offset + w, theory_anc0, width=w * 0.9,
175           color="#d62728" if j == 0 else "#ff9896",
176           edgecolor="black", label=f"theory |(A/alpha)[:, {q_s_target}]|^2" if j == 0 else None)
177    for i, (pm, pt) in enumerate(zip(measured_anc0, theory_anc0)):
178        ax.text(x[i] + offset, pm + 0.012, f"{pm:.3f}", ha="center", va="bottom",
179                fontsize=8, color="darkblue")
180        ax.text(x[i] + offset + w, pt + 0.012, f"{pt:.3f}", ha="center", va="bottom",
181                fontsize=8, color="darkred")
182ax.set_xticks(x)
183ax.set_xticklabels(system_states)
184ax.set_xlabel("input system state |s_s>")
185ax.set_ylabel("P(ancilla = 0 AND q_s = s')")
186ax.set_title(
187    "Block-encoding: P(anc=0, q_s=s' | sys=s) should equal |(A/alpha)_{s', s}|^2\n"
188    "(two columns per system: q_s=0 outcome first, then q_s=1)"
189)
190ax.set_ylim(0, 0.55)
191ax.legend(loc="upper right", fontsize=7, ncol=2)
192ax.grid(axis="y", alpha=0.3)
193
194fig.suptitle(
195    f"Block encoding of A = [[1, 0.5], [0.5, 1]]  "
196    f"(alpha = {ALPHA:.3f}, theta = [{THETA[0]:.3f}, {THETA[1]:.3f}])",
197    fontsize=13, y=1.02,
198)
199plt.tight_layout()
200plt.savefig(OUT / "02_block_encoding_hermitian.png", dpi=120, bbox_inches="tight")
201plt.close()
202
203
204unit_err = float(np.linalg.norm(U_A_circuit @ U_A_circuit.conj().T - np.eye(DIM)))
205print("ch05-hamiltonian-qsp demo 2: Block encoding of 2x2 Hermitian")
206print(f"  A = [[1, 0.5], [0.5, 1]],  alpha = ||A||_op = {ALPHA:.6f}")
207print(f"  A/alpha =\n    {A_NORM}")
208print(f"  normalized eigenvalues (descending): {EIGVALS.tolist()}")
209print(f"  theta_0 = {THETA[0]:.6f}, theta_1 = {THETA[1]:.6f}")
210print(f"  ||circuit U_A - theory U_A||_F  = {match_err:.2e}")
211print(f"  ||circuit upper-left block - A/alpha||_F  = {be_err:.2e}")
212print(f"  ||U_A U_A^dag - I||_F (unitarity)  = {unit_err:.2e}")
213print(f"  measured P(anc=0, q_s=0 | sys=0_s) = {joint_s0[(0, 0)]:.4f}  (theory |A/alpha[0,0]|^2 = {theory_p0_s0_row[0]:.4f})")
214print(f"  measured P(anc=0, q_s=1 | sys=0_s) = {joint_s0[(1, 0)]:.4f}  (theory |A/alpha[1,0]|^2 = {theory_p0_s0_row[1]:.4f})")
215print(f"  measured P(anc=0, q_s=0 | sys=1_s) = {joint_s1[(0, 0)]:.4f}  (theory |A/alpha[0,1]|^2 = {theory_p0_s1_row[0]:.4f})")
216print(f"  measured P(anc=0, q_s=1 | sys=1_s) = {joint_s1[(1, 0)]:.4f}  (theory |A/alpha[1,1]|^2 = {theory_p0_s1_row[1]:.4f})")
217print(f"  saved: {OUT / '02_block_encoding_hermitian.png'}")

运行结果

Demo 2:块编码

运行方式

# 仓库根目录执行;输出 PNG 到 demos/ch05-hamiltonian-qsp/02_block_encoding_hermitian.png,关键统计到 stdout
.venv/bin/python3 demos/ch05-hamiltonian-qsp/02_block_encoding_hermitian.py

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