Demo 1:碰撞 BHT

第 11 章 查询复杂度 · 本章演示 · 代码演示总索引

可视化:双图 —— N=8 标记命中 + log-log 查询数对比

对应教程:第 11 章 查询复杂度 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"

实现要点

核心:在 N=8 的二对一承诺函数 f=[0,0,1,1,2,2,3,3] 上演示 Brassard-Hoyer-Tapp 抽样表 + Grover 算法的 r = N^(1/3) 平衡点。画三幅图: (左) hash 函数结构 + 抽样表 + "另一半"原像标记 (中) 经典生日攻击 sqrt(N) vs 量子 BHT N^(1/3) 查询数 log-log 对比 (右) N=8 实际跑 Grover: 命中 marked 状态的概率分布

完整源码

demos/ch11-query-complexity/01_collision_bht.py
  1"""
  2ch11 演示 1:碰撞问题查询复杂度对比 + BHT 量子算法小实例
  3
  4对应教程:chenzhaoyun.com/quantum-tutorial/ch11-query-complexity/collision-element-distinctness.html
  5核心:在 N=8 的二对一承诺函数 f=[0,0,1,1,2,2,3,3] 上演示 Brassard-Hoyer-Tapp
  6     抽样表 + Grover 算法的 r = N^(1/3) 平衡点。画三幅图:
  7       (左) hash 函数结构 + 抽样表 + "另一半"原像标记
  8       (中) 经典生日攻击 sqrt(N) vs 量子 BHT N^(1/3) 查询数 log-log 对比
  9       (右) N=8 实际跑 Grover: 命中 marked 状态的概率分布
 10
 11依赖:pip install unified-quantum[visualization]
 12运行:python demos/ch11-query-complexity/01_collision_bht.py
 13"""
 14from pathlib import Path
 15
 16import matplotlib
 17matplotlib.use("Agg")
 18import matplotlib.pyplot as plt
 19import numpy as np
 20
 21from uniqc import Circuit, submit_task, wait_for_result
 22
 23OUT = Path(__file__).parent
 24SHOTS = 2000
 25N_DOMAIN = 8
 26HASH_TABLE = np.array([0, 0, 1, 1, 2, 2, 3, 3], dtype=int)
 27
 28
 29def mark_state(c: Circuit, ms: int, n_qubits: int = 3) -> None:
 30    """对 n-qubit 寄存器的 |ms> 加相位翻转 (ms 用 little-endian 二进制)."""
 31    bits = [(ms >> (n_qubits - 1 - b)) & 1 for b in range(n_qubits)]
 32    for q in range(n_qubits):
 33        if bits[q] == 0:
 34            c.x(q)
 35    c.h(n_qubits - 1)
 36    c.toffoli(0, 1, n_qubits - 1)
 37    c.h(n_qubits - 1)
 38    for q in range(n_qubits):
 39        if bits[q] == 0:
 40            c.x(q)
 41
 42
 43def diffusion(c: Circuit, n_qubits: int = 3) -> None:
 44    for q in range(n_qubits):
 45        c.h(q); c.x(q)
 46    c.h(n_qubits - 1)
 47    c.toffoli(0, 1, n_qubits - 1)
 48    c.h(n_qubits - 1)
 49    for q in range(n_qubits):
 50        c.x(q); c.h(q)
 51
 52
 53def find_partners(sample: list[int]) -> list[int]:
 54    """给定抽样 index, 返回表外 '另一半' 原像 (sorted, dedup)."""
 55    s_set = set(sample)
 56    marked = []
 57    for i in sample:
 58        partner = int(np.where(HASH_TABLE == HASH_TABLE[i])[0][
 59            np.where(HASH_TABLE == HASH_TABLE[i])[0] != i][0])
 60        if partner not in s_set:
 61            marked.append(partner)
 62    return sorted(set(marked))
 63
 64
 65def run_grover(marked: list[int], iters: int = 2) -> dict:
 66    c = Circuit()
 67    for q in range(3):
 68        c.h(q)
 69    for _ in range(iters):
 70        for ms in marked:
 71            mark_state(c, ms)
 72        diffusion(c)
 73    for q in range(3):
 74        c.measure(q)
 75    r = wait_for_result(submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS))
 76    return dict(r.counts)
 77
 78
 79print("ch11-query-complexity demo 1: collision problem + BHT")
 80print(f"  domain N = {N_DOMAIN},  hash f = {HASH_TABLE.tolist()} (two-to-one),  shots = {SHOTS}")
 81
 82# === 复杂度对比表 ===
 83N_LIST = [8, 27, 64, 216, 1000, 8000, 27000, 100000, 1000000]
 84classical_queries = [int(np.ceil(np.sqrt(2 * np.log(2) * n))) for n in N_LIST]
 85quantum_queries = [2 * int(round(n ** (1.0 / 3.0))) for n in N_LIST]
 86logN = np.log10(N_LIST)
 87logC, logQ = np.log10(classical_queries), np.log10(quantum_queries)
 88
 89# === N=8 上跑实际 Grover ===
 90sample_indices = [0, 2]
 91sample_outputs = [int(HASH_TABLE[i]) for i in sample_indices]
 92marked_states = find_partners(sample_indices)
 93grover_counts = run_grover(marked_states, iters=2)
 94total = sum(grover_counts.values())
 95mark_total = sum(v for k, v in grover_counts.items() if int(k, 2) in marked_states)
 96n_labels = [f"{i:03b}" for i in range(N_DOMAIN)]
 97grover_probs = np.array([grover_counts.get(lbl, 0) / total * 100 for lbl in n_labels])
 98
 99# === 画图 ===
100fig, axes = plt.subplots(1, 3, figsize=(16.0, 5.0))
101partner_map = {i: [j for j in range(N_DOMAIN) if j != i and HASH_TABLE[j] == HASH_TABLE[i]][0]
102               for i in range(N_DOMAIN)}
103
104# (左) hash 结构
105ax = axes[0]
106colors = ["#1f77b4" if i not in sample_indices else "#ffd700" for i in range(N_DOMAIN)]
107bars = ax.bar(np.arange(N_DOMAIN), HASH_TABLE, color=colors, edgecolor="black")
108for i, b in enumerate(bars):
109    star = "*" if i in sample_indices else ""
110    ax.text(b.get_x() + b.get_width() / 2, b.get_height() + 0.06,
111            f"f({i})={HASH_TABLE[i]}\npart={partner_map[i]}{star}",
112            ha="center", va="bottom", fontsize=7)
113ax.set_xticks(np.arange(N_DOMAIN))
114ax.set_xticklabels([f"in {i}" for i in range(N_DOMAIN)], fontsize=8)
115ax.set_xlabel("input index")
116ax.set_ylabel("hash value")
117ax.set_title(f"two-to-one hash f  (gold = sampled)\n"
118             f"sample = {sample_indices}  outputs = {sample_outputs}")
119ax.set_ylim(0, HASH_TABLE.max() + 0.6)
120ax.grid(axis="y", alpha=0.3)
121
122# (中) 经典 vs 量子 log-log
123ax = axes[1]
124ax.plot(logN, logC, "o-", color="#d62728", lw=2, ms=7, label=r"classical birthday $O(\sqrt{N})$")
125ax.plot(logN, logQ, "s-", color="#1f77b4", lw=2, ms=7, label=r"quantum BHT $O(N^{1/3})$")
126ref = np.array([logN[0], logN[-1]])
127ax.plot(ref, 0.5 * ref + logC[0] - 0.5 * logN[0], "--", color="#d62728", alpha=0.4, label="slope 1/2")
128ax.plot(ref, (1.0 / 3.0) * ref + logQ[0] - (1.0 / 3.0) * logN[0], "--", color="#1f77b4", alpha=0.4, label="slope 1/3")
129for x, yc, yq in zip(logN, logC, logQ):
130    ax.annotate(f"{10**yc:.0f}", (x, yc), textcoords="offset points", xytext=(0, 6),
131                ha="center", fontsize=6.5, color="#d62728")
132    ax.annotate(f"{10**yq:.0f}", (x, yq), textcoords="offset points", xytext=(0, -12),
133                ha="center", fontsize=6.5, color="#1f77b4")
134ax.set_xlabel(r"$\log_{10} N$")
135ax.set_ylabel(r"$\log_{10}$ # queries")
136ax.set_title("query complexity: classical vs quantum\n"
137             r"$Q_{\mathrm{cl}} = O(\sqrt{N})$  vs  $Q_{\mathrm{qu}} = O(N^{1/3})$")
138ax.legend(loc="upper left", fontsize=8)
139ax.grid(alpha=0.3)
140
141# (右) Grover 分布
142ax = axes[2]
143colors = ["#2ca02c" if i in marked_states else "#9467bd" for i in range(N_DOMAIN)]
144bars = ax.bar(np.arange(N_DOMAIN), grover_probs, color=colors, edgecolor="black")
145for i, b in enumerate(bars):
146    if grover_probs[i] > 0.5:
147        ax.text(b.get_x() + b.get_width() / 2, b.get_height() + 1.0,
148                f"{grover_probs[i]:.1f}%", ha="center", va="bottom", fontsize=8, fontweight="bold")
149ax.set_xticks(np.arange(N_DOMAIN))
150ax.set_xticklabels(n_labels, fontsize=9)
151ax.axhline(100.0 / N_DOMAIN, color="gray", ls=":", lw=1.5, label=f"uniform {100/N_DOMAIN:.1f}%")
152ax.set_xlabel("Grover measurement |xyz>")
153ax.set_ylabel("probability (%)")
154ax.set_title(f"BHT Grover (sampled r={len(sample_indices)}, "
155             f"marked = {marked_states})\n"
156             fr"$P(\mathrm{{mark}}) = {mark_total/total*100:.1f}$%")
157ax.legend(loc="upper right", fontsize=8)
158ax.set_ylim(0, max(50, grover_probs.max() + 8))
159ax.grid(axis="y", alpha=0.3)
160
161fig.suptitle("ch11 demo 1: collision problem — BHT sampling + Grover beats classical birthday",
162             y=1.01, fontsize=12)
163plt.tight_layout()
164out_path = OUT / "01_collision_bht.png"
165plt.savefig(out_path, dpi=120, bbox_inches="tight")
166plt.close()
167
168print(f"  sample table: {sample_indices} -> f-values {sample_outputs}")
169print(f"  marked 'other halves' (Grover targets): {marked_states}")
170print(f"  Grover P(marked) measured = {mark_total/total:.3f} "
171      f"(uniform = {1.0/N_DOMAIN:.3f}, "
172      f"speedup = {(mark_total/total)/(1.0/N_DOMAIN):.2f}x)")
173print(f"  complexity scaling:")
174for n, c, q in zip(N_LIST, classical_queries, quantum_queries):
175    print(f"    N = {n:>7d}  Q_classical = {c:>5d}  Q_quantum = {q:>4d}  speedup = {c/max(q,1):>4.1f}x")
176print(f"  saved: {out_path}")

运行结果

Demo 1:碰撞 BHT

运行方式

# 仓库根目录执行;输出 PNG 到 demos/ch11-query-complexity/01_collision_bht.png,关键统计到 stdout
.venv/bin/python3 demos/ch11-query-complexity/01_collision_bht.py

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