InteractiveQAOATool / src /generate_figures.py
speederracer123's picture
Upload 17 files
d3527de verified
Raw
History Blame Contribute Delete
9.86 kB
"""
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.")