Demo 1:Shor 离散对数¶
可视化:双图(轨道+直方图) —— N=15 离散对数骨架 + QPE 相位
对应教程:第 9 章 高等代数与数论量子算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
模型: 给定 N=15 (模合数), a=2, 寻找 x 使 g = a^x mod N. 乘法群 Z_15^* 的阶 phi(15)=8; a=2 在其中的阶为 4, 因为 2^1 = 2, 2^2 = 4, 2^3 = 8, 2^4 = 16 mod 15 = 1. 因此 x 仅在模 4 意义下唯一. Shor 算法的核心是把离散对数 归约到乘法算子 U_a |y> = |a*y mod N> 的周期查找: |QPE 估相位 r / 2^n> -> r = 4 -> x 在轨道 [1, 2, 4, 8] 中的位置.
量子部分用 1 比特 S 门做"简化路径" eigenstate (类似 ch04 demo 1): |1> 是 S 的本征态, 特征值 i = e^{2pii/4}, 用 4-qubit 精度寄存器 跑 QPE 应集中在 phase = 1/4 (即 k=4 of 16). 该相位对应 r = 4. 然后对每个目标 g in {1, 2, 4, 8}, 由 r 与轨道位置反推 x.
完整源码¶
demos/ch09-algebra-number-theory/01_discrete_log.py¶
1"""
2ch09 演示 1: Shor 离散对数小实例 N=15, a=2.
3
4模型: 给定 N=15 (模合数), a=2, 寻找 x 使 g = a^x mod N.
5乘法群 Z_15^* 的阶 phi(15)=8; a=2 在其中的阶为 4, 因为
62^1 = 2, 2^2 = 4, 2^3 = 8, 2^4 = 16 mod 15 = 1.
7因此 x 仅在模 4 意义下唯一. Shor 算法的核心是把离散对数
8归约到乘法算子 U_a |y> = |a*y mod N> 的周期查找:
9|QPE 估相位 r / 2^n> -> r = 4 -> x 在轨道 [1, 2, 4, 8] 中的位置.
10
11量子部分用 1 比特 S 门做"简化路径" eigenstate (类似 ch04 demo 1):
12|1> 是 S 的本征态, 特征值 i = e^{2*pi*i/4}, 用 4-qubit 精度寄存器
13跑 QPE 应集中在 phase = 1/4 (即 k=4 of 16). 该相位对应 r = 4.
14然后对每个目标 g in {1, 2, 4, 8}, 由 r 与轨道位置反推 x.
15
16对应教程: chenzhaoyun.com/quantum-tutorial/ch09-algebra-number-theory/finite-rings-ideals.html
17依赖: pip install unified-quantum[visualization]
18运行: python demos/ch09-algebra-number-theory/01_discrete_log.py
19"""
20from uniqc import Circuit, submit_task, wait_for_result
21from uniqc.algorithms.core.circuits import qpe_circuit
22import matplotlib
23matplotlib.use("Agg")
24import matplotlib.pyplot as plt
25import numpy as np
26from pathlib import Path
27
28OUT = Path(__file__).parent
29SHOTS = 2000
30N_MOD = 15
31A = 2
32N_PREC = 4
33N_STATES = 2 ** N_PREC # 16
34
35
36def orbit_mult(a, n_mod, start, max_steps=12):
37 """返回从 |start> 出发, 应用 U_a (乘 a mod n_mod) 的轨道, 及周期 r.
38 周期定义为轨道中首次出现重复时, 两次出现位置之差."""
39 seen = {}
40 state = start
41 cycle = []
42 period = -1
43 for k in range(max_steps):
44 if state in seen:
45 period = k - seen[state]
46 break
47 seen[state] = k
48 cycle.append(state)
49 state = (a * state) % n_mod
50 return cycle, period
51
52
53def classical_dlog(a, g, n_mod, max_x=20):
54 """经典暴力法: 找 x 使 a^x mod n_mod == g. 返回最小 x."""
55 for x in range(max_x):
56 if pow(a, x, n_mod) == g:
57 return x
58 return -1
59
60
61def dlog_from_orbit(a, g, orbit):
62 """由轨道与目标 g 反推 x (假定 g 在轨道中)."""
63 rev = {val: idx for idx, val in enumerate(orbit)}
64 return rev.get(g, -1)
65
66
67# ---- 1. 经典前置: 算轨道、r、离散对数 ----
68full_cycle, period_r = orbit_mult(A, N_MOD, 1)
69targets = [1, 2, 4, 8] # 轨道上的元素
70known_x = [classical_dlog(A, g, N_MOD) for g in targets]
71
72# ---- 2. uniqc: 1-qubit S gate eigenstate QPE, 模拟周期 4 的相位估计 ----
73unitary = Circuit()
74unitary.s(0)
75prep = Circuit()
76prep.x(0)
77c_qpe = qpe_circuit(
78 n_precision=N_PREC,
79 unitary_circuit=unitary,
80 state_prep=prep,
81 measure=True,
82)
83tid = submit_task(circuit=c_qpe, backend="dummy:local:simulator", shots=SHOTS)
84qpe_result = wait_for_result(tid)
85qpe_counts = qpe_result.counts
86
87
88def precision_from_bitstring(bs, n_prec):
89 """从 QPE 总 bitstring 中取精度寄存器, 转成整数.
90 qpe_circuit 的 bitstring 长度为 n_prec + 1 (含 1 个未测量的 eigenstate qubit,
91 显示在 bitstring 末尾固定为 '0'). 精度寄存器占据前 n_prec 个字符,
92 按 q[1] 为 MSB 的二进制读法 (无需翻转)."""
93 return int(bs[-n_prec:], 2)
94
95
96qpe_dist = np.zeros(N_STATES, dtype=float)
97for bs, cnt in qpe_counts.items():
98 k = precision_from_bitstring(bs, N_PREC)
99 qpe_dist[k] += cnt
100qpe_dist /= qpe_dist.sum()
101
102
103# ---- 3. 由 QPE 直方图推断 r, 然后由 r 反推 x ----
104# QPE 峰位应为 4 (= N_STATES / period_r), period 4.
105# 选 top-1 peak 推断周期
106peak_k = int(np.argmax(qpe_dist))
107estimated_period = N_STATES // peak_k if peak_k > 0 else N_STATES
108# 简化: 用真实周期 4 做反推 (演示算法意图)
109estimated_x = [dlog_from_orbit(A, g, full_cycle) for g in targets]
110
111# ---- 4. matplotlib 可视化: 轨道柱图 + QPE 相位直方图 ----
112fig, axes = plt.subplots(1, 2, figsize=(13.0, 4.6))
113
114ax = axes[0]
115orbit_labels = [f"|{x:04b}>" for x in full_cycle]
116colors = ["#1f77b4"] * len(full_cycle)
117ax.bar(range(len(full_cycle)), [1.0] * len(full_cycle), color=colors, edgecolor="black")
118ax.set_xticks(range(len(full_cycle)))
119ax.set_xticklabels(orbit_labels, fontsize=9)
120for i in range(len(full_cycle)):
121 ax.text(i, 1.02, f"k={i}\nx={i}", ha="center", va="bottom", fontsize=8, color="dimgray")
122ax.set_ylim(0, 1.3)
123ax.set_xlabel("U_a^k applied to |1>")
124ax.set_ylabel("probability")
125ax.set_title(
126 f"Multiplication-by-{A} orbit in Z_{N_MOD}^*\n"
127 f"|1> -> |{full_cycle[1]:04b}> -> |{full_cycle[2]:04b}> -> |{full_cycle[3]:04b}> -> |1> (period r = {period_r})"
128)
129ax.grid(axis="y", alpha=0.3)
130
131ax = axes[1]
132xs = np.arange(N_STATES)
133probs = qpe_dist * 100
134bar_colors = ["#d62728" if (k in (0, 4, 8, 12)) else "#1f77b4" for k in xs]
135ax.bar(xs, probs, color=bar_colors, edgecolor="black")
136ax.set_xticks(xs)
137ax.set_xticklabels([f"{k:04b}" for k in xs], rotation=0, fontsize=7)
138ax.set_xlabel("counting register (4 bits) -> phase = k / 16")
139ax.set_ylabel("probability (%)")
140ax.set_title(
141 f"QPE on eigenstate |1> of S gate (proxy for U_a)\n"
142 f"shots={SHOTS}, peak at k=4 (phase=1/4) => r=4"
143)
144for k in (0, 4, 8, 12):
145 ax.text(k, probs[k] + 1.5, f"k={k}\nphase={k/N_STATES:.3f}",
146 ha="center", va="bottom", fontsize=8, color="darkred")
147ax.set_ylim(0, max(probs.max() * 1.25, 25))
148ax.grid(axis="y", alpha=0.3)
149
150fig.suptitle(
151 f"Shor discrete-log skeleton: N={N_MOD}, a={A}, base order r={period_r} "
152 f"(QPE shots={SHOTS}, dummy:local:simulator)",
153 fontsize=12, y=1.02,
154)
155plt.tight_layout()
156plt.savefig(OUT / "01_discrete_log.png", dpi=120, bbox_inches="tight")
157plt.close()
158
159
160# ---- 5. stdout 关键统计 ----
161peak_probs = qpe_dist
162total_top = peak_probs[peak_k]
163known_str = ", ".join(f"g={g} -> x={known_x[i]}" for i, g in enumerate(targets))
164est_str = ", ".join(f"g={g} -> x={estimated_x[i]}" for i, g in enumerate(targets))
165
166print("ch09-algebra-number-theory demo 1: Shor DLP N=15, a=2")
167print(f" shots: {SHOTS}")
168print(f" multiplicative orbit of |1> under *{A} mod {N_MOD}: {[f'{x:04b}' for x in full_cycle]}")
169print(f" order (period) r = {period_r}")
170print(f" QPE peak counting register: k = {peak_k} (phase = {peak_k/N_STATES:.4f})")
171print(f" QPE peak probability: {total_top*100:.2f}%")
172print(f" phase 1/4 corresponds to r = {N_STATES // 4}")
173print(f" discrete log (classical brute-force): {known_str}")
174print(f" discrete log (estimated from orbit, r={period_r}): {est_str}")
175all_match = all(known_x[i] == estimated_x[i] for i in range(len(targets)))
176print(f" agreement: {all_match}")
177print(f" saved: {OUT / '01_discrete_log.png'}")
运行结果¶

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