Demo 3:Grover 密码破解

第 16 章 译码与量子密码分析 · 本章演示 · 代码演示总索引

可视化:单子图 —— 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}")

运行结果

Demo 3:Grover 密码破解

运行方式

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

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