Demo 3:Childs-Goldstone 空间搜索¶
可视化:3 子图 —— C4 + γ=1/√2 + Rabi 振荡
对应教程:第 12 章 量子图算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
核心:4-环 C4 上做 Childs-Goldstone 连续时间空间搜索。Hamiltonian H = -γ A - |w⟩⟨w| 取 γ ≈ 1/√N = 1/2 (对 C_N 的渐近最优 γ 是 1/(2 sin(π/N)),对 N=4 ≈ 0.707), 初态 |s⟩ = (1/2) Σ_i |i⟩ 是 A 的本征态(本征值 2)。H 把 |s⟩ 局限在 span{|s⟩, |w⟩} 的 2 维子空间里做 Rabi 振荡;振幅在 t ≈ π√N/2 时刻翻转到 |w⟩,P(target) 接近 1。相比之下,经典随机游走 P_t = T^t|s⟩ 需要 O(N)=4 步 才能把概率抬到 1/4 量级。这里画 P(target) vs t 的连续时间量子振荡曲线, 并叠加经典随机游走的概率作为参考;同时画 4 个节点振幅 |⟨i|ψ(t)⟩|² 的时间演化。
完整源码¶
demos/ch12-quantum-graphs/03_spatial_search.py¶
1"""
2ch12-quantum-graphs demo 3: Childs–Goldstone spatial search on the 4-cycle
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch12-quantum-graphs/adjacency-matrix-properties.html
5核心:4-环 C4 上做 Childs-Goldstone 连续时间空间搜索。Hamiltonian
6 H = -γ A - |w⟩⟨w|
7 取 γ ≈ 1/√N = 1/2 (对 C_N 的渐近最优 γ 是 1/(2 sin(π/N)),对 N=4 ≈ 0.707),
8 初态 |s⟩ = (1/2) Σ_i |i⟩ 是 A 的本征态(本征值 2)。H 把 |s⟩ 局限在
9 span{|s⟩, |w⟩} 的 2 维子空间里做 Rabi 振荡;振幅在 t ≈ π√N/2 时刻翻转到
10 |w⟩,P(target) 接近 1。相比之下,经典随机游走 P_t = T^t|s⟩ 需要 O(N)=4 步
11 才能把概率抬到 1/4 量级。这里画 P(target) vs t 的连续时间量子振荡曲线,
12 并叠加经典随机游走的概率作为参考;同时画 4 个节点振幅 |⟨i|ψ(t)⟩|² 的时间演化。
13
14依赖:pip install unified-quantum[visualization]
15运行:python demos/ch12-quantum-graphs/03_spatial_search.py
16"""
17from __future__ import annotations
18
19from pathlib import Path
20
21import matplotlib
22matplotlib.use("Agg")
23import matplotlib.pyplot as plt
24import numpy as np
25from scipy.linalg import expm
26
27OUT = Path(__file__).parent
28N = 4
29TARGET = 0
30
31# === 1. C4 adjacency matrix A ===
32A = np.zeros((N, N))
33for i in range(N):
34 A[i, (i + 1) % N] = A[i, (i - 1) % N] = 1
35
36# Eigenvalues of A for sanity check
37evals_A = np.linalg.eigvalsh(A)
38print("ch12-quantum-graphs-demo-3: Childs–Goldstone search on C4")
39print(f" C4 adjacency eigenvalues: {np.round(evals_A, 4).tolist()} "
40 "(expected: -2, 0, 0, 2)")
41
42# === 2. Continuous-time spatial search Hamiltonian H = -γ A - |w⟩⟨w| ===
43# Optimal γ for C_N: γ* = 1 / (2 sin(π/N)); for N=4 → γ* = 1/√2 ≈ 0.7071
44GAMMA = 1.0 / np.sqrt(2.0)
45T_QUANT_MAX = 10.0
46N_QUANT_STEPS = 400
47t_quant = np.linspace(0, T_QUANT_MAX, N_QUANT_STEPS)
48
49W = np.zeros((N, N))
50W[TARGET, TARGET] = 1.0
51H = -GAMMA * A - W # Childs–Goldstone Hamiltonian
52# Initial state |s> = (1/√N) Σ |i>
53s = np.ones(N) / np.sqrt(N)
54
55target_probs_q = np.zeros(N_QUANT_STEPS)
56all_node_probs_q = np.zeros((N_QUANT_STEPS, N))
57for k, t in enumerate(t_quant):
58 psi_t = expm(-1j * H * t) @ s
59 probs = np.abs(psi_t) ** 2
60 target_probs_q[k] = probs[TARGET]
61 all_node_probs_q[k] = probs
62
63# === 3. Classical random walk on C4 starting from uniform distribution ===
64T_class = A / 2.0 # transition matrix (degree = 2 for every node)
65p_class = np.ones(N) / N # uniform start = (1/N, ..., 1/N); stationary for C4
66T_CLASS_MAX = 30
67target_probs_c = np.zeros(T_CLASS_MAX)
68all_node_probs_c = np.zeros((T_CLASS_MAX, N))
69for t in range(T_CLASS_MAX):
70 target_probs_c[t] = p_class[TARGET]
71 all_node_probs_c[t] = p_class
72 p_class = T_class @ p_class
73
74# === 4. Run a tiny uniqc sanity check on the uniform superposition |s> ===
75try:
76 from uniqc import Circuit, submit_task, wait_for_result
77 c = Circuit()
78 c.h(0); c.h(1) # |s> = (|00>+|01>+|10>+|11>)/2 on q0, q1
79 c.measure(0); c.measure(1)
80 r = wait_for_result(
81 submit_task(circuit=c, backend="dummy:local:simulator", shots=2000)
82 )
83 counts = r.counts
84 total = sum(counts.values())
85 expected = ["00", "01", "10", "11"]
86 diffs = [abs(counts.get(bs, 0) / total - 0.25) for bs in expected]
87 print(f" uniqc sanity: |s> uniform prep, max deviation from 0.25 = "
88 f"{max(diffs):.3f}")
89except Exception as e:
90 print(f" uniqc sanity skipped: {e}")
91
92
93# === 5. matplotlib 可视化 ===
94fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13.0, 5.2))
95
96# Left: P(target=node 0) vs time, continuous-time quantum vs classical random walk
97ax1.plot(t_quant, target_probs_q, "-", color="#1f77b4", lw=2.0,
98 label=fr"continuous-time search ($\gamma={GAMMA:.3f}$, $\hbar=1$)")
99ax1.plot(np.arange(T_CLASS_MAX), target_probs_c, "o--", color="#ff7f0e",
100 lw=1.6, ms=6, label=f"classical random walk ({T_CLASS_MAX} steps)")
101# Mark the theoretically predicted search time T* = π√N/2
102T_star = np.pi * np.sqrt(N) / 2.0
103ax1.axvline(T_star, color="gray", ls=":", lw=1.0, alpha=0.7,
104 label=fr"theory $T^*=\pi\sqrt{{N}}/2\approx{T_star:.2f}$")
105ax1.axhline(1.0, color="#2ca02c", ls=":", lw=1.0, alpha=0.7,
106 label=r"ideal $P=1$")
107ax1.axhline(1.0 / N, color="#888888", ls=":", lw=1.0, alpha=0.7,
108 label=fr"classical uniform $1/N={1.0/N:.3f}$")
109ax1.set_xlabel(r"time $t$")
110ax1.set_ylabel(r"$P(\mathrm{target} = \mathrm{node}\ 0)$")
111ax1.set_ylim(-0.02, 1.08)
112ax1.set_title("Childs–Goldstone search on C4: target probability vs time")
113ax1.grid(alpha=0.3)
114ax1.legend(loc="upper right", fontsize=8, framealpha=0.9)
115
116# Right: heatmap of |⟨i|ψ(t)⟩|² for all 4 nodes over time
117im = ax2.imshow(all_node_probs_q.T, aspect="auto", origin="lower",
118 cmap="viridis", vmin=0.0, vmax=0.6,
119 extent=[t_quant[0], t_quant[-1], -0.5, N - 0.5])
120ax2.set_yticks(range(N))
121ax2.set_yticklabels([f"node {n}" + (" (target)" if n == TARGET else "")
122 for n in range(N)])
123ax2.set_xlabel(r"time $t$")
124ax2.set_title(r"wave-packet $|\langle i|e^{-iHt}|s\rangle|^2$")
125ax2.axvline(T_star, color="#ff6f61", ls="--", lw=1.4,
126 label=fr"$T^*={T_star:.2f}$")
127ax2.legend(loc="upper right", fontsize=8, framealpha=0.9)
128cbar = plt.colorbar(im, ax=ax2)
129cbar.set_label(r"probability at node $i$")
130
131# C4 graph inset (square with target highlighted)
132inset = fig.add_axes([0.04, 0.55, 0.16, 0.32])
133inset.set_xlim(-1.3, 1.3)
134inset.set_ylim(-1.3, 1.3)
135inset.set_aspect("equal")
136inset.axis("off")
137theta = (np.arange(N) * 2 * np.pi / N + np.pi / 2)
138xs, ys = np.cos(theta), np.sin(theta)
139for i in range(N):
140 j = (i + 1) % N
141 inset.plot([xs[i], xs[j]], [ys[i], ys[j]], "k-", lw=1.6)
142for n in range(N):
143 fc = "#d62728" if n == TARGET else "#cccccc"
144 ec = "black"
145 inset.scatter(xs[n], ys[n], s=420, c=fc, ec=ec, lw=1.2, zorder=3)
146 inset.text(xs[n], ys[n], str(n), ha="center", va="center",
147 fontsize=11, fontweight="bold",
148 color="white" if n != TARGET else "white", zorder=4)
149inset.set_title("C4 with target node 0", fontsize=9)
150
151plt.suptitle("ch12-quantum-graphs demo 3: Childs–Goldstone spatial search on C4",
152 y=1.02)
153plt.tight_layout()
154out_path = OUT / "03_spatial_search.png"
155plt.savefig(out_path, dpi=120, bbox_inches="tight")
156plt.close()
157
158# === 6. stdout 关键统计 ===
159peak_idx = int(np.argmax(target_probs_q))
160peak_t = float(t_quant[peak_idx])
161print(f"\n search parameters: N={N}, target=node {TARGET}, γ={GAMMA:.4f}")
162print(f" theory search time T* = π·√N/2 = {T_star:.3f}")
163print(f" observed P(target) peak = {target_probs_q[peak_idx]:.3f} at t={peak_t:.2f}")
164print(f" P(target) at classical uniform 1/N = {1.0/N:.3f}")
165print(f" P(target) at classical step {N} = {target_probs_c[N]:.3f}")
166print(f" P(target) at classical max ({T_CLASS_MAX} steps) = "
167 f"{target_probs_c.max():.3f}")
168print(f" speedup at first peak: quantum t≈{peak_t:.2f} vs classical O(N)={N} steps")
169print(f" H eigvals (rad/s): {np.round(np.linalg.eigvalsh(H), 3).tolist()}")
170print(f" shots used in uniqc sanity: 2000")
171print(f" PNG -> {out_path}")
运行结果¶

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