Demo 2:凸优化¶
可视化:2 子图 —— FD 经典 vs Jordan 量子梯度
对应教程:第 14 章 优化与数值量子算法 中相关小节 · 依赖安装:pip install "unified-quantum[visualization,simulation]"
实现要点¶
核心:2D convex quadratic f(x, y) = 0.5 (x-2)^2 + 0.5 (y+1)^2, gradient g(x, y) = (x-2, y+1). The convex-optimization chapter contrasts two oracle models:
classical finite-difference gradient = 2d function-value queries per step
Jordan's quantum gradient (QPE + iQFT) = 1 coherent phase query per step
Here d=2 → 4× query gap. Both loops run gradient descent with the
same starting point and step size; the difference is the per-step
query counter. The quantum loop builds the Jordan-style phase oracle
explicitly so its OriginIR is visible — the analytical gradient is
used for the actual descent step (the *full* Jordan oracle recovers
it exactly given a sufficiently wide grid).
完整源码¶
demos/ch14-optimization-numerics/02_convex_optimization.py¶
1"""
2ch14-demo-2: convex quadratic optimization, classical FD vs Jordan quantum gradient
3
4对应教程:chenzhaoyun.com/quantum-tutorial/ch14-optimization-numerics/convex-optimization.html
5核心:2D convex quadratic f(x, y) = 0.5 (x-2)^2 + 0.5 (y+1)^2, gradient
6 g(x, y) = (x-2, y+1). The convex-optimization chapter contrasts two
7 oracle models:
8
9 classical finite-difference gradient = 2d function-value queries per step
10 Jordan's quantum gradient (QPE + iQFT) = 1 coherent phase query per step
11
12 Here d=2 → 4× query gap. Both loops run gradient descent with the
13 same starting point and step size; the difference is the per-step
14 query counter. The quantum loop builds the Jordan-style phase oracle
15 explicitly so its OriginIR is visible — the analytical gradient is
16 used for the actual descent step (the *full* Jordan oracle recovers
17 it exactly given a sufficiently wide grid).
18
19依赖:pip install unified-quantum[visualization]
20运行:python demos/ch14-optimization-numerics/02_convex_optimization.py
21"""
22from __future__ import annotations
23
24from pathlib import Path
25
26import matplotlib
27matplotlib.use("Agg")
28import matplotlib.pyplot as plt
29import numpy as np
30
31from uniqc import Circuit, submit_task, wait_for_result
32
33OUT = Path(__file__).parent
34
35# === 1. Convex quadratic & gradient ===
36def f(x, y):
37 return 0.5 * (x - 2.0) ** 2 + 0.5 * (y + 1.0) ** 2
38
39
40def grad(x, y):
41 return np.array([x - 2.0, y + 1.0])
42
43
44OPT = np.array([2.0, -1.0])
45LR, N_STEPS, SHOTS = 0.45, 8, 600
46
47
48# === 2. Classical finite-difference descent (4 fn evals per step) ===
49def class_descent(start, h=0.05):
50 xy = np.array(start, float)
51 traj = [xy.copy()]
52 fn_evals = 0
53 for _ in range(N_STEPS):
54 g = np.array([
55 (f(xy[0] + h, xy[1]) - f(xy[0] - h, xy[1])) / (2 * h),
56 (f(xy[0], xy[1] + h) - f(xy[0], xy[1] - h)) / (2 * h),
57 ])
58 fn_evals += 4
59 xy = xy - LR * g
60 traj.append(xy.copy())
61 return np.array(traj), fn_evals
62
63
64# === 3. Jordan-style quantum gradient phase oracle (4 qubits) ===
65N_BITS, S = 2, 4
66
67
68def iqft_2bit(c, qs):
69 a, b = qs
70 c.swap(a, b)
71 c.cp(a, b, -np.pi / 2)
72 c.h(a); c.h(b)
73
74
75def jordan_gradient_circ(xy):
76 """Per-bit rotation: g_axis · 2π · 2^(N-1-b) / S."""
77 c = Circuit()
78 for q in range(4):
79 c.h(q)
80 gx, gy = grad(*xy)
81 for b in range(N_BITS):
82 c.rz(b, gx * 2 * np.pi * (2 ** (N_BITS - 1 - b)) / S)
83 c.rz(b + 2, gy * 2 * np.pi * (2 ** (N_BITS - 1 - b)) / S)
84 iqft_2bit(c, [0, 1])
85 iqft_2bit(c, [2, 3])
86 for q in range(4):
87 c.measure(q)
88 return c
89
90
91def quantum_descent(start):
92 """1 coherent query per step; uses analytical gradient for the step."""
93 xy = np.array(start, float)
94 traj = [xy.copy()]
95 queries = 0
96 for _ in range(N_STEPS):
97 c = jordan_gradient_circ(xy)
98 wait_for_result(
99 submit_task(circuit=c, backend="dummy:local:simulator", shots=SHOTS)
100 )
101 queries += 1
102 xy = xy - LR * grad(*xy)
103 traj.append(xy.copy())
104 return np.array(traj), queries
105
106
107# === 4. Run both ===
108print("ch14-demo-2: convex quadratic f(x, y) = 0.5(x-2)^2 + 0.5(y+1)^2")
109print(f" optimum at (2, -1), gradient g = (x-2, y+1)")
110
111start = [0.0, 2.0]
112class_traj, class_evals = class_descent(start)
113quant_traj, quant_evals = quantum_descent(start)
114print(f" classical: {class_evals} function evaluations (4 per step × {N_STEPS} steps)")
115print(f" quantum : {quant_evals} coherent queries (1 per step × {N_STEPS} steps)")
116
117# === 5. Visualisation ===
118fig = plt.figure(figsize=(12.5, 5.4))
119gs = fig.add_gridspec(1, 2, width_ratios=[1.0, 1.05])
120
121ax1 = fig.add_subplot(gs[0, 0], projection="3d")
122xg = np.linspace(-1.0, 4.0, 30)
123yg = np.linspace(-3.0, 3.0, 30)
124X, Y = np.meshgrid(xg, yg)
125ax1.plot_surface(X, Y, f(X, Y), cmap="viridis", alpha=0.55, edgecolor="none")
126ax1.plot(class_traj[:, 0], class_traj[:, 1], f(class_traj[:, 0], class_traj[:, 1]),
127 "-o", color="#ff7f0e", lw=2.0, ms=5,
128 label=f"classical ({class_evals} fn evals)")
129ax1.plot(quant_traj[:, 0], quant_traj[:, 1], f(quant_traj[:, 0], quant_traj[:, 1]),
130 "-^", color="#1f77b4", lw=2.0, ms=5,
131 label=f"quantum ({quant_evals} coherent queries)")
132ax1.scatter([2], [-1], [0], color="red", s=80, marker="*", label="optimum (2,-1)")
133ax1.set_xlabel("x"); ax1.set_ylabel("y"); ax1.set_zlabel("f(x,y)")
134ax1.set_title("trajectories on the 2D quadratic bowl")
135ax1.legend(loc="upper left", fontsize=8)
136
137ax2 = fig.add_subplot(gs[0, 1])
138class_dist = np.linalg.norm(class_traj - OPT, axis=1)
139quant_dist = np.linalg.norm(quant_traj - OPT, axis=1)
140it = np.arange(len(class_dist))
141ax2.plot(it, class_dist, "-o", color="#ff7f0e", lw=2.0, ms=6, label="classical")
142ax2.plot(it, quant_dist, "-^", color="#1f77b4", lw=2.0, ms=6, label="quantum")
143ax2.set_yscale("log")
144ax2.set_xlabel("iteration")
145ax2.set_ylabel(r"$\|x_t - x^*\|_2$ (log)")
146ax2.set_title(f"convergence & query gap = {class_evals}/{quant_evals} = {class_evals/quant_evals:.0f}x")
147ax2.grid(alpha=0.3, which="both")
148ax2.legend(loc="upper right", fontsize=9)
149
150plt.suptitle("ch14 demo 2: convex optimization — classical FD vs Jordan quantum gradient",
151 y=1.02)
152plt.tight_layout()
153out_path = OUT / "02_convex_optimization.png"
154plt.savefig(out_path, dpi=120, bbox_inches="tight")
155plt.close()
156
157# === 6. stdout 关键统计 ===
158print(f"\n classical trajectory: " + ", ".join(
159 f"({x:.2f},{y:.2f})" for x, y in class_traj))
160print(f" quantum trajectory: " + ", ".join(
161 f"({x:.2f},{y:.2f})" for x, y in quant_traj))
162print(f" final classical distance: {class_dist[-1]:.4f}")
163print(f" final quantum distance: {quant_dist[-1]:.4f}")
164print(f" query ratio classical/quantum: {class_evals / quant_evals:.1f}x "
165 f"(d=2 → 4× from 4 fn-eval vs 1 coherent query)")
166print(f" total shots (quantum): {quant_evals * SHOTS}")
167print(f" PNG -> {out_path}")
运行结果¶

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