Demo 3:隐藏位移¶
可视化:双图 —— 7-qubit van Dam 量子算法 + 经典/量子查询数对比
对应教程:第 10 章 隐藏结构与群算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
问题(Z_2^3 上的隐藏位移):f_0(x)=x, f_1(x)=x ⊕ s,s=101 未知。 Bernstein-Hadamard(van Dam):
准备 |+⟩_sel ⊗ |+⟩^3_data ⊗ |0⟩^3_anc
受控查询 anc ⊕= data ⊕ (sel · s)
H on sel + H^⊗3 on data → 测得 (y, b) with b = y·s (mod 2)
收集 n 个样本 + F_2 高斯消元恢复 s
经典查询复杂度 Θ(2^{n/2})(生日碰撞) vs 量子 O(n)。
完整源码¶
demos/ch10-hidden-structures/03_hidden_shift.py¶
1"""
2ch10 演示 3:隐藏位移(hidden shift)的 Bernstein-Hadamard 量子算法
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch10-hidden-structures/hidden-shift.html
5依赖:pip install unified-quantum[visualization]
6运行:python demos/ch10-hidden-structures/03_hidden_shift.py
7
8问题(Z_2^3 上的隐藏位移):f_0(x)=x, f_1(x)=x ⊕ s,s=101 未知。
9Bernstein-Hadamard(van Dam):
10 1. 准备 |+⟩_sel ⊗ |+⟩^3_data ⊗ |0⟩^3_anc
11 2. 受控查询 anc ⊕= data ⊕ (sel · s)
12 3. H on sel + H^⊗3 on data → 测得 (y, b) with b = y·s (mod 2)
13 4. 收集 n 个样本 + F_2 高斯消元恢复 s
14
15经典查询复杂度 Θ(2^{n/2})(生日碰撞) vs 量子 O(n)。
16"""
17from uniqc import Circuit, submit_task, wait_for_result
18import matplotlib
19matplotlib.use("Agg")
20import matplotlib.pyplot as plt
21import numpy as np
22from pathlib import Path
23
24OUT = Path(__file__).parent
25N = 3
26S = 0b101 # 隐藏位移 s = (1, 0, 1)
27SHOTS = 2000
28N_RUNS = 8
29
30
31def build_hs_circuit(s: int, n: int) -> Circuit:
32 """anc[j] = data[j] XOR (sel AND s[j]) via 2-step CNOT 序列。"""
33 c = Circuit()
34 sel, data, anc = 0, list(range(1, n + 1)), list(range(n + 1, 2 * n + 1))
35 c.h(sel)
36 for q in data:
37 c.h(q)
38 for j in range(n):
39 c.cnot(data[j], anc[j]) # anc[j] ^= data[j]
40 for j in range(n):
41 if (s >> j) & 1:
42 c.cnot(sel, anc[j]) # anc[j] ^= sel
43 c.h(sel)
44 for q in data:
45 c.h(q)
46 c.measure(sel)
47 for q in data:
48 c.measure(q)
49 return c
50
51
52def gauss_elim_f2(matrix: np.ndarray) -> np.ndarray:
53 """F_2 高斯消元,返回增广矩阵的行最简形。"""
54 A = matrix.copy().astype(np.uint8)
55 n_rows, n_cols = A.shape
56 pivot_row = 0
57 for col in range(n_cols):
58 pr = -1
59 for r in range(pivot_row, n_rows):
60 if A[r, col] == 1:
61 pr = r
62 break
63 if pr == -1:
64 continue
65 A[[pivot_row, pr]] = A[[pr, pivot_row]]
66 for r in range(n_rows):
67 if r != pivot_row and A[r, col] == 1:
68 A[r] ^= A[pivot_row]
69 pivot_row += 1
70 return A
71
72
73def recover_s_from_samples(samples_y, samples_b, n):
74 """从 {(y_i, b_i)} 用 F_2 高斯消元恢复 s,满足 y_i · s = b_i。"""
75 if len(samples_y) == 0:
76 return 0
77 rows = [[(y >> i) & 1 for i in range(n)] + [int(b)]
78 for y, b in zip(samples_y, samples_b)]
79 rref = gauss_elim_f2(np.array(rows, dtype=np.uint8))
80 pivot_rows, non_pivot_cols = {}, []
81 for col in range(n):
82 for r in range(rref.shape[0]):
83 if rref[r, col] == 1 and all(rref[r, cc] == 0 for cc in range(col)):
84 pivot_rows[col] = r
85 break
86 else:
87 non_pivot_cols.append(col)
88 s_vec = np.zeros(n, dtype=np.uint8)
89 if not non_pivot_cols:
90 for pc, pr in pivot_rows.items():
91 s_vec[pc] = rref[pr, n]
92 else:
93 fc = non_pivot_cols[-1]
94 s_vec[fc] = 1
95 for pc, pr in pivot_rows.items():
96 s_vec[pc] = (rref[pr, fc] + rref[pr, n]) % 2
97 return sum(int(s_vec[i]) << i for i in range(n))
98
99
100# === 1. 跑 N_RUNS 次电路,收集 (y, b) 样本 ===
101circuit = build_hs_circuit(S, N)
102print(f"circuit qubits: {circuit.qubit_num}, depth: {circuit.depth}")
103
104samples_y, samples_b = [], []
105rng = np.random.default_rng(seed=2024)
106for run in range(N_RUNS):
107 counts = wait_for_result(
108 submit_task(circuit=circuit, backend="dummy:local:simulator", shots=SHOTS)
109 ).counts
110 # 7 qubits: key = [q6..q0]; sel=key[-1], data(q3,q2,q1)=key[3:6]
111 joint = {}
112 for bs, cnt in counts.items():
113 key = (bs[3:6], int(bs[-1]))
114 joint[key] = joint.get(key, 0) + cnt
115 max_cnt = max(joint.values())
116 chosen = rng.choice([k for k, v in joint.items() if v == max_cnt])
117 samples_y.append(int(chosen[0], 2))
118 samples_b.append(int(chosen[1]))
119
120samples_y = np.array(samples_y, dtype=np.uint8)
121samples_b = np.array(samples_b, dtype=np.uint8)
122s_recovered = recover_s_from_samples(samples_y, samples_b, N)
123
124# === 2. matplotlib 可视化(2 子图:测量散点 + 复杂度对比) ===
125ns = list(range(3, 14))
126classical_q = [2 ** (n / 2) for n in ns]
127quantum_q = [n / 0.288 for n in ns]
128
129fig, axes = plt.subplots(1, 2, figsize=(13, 5))
130
131# 左图:测得的 (y, b) 样本与真实约束
132ax = axes[0]
133y_unique = np.arange(2 ** N)
134ax.scatter(samples_y, samples_b, s=120, c="#1f77b4", edgecolor="black", zorder=3,
135 label=f"measured (y, b), {N_RUNS} runs")
136for y in y_unique:
137 b_true = bin(y & S).count("1") % 2
138 ax.scatter(y, b_true, s=30, c="#d62728", marker="x", alpha=0.5,
139 zorder=2, label="true constraint b = y·s" if y == 0 else None)
140ax.set_xticks(y_unique)
141ax.set_xticklabels([f"{y:0{N}b}" for y in y_unique], fontsize=9)
142ax.set_yticks([0, 1])
143ax.set_xlabel("Data register y")
144ax.set_ylabel("Selection bit b")
145ax.set_title(
146 f"Van Dam Bernstein-Hadamard for hidden shift\n"
147 f"s = {S:0{N}b} → recovered s = {s_recovered:0{N}b} (match = {s_recovered == S})",
148 fontsize=10,
149)
150ax.set_ylim(-0.4, 1.4)
151ax.grid(axis="y", alpha=0.3)
152ax.legend(loc="upper right", fontsize=9)
153
154# 右图:经典 vs 量子查询复杂度
155ax = axes[1]
156ax.semilogy(ns, classical_q, "o-", color="#d62728", lw=2, label=r"Classical: $\Theta(2^{n/2})$")
157ax.semilogy(ns, quantum_q, "s-", color="#1f77b4", lw=2, label=r"Quantum: $O(n)$ queries")
158ax.scatter([N], [2 ** (N / 2)], s=180, color="#d62728", marker="*", edgecolor="black",
159 zorder=4, label=f"n={N} (demo)")
160ax.scatter([N], [quantum_q[0]], s=180, color="#1f77b4", marker="*",
161 edgecolor="black", zorder=4)
162ax.set_xlabel("Hidden shift length n")
163ax.set_ylabel("Oracle queries (log scale)")
164ax.set_title(
165 "Exponential separation:\nclassical birthday collision vs quantum Bernstein-Hadamard",
166 fontsize=10,
167)
168ax.grid(True, which="both", alpha=0.3)
169ax.legend(loc="upper left", fontsize=9)
170
171fig.suptitle(
172 f"Hidden shift problem on Z_2^{N}: f_0(x)=x, f_1(x)=x ⊕ {S:0{N}b} "
173 f"({N_RUNS} circuit runs × {SHOTS} shots, dummy:local:simulator)",
174 fontsize=11, y=1.02,
175)
176plt.tight_layout()
177plt.savefig(OUT / "03_hidden_shift.png", dpi=120, bbox_inches="tight")
178plt.close()
179
180# === 3. stdout 关键统计 ===
181print("ch10-hidden-structures demo 3: Hidden shift (van Dam Bernstein-Hadamard)")
182print(f" hidden shift s = {S:0{N}b} (binary)")
183print(f" samples collected ({N_RUNS} runs):")
184for i, (y, b) in enumerate(zip(samples_y, samples_b)):
185 print(f" run {i}: y={y:0{N}b} ({y}), b={b} → y·s = {(y & S).bit_count() % 2} (should = {b})")
186print(f" recovered s = {s_recovered:0{N}b} (true s = {S:0{N}b}, match = {s_recovered == S})")
187print(f" classical queries for n=3: 2^(n/2) ≈ {2**(N/2):.2f}; quantum: n = {N}")
188print(f" saved: {OUT / '03_hidden_shift.png'}")
运行结果¶

运行方式¶
# 仓库根目录执行;输出 PNG 到 demos/ch10-hidden-structures/03_hidden_shift.png,关键统计到 stdout
.venv/bin/python3 demos/ch10-hidden-structures/03_hidden_shift.py