Demo 3:Grover 密码破解¶
可视化:单子图 —— 8-qubit 搜索 P=0b11010110
对应教程:第 16 章 译码与量子密码分析 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
核心:Grover 算法把 N=2^K 个候选密钥上的穷搜加速到 O(√N) 次迭代。本 demo 演示一个 toy 8-bit 密码 P = 0b11010110 = 214: 密码空间 N = 2^8 = 256; 完整 Grover 最优迭代 ⌊π/4 · √N⌋ = ⌊π/4 · 16⌋ ≈ 12; 本 demo 跑 0..4 次迭代展示 P(correct) 从均匀 1/256 ≈ 0.004 单调上升 到接近 1。每一步 Grover 包含 phase oracle(标记 |P⟩)和 diffusion operator(绕平均振幅反射)。
完整源码¶
demos/ch16-cryptanalysis-coding/03_grover_code_crack.py¶
1"""
2ch16-cryptanalysis-coding demo 3: Grover's algorithm brute-forcing an 8-bit password
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch16-cryptanalysis-coding/quantum-cryptanalysis.html
5核心:Grover 算法把 N=2^K 个候选密钥上的穷搜加速到 O(√N) 次迭代。本 demo
6 演示一个 toy 8-bit 密码 P = 0b11010110 = 214:
7 密码空间 N = 2^8 = 256;
8 完整 Grover 最优迭代 ⌊π/4 · √N⌋ = ⌊π/4 · 16⌋ ≈ 12;
9 本 demo 跑 0..4 次迭代展示 P(correct) 从均匀 1/256 ≈ 0.004 单调上升
10 到接近 1。每一步 Grover 包含 phase oracle(标记 |P⟩)和 diffusion
11 operator(绕平均振幅反射)。
12
13依赖:pip install unified-quantum[visualization]
14运行:python demos/ch16-cryptanalysis-coding/03_grover_code_crack.py
15"""
16from __future__ import annotations
17
18from pathlib import Path
19
20import matplotlib
21matplotlib.use("Agg")
22import matplotlib.pyplot as plt
23import numpy as np
24
25from uniqc import Circuit, submit_task, wait_for_result
26
27OUT = Path(__file__).parent
28PASSWORD_INT = 0b11010110 # = 214
29PASSWORD_BIN = format(PASSWORD_INT, "08b")
30N_QUBITS = 8
31SHOTS = 1500
32MAX_ITERATIONS = 5 # demo: 0..4 iterations
33
34
35def password_bits(p_int: int, n_bits: int) -> list[int]:
36 """Return little-endian bit list (q0 = LSB)."""
37 return [(p_int >> i) & 1 for i in range(n_bits)]
38
39
40P_BITS_LE = password_bits(PASSWORD_INT, N_QUBITS) # little-endian (q0 first)
41
42
43def phase_oracle(c: Circuit) -> None:
44 """Apply -|P> (phase flip on the marked state |P>)."""
45 # 1. flip bits that are 0 in P so the marked state is |11111111>
46 for q, bit in enumerate(P_BITS_LE):
47 if bit == 0:
48 c.x(q)
49 # 2. multi-controlled Z on |11111111>: H(q_last); MCX; H(q_last)
50 c.h(N_QUBITS - 1)
51 c.add_gate("X", qubits=[N_QUBITS - 1],
52 control_qubits=list(range(N_QUBITS - 1)))
53 c.h(N_QUBITS - 1)
54 # 3. uncompute the X flips
55 for q, bit in enumerate(P_BITS_LE):
56 if bit == 0:
57 c.x(q)
58
59
60def diffusion(c: Circuit) -> None:
61 """Standard Grover diffusion operator: 2|s><s| - I on n-qubit register."""
62 for q in range(N_QUBITS):
63 c.h(q)
64 c.x(q)
65 c.h(N_QUBITS - 1)
66 c.add_gate("X", qubits=[N_QUBITS - 1],
67 control_qubits=list(range(N_QUBITS - 1)))
68 c.h(N_QUBITS - 1)
69 for q in range(N_QUBITS):
70 c.x(q)
71 c.h(q)
72
73
74def grover_circuit(num_iterations: int) -> Circuit:
75 """Build the Grover search circuit with k phase-oracle + diffusion rounds."""
76 c = Circuit()
77 # 1. uniform superposition
78 for q in range(N_QUBITS):
79 c.h(q)
80 # 2. k Grover iterations
81 for _ in range(num_iterations):
82 phase_oracle(c)
83 diffusion(c)
84 # 3. measure
85 for q in range(N_QUBITS):
86 c.measure(q)
87 return c
88
89
90# === 1. Run Grover at iteration counts 0..MAX_ITERATIONS-1 ===
91print("ch16-cryptanalysis-coding-demo-3: Grover for 8-bit password P = 0b"
92 f"{PASSWORD_BIN} = {PASSWORD_INT}")
93print(f" password space: 2^{N_QUBITS} = {1 << N_QUBITS}")
94print(f" shots per circuit: {SHOTS}")
95print(f" iteration counts tested: 0..{MAX_ITERATIONS - 1}")
96
97probs_at_target = [] # P(P) at each iteration count
98for it in range(MAX_ITERATIONS):
99 c = grover_circuit(it)
100 r = wait_for_result(
101 submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS)
102 )
103 counts = r.counts
104 total = sum(int(v) for v in counts.values())
105 p_correct = counts.get(PASSWORD_BIN, 0) / max(total, 1)
106 probs_at_target.append(p_correct)
107 n_unique = len(counts)
108 print(f" iterations={it}: P(P=0b{PASSWORD_BIN}) = {p_correct:.3f}, "
109 f"unique outcomes = {n_unique}")
110
111
112# === 2. matplotlib visualisation: P(P) vs iteration count + per-iteration distribution ===
113fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13.0, 5.0))
114
115# Left: P(P) vs iteration count
116iter_axis = np.arange(MAX_ITERATIONS)
117uniform_prob = 1.0 / (1 << N_QUBITS)
118ax1.plot(iter_axis, probs_at_target, "o-", color="#1f77b4", lw=2.2, ms=10,
119 label=r"measured $P(\mathrm{correct})$")
120ax1.axhline(uniform_prob, color="gray", ls=":", lw=1.2,
121 label=fr"uniform $1/2^{N_QUBITS} \approx {uniform_prob:.4f}$")
122# Theoretical Grover after k steps: sin^2((2k+1) * arcsin(1/sqrt(N)))
123N = 1 << N_QUBITS
124theory_iter = np.linspace(0, MAX_ITERATIONS - 1, 200)
125theory_probs = [np.sin((2 * k + 1) * np.arcsin(1.0 / np.sqrt(N))) ** 2
126 for k in theory_iter]
127ax1.plot(theory_iter, theory_probs, "--", color="#d62728", lw=1.4,
128 label=r"theory $\sin^2((2k+1)\arcsin(1/\sqrt{N}))$")
129opt_iter = int(np.floor(np.pi / 4 * np.sqrt(N)))
130ax1.axvline(opt_iter, color="#2ca02c", ls=":", lw=1.0, alpha=0.7,
131 label=fr"optimal $k^*=\lfloor\pi\sqrt{{N}}/4\rfloor={opt_iter}$")
132ax1.set_xticks(iter_axis)
133ax1.set_xticklabels([f"{k}" for k in iter_axis])
134ax1.set_xlabel("Grover iteration count k")
135ax1.set_ylabel(r"$P(\mathrm{measuring } k = P)$")
136ax1.set_ylim(0, 1.05)
137ax1.set_title("Grover amplification of correct password\n"
138 f"P = 0b{PASSWORD_BIN} ({PASSWORD_INT}), N = 2^{N_QUBITS}")
139ax1.grid(alpha=0.3)
140ax1.legend(loc="upper left", fontsize=9)
141
142# Right: outcome distribution at iteration MAX_ITERATIONS-1 (most informative)
143last_circuit = grover_circuit(MAX_ITERATIONS - 1)
144r_last = wait_for_result(
145 submit_task(circuit=last_circuit, backend="dummy:local:simulator", shots=SHOTS)
146)
147counts_last = r_last.counts
148total_last = sum(int(v) for v in counts_last.values())
149top_outcomes = sorted(counts_last.items(), key=lambda kv: -kv[1])[:12]
150top_labels = [bs for bs, _ in top_outcomes]
151top_probs = [cnt / total_last for _, cnt in top_outcomes]
152colors = ["#d62728" if bs == PASSWORD_BIN else "#1f77b4" for bs in top_labels]
153bars = ax2.barh(range(len(top_labels))[::-1], top_probs, color=colors, ec="black", lw=0.5)
154for i, p in enumerate(top_probs):
155 ax2.text(p + 0.005, len(top_labels) - 1 - i,
156 f"{p:.3f}", va="center", fontsize=9)
157ax2.set_yticks(range(len(top_labels))[::-1])
158ax2.set_yticklabels(top_labels, fontsize=9, family="monospace")
159ax2.axvline(uniform_prob, color="gray", ls=":", lw=1.0,
160 label=fr"uniform $1/2^{N_QUBITS} \approx {uniform_prob:.4f}$")
161ax2.set_xlabel("probability")
162ax2.set_xlim(0, max(top_probs) * 1.18)
163ax2.set_title(f"top outcomes after {MAX_ITERATIONS - 1} Grover iterations\n"
164 f"(red bar = password 0b{PASSWORD_BIN})")
165ax2.grid(alpha=0.3, axis="x")
166ax2.legend(loc="lower right", fontsize=9)
167
168plt.suptitle(f"ch16-cryptanalysis-coding demo 3: Grover for 8-bit password "
169 f"P = 0b{PASSWORD_BIN}", y=1.02)
170plt.tight_layout()
171out_path = OUT / "03_grover_code_crack.png"
172plt.savefig(out_path, dpi=120, bbox_inches="tight")
173plt.close()
174
175
176# === 3. stdout key statistics ===
177print(f"\n P(P) growth with Grover iterations:")
178for k, p in enumerate(probs_at_target):
179 ratio = p / uniform_prob if uniform_prob > 0 else float('inf')
180 print(f" k={k}: P(P) = {p:.3f}, amplification ratio over uniform = {ratio:.1f}x")
181print(f" theoretical max amplification (k*={opt_iter}): "
182 f"P_max ~ {np.sin((2*opt_iter+1)*np.arcsin(1/np.sqrt(N)))**2:.3f}")
183print(f" uniform P = 1/{1 << N_QUBITS} = {uniform_prob:.4f}")
184print(f" total shots across all circuits: {SHOTS * MAX_ITERATIONS}")
185print(f" PNG -> {out_path}")
运行结果¶

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