summaryrefslogtreecommitdiff
path: root/ep_run/eig_v2_smoke.py
diff options
context:
space:
mode:
authorYuren Hao <yurenh2@illinois.edu>2026-07-03 07:57:22 -0500
committerYuren Hao <yurenh2@illinois.edu>2026-07-03 07:57:22 -0500
commitbcec9560cf5c9b113e9381a52d1a941daa8865f2 (patch)
treebae3baf6d742b816d90e642d70b9744a86a4d189 /ep_run/eig_v2_smoke.py
parentc0b507fb1760be291e1e1ed33f33fb18f16d8c2d (diff)
omega/norm-family refuted as stability signal; fingerprint story retracted; eigreg v2 = true map-eigenvalue (spec_penalty)HEADmaster
- eig_control: fix plain-PI bug (shifted PI for lambda_max of indefinite Sym); add lead_rho + spec_penalty (soft one-sided cap on |lam|(I+eps*J_F), 2-D Rayleigh-Ritz, matvec-only) — aep 'spectral' ported. eig_penalty demoted to diagnostic. - eig_recheck.py (Lanczos audit): omega=+5..+13 on ALL operators incl the stablest (s2000 +12.8 while true alpha=-0.02); gap omega-alpha~10; old 'warm -10.14 vs scratch +1.11' numbers were PI-mixture artifacts. RETRACTED. - eig_v2_smoke/depth: v2 mechanics validated vs ARPACK; z_T1 readings >1 are unconverged-state contamination (150: 1.009 -> 400/800: 0.997-0.999, mu=-0.02..-0.006 matching eig_probe); fixed-point top = BAND of slow modes. - lt_ep_train: --eigreg now spec_penalty (--eig_margin 0.995 = rho target); --fingerprint reports rho/Re_mu instead of num_abscissa. - ONBOARDING §4-7 + FINDINGS 2026-07-03: retraction + verdict (fundamental quantity = finite-horizon path LE / resreg axis; de-cliff via floss-ept; spec_penalty = measure-mode scalpel for a detaching Hopf pair). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FAPDWQ49M5Ye3NpTndTpn
Diffstat (limited to 'ep_run/eig_v2_smoke.py')
-rw-r--r--ep_run/eig_v2_smoke.py56
1 files changed, 56 insertions, 0 deletions
diff --git a/ep_run/eig_v2_smoke.py b/ep_run/eig_v2_smoke.py
new file mode 100644
index 0000000..241be7d
--- /dev/null
+++ b/ep_run/eig_v2_smoke.py
@@ -0,0 +1,56 @@
+"""Validate lead_rho/spec_penalty (eigreg v2, TRUE map-eigenvalue) against ARPACK on the same operator.
+Checks, per operator (s2000 warm source + rand-init):
+ 1. lead_rho's (rho, Re mu) vs scipy eigs LM top eigenvalues of M = I + eps*J_F (gold standard)
+ 2. cross-check vs eig_probe's known s2000 number (Re mu ~ -0.02)
+ 3. spec_penalty fires (non-empty grads, finite) when rho > target, stays off when below
+"""
+import numpy as np, torch, scipy.sparse.linalg as sla
+from torch.autograd.functional import jvp
+import lt_ep_train as L
+from eig_control import lead_rho, spec_penalty
+
+T1, EPS, B, C = 150, 0.1, 6, 1.0
+
+
+def load_op(path):
+ torch.manual_seed(0)
+ blk = L.EQBlock(512, 16, 256, 256, c=C, attn_mode='thick'); blk.qknorm = True
+ if path:
+ ck = torch.load(path, map_location=L.dev)
+ with torch.no_grad():
+ for p, w in zip(blk.allp, ck['allp']):
+ p.copy_(w.to(L.dev))
+ return blk
+
+
+def arpack_map(blk, z, k=4):
+ sh, n = z.shape, z.numel()
+ kk = 1.0 - EPS * (1.0 + C)
+
+ def mv(x):
+ v = torch.from_numpy(np.asarray(x, dtype=np.float32)).to(L.dev).view(sh)
+ with torch.no_grad():
+ Mv = kk * v + EPS * jvp(blk.nc_force, z, v)[1]
+ return Mv.reshape(-1).double().cpu().numpy()
+
+ A = sla.LinearOperator((n, n), matvec=mv, dtype=np.float64)
+ vals = sla.eigs(A, k=k, which='LM', return_eigenvectors=False, maxiter=2000, tol=1e-4)
+ return sorted(vals, key=lambda x: -abs(x))
+
+
+for name, path in [('s2000', 'runs/redx_traj/s2000.pt'), ('rand-init', None)]:
+ blk = load_op(path)
+ torch.manual_seed(42)
+ idx, _ = L.get_batch('train', B, 256)
+ xin = blk.embed(idx).detach()
+ zs = L.relax(blk, xin.clone(), xin, T1, EPS)
+ _, rho, mu = lead_rho(blk, zs, EPS, C, {}, iters=40)
+ top = arpack_map(blk, zs)
+ lam0 = top[0]
+ print(f"[{name}] lead_rho: rho={rho:.5f} Re_mu={mu:+.4f}", flush=True)
+ print(f"[{name}] ARPACK : " + " ".join(f"|l|={abs(l):.5f}({l.real:+.4f}{l.imag:+.4f}j)" for l in top[:3]))
+ print(f"[{name}] agree : d_rho={abs(rho - abs(lam0)):.4f} d_Remu={abs(mu - (lam0.real - 1) / EPS):.4f}")
+ g, r0, m0 = spec_penalty(blk, zs, EPS, C, 0.1, 0.995, {}, iters=40)
+ fin = all(torch.isfinite(v).all().item() for v in g.values()) if g else True
+ print(f"[{name}] penalty : rho={r0:.5f} fired={bool(g)} n_grads={len(g)} finite={fin}\n", flush=True)
+print("EIG_V2_SMOKE_DONE", flush=True)