Demo 3:隐藏位移

第 10 章 隐藏结构与群算法 · 本章演示 · 代码演示总索引

可视化:双图 —— 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):

  1. 准备 |+⟩_sel ⊗ |+⟩^3_data ⊗ |0⟩^3_anc

  2. 受控查询 anc ⊕= data ⊕ (sel · s)

  3. H on sel + H^⊗3 on data → 测得 (y, b) with b = y·s (mod 2)

  4. 收集 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'}")

运行结果

Demo 3:隐藏位移

运行方式

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

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