Demo 2:元素唯一性¶
可视化:双图 —— toy 数组 + O(N) vs O(N^{2/3}) + Johnson 平衡
对应教程:第 11 章 查询复杂度 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
核心:对玩具数组 arr = [3, 1, 4, 1, 5, 9, 2, 6] (idx=1,3 重复值 1) 演示 Ambainis 量子行走算法的 N^{2/3} 平衡点。画三幅图: (左) 数组可视化 + 标记重复对 (中) N=8..10^5 查询数对比: 经典 O(N) vs Grover sqrt(N) vs 量子 O(N^{2/3}) (右) Johnson 图参数平衡 (固定 N=1000): epsilon, delta, 总成本 vs r 并用 uniqc 跑 4 个 1-qubit oracle 验证 arr[idx] == 1 的检测能力。
完整源码¶
demos/ch11-query-complexity/02_element_distinctness.py¶
1"""
2ch11 演示 2:元素唯一性 Johnson 图量子行走平衡 + toy 实例量子探测
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch11-query-complexity/collision-element-distinctness.html
5核心:对玩具数组 arr = [3, 1, 4, 1, 5, 9, 2, 6] (idx=1,3 重复值 1)
6 演示 Ambainis 量子行走算法的 N^{2/3} 平衡点。画三幅图:
7 (左) 数组可视化 + 标记重复对
8 (中) N=8..10^5 查询数对比: 经典 O(N) vs Grover sqrt(N) vs 量子 O(N^{2/3})
9 (右) Johnson 图参数平衡 (固定 N=1000): epsilon, delta, 总成本 vs r
10 并用 uniqc 跑 4 个 1-qubit oracle 验证 arr[idx] == 1 的检测能力。
11
12依赖:pip install unified-quantum[visualization]
13运行:python demos/ch11-query-complexity/02_element_distinctness.py
14"""
15from pathlib import Path
16
17import matplotlib
18matplotlib.use("Agg")
19import matplotlib.pyplot as plt
20import numpy as np
21
22from uniqc import Circuit, submit_task, wait_for_result
23
24OUT = Path(__file__).parent
25SHOTS = 600 # 4 probes * 600 = 2400 total, < 3000 budget
26ARR = np.array([3, 1, 4, 1, 5, 9, 2, 6], dtype=int)
27N = len(ARR)
28
29
30def find_duplicates_classical(arr: np.ndarray) -> tuple[list[tuple[int, int]], int]:
31 queries, pairs = 0, []
32 indexed = sorted(enumerate(arr), key=lambda x: x[1])
33 for k in range(len(indexed) - 1):
34 queries += 1
35 if indexed[k][1] == indexed[k + 1][1]:
36 pairs.append((indexed[k][0], indexed[k + 1][0]))
37 return pairs, queries
38
39
40def eps_ambainis(N: int, r: int) -> float:
41 """唯一碰撞对的标记比例: epsilon = C(N-2,r-2)/C(N,r) = r(r-1)/(N(N-1))."""
42 return r * (r - 1) / (N * (N - 1)) if r >= 2 else 0.0
43
44
45def delta_johnson(N: int, r: int) -> float:
46 return N / (r * (N - r)) if 0 < r < N else 0.0
47
48
49def value_test_circuit(idx: int, value: int) -> Circuit:
50 """1-qubit oracle: H |0>, RZ(theta_real - theta_test) |0>, H |0>, 测 P(0)."""
51 c = Circuit()
52 max_val = int(ARR.max())
53 theta = 2 * np.pi * (float(ARR[idx]) - float(value)) / max_val
54 c.h(0); c.rz(0, float(theta)); c.h(0)
55 c.measure(0)
56 return c
57
58
59def measure_p0(circuit: Circuit) -> float:
60 r = wait_for_result(submit_task(circuit=circuit, backend="dummy:local:simulator", shots=SHOTS))
61 return float(r.probabilities.get("0", 0.0))
62
63
64duplicates, classical_q = find_duplicates_classical(ARR)
65N_list = [8, 27, 64, 216, 1000, 8000, 27000, 100000]
66classical_curve = np.array([float(n) for n in N_list])
67sqrt_curve = np.array([np.sqrt(n) for n in N_list])
68quantum_curve = np.array([float(n) ** (2.0 / 3.0) * 2.0 for n in N_list])
69
70N_FIXED = 1000
71r_grid = np.arange(1, N_FIXED + 1, 5)
72eps_curve = np.array([eps_ambainis(N_FIXED, int(r)) for r in r_grid])
73del_curve = np.array([delta_johnson(N_FIXED, int(r)) for r in r_grid])
74walk_steps = 1.0 / np.sqrt(np.maximum(eps_curve * del_curve, 1e-30))
75total_cost = r_grid + walk_steps
76opt_idx = int(np.argmin(total_cost))
77opt_r = int(r_grid[opt_idx])
78
79# === 量子 oracle 验证 ===
80print("ch11-query-complexity demo 2: element distinctness (Johnson walk)")
81print(f" toy array (N = {N}): {ARR.tolist()}, duplicate pairs = {duplicates}, shots = {SHOTS}")
82p_correct_1 = measure_p0(value_test_circuit(1, 1))
83p_correct_3 = measure_p0(value_test_circuit(3, 1))
84p_wrong_0 = measure_p0(value_test_circuit(0, 1))
85p_wrong_6 = measure_p0(value_test_circuit(6, 1))
86
87fig, axes = plt.subplots(1, 3, figsize=(16.5, 5.2))
88
89# (左) 数组 + 重复对
90ax = axes[0]
91dup_set = {d[0] for d in duplicates} | {d[1] for d in duplicates}
92colors = ["#d62728" if i in dup_set else "#1f77b4" for i in range(N)]
93bars = ax.bar(np.arange(N), ARR, color=colors, edgecolor="black", lw=0.8)
94for i, b in enumerate(bars):
95 ax.text(b.get_x() + b.get_width() / 2, b.get_height() + 0.15, f"{ARR[i]}",
96 ha="center", va="bottom", fontsize=10, fontweight="bold")
97for d in duplicates:
98 ax.annotate("", xy=(d[0], ARR[d[0]] + 0.6), xytext=(d[1], ARR[d[1]] + 0.6),
99 arrowprops=dict(arrowstyle="<->", color="#d62728", lw=2.0))
100 ax.text(0.5 * (d[0] + d[1]), max(ARR[d[0]], ARR[d[1]]) + 1.1,
101 f"DUPLICATE\nf({d[0]})=f({d[1]})={ARR[d[0]]}",
102 ha="center", va="bottom", fontsize=9, color="#d62728", fontweight="bold")
103ax.set_xticks(np.arange(N))
104ax.set_xticklabels([f"idx {i}" for i in range(N)], fontsize=8)
105ax.set_xlabel("array index")
106ax.set_ylabel("stored value f(idx)")
107ax.set_title(f"toy array with one duplicate pair\nclassical queries = N = {N} (sort + scan)")
108ax.set_ylim(0, max(ARR) + 2.6)
109ax.grid(axis="y", alpha=0.3)
110
111# (中) 复杂度 log-log 对比
112ax = axes[1]
113ax.loglog(N_list, classical_curve, "o-", color="#d62728", lw=2, ms=8, label=r"classical $Q = O(N)$")
114ax.loglog(N_list, sqrt_curve, "^-", color="#ff7f0e", lw=2, ms=7, label=r"Grover-style $O(\sqrt{N})$")
115ax.loglog(N_list, quantum_curve, "s-", color="#1f77b4", lw=2, ms=8, label=r"Ambainis walk $O(N^{2/3})$")
116for n, qc, qq in zip(N_list, classical_curve, quantum_curve):
117 ax.annotate(f"{int(qc)}", (n, qc), textcoords="offset points", xytext=(4, 4),
118 fontsize=7, color="#d62728")
119 ax.annotate(f"{int(qq)}", (n, qq), textcoords="offset points", xytext=(4, -12),
120 fontsize=7, color="#1f77b4")
121ref_x = np.array([N_list[0], N_list[-1]], dtype=float)
122ax.loglog(ref_x, ref_x, ":", color="#d62728", alpha=0.4)
123ax.loglog(ref_x, ref_x ** (2.0 / 3.0) * 1.5, ":", color="#1f77b4", alpha=0.4)
124ax.set_xlabel(r"domain size $N$")
125ax.set_ylabel(r"# oracle queries")
126ax.set_title("element distinctness: classical vs quantum\n"
127 r"$\Theta(N)$ vs $\Theta(N^{2/3})$ (gain over Grover $\sqrt{N}$)")
128ax.legend(loc="upper left", fontsize=9)
129ax.grid(alpha=0.3, which="both")
130
131# (右) Johnson trade-off
132ax = axes[2]
133ax.semilogy(r_grid, r_grid, "-", color="#2ca02c", lw=2, label=r"setup cost $S(r) = r$")
134ax.semilogy(r_grid, walk_steps, "-", color="#9467bd", lw=2, label=r"walk cost $N / \sqrt{r}$")
135ax.semilogy(r_grid, total_cost, "-", color="#1f77b4", lw=2.4, label=r"total $Q(r) = r + N/\sqrt{r}$")
136ax.axvline(opt_r, color="#d62728", ls="--", lw=2,
137 label=fr"optimal $r^* = N^{{2/3}} = {opt_r}$")
138ax.plot(opt_r, total_cost[opt_idx], "*", color="#d62728", ms=18, mec="black",
139 label=fr"min cost $\approx${total_cost[opt_idx]:.0f}")
140ax.text(opt_r * 1.15, total_cost[opt_idx] * 1.4,
141 fr"$\epsilon = {eps_curve[opt_idx]:.4f}$" + "\n"
142 fr"$\delta = {del_curve[opt_idx]:.4f}$" + "\n"
143 fr"$1/\sqrt{{\delta\epsilon}} = {walk_steps[opt_idx]:.0f}$",
144 fontsize=8, color="#333")
145ax.set_xlabel(r"subset size $r$")
146ax.set_ylabel("query cost (log)")
147ax.set_title(fr"Johnson $J(N={N_FIXED}, r)$ parameter balance"
148 "\nmark prob + spectral gap determine walk steps")
149ax.legend(loc="upper right", fontsize=8)
150ax.set_xlim(0, N_FIXED // 2)
151ax.grid(alpha=0.3, which="both")
152
153fig.suptitle("ch11 demo 2: element distinctness — Ambainis Johnson graph quantum walk",
154 y=1.01, fontsize=12)
155plt.tight_layout()
156out_path = OUT / "02_element_distinctness.png"
157plt.savefig(out_path, dpi=120, bbox_inches="tight")
158plt.close()
159
160print(f" optimal quantum r* for N=216: {int(round(216**(2/3)))}, "
161 f"N=10^5: {int(round(100000**(2/3)))}")
162print(f" Johnson trade-off at N={N_FIXED}: r* = {opt_r}, "
163 f"epsilon = {eps_curve[opt_idx]:.5f}, delta = {del_curve[opt_idx]:.5f}, "
164 f"cost = {total_cost[opt_idx]:.0f}")
165print(f" oracle probes (uniqc) on duplicate pair:")
166print(f" P(0| f(1)==1) = {p_correct_1:.4f} (should be ~1, value correct)")
167print(f" P(0| f(3)==1) = {p_correct_3:.4f} (should be ~1, value correct)")
168print(f" P(0| f(0)==1) = {p_wrong_0:.4f} (should be <1, arr[0]=3 != 1)")
169print(f" P(0| f(6)==1) = {p_wrong_6:.4f} (should be <1, arr[6]=2 != 1)")
170print(f" complexity scaling:")
171for n, qc, qq in zip(N_list, classical_curve, quantum_curve):
172 print(f" N = {n:>6d} classical = {int(qc):>6d} quantum = {int(qq):>5d} speedup = {qc/qq:.1f}x")
173print(f" saved: {out_path}")
运行结果¶

运行方式¶
# 仓库根目录执行;输出 PNG 到 demos/ch11-query-complexity/02_element_distinctness.png,关键统计到 stdout
.venv/bin/python3 demos/ch11-query-complexity/02_element_distinctness.py