Demo 2:Welded Tree¶
可视化:2 子图 —— 14 顶点焊缝树 + 经典 vs 量子 P(EXIT)
对应教程:第 12 章 量子图算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
核心:取 n=2 的焊接树 (2n+2=6 列、14 顶点)。左侧三列大小 1,2,4;右侧三列 大小 4,2,1;焊接在第 2 与第 3 列之间。整图邻接矩阵 A 在列子空间上压缩成 一条带权路径(边权树内 √2、焊缝 2)。连续时间量子游走 e^{-iAt}|0⟩_col 在 这个 6 维子空间上演化,振幅沿链传播并在 O(n) 时间内到达 EXIT 列;经典 随机游走 P_t = T^t |ENTRANCE⟩ 受生日悖论下界 2^{Ω(n)} 限制。这里画 P(EXIT) vs t 的对比曲线 + 列链上的概率剖面演化。
完整源码¶
demos/ch12-quantum-graphs/02_welded_tree.py¶
1"""
2ch12-quantum-graphs demo 2: Welded Tree — classical vs continuous-time quantum walk
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch12-quantum-graphs/welded-tree.html
5核心:取 n=2 的焊接树 (2n+2=6 列、14 顶点)。左侧三列大小 1,2,4;右侧三列
6 大小 4,2,1;焊接在第 2 与第 3 列之间。整图邻接矩阵 A 在列子空间上压缩成
7 一条带权路径(边权树内 √2、焊缝 2)。连续时间量子游走 e^{-iAt}|0⟩_col 在
8 这个 6 维子空间上演化,振幅沿链传播并在 O(n) 时间内到达 EXIT 列;经典
9 随机游走 P_t = T^t |ENTRANCE⟩ 受生日悖论下界 2^{Ω(n)} 限制。这里画
10 P(EXIT) vs t 的对比曲线 + 列链上的概率剖面演化。
11
12依赖:pip install unified-quantum[visualization]
13运行:python demos/ch12-quantum-graphs/02_welded_tree.py
14"""
15from __future__ import annotations
16
17from pathlib import Path
18
19import matplotlib
20matplotlib.use("Agg")
21import matplotlib.pyplot as plt
22import numpy as np
23from scipy.linalg import expm
24
25OUT = Path(__file__).parent
26
27# === 1. Build welded tree graph (n = 2, N = 14) ===
28N_DEPTH = 2
29N = 2 ** (N_DEPTH + 2) - 2 # 14
30N_COLS = 2 * N_DEPTH + 2 # 6
31COL_SIZES = [2 ** j if j <= N_DEPTH else 2 ** (2 * N_DEPTH + 1 - j)
32 for j in range(N_COLS)]
33COL_OFFSET = np.concatenate([[0], np.cumsum(COL_SIZES)])
34
35A = np.zeros((N, N), dtype=int)
36# Left tree (cols 0..N_DEPTH-1): each vertex in col j has 2 children in col j+1
37for col in range(N_DEPTH):
38 for v_local in range(COL_SIZES[col]):
39 v = COL_OFFSET[col] + v_local
40 for c in range(2):
41 A[v, COL_OFFSET[col + 1] + 2 * v_local + c] = 1
42# Right tree (cols N_DEPTH+1..N_COLS-2): each vertex in col j has 1 parent in col j+1
43for col in range(N_DEPTH + 1, N_COLS - 1):
44 for v_local in range(COL_SIZES[col]):
45 v = COL_OFFSET[col] + v_local
46 A[v, COL_OFFSET[col + 1] + v_local // 2] = 1
47# Weld: each left leaf connects to 2 right leaves (cyclic pairing)
48left_n = COL_SIZES[N_DEPTH]
49right_n = COL_SIZES[N_DEPTH + 1]
50for v_local in range(left_n):
51 v = COL_OFFSET[N_DEPTH] + v_local
52 for r in [v_local % right_n, (v_local + 1) % right_n]:
53 A[v, COL_OFFSET[N_DEPTH + 1] + r] = 1
54A = np.maximum(A, A.T) # symmetrize
55
56deg = A.sum(axis=1)
57max_deg = int(deg.max())
58assert max_deg == 3, f"welded tree should have max degree 3, got {max_deg}"
59
60# === 2. Column-subspace 6×6 tridiagonal matrix (weights √2 except weld=2) ===
61A_col = np.zeros((N_COLS, N_COLS))
62for j in range(N_COLS - 1):
63 w = 2.0 if j == N_DEPTH else np.sqrt(2.0)
64 A_col[j, j + 1] = A_col[j + 1, j] = w
65
66# === 3. Classical random walk on full graph (start at ENTRANCE = vertex 0) ===
67T = A / deg[None, :] # transition matrix
68T_MAX = 80
69exit_probs_class = np.zeros(T_MAX)
70p = np.zeros(N); p[0] = 1.0
71EXIT_VERTEX = N - 1
72for t in range(T_MAX):
73 exit_probs_class[t] = p[EXIT_VERTEX]
74 p = T @ p
75
76# === 4. Continuous-time quantum walk |psi(t)> = e^{-i A_col t} |0>_col ===
77T_QUANT_MAX = 20.0
78N_QUANT_STEPS = 200
79t_quant = np.linspace(0, T_QUANT_MAX, N_QUANT_STEPS)
80exit_probs_quant = np.zeros(N_QUANT_STEPS)
81profile_probs = np.zeros((N_QUANT_STEPS, N_COLS))
82v0 = np.zeros(N_COLS); v0[0] = 1.0
83for k, t in enumerate(t_quant):
84 probs = np.abs(expm(-1j * A_col * t) @ v0) ** 2
85 exit_probs_quant[k] = probs[-1]
86 profile_probs[k] = probs
87
88# === 5. uniqc sanity: ENTRANCE = |0000⟩ should be deterministic ===
89print("ch12-quantum-graphs-demo-2: Welded Tree (n=2) classical vs quantum walk")
90print(f" N = {N} vertices, columns = {N_COLS}, column sizes = {COL_SIZES}")
91print(f" max degree = {max_deg} (welded-tree invariant)")
92try:
93 from uniqc import Circuit, submit_task, wait_for_result
94 c = Circuit()
95 for q in range(4):
96 c.measure(q)
97 r = wait_for_result(
98 submit_task(circuit=c, backend="dummy:local:simulator", shots=100)
99 )
100 counts = r.counts
101 top = max(counts, key=counts.get)
102 print(f" uniqc sanity: |ENTRANCE>=|0000> with p={counts[top]/sum(counts.values()):.2f}")
103except Exception as e:
104 print(f" uniqc sanity skipped: {e}")
105
106
107# === 6. matplotlib 可视化 ===
108fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(13.0, 5.2))
109
110# Left: P(EXIT) vs time, classical (steps) vs quantum (continuous)
111ax1.semilogy(np.arange(T_MAX), np.maximum(exit_probs_class, 1e-4),
112 "-", color="#ff7f0e", lw=2.0, label=f"classical ({T_MAX} steps)")
113ax1.semilogy(t_quant, np.maximum(exit_probs_quant, 1e-4),
114 "-", color="#1f77b4", lw=1.6,
115 label=r"continuous-time $e^{-iA_S t}$")
116ax1.axvline(N_DEPTH, color="gray", ls=":", lw=1, alpha=0.6,
117 label=f"n={N_DEPTH} depth")
118ax1.set_ylim(1e-4, 1.0)
119ax1.set_xlabel(r"time $t$ (steps for classical, $\hbar=1$ for quantum)")
120ax1.set_ylabel(r"$P(\mathrm{EXIT})$")
121ax1.set_title("welded tree n=2: probability of reaching EXIT")
122ax1.grid(alpha=0.3, which="both")
123ax1.legend(loc="lower right", fontsize=9, framealpha=0.9)
124
125# Right: column-probability profiles at selected times (wave packet propagation)
126profile_times = [0.0, 2.5, 5.0, 7.5, 10.0]
127colors_prof = ["#888888", "#1f77b4", "#2ca02c", "#d62728", "#9467bd"]
128for tk, col_t in zip(profile_times, colors_prof):
129 probs = np.abs(expm(-1j * A_col * tk) @ v0) ** 2
130 ax2.plot(np.arange(N_COLS), probs, "-o", color=col_t, lw=1.6, ms=7,
131 label=f"t={tk:.1f}")
132ax2.set_xticks(np.arange(N_COLS))
133ax2.set_xticklabels([f"C{j}\n(|C|={COL_SIZES[j]})" for j in range(N_COLS)],
134 fontsize=8)
135ax2.set_xlabel("column index j (size of C_j)")
136ax2.set_ylabel(r"$|\langle j | e^{-iA_S t} | 0\rangle_\mathrm{col}|^2$")
137ax2.set_title("wave-packet propagating across the 6-column chain")
138ax2.set_ylim(-0.02, 1.0)
139ax2.grid(alpha=0.3)
140ax2.legend(loc="upper left", fontsize=8, framealpha=0.9)
141
142# Inset: welded tree sketch
143inset = fig.add_axes([0.04, 0.55, 0.18, 0.36])
144inset.set_xlim(-0.5, N_COLS - 0.5)
145inset.set_ylim(-2.0, 2.0)
146inset.axis("off")
147node_pos = {}
148for col, size in enumerate(COL_SIZES):
149 ys = np.linspace(-1.6, 1.6, size) if size > 1 else [0.0]
150 for i, y_ in enumerate(ys):
151 node_pos[COL_OFFSET[col] + i] = (col, y_)
152for i in range(N):
153 for j in range(i + 1, N):
154 if A[i, j]:
155 x0, y0 = node_pos[i]; x1, y1 = node_pos[j]
156 is_weld = (node_pos[i][0] == N_DEPTH and node_pos[j][0] == N_DEPTH + 1)
157 color, lw, alpha = ("#d62728", 1.4, 0.55) if is_weld else ("#444", 0.7, 0.45)
158 inset.plot([x0, x1], [y0, y1], "-", color=color, lw=lw, alpha=alpha)
159inset.scatter(*node_pos[0], s=160, c="#2ca02c", ec="black", zorder=5)
160inset.text(-0.3, 0, "ENTRANCE", ha="right", va="center",
161 fontsize=8, fontweight="bold")
162inset.scatter(*node_pos[N - 1], s=160, c="#1f77b4", ec="black", zorder=5)
163inset.text(N_COLS - 0.3, 0, "EXIT", ha="left", va="center",
164 fontsize=8, fontweight="bold")
165inset.set_title("welded tree n=2 (red = weld)", fontsize=9)
166
167plt.suptitle("ch12-quantum-graphs demo 2: Welded Tree classical vs quantum walk",
168 y=1.02)
169plt.tight_layout()
170out_path = OUT / "02_welded_tree.png"
171plt.savefig(out_path, dpi=120, bbox_inches="tight")
172plt.close()
173
174# === 7. stdout 关键统计 ===
175peak_idx = int(np.argmax(exit_probs_quant))
176print(f"\n classical: max P(EXIT) over {T_MAX} steps = "
177 f"{exit_probs_class.max():.4e}")
178print(f" classical: P(EXIT) at step {N_DEPTH} = "
179 f"{exit_probs_class[N_DEPTH]:.4e}")
180print(f" quantum : peak P(EXIT)={exit_probs_quant[peak_idx]:.3f} at "
181 f"t={t_quant[peak_idx]:.2f}")
182print(f" quantum : P(EXIT) at t=π={np.pi:.2f} = "
183 f"{exit_probs_quant[np.argmin(np.abs(t_quant - np.pi))]:.3f}")
184print(f" A_col eigvals (rad/s): "
185 f"{np.sort(np.linalg.eigvalsh(A_col)).round(3).tolist()}")
186print(f" theoretical weld weight: 2.0, tree weight: sqrt(2)={np.sqrt(2):.4f}")
187print(f" PNG -> {out_path}")
运行结果¶

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