Demo 3:CRT 恢复

第 9 章 高等代数与数论量子算法 · 本章演示 · 代码演示总索引

可视化:3 子图(子群轨道+CRT 流程) —— Z_3^* + Z_5^* 拆解后合并

对应教程:第 9 章 高等代数与数论量子算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"

实现要点

模型: N=15 = 3 * 5, a=2, 目标 g=8. Shor DLP 在 N 为合数时, 可拆成在 p_i = 3, 5 上的子 DLP (量子周期查找), 然后用 CRT 拼回 x (mod ord_N(a)). 这里: Z_3^: a=2, ord_3(2)=2, 轨道 1 -> 2 -> 1; 8 mod 3 = 2, 故 x_1 = 1. Z_5^: a=2, ord_5(2)=4, 轨道 1 -> 2 -> 4 -> 3 -> 1; 8 mod 5 = 3, 故 x_2 = 3. CRT (n_1=2, n_2=4): x = 3 满足 x mod 2 = 1, x mod 4 = 3, 与期望 x=3 一致.

量子部分用 1-qubit S/T 门做"简化路径" eigenstate QPE, 对应各子群的阶: ord_3(2) = 2 -> S eigenstate phase 1/4 (对应 r=4 in S gate, 或换 T 适配 r=2) ord_5(2) = 4 -> S eigenstate phase 1/4.

完整源码

demos/ch09-algebra-number-theory/03_crt_recovery.py
  1"""
  2ch09 演示 3: 用中国剩余定理 (CRT) 拼接各素数子群上的 Shor 离散对数结果.
  3
  4模型: N=15 = 3 * 5, a=2, 目标 g=8.
  5Shor DLP 在 N 为合数时, 可拆成在 p_i = 3, 5 上的子 DLP (量子周期查找),
  6然后用 CRT 拼回 x (mod ord_N(a)). 这里:
  7  Z_3^*: a=2, ord_3(2)=2, 轨道 1 -> 2 -> 1; 8 mod 3 = 2, 故 x_1 = 1.
  8  Z_5^*: a=2, ord_5(2)=4, 轨道 1 -> 2 -> 4 -> 3 -> 1; 8 mod 5 = 3, 故 x_2 = 3.
  9  CRT (n_1=2, n_2=4): x = 3 满足 x mod 2 = 1, x mod 4 = 3, 与期望 x=3 一致.
 10
 11量子部分用 1-qubit S/T 门做"简化路径" eigenstate QPE, 对应各子群的阶:
 12  ord_3(2) = 2  -> S eigenstate phase 1/4 (对应 r=4 in S gate, 或换 T 适配 r=2)
 13  ord_5(2) = 4  -> S eigenstate phase 1/4.
 14
 15对应教程: chenzhaoyun.com/quantum-tutorial/ch09-algebra-number-theory/finite-rings-ideals.html
 16依赖: pip install unified-quantum[visualization]
 17运行: python demos/ch09-algebra-number-theory/03_crt_recovery.py
 18"""
 19from uniqc import Circuit, submit_task, wait_for_result
 20from uniqc.algorithms.core.circuits import qpe_circuit
 21import matplotlib
 22matplotlib.use("Agg")
 23import matplotlib.pyplot as plt
 24import numpy as np
 25from pathlib import Path
 26
 27OUT = Path(__file__).parent
 28SHOTS = 2000
 29N_PREC = 4
 30
 31
 32def orbit_mult(a, n_mod, start, max_steps=20):
 33    seen = {start: 0}
 34    state = start
 35    cycle = [state]
 36    while True:
 37        state = (a * state) % n_mod
 38        if state in seen:
 39            break
 40        seen[state] = len(cycle)
 41        cycle.append(state)
 42    return cycle, len(cycle) - seen[state]
 43
 44
 45def classical_dlog(a, g, n_mod):
 46    """返回最小 x 使 a^x ≡ g (mod n_mod), 找不到返回 -1."""
 47    for x in range(n_mod):
 48        if pow(a, x, n_mod) == g % n_mod:
 49            return x
 50    return -1
 51
 52
 53def crt_pair(x1, n1, x2, n2):
 54    """对两个未必互素的模 n_1, n_2, 解 x ≡ x_1 (mod n_1), x ≡ x_2 (mod n_2).
 55    返回 (x, n) 使 x 是该线性同余方程在模 n 下的唯一解.
 56    若 x_1, x_2 不一致 (mod gcd(n_1, n_2)) 则返回 None."""
 57    from math import gcd
 58    g = gcd(n1, n2)
 59    if (x1 - x2) % g != 0:
 60        return None
 61    n = n1 * n2 // g
 62    # 用扩展欧几里得合并
 63    def egcd(a, b):
 64        if b == 0:
 65            return (a, 1, 0)
 66        d, u, v = egcd(b, a % b)
 67        return (d, v, u - (a // b) * v)
 68    _, u1, _ = egcd(n1, n2 // g)
 69    x = (x1 * (n2 // g) * u1 + x2 * (n1 // g)) % n
 70    return x, n
 71
 72
 73# ---- 1. 经典前置 ----
 74N = 15
 75A = 2
 76G = 8  # 目标: 找 x 使 2^x ≡ 8 (mod 15)
 77P1, P2 = 3, 5
 78
 79ord_3 = 2
 80ord_5 = 4
 81cycle_p1, r_p1 = orbit_mult(A, P1, 1)
 82cycle_p2, r_p2 = orbit_mult(A, P2, 1)
 83
 84g1 = G % P1  # 8 mod 3 = 2
 85g2 = G % P2  # 8 mod 5 = 3
 86x1 = classical_dlog(A, g1, P1)  # log_2(2) mod 3 → 1
 87x2 = classical_dlog(A, g2, P2)  # log_2(3) mod 5 → 3
 88
 89crt_res = crt_pair(x1, ord_3, x2, ord_5)
 90x_crt, mod_crt = crt_res  # x = 3 (mod lcm(2, 4)=4)
 91
 92# 验证
 93x_true = classical_dlog(A, G, N)  # log_2(8) mod 15 → 3
 94
 95
 96# ---- 2. QPE on S eigenstate for both subgroups (period proxy) ----
 97def run_qpe_phase(unitary, prep, n_prec=N_PREC, shots=SHOTS):
 98    c = qpe_circuit(n_precision=n_prec, unitary_circuit=unitary,
 99                     state_prep=prep, measure=True)
100    tid = submit_task(circuit=c, backend="dummy:local:simulator", shots=shots)
101    res = wait_for_result(tid)
102    counts = res.counts
103    n_states = 2 ** n_prec
104    dist = np.zeros(n_states, dtype=float)
105    for bs, cnt in counts.items():
106        # uniqc qpe bitstring length = n_prec + 1 (末位是未测量 eigenstate),
107        # 末 n_prec 字符直接读为二进制整数.
108        k = int(bs[-n_prec:], 2)
109        dist[k] += cnt
110    dist /= dist.sum()
111    return dist
112
113
114unitary_s = Circuit()
115unitary_s.s(0)
116prep_eigen = Circuit()
117prep_eigen.x(0)
118phase_dist = run_qpe_phase(unitary_s, prep_eigen, n_prec=N_PREC, shots=SHOTS)
119peak_k = int(np.argmax(phase_dist))
120peak_phase = peak_k / (2 ** N_PREC)
121
122
123# ---- 3. matplotlib 可视化: 两个子群轨道 + QPE + CRT 流程 ----
124fig, axes = plt.subplots(1, 3, figsize=(16.5, 4.8))
125
126ax = axes[0]
127labels_p1 = [f"|{x:02b}>" for x in cycle_p1]
128ax.bar(range(len(cycle_p1)), [1.0] * len(cycle_p1), color="#1f77b4", edgecolor="black")
129ax.set_xticks(range(len(cycle_p1)))
130ax.set_xticklabels(labels_p1, fontsize=10)
131for i in range(len(cycle_p1)):
132    ax.text(i, 1.02, f"k={i}", ha="center", va="bottom", fontsize=9, color="dimgray")
133ax.set_ylim(0, 1.3)
134ax.set_xlabel(f"U_a^k |1> in Z_{P1}^* (a={A})")
135ax.set_ylabel("probability")
136ax.set_title(
137    f"Subgroup Z_{P1}^*: order r={r_p1}\n"
138    f"g mod {P1} = {g1},  log_{A}({g1}) = x_{P1} = {x1}"
139)
140ax.grid(axis="y", alpha=0.3)
141
142ax = axes[1]
143labels_p2 = [f"|{x:03b}>" for x in cycle_p2]
144ax.bar(range(len(cycle_p2)), [1.0] * len(cycle_p2), color="#1f77b4", edgecolor="black")
145ax.set_xticks(range(len(cycle_p2)))
146ax.set_xticklabels(labels_p2, fontsize=10)
147for i in range(len(cycle_p2)):
148    ax.text(i, 1.02, f"k={i}\nx={i}", ha="center", va="bottom", fontsize=8, color="dimgray")
149ax.set_ylim(0, 1.4)
150ax.set_xlabel(f"U_a^k |1> in Z_{P2}^* (a={A})")
151ax.set_ylabel("probability")
152ax.set_title(
153    f"Subgroup Z_{P2}^*: order r={r_p2}\n"
154    f"g mod {P2} = {g2},  log_{A}({g2}) = x_{P2} = {x2}"
155)
156ax.grid(axis="y", alpha=0.3)
157
158ax = axes[2]
159xs = np.arange(2 ** N_PREC)
160probs = phase_dist * 100
161bar_colors = ["#d62728" if k in (0, 4, 8, 12) else "#1f77b4" for k in xs]
162ax.bar(xs, probs, color=bar_colors, edgecolor="black")
163ax.set_xticks(xs)
164ax.set_xticklabels([f"{k:04b}" for k in xs], rotation=0, fontsize=7)
165ax.set_xlabel("counting register (k)  ->  phase = k / 16")
166ax.set_ylabel("probability (%)")
167ax.set_title(
168    f"QPE on S eigenstate |1>  (proxy for r=4)\n"
169    f"peak k={peak_k}, phase={peak_phase:.3f} -> r ~ 4 (matches ord_{P2})"
170)
171for k in (0, 4, 8, 12):
172    ax.text(k, probs[k] + 1.5, f"k={k}\nphase={k/(2**N_PREC):.3f}",
173            ha="center", va="bottom", fontsize=7, color="darkred")
174ax.set_ylim(0, max(probs.max() * 1.25, 30))
175ax.grid(axis="y", alpha=0.3)
176
177fig.suptitle(
178    f"CRT recovery of discrete log:  N={N}={P1}*{P2}, a={A}, g={G}  "
179    f"(QPE shots={SHOTS})",
180    fontsize=12, y=1.02,
181)
182plt.tight_layout()
183plt.savefig(OUT / "03_crt_recovery.png", dpi=120, bbox_inches="tight")
184plt.close()
185
186
187# ---- 4. stdout 关键统计 ----
188print("ch09-algebra-number-theory demo 3: CRT recovery of Shor DLP")
189print(f"  N = {N} = {P1} * {P2},  base a = {A},  target g = {G}")
190print(f"  subgroup Z_{P1}^*: order r1 = {r_p1},  g mod {P1} = {g1},  x_{P1} = {x1}")
191print(f"  subgroup Z_{P2}^*: order r2 = {r_p2},  g mod {P2} = {g2},  x_{P2} = {x2}")
192print(f"  CRT (x ≡ {x1} mod {r_p1}, x ≡ {x2} mod {r_p2}): x = {x_crt}  (mod {mod_crt})")
193print(f"  true x by brute force (mod {r_p1 * r_p2}): {x_true}")
194print(f"  agreement (CRT vs classical brute-force): {x_crt == x_true}")
195print(f"  verify: {A}^{x_crt} mod {N} = {pow(A, x_crt, N)}  (target g = {G})")
196print(f"  QPE phase histogram peak: k = {peak_k}  (phase = {peak_phase:.4f})")
197print(f"  QPE peak probability: {phase_dist[peak_k]*100:.2f}%")
198print(f"  saved: {OUT / '03_crt_recovery.png'}")

运行结果

Demo 3:CRT 恢复

运行方式

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

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