Demo 3:Simon 算法¶
可视化: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'}")
运行结果¶

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