Spaces:
Runtime error
Runtime error
File size: 9,860 Bytes
d3527de | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | """
Generate high-resolution journal-quality figures for the IEEE QSEEC paper.
Run from the qaoa_pedagogical_tool/ directory:
python generate_figures.py
Output: paper_figures/
fig1_circuit_standard.pdf - Standard 3-qubit QAOA circuit (full)
fig2_circuit_equality.pdf - Equality-constrained circuit (full)
fig3_circuit_inequality.pdf - Inequality / slack-variable circuit (full)
fig4_topology.pdf - NetworkX qubit-interaction topologies (3 panels)
fig5_phase_disconnect.pdf - sv_disc at 3 stages (init / cost / mixer)
fig6_convergence.pdf - COBYLA convergence + final probability bar chart
"""
import sys, os
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import networkx as nx
# Make project root importable
_ROOT = os.path.dirname(os.path.abspath(__file__))
if _ROOT not in sys.path:
sys.path.insert(0, _ROOT)
from core.costs import cost_standard, cost_equality, cost_inequality
from core.simulator import simulate_qaoa, get_statevector_after_init, get_statevector_after_cost
from core.optimizer import run_cobyla
from viz.circuits import draw_circuit
from sv_disc import sv_disc
_OUT = os.path.join(_ROOT, "paper_figures")
os.makedirs(_OUT, exist_ok=True)
DPI = 300
_FONT = {"family": "serif", "size": 9}
matplotlib.rc("font", **_FONT)
matplotlib.rc("text", usetex=False) # set True if LaTeX is available on system
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# FIG 1-3 QAOA Circuit Diagrams
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def fig_circuits():
for mode, fname in [
("standard", "fig1_circuit_standard.pdf"),
("equality", "fig2_circuit_equality.pdf"),
("inequality", "fig3_circuit_inequality.pdf"),
]:
fig = draw_circuit("full", mode, scale=0.75)
fig.savefig(os.path.join(_OUT, fname), dpi=DPI, bbox_inches="tight")
plt.close(fig)
print(f" saved {fname}")
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# FIG 4 Qubit Interaction Topology (NetworkX)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def fig_topology():
fig, axes = plt.subplots(1, 3, figsize=(7, 2.4))
titles = ["Standard\n(Sparse)", "Equality\n(Kβ full)", "Inequality\n(Kβ + slack)"]
node_labels = [
{0: "xβ", 1: "xβ", 2: "xβ"},
{0: "xβ", 1: "xβ", 2: "xβ"},
{0: "xβ", 1: "xβ", 2: "xβ", 3: "s"},
]
edges = [
[(1, 2)], # standard: only Z2Z3
[(0, 1), (1, 2), (0, 2)], # equality: K3
[(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)], # inequality: K4
]
colors = ["#6366F1", "#7C3AED", "#0F766E"]
layouts = [
{0: (0, 0), 1: (1, 0), 2: (2, 0)}, # line
{0: (0, 0), 1: (1, 1), 2: (2, 0)}, # triangle
{0: (0, 1), 1: (1, 2), 2: (2, 1), 3: (1, 0)}, # diamond
]
for ax, title, labels, edg, col, pos in zip(axes, titles, node_labels, edges, colors, layouts):
n = len(labels)
G = nx.Graph()
G.add_nodes_from(range(n))
G.add_edges_from(edg)
nx.draw(G, pos=pos, ax=ax, with_labels=True, labels=labels,
node_color=col, node_size=600, font_color="white",
font_size=8, font_weight="bold",
edge_color="#334155", width=2)
ax.set_title(title, fontsize=9, fontweight="bold", pad=6)
fig.suptitle("Qubit Interaction Topologies (Cost Layer)", fontsize=10, fontweight="bold", y=1.02)
fig.tight_layout()
fname = "fig4_topology.pdf"
fig.savefig(os.path.join(_OUT, fname), dpi=DPI, bbox_inches="tight")
plt.close(fig)
print(f" saved {fname}")
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# FIG 5 PhaseβProbability Disconnect (sv_disc, 3 stages)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def fig_phase_disconnect():
GAMMA = 3.14
BETA = 0.5
sv_init = get_statevector_after_init(3)
sv_cost = get_statevector_after_cost(GAMMA, cost_standard, 3)
_, _, _, sv_full = simulate_qaoa(GAMMA, BETA, cost_standard, 3, shots=1024, seed=42)
stages = [
(sv_init, "Stage A: Init\n(H gates only)"),
(sv_cost, f"Stage B: Cost Layer\n(Ξ³ = {GAMMA:.2f})"),
(sv_full, f"Stage C: After Mixer\n(Ξ³ = {GAMMA:.2f}, Ξ² = {BETA:.2f})"),
]
fig = plt.figure(figsize=(10, 3.2))
outer = gridspec.GridSpec(1, 3, figure=fig, wspace=0.05)
for col_idx, (sv, title) in enumerate(stages):
# draw sv_disc into a sub-figure
sub_fig = sv_disc(sv, show_labels=True, phase_colors=True, num_columns=4)
sub_axes = sub_fig.get_axes()
inner = gridspec.GridSpecFromSubplotSpec(
len(sub_axes) // 4, 4, subplot_spec=outer[col_idx], wspace=0, hspace=0
)
# Copy patches and lines from sv_disc sub-figure into main figure axes
for i, ax_src in enumerate(sub_axes):
ax_dst = fig.add_subplot(inner[i // 4, i % 4])
ax_dst.set_xlim(ax_src.get_xlim())
ax_dst.set_ylim(ax_src.get_ylim())
ax_dst.set_aspect("equal")
ax_dst.set_axis_off()
for patch in ax_src.patches:
import copy
ax_dst.add_patch(copy.copy(patch))
for line in ax_src.lines:
ax_dst.add_line(copy.copy(line))
for txt in ax_src.texts:
ax_dst.text(*txt.get_position(), txt.get_text(),
ha=txt.get_ha(), va=txt.get_va(),
fontsize=txt.get_fontsize())
plt.close(sub_fig)
# Column title
fig.text(
(col_idx + 0.5) / 3, 1.02, title,
ha="center", va="bottom", fontsize=9, fontweight="bold",
transform=fig.transFigure,
)
fig.suptitle("PhaseβProbability Disconnect in QAOA (Standard, 3 Qubits)",
fontsize=10, fontweight="bold", y=1.08)
fname = "fig5_phase_disconnect.pdf"
fig.savefig(os.path.join(_OUT, fname), dpi=DPI, bbox_inches="tight")
plt.close(fig)
print(f" saved {fname}")
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# FIG 6 COBYLA Convergence + Final Probability Bar Chart
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def fig_convergence():
print(" running COBYLA for standard module (fig6)β¦")
_, hist = run_cobyla(cost_standard, 3, mode="standard")
g_fin = hist["gamma"][-1]; b_fin = hist["beta"][-1]
probs_fin, _, cv_fin, _ = simulate_qaoa(g_fin, b_fin, cost_standard, 3,
shots=8192, seed=999)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7.5, 2.8))
# Convergence curve
ax1.plot(hist["energy"], color="#4F46E5", linewidth=1.8, marker="o",
markersize=3, label="β¨Cβ©")
ax1.set_xlabel("COBYLA Iteration", fontsize=9)
ax1.set_ylabel("Expected Energy β¨Cβ©", fontsize=9)
ax1.set_title("Variational Convergence", fontsize=9, fontweight="bold")
ax1.grid(True, linestyle="--", alpha=0.4)
ax1.legend(fontsize=8)
# Probability bar chart
n = 3
states = [f"|{format(i, f'0{n}b')}β©" for i in range(2 ** n)]
min_c = float(np.min(cv_fin))
bar_colors = ["#4F46E5" if np.isclose(c, min_c) else "#CBD5E1" for c in cv_fin]
ax2.bar(states, probs_fin, color=bar_colors, edgecolor="white", linewidth=0.5)
ax2.set_xlabel("Basis State", fontsize=9)
ax2.set_ylabel("Probability", fontsize=9)
ax2.set_title("Final Measurement Distribution", fontsize=9, fontweight="bold")
ax2.set_ylim(0, 1)
ax2.tick_params(axis="x", rotation=45, labelsize=7)
ax2.grid(axis="y", linestyle="--", alpha=0.4)
fig.tight_layout()
fname = "fig6_convergence.pdf"
fig.savefig(os.path.join(_OUT, fname), dpi=DPI, bbox_inches="tight")
plt.close(fig)
print(f" saved {fname}")
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if __name__ == "__main__":
print("Generating paper figures β paper_figures/")
fig_circuits()
fig_topology()
fig_phase_disconnect()
fig_convergence()
print("All figures saved.")
|