Files
jspace/scripts/fig_phase.py
T

78 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Phase diagram of frozen-band recurrence: dynamics dial vs fidelity.
x: spectral radius rho(A) of the trained state map (log scale)
y: substrate fidelity = easy-bucket pass@1 at k=4 (free-running generation)
label: hard-bucket pass@1 at k=4 (the gain every regime buys)
All points: same frozen E2B band, same data, same 250-item eval, e400.
"""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
OUT = Path(__file__).resolve().parent.parent / "results-loop"
# name rho easy hard color
# name rho(plot x) easy hard color dy
PTS = [
("untrained α-merge\n(training-free)\nhard 14%", 0.40, 0.885, 0.143, "#8a8f98", -52),
("trained merge\n(anchored B, curriculum)", 0.26, 0.885, 0.464, "#2b6cb0", -60),
("per-depth merges\n(time-varying, anchored)\nhard 36% (depth-stranded)", 0.325, 0.893, 0.357, "#805ad5", 18),
("Parcae rec\n(ρ<1 enforced, learned B)", 0.292, 0.713, 0.429, "#2f855a", -58),
("unconstrained rec\n(learned A,B)", 4.5, 0.697, 0.393, "#c53030", 16),
]
# x jittered around true rho=0.3 for the three anchored arms (visibility)
# untrained merge x offset for visibility (true rho = 0.30)
fig, ax = plt.subplots(figsize=(7.6, 5.2))
ax.set_xscale("log")
ax.axvspan(1.0, 20, color="#fff5f5", zorder=0)
ax.axvline(1.0, color="#c53030", lw=1.2, ls="--")
ax.text(1.06, 0.99, "ρ = 1 stability boundary", rotation=90, fontsize=8,
color="#c53030", va="top")
ax.text(3.1, 0.615, "norm projection converts\nexplosion → stationary churn\n"
"(training survives, substrate pays)", fontsize=8, color="#c53030",
ha="center")
for name, rho, easy, hard, c, dy in PTS:
ax.scatter(rho, easy, s=90 + 900 * hard, color=c, alpha=0.85, zorder=3,
edgecolor="white", linewidth=1.5)
lbl = name if ("stranded" in name or "14%" in name) else f"{name}\nhard {hard:.0%}"
dx = 62 if "14%" in name else 0
ax.annotate(lbl,
(rho, easy), textcoords="offset points", xytext=(dx, dy),
ha="center", fontsize=8.5, color=c)
ax.annotate("", xy=(0.278, 0.735), xytext=(0.288, 0.862),
arrowprops=dict(arrowstyle="->", color="#2f855a", lw=1.3))
ax.text(0.46, 0.775, "learned B + no curriculum:\nfixed point leaves the\n"
"substrate manifold", fontsize=8, color="#2f855a", ha="left")
ax.set_xlim(0.2, 12)
ax.set_ylim(0.58, 1.0)
ax.set_xlabel("ρ(A) — spectral radius of the trained state map (dynamics dial)")
ax.set_ylabel("substrate fidelity — easy-bucket pass@1 at k=4")
ax.set_title("Frozen-band recurrence phase diagram: stability ≠ fidelity\n"
"(marker size ∝ hard-bucket gain; every regime buys the same "
"~4046%, only one keeps the substrate)", fontsize=10.5)
ax.grid(True, color="#ececec", lw=0.7, which="both")
ax.set_axisbelow(True)
for s in ("top", "right"):
ax.spines[s].set_visible(False)
fig.text(0.13, -0.06,
"Same frozen gemma-4-E2B band (L1430), same MBPP data, same "
"250-item eval, e400 checkpoints. ρ from checkpoints (power "
"iteration / diag max). Dynamics (x) and fixed-point location "
"(y) are independent dials: contraction guarantees convergence "
"and certified tail gradients, but only anchored content — fixed "
"B=(1−α)I, zero-init correction, difficulty→depth curriculum — "
"keeps the fixed point substrate-preserving.", fontsize=7.5,
color="#555", wrap=True)
fig.tight_layout()
fig.savefig(OUT / "fig_phase.png", dpi=140, facecolor="white",
bbox_inches="tight")
print("wrote", OUT / "fig_phase.png")