Demo 3:QAOA 4-node MaxCut¶
可视化:2D 热图+最优 cuts —— 25×25 (γ,β) 网格扫描
对应教程:第 6 章 量子科学计算 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
对 4 节点方环图 (边 0-1, 1-2, 2-3, 3-0),构造 p=1 QAOA ansatz 并扫描
(gamma, beta) 网格,用 pauli_expectation 求 <H_C> = sum_{
完整源码¶
demos/ch06-scientific-computing/03_qaoa_maxcut.py¶
1"""
2ch06 演示 3:4-node Max-Cut QAOA (p=1)
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch06-scientific-computing/qaoa-tutorial.html
5依赖:pip install unified-quantum[visualization]
6运行:python demos/ch06-scientific-computing/03_qaoa_maxcut.py
7
8对 4 节点方环图 (边 0-1, 1-2, 2-3, 3-0),构造 p=1 QAOA ansatz 并扫描
9(gamma, beta) 网格,用 pauli_expectation 求 <H_C> = sum_{<ij>} (1 - Z_i Z_j)/2。
10PNG 画出:图拓扑 + <H_C>(gamma, beta) 热图 + 最优 gamma/beta 对应的概率分布。
11"""
12from __future__ import annotations
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, pauli_expectation, qaoa_ansatz
22
23OUT = Path(__file__).parent
24
25# --------------------------------------------------------------------------
26# 1) 问题设置:方环图 (square graph) 上 Max-Cut
27# --------------------------------------------------------------------------
28edges = [(0, 1), (1, 2), (2, 3), (3, 0)]
29H_C = [(f"Z{i}Z{j}", -0.5) for (i, j) in edges] # H_C = -sum (Z_i Z_j)/2
30# (choose -0.5 so that maximizing <H_C> recovers the cut size)
31n_qubits = 4
32
33# 经典最优解与 cut 值
34brute = list(np.ndindex(*([2] * n_qubits)))
35cut_sizes = []
36for x in brute:
37 bs = "".join(str(b) for b in x)
38 cut = sum(1 for (i, j) in edges if bs[i] != bs[j])
39 cut_sizes.append(cut)
40cut_sizes = np.array(cut_sizes)
41max_cut = int(cut_sizes.max())
42n_max = int((cut_sizes == max_cut).sum())
43opt_strings = ["".join(str(b) for b in x) for x in brute if cut_sizes[brute.index(x)] == max_cut]
44
45# --------------------------------------------------------------------------
46# 2) 在 (gamma, beta) 网格上计算 <H_C>
47# --------------------------------------------------------------------------
48gammas = np.linspace(0.0, np.pi, 25)
49betas = np.linspace(0.0, np.pi, 25)
50exp_grid = np.zeros((len(gammas), len(betas)))
51
52for gi, gamma in enumerate(gammas):
53 for bi, beta in enumerate(betas):
54 c = qaoa_ansatz(H_C, p=1,
55 gammas=np.array([gamma]),
56 betas=np.array([beta]))
57 for q in range(n_qubits):
58 c.measure(q)
59 val = sum(coeff * pauli_expectation(c, ps, shots=None)
60 for ps, coeff in H_C)
61 exp_grid[gi, bi] = val
62
63# cut_value = (|E| - sum_ZZ)/2 = 2 + <H_C> (因为 H_C = -0.5 sum Z_i Z_j)
64cut_grid = 2.0 + exp_grid
65
66gi_opt, bi_opt = np.unravel_index(cut_grid.argmax(), cut_grid.shape)
67gamma_opt, beta_opt = gammas[gi_opt], betas[bi_opt]
68exp_opt = float(exp_grid[gi_opt, bi_opt])
69cut_opt = float(cut_grid[gi_opt, bi_opt])
70
71# --------------------------------------------------------------------------
72# 3) 在 (gamma_opt, beta_opt) 下采样:得到 cut-bitstring 概率分布
73# --------------------------------------------------------------------------
74c_opt = qaoa_ansatz(H_C, p=1,
75 gammas=np.array([gamma_opt]),
76 betas=np.array([beta_opt]))
77for q in range(n_qubits):
78 c_opt.measure(q)
79
80from uniqc import submit_task, wait_for_result # noqa: E402
81task_id = submit_task(circuit=c_opt, backend="dummy:local:simulator", shots=4000)
82result = wait_for_result(task_id)
83counts = result.counts
84total = sum(counts.values())
85
86probs = np.zeros(16)
87for bs, n in counts.items():
88 probs[int(bs, 2)] = n / total
89measured_cuts = np.array([
90 sum(1 for (i, j) in edges if int(bs[i]) != int(bs[j]))
91 for bs in (format(k, "04b") for k in range(16))
92])
93approx_ratio = sum(probs[k] * measured_cuts[k] for k in range(16)) / max_cut
94
95# --------------------------------------------------------------------------
96# 4) 可视化
97# --------------------------------------------------------------------------
98fig = plt.figure(figsize=(15, 4.8))
99gs = fig.add_gridspec(1, 3, width_ratios=[1, 1.7, 1.7])
100
101# 4a) 图拓扑 + H_C 矩阵
102ax0 = fig.add_subplot(gs[0, 0])
103pos = {0: (0, 1), 1: (1, 1), 2: (1, 0), 3: (0, 0)}
104for (i, j) in edges:
105 x0, y0 = pos[i]
106 x1, y1 = pos[j]
107 ax0.plot([x0, x1], [y0, y1], "k-", linewidth=2)
108for q in range(n_qubits):
109 x, y = pos[q]
110 ax0.scatter(x, y, s=600, color="steelblue", edgecolors="navy",
111 linewidths=2, zorder=3)
112 ax0.text(x, y, str(q), ha="center", va="center",
113 color="white", fontsize=12, fontweight="bold")
114ax0.set_xlim(-0.4, 1.4); ax0.set_ylim(-0.4, 1.4)
115ax0.set_aspect("equal")
116ax0.axis("off")
117ax0.set_title(f"square graph (|V|=4, |E|=4)\n"
118 f"H_C = -sum_{{ij}} Z_i Z_j / 2\n"
119 f"max-cut = {max_cut} ({n_max} optima)")
120
121# 4b) <H_C> 热图
122ax1 = fig.add_subplot(gs[0, 1])
123im = ax1.imshow(cut_grid, origin="lower",
124 extent=(betas[0], betas[-1], gammas[0], gammas[-1]),
125 aspect="auto", cmap="viridis",
126 vmin=0, vmax=max_cut)
127ax1.scatter([beta_opt], [gamma_opt], marker="*", s=180,
128 color="red", edgecolors="white", linewidths=1.2,
129 label=f"opt (gamma={gamma_opt:.2f}, beta={beta_opt:.2f})")
130plt.colorbar(im, ax=ax1, label="<cut value>")
131ax1.set_xlabel("beta (mixer)")
132ax1.set_ylabel("gamma (cost)")
133ax1.set_title(f"E[cut](gamma, beta) heatmap, p=1 QAOA\n"
134 f"approx ratio = {approx_ratio:.3f}")
135ax1.legend(loc="upper right", fontsize=8)
136
137# 4c) 在最优参数下的概率分布
138ax2 = fig.add_subplot(gs[0, 2])
139order = sorted(range(16), key=lambda k: measured_cuts[k])
140bars = ax2.bar(range(16), probs[order], color=[
141 "seagreen" if measured_cuts[k] == max_cut else "lightgray"
142 for k in order
143])
144ax2.set_xticks(range(16))
145ax2.set_xticklabels([format(k, "04b") for k in order], rotation=90, fontsize=7)
146ax2.set_ylabel("probability")
147ax2.set_title(f"bitstring probs at (gamma*, beta*)\n"
148 f"E[cut]={approx_ratio*max_cut:.2f} / {max_cut}")
149ax2.axhline(0, color="black", linewidth=0.5)
150ax2.grid(True, alpha=0.3, axis="y")
151
152plt.tight_layout()
153plt.savefig(OUT / "03_qaoa_maxcut.png", dpi=120, bbox_inches="tight")
154plt.close()
155
156# --------------------------------------------------------------------------
157# 5) stdout 关键统计
158# --------------------------------------------------------------------------
159print("ch06-demo-3: 4-node square Max-Cut QAOA (p=1)")
160print(f" max cut (brute force) = {max_cut} ({n_max} optima)")
161print(f" shots at (gamma*, beta*) = {total}")
162print(f" opt (gamma*, beta*) = ({gamma_opt:.3f}, {beta_opt:.3f}) "
163 f"<H_C> = {exp_opt:.3f} cut_value = {cut_opt:.3f}")
164print(f" QAOA approximate ratio = {approx_ratio:.4f}")
165print(f" top bitstrings at optimum:")
166top3 = sorted(range(16), key=lambda k: -probs[k])[:3]
167for k in top3:
168 bs = format(k, "04b")
169 print(f" {bs} cut={int(measured_cuts[k])} p={probs[k]:.3f}")
运行结果¶

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