Demo 3:Simon 算法

第 4 章 经典量子算法 · 本章演示 · 代码演示总索引

可视化:3 子图 —— 3-qubit 隐藏 s=110 经典高斯消元恢复

对应教程:第 4 章 经典量子算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"

实现要点

Oracle 选取 f(x) = f(x XOR s), 满足: f(000)=f(110)=0, f(001)=f(111)=1, f(010)=f(100)=2, f(011)=f(101)=3. 构造此 oracle 仅需 3 个 CNOT (具体推导见教程), 1 次查询就能从 输入寄存器的量子 Fourier 采样得到线性方程 y . s = 0 (mod 2). 经典需要 O(n . 2^n) 次查询, Simon 算法只需 O(n) 次即可解出 s.

完整源码

demos/ch04-classic-algorithms/03_simon_3qubit.py
  1"""
  2ch04 演示 3: Simon 算法, n=3, 隐藏字符串 s=110.
  3Oracle 选取 f(x) = f(x XOR s), 满足:
  4  f(000)=f(110)=0, f(001)=f(111)=1,
  5  f(010)=f(100)=2, f(011)=f(101)=3.
  6构造此 oracle 仅需 3 个 CNOT (具体推导见教程), 1 次查询就能从
  7输入寄存器的量子 Fourier 采样得到线性方程 y . s = 0 (mod 2).
  8经典需要 O(n . 2^n) 次查询, Simon 算法只需 O(n) 次即可解出 s.
  9
 10对应教程: chenzhaoyun.com/quantum-tutorial/ch04-classic-algorithms/simons-algorithm tutorial.html
 11依赖: pip install unified-quantum[visualization]
 12运行: python demos/ch04-classic-algorithms/03_simon_3qubit.py
 13"""
 14from uniqc import Circuit, submit_task, wait_for_result
 15import matplotlib
 16matplotlib.use("Agg")
 17import matplotlib.pyplot as plt
 18import numpy as np
 19from pathlib import Path
 20
 21OUT = Path(__file__).parent
 22SHOTS = 2000
 23N = 3
 24S_BITSTRING = "110"  # 隐串, bitstring[0]=MSB=q[2], bitstring[1]=q[1], bitstring[2]=LSB=q[0]
 25S_INT = int(S_BITSTRING, 2)  # = 6
 26
 27
 28def build_simon_circuit():
 29    """构造完整 Simon 电路: H on input, oracle (3 CNOT),
 30    H on input, 测量 input 与 output."""
 31    c = Circuit()
 32    # H on input register (qubits 0..N-1)
 33    for q in range(N):
 34        c.h(q)
 35    # Oracle U_f for f defined above (see docstring), 仅 3 个 CNOT:
 36    #   f_bit_0(x) = x_0  -> CNOT(q[0], q[3])
 37    #   f_bit_1(x) = x_1 XOR x_2  -> CNOT(q[1], q[4]); CNOT(q[2], q[4])
 38    c.cnot(0, N)
 39    c.cnot(1, N + 1)
 40    c.cnot(2, N + 1)
 41    # H on input
 42    for q in range(N):
 43        c.h(q)
 44    # 测量 input 与 output
 45    for q in range(2 * N):
 46        c.measure(q)
 47    return c
 48
 49
 50c = build_simon_circuit()
 51tid = submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS)
 52result = wait_for_result(tid)
 53counts = result.counts
 54
 55# uniqc bitstring[i] = qubit[n-1-i], 整根 bitstring 长度 = 6 (3 input + 3 output).
 56def split_input_output(bs):
 57    """bitstring 长度 6, 前 3 位 = output (q[5], q[4], q[3]), 后 3 位 = input (q[2], q[1], q[0])."""
 58    inp = bs[-N:][::-1]  # 末 3 位 -> qubit 顺序 q[0] q[1] q[2]
 59    out = bs[:N][::-1]   # 前 3 位 -> qubit 顺序 q[3] q[4] q[5]
 60    return inp, out
 61
 62
 63total = sum(counts.values())
 64input_counts = {}
 65output_counts = {}
 66joint_counts = {}
 67for bs, cnt in counts.items():
 68    inp, out = split_input_output(bs)
 69    input_counts[inp] = input_counts.get(inp, 0) + cnt
 70    output_counts[out] = output_counts.get(out, 0) + cnt
 71    joint_counts[(inp, out)] = joint_counts.get((inp, out), 0) + cnt
 72
 73
 74input_labels = [f"{i:0{N}b}" for i in range(2 ** N)]
 75input_vals = np.array([input_counts.get(lbl, 0) for lbl in input_labels])
 76
 77# 检查 y . s = 0 (mod 2): 对 s_bitstring = "110", 条件是 y_1 XOR y_2 = 0 (即 y_1 = y_2).
 78def satisfies_simons(y_bitstring):
 79    """y_bitstring 长度 3, y_bitstring[0]=q[2]=MSB, y_bitstring[2]=q[0]=LSB.
 80    s=110 -> y_1 + y_2 = 0 (mod 2), 即 y[1] == y[2]."""
 81    return (int(y_bitstring[1]) + int(y_bitstring[2])) % 2 == 0
 82
 83
 84consistent = sum(cnt for y, cnt in input_counts.items() if satisfies_simons(y))
 85inconsistent = total - consistent
 86forbidden_labels = [lbl for lbl in input_labels if not satisfies_simons(lbl)]
 87
 88
 89fig, axes = plt.subplots(1, 3, figsize=(15.5, 5.0))
 90
 91ax = axes[0]
 92colors = [
 93    "#2ca02c" if satisfies_simons(lbl) else "#d62728"
 94    for lbl in input_labels
 95]
 96ax.bar(np.arange(len(input_labels)), input_vals / total * 100,
 97       color=colors, edgecolor="black")
 98ax.set_xticks(np.arange(len(input_labels)))
 99ax.set_xticklabels(input_labels, rotation=0, fontsize=10)
100ax.set_xlabel("input register  y  (q[2] q[1] q[0])")
101ax.set_ylabel("probability (%)")
102ax.set_title(
103    f"Simon measurement on input register\n"
104    f"green = y . s = 0  red = forbidden  (s = {S_BITSTRING})"
105)
106for i, lbl in enumerate(input_labels):
107    if input_vals[i] > 0:
108        tag = "ok" if satisfies_simons(lbl) else "bad"
109        ax.text(i, input_vals[i] / total * 100 + 1.2,
110                f"{input_vals[i]/total*100:.1f}%\n[{tag}]",
111                ha="center", va="bottom", fontsize=7,
112                color=("darkgreen" if tag == "ok" else "darkred"))
113ax.set_ylim(0, max(60, input_vals.max() / total * 100 + 15))
114ax.grid(axis="y", alpha=0.3)
115
116ax = axes[1]
117out_labels = [f"{i:0{N}b}" for i in range(2 ** N)]
118out_vals = np.array([output_counts.get(lbl, 0) for lbl in out_labels])
119ax.bar(np.arange(len(out_labels)), out_vals / total * 100,
120       color="#9467bd", edgecolor="black")
121ax.set_xticks(np.arange(len(out_labels)))
122ax.set_xticklabels(out_labels, rotation=0, fontsize=10)
123ax.set_xlabel("output register  f(y)  (q[3] q[4] q[5])")
124ax.set_ylabel("probability (%)")
125ax.set_title(
126    f"Simon measurement on output (f(y))\n"
127    f"each output value appears twice (from y and y XOR s)"
128)
129for i, lbl in enumerate(out_labels):
130    if out_vals[i] > 0:
131        ax.text(i, out_vals[i] / total * 100 + 1.2,
132                f"{out_vals[i]/total*100:.1f}%",
133                ha="center", va="bottom", fontsize=8)
134ax.set_ylim(0, max(60, out_vals.max() / total * 100 + 15))
135ax.grid(axis="y", alpha=0.3)
136
137ax = axes[2]
138s_bits = [int(b) for b in S_BITSTRING]
139xs = np.array([0, 1, 2])
140ax.bar(xs, s_bits, color="#1f77b4", edgecolor="black")
141ax.set_xticks(xs)
142ax.set_xticklabels([f"s_{i}  (q[{N-1-i}])" for i in range(N)])
143ax.set_yticks([0, 1])
144ax.set_yticklabels(["0", "1"])
145ax.set_ylabel("secret bit value")
146ax.set_title(
147    f"Hidden string s = {S_BITSTRING} (= {S_INT})\n"
148    f"y · s = 0 (mod 2)  iff  y[1] = y[2]"
149)
150for i in range(N):
151    ax.text(i, s_bits[i] + 0.05, str(s_bits[i]),
152            ha="center", va="bottom", fontsize=12)
153ax.set_ylim(0, 1.4)
154ax.grid(axis="y", alpha=0.3)
155
156fig.suptitle(
157    f"Simon's algorithm  n={N}, s={S_BITSTRING}  "
158    f"(shots={SHOTS}, dummy:local:simulator)",
159    fontsize=12, y=1.02,
160)
161plt.subplots_adjust(left=0.06, right=0.99, top=0.84, bottom=0.14)
162plt.savefig(OUT / "03_simon_3qubit.png", dpi=120, bbox_inches="tight")
163plt.close()
164
165
166p_consistent = consistent / total
167print("ch04-classic-algorithms demo 3: Simon n=3, s=110")
168print(f"  shots: {total}")
169print(f"  hidden string: s = {S_BITSTRING} (= {S_INT})")
170print(f"  P(measured y satisfies y . s = 0): {p_consistent:.4f}")
171print(f"  unique input measurement outcomes: {int((input_vals > 0).sum())} of 8")
172print(f"  unique output measurement outcomes: {int((out_vals > 0).sum())} of 8")
173print(f"  forbidden labels (y . s = 1): {forbidden_labels}")
174bad = {lbl: cnt for lbl, cnt in input_counts.items() if not satisfies_simons(lbl)}
175if bad:
176    print(f"  forbidden shots observed: {bad}")
177else:
178    print("  no forbidden y observed: all shots satisfy y . s = 0")
179print(f"  classical Gaussian elimination on 2 linearly independent y's would recover s")
180print(f"  saved: {OUT / '03_simon_3qubit.png'}")

运行结果

Demo 3:Simon 算法

运行方式

# 仓库根目录执行;输出 PNG 到 demos/ch04-classic-algorithms/03_simon_3qubit.png,关键统计到 stdout
.venv/bin/python3 demos/ch04-classic-algorithms/03_simon_3qubit.py

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