| """Generate figures from results/*.json into paper/figures/.""" |
| from __future__ import annotations |
|
|
| import json |
| from pathlib import Path |
|
|
| import matplotlib |
| matplotlib.use("Agg") |
| import matplotlib.pyplot as plt |
|
|
| ROOT = Path(__file__).resolve().parent.parent |
| RESULTS = ROOT / "results" |
| FIGS = ROOT / "figures" |
|
|
| ARM_STYLE = { |
| "no_memory": ("No memory (schema draft)", "#888888", "o", "--"), |
| "static_global": ("Static datastore (ToolSpec-style)", "#d1495b", "s", "-"), |
| "personal_memory": ("Ours (personal + evict)", "#1b6ca8", "D", "-"), |
| } |
|
|
|
|
| def plot_acceptance(tag=""): |
| fname = f"{tag}_accept_results.json" if tag else "accept_results.json" |
| data = json.loads((RESULTS / fname).read_text()) |
| summary = data["summary"] |
| n_sessions = data["config"]["sessions"] |
|
|
| fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 4.2)) |
| for arm, (label, color, marker, ls) in ARM_STYLE.items(): |
| if arm not in summary: |
| continue |
| xs = sorted(int(s) for s in summary[arm]) |
| mat = [summary[arm][str(s)]["MAT"] for s in xs] |
| frac = [summary[arm][str(s)]["accepted_frac"] for s in xs] |
| ax1.plot(xs, mat, marker=marker, ls=ls, color=color, label=label) |
| ax2.plot(xs, frac, marker=marker, ls=ls, color=color, label=label) |
|
|
| for ax, ylab, title in ((ax1, "Mean accepted tokens (MAT)", |
| "Draft acceptance vs. session"), |
| (ax2, "Accepted fraction of target", |
| "Accepted fraction vs. session")): |
| ax.set_xlabel("Session index") |
| ax.set_ylabel(ylab) |
| ax.set_title(title) |
| ax.grid(alpha=0.3) |
| ax.axvspan(-0.4, 0.4, color="k", alpha=0.05) |
| ax1.legend(fontsize=8, loc="best") |
| ax1.annotate("warmup", (0, ax1.get_ylim()[1]*0.05), fontsize=7, ha="center") |
| fig.tight_layout() |
| FIGS.mkdir(parents=True, exist_ok=True) |
| fig.savefig(FIGS / "acceptance_by_session.pdf") |
| fig.savefig(FIGS / "acceptance_by_session.png", dpi=140) |
| print("wrote acceptance_by_session.{pdf,png}") |
|
|
|
|
| def plot_safety(): |
| """Two-panel safety figure over the three execution policies: |
| (left) severity-weighted cost of being wrong; (right) safe spec-executions |
| preserved (the latency win) and bad irreversible actions.""" |
| data = json.loads((RESULTS / "safety_results.json").read_text()) |
| policies = ["naive_exec", "conf_gate", "gated_exec"] |
| labels = ["Naive\nspec-exec", "Confidence\ngate", "Idempotency\ngate (ours)"] |
| colors = ["#d1495b", "#e8a33d", "#1b6ca8"] |
|
|
| wcost = [data[p]["weighted_cost"] for p in policies] |
| bad = [data[p]["bad_irreversible_actions"] for p in policies] |
| safe = [data[p]["safe_spec_executions"] for p in policies] |
| x = list(range(len(policies))) |
|
|
| fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4.1)) |
|
|
| ax1.bar(x, wcost, color=colors) |
| for i, w in enumerate(wcost): |
| ax1.text(i, w + max(wcost) * 0.02, f"{w:.0f}", ha="center", |
| fontsize=10, fontweight="bold", color=colors[i]) |
| ax1.set_xticks(x) |
| ax1.set_xticklabels(labels, fontsize=8.5) |
| ax1.set_ylabel("Severity-weighted cost of being wrong") |
| ax1.set_title("Cost of wrong irreversible speculative executions") |
| ax1.grid(axis="y", alpha=0.3) |
|
|
| ax2.bar(x, safe, color="#1b6ca8", label="Safe spec-executions (latency win)") |
| ax2.bar(x, bad, bottom=safe, color="#d1495b", |
| label="Bad irreversible actions") |
| for i, b in enumerate(bad): |
| ax2.text(i, safe[i] + b + max(safe) * 0.02, |
| f"{b:.0f} bad", ha="center", fontsize=8.5, |
| color="#d1495b", fontweight="bold") |
| ax2.set_xticks(x) |
| ax2.set_xticklabels(labels, fontsize=8.5) |
| ax2.set_ylabel("Speculative executions (mean/stream)") |
| ax2.set_title("Latency win preserved vs. harm incurred") |
| ax2.legend(fontsize=8, loc="upper right") |
| ax2.grid(axis="y", alpha=0.3) |
|
|
| fig.tight_layout() |
| FIGS.mkdir(parents=True, exist_ok=True) |
| fig.savefig(FIGS / "safety.pdf") |
| fig.savefig(FIGS / "safety.png", dpi=140) |
| print("wrote safety.{pdf,png}") |
|
|
|
|
| if __name__ == "__main__": |
| import sys |
| tag = sys.argv[1] if len(sys.argv) > 1 else "" |
| plot_acceptance(tag) |
| plot_safety() |
|
|