Demo 2:模幂 U_7¶
可视化:3 子图 —— 16 输入态全测 + QPE
对应教程:第 9 章 高等代数与数论量子算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
模型: 取 N=15, a=7. U_7 在 Z_15 上的轨道结构 (用于 Shor 周期查找): 固定点: 0, 5, 10, 15 (其中 15 -> 0, 不是真正的"不动"而是溢出折叠). 3 个不相交 4-cycle: (1,7,4,13), (2,14,8,11), (3,6,12,9). 所以周期 r = 4.
量子部分: 因为 uniqc 不支持 add_gate('U', matrix=...), 这里用"单输入目标" 的等价实现: 对每个 x in [0, 15], 构造多受控 X 门, 只在输入为 |x> 时将 diff 位置翻转, 把 |x> 映射到 |7x mod 15>. 控制只用"非 diff"位, 避免互相干扰. 对其它输入态, 电路作用为 identity (按基态正交, 不会污染 |x> 那一支). 16 次独立测量验证全部 a*x mod N 映射.
QPE 部分: 用 1-qubit S 门 eigenstate |1> (与 ch04 demo 1 一致的简化路径), 4-qubit 精度寄存器测相位 1/4 -> r=4.
完整源码¶
demos/ch09-algebra-number-theory/02_modular_exp.py¶
1"""
2ch09 演示 2: Shor 算法的模幂子程序 U_a |x> = |a*x mod N>.
3
4模型: 取 N=15, a=7. U_7 在 Z_15 上的轨道结构 (用于 Shor 周期查找):
5 固定点: 0, 5, 10, 15 (其中 15 -> 0, 不是真正的"不动"而是溢出折叠).
6 3 个不相交 4-cycle: (1,7,4,13), (2,14,8,11), (3,6,12,9).
7 所以周期 r = 4.
8
9量子部分: 因为 uniqc 不支持 add_gate('U', matrix=...), 这里用"单输入目标"
10的等价实现: 对每个 x in [0, 15], 构造多受控 X 门, 只在输入为 |x> 时将 diff
11位置翻转, 把 |x> 映射到 |7x mod 15>. 控制只用"非 diff"位, 避免互相干扰.
12对其它输入态, 电路作用为 identity (按基态正交, 不会污染 |x> 那一支).
1316 次独立测量验证全部 a*x mod N 映射.
14
15QPE 部分: 用 1-qubit S 门 eigenstate |1> (与 ch04 demo 1 一致的简化路径),
164-qubit 精度寄存器测相位 1/4 -> r=4.
17
18对应教程: chenzhaoyun.com/quantum-tutorial/ch04-classic-algorithms/shors-algorithm-tutorial.html
19依赖: pip install unified-quantum[visualization]
20运行: python demos/ch09-algebra-number-theory/02_modular_exp.py
21"""
22from uniqc import Circuit, submit_task, wait_for_result
23from uniqc.algorithms.core.circuits import qpe_circuit
24import matplotlib
25matplotlib.use("Agg")
26import matplotlib.pyplot as plt
27import numpy as np
28from pathlib import Path
29
30OUT = Path(__file__).parent
31SHOTS_PER_X = 60
32N_Q = 4
33N_STATES = 2 ** N_Q
34N_MOD = 15
35A = 7
36N_PREC = 4
37
38
39def u7_map():
40 """返回 16 元素数组 perm, perm[x] = 7x mod 15, 并把 15 折叠到 0."""
41 perm = [(A * x) % N_MOD for x in range(N_STATES)]
42 perm[-1] = 0
43 return perm
44
45
46def orbit(start):
47 seen = {start: 0}
48 state, cycle = start, [start]
49 while True:
50 state = (A * state) % N_MOD
51 if state in seen:
52 return cycle, len(cycle) - seen[state]
53 seen[state] = len(cycle)
54 cycle.append(state)
55
56
57def cx_neg(c, target, controls):
58 """X(target) 受控于 (qubit, value) 列表 (0/1), 用 pre/post-flip 处理 0."""
59 for q, v in controls:
60 if v == 0:
61 c.x(q)
62 c.add_gate('X', qubits=target, control_qubits=[q for q, _ in controls])
63 for q, v in controls:
64 if v == 0:
65 c.x(q)
66
67
68def build_u7_single_target(x_val):
69 """构造对单一输入 |x_val> -> |7x_val mod 15> 的电路. 控制只用非 diff 位."""
70 y_val = (A * x_val) % N_MOD if x_val < 15 else 0
71 xb = [(x_val >> i) & 1 for i in range(N_Q)]
72 yb = [(y_val >> i) & 1 for i in range(N_Q)]
73 diff = [i for i in range(N_Q) if xb[i] != yb[i]]
74 nd = [i for i in range(N_Q) if xb[i] == yb[i]]
75 if not diff:
76 return None
77 c = Circuit()
78 for i in range(N_Q):
79 if xb[i]:
80 c.x(i)
81 for tgt in diff:
82 ctrls = [(j, xb[j]) for j in nd if j != tgt]
83 if ctrls:
84 cx_neg(c, tgt, ctrls)
85 else:
86 c.x(tgt)
87 for q in range(N_Q):
88 c.measure(q)
89 return c
90
91
92def decode(bs):
93 """uniqc bitstring 直接当二进制读即为整数值."""
94 return int(bs[-N_Q:], 2)
95
96
97# ---- 1. 经典前置 ----
98perm = u7_map()
99M = np.zeros((N_STATES, N_STATES), dtype=int)
100for x, y in enumerate(perm):
101 M[y, x] = 1
102cycle1, period_r = orbit(1)
103cycle2, _ = orbit(2)
104cycle3, _ = orbit(3)
105
106# 16 次测量验证 7x mod 15 映射
107measured_y, expected_y = [], []
108for x in range(N_STATES):
109 y_exp = perm[x]
110 expected_y.append(y_exp)
111 if x == y_exp:
112 measured_y.append(y_exp)
113 continue
114 c = build_u7_single_target(x)
115 res = wait_for_result(submit_task(circuit=c, backend="dummy:local:simulator",
116 shots=SHOTS_PER_X))
117 measured_y.append(decode(max(res.counts, key=res.counts.get)))
118
119# ---- 2. QPE on S eigenstate (proxy for r=4) ----
120unitary = Circuit(); unitary.s(0)
121prep = Circuit(); prep.x(0)
122c_qpe = qpe_circuit(n_precision=N_PREC, unitary_circuit=unitary,
123 state_prep=prep, measure=True)
124qpe_counts = wait_for_result(submit_task(circuit=c_qpe, backend="dummy:local:simulator",
125 shots=2000)).counts
126qpe_dist = np.zeros(N_STATES, dtype=float)
127for bs, cnt in qpe_counts.items():
128 qpe_dist[int(bs[-N_PREC:], 2)] += cnt
129qpe_dist /= qpe_dist.sum()
130
131
132# ---- 3. 可视化: 排列热图 + 验证柱图 + QPE 相位直方图 ----
133fig, axes = plt.subplots(1, 3, figsize=(16.5, 4.8))
134labels_n4 = [f"{i:04b}" for i in range(N_STATES)]
135
136ax = axes[0]
137ax.imshow(M, cmap="Blues", vmin=0, vmax=1)
138for i in range(N_STATES):
139 for j in range(N_STATES):
140 if M[i, j] == 1:
141 ax.text(j, i, "1", ha="center", va="center", fontsize=7, color="red")
142ax.set_title(f"U_7 map |x> -> |{A}x mod {N_MOD}> (16x16, 15->0 fold, r={period_r})")
143ax.set_xlabel("input |x>")
144ax.set_ylabel("output |7x mod 15>")
145ax.set_xticks(np.arange(N_STATES))
146ax.set_yticks(np.arange(N_STATES))
147ax.set_xticklabels(labels_n4, rotation=0, fontsize=7)
148ax.set_yticklabels(labels_n4, fontsize=7)
149
150ax = axes[1]
151xs = np.arange(N_STATES); w = 0.4
152agree = sum(1 for i in range(N_STATES) if expected_y[i] == measured_y[i])
153ax.bar(xs - w / 2, expected_y, width=w, color="#1f77b4", edgecolor="black",
154 label=f"expected {A}x mod {N_MOD}")
155ax.bar(xs + w / 2, measured_y, width=w, color="#d62728", edgecolor="black",
156 label=f"measured (shots={SHOTS_PER_X})")
157ax.set_xticks(xs)
158ax.set_xticklabels(labels_n4, rotation=0, fontsize=7)
159ax.set_xlabel("input |x>"); ax.set_ylabel("output index")
160ax.set_title(f"Verification: x -> {A}x mod {N_MOD} for all 16 inputs\nagreement = {agree}/{N_STATES}")
161ax.set_ylim(0, N_STATES); ax.legend(fontsize=8, loc="upper left"); ax.grid(axis="y", alpha=0.3)
162
163ax = axes[2]
164probs = qpe_dist * 100
165ax.bar(np.arange(N_STATES), probs,
166 color=["#d62728" if k in (0, 4, 8, 12) else "#1f77b4" for k in range(N_STATES)],
167 edgecolor="black")
168ax.set_xticks(np.arange(N_STATES))
169ax.set_xticklabels([f"{k:04b}" for k in range(N_STATES)], rotation=0, fontsize=7)
170ax.set_xlabel("counting register (k) -> phase = k / 16"); ax.set_ylabel("probability (%)")
171ax.set_title("QPE on eigenstate |1> of S gate (period-4 proxy)\npeak at k=4 (phase 1/4) confirms r = 4")
172for k in (0, 4, 8, 12):
173 ax.text(k, probs[k] + 1.5, f"k={k}\nphase={k/N_STATES:.3f}",
174 ha="center", va="bottom", fontsize=7, color="darkred")
175ax.set_ylim(0, max(probs.max() * 1.25, 30))
176ax.grid(axis="y", alpha=0.3)
177fig.suptitle(f"Modular exponentiation U_{A} on Z_{N_MOD}: "
178 f"permutation matrix + measured mapping + QPE phase histogram",
179 fontsize=12, y=1.02)
180plt.tight_layout()
181plt.savefig(OUT / "02_modular_exp.png", dpi=120, bbox_inches="tight")
182plt.close()
183
184
185# ---- 4. stdout 关键统计 ----
186peak_k = int(np.argmax(qpe_dist))
187print("ch09-algebra-number-theory demo 2: modular exponentiation U_7 on N=15")
188print(f" per-x shots: {SHOTS_PER_X} (16 runs total = {16 * SHOTS_PER_X} shots)")
189print(f" orbit structure:")
190print(f" cycle 1: {[f'{x:04b}' for x in cycle1]} (r = {period_r})")
191print(f" cycle 2: {[f'{x:04b}' for x in cycle2]}")
192print(f" cycle 3: {[f'{x:04b}' for x in cycle3]}")
193print(f" agreement (16 inputs): {agree}/{N_STATES}")
194disc = [(i, measured_y[i], expected_y[i])
195 for i in range(N_STATES) if measured_y[i] != expected_y[i]]
196print(f" discrepancies: {disc}")
197print(f" QPE peak: k = {peak_k} (phase = {peak_k/N_STATES:.4f})")
198print(f" QPE peak probability: {qpe_dist[peak_k]*100:.2f}%")
199print(f" saved: {OUT / '02_modular_exp.png'}")
运行结果¶

运行方式¶
# 仓库根目录执行;输出 PNG 到 demos/ch09-algebra-number-theory/02_modular_exp.png,关键统计到 stdout
.venv/bin/python3 demos/ch09-algebra-number-theory/02_modular_exp.py