summaryrefslogtreecommitdiff
path: root/artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py
diff options
context:
space:
mode:
authorYurenHao0426 <Blackhao0426@gmail.com>2026-08-01 21:37:55 -0500
committerYurenHao0426 <Blackhao0426@gmail.com>2026-08-01 21:37:55 -0500
commit22acd2899958def0d103f11da49c2c4a499be773 (patch)
tree892b422c1e04cd9854420eda0c965e21df81c8db /artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py
parentef104fe4f07713bf11f266d2954b7446f176f8ae (diff)
The missing term was unary: omit-size solved at its ceiling in 1.4s
Adversarial review of the artifacts found three of my numbers to be artifacts of my own code. All three reproduced here before acceptance: - anchor_bound presented probe rows in the same index order on both sides, so exact twins had their tie broken onto the diagonal. 0.997 -> 0.920 on omit-size. Fixed by scrambling the T-side presentation. - The truth is not a strict local minimum: 51 transpositions have exactly zero energy delta. fast_pair_descent only looked stationary because its break test treats zero as no-improvement. - scipy's FAQ takes no n_init, so it was swallowed into unknown_options and 'FAQ x30 restarts' computed bit-identically to plain FAQ. Replaced with a real restart loop over P0='randomized'. The blind ceiling for omit-size is 0.836, not 1.0: the text field has 51 exact transposition automorphisms, so T[s,s] is bitwise identical to T and no objective f(V, P T P^T) can separate an orbit at any order. Every synthetic accuracy was being divided by the wrong denominator. The fifteen failed solvers share one property -- all purely quadratic or purely spectral, none with a node-level term. Eight moments of each node's own field row, blended with the quadratic term through Frank-Wolfe, reach 0.837 with an energy gap of exactly zero. The term must stay in the loop: as a seed for pure-quadratic descent it scores 0.21, pinned through the iterations it scores 0.84 -- which is also why amplification plateaued, being itself pure-quadratic. Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py')
-rw-r--r--artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py78
1 files changed, 78 insertions, 0 deletions
diff --git a/artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py b/artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py
new file mode 100644
index 0000000..d97d239
--- /dev/null
+++ b/artifacts/spectral_frontier_probe/eval_voteanchor_vs_bestE.py
@@ -0,0 +1,78 @@
+"""Replication with BLIND energy selection over the (m,K) grid, two fresh hidden perms."""
+import sys, time, numpy as np, torch
+sys.path.insert(0, '/home/yurenh2/emm')
+from scipy.optimize import linear_sum_assignment
+from scipy.stats import ortho_group
+from worldalign.synth_fast_gate import fast_pair_descent
+
+dev = 'cuda:0'
+def standardise(M):
+ M = np.asarray(M, dtype=np.float64); m = ~np.eye(len(M), dtype=bool); v = M[m]
+ o = (M - v.mean())/v.std(); np.fill_diagonal(o, 0.0); return o
+d = torch.load('/home/yurenh2/emm/artifacts/synth_v1/omit_size.pt', map_location='cpu', weights_only=False)
+V = standardise(d['visual_field']); T0 = standardise(d['text_field']); N = len(V)
+Vt = torch.tensor(V, dtype=torch.float32, device=dev)
+wV, UV = np.linalg.eigh(V); wV = wV[::-1]; UV = UV[:, ::-1]
+
+def hung(A, B):
+ C = ((A**2).sum(1)[:, None] + (B**2).sum(1)[None, :] - 2*A@B.T)
+ r, c = linear_sum_assignment(C); return c
+def icp(XV, XT, O, iters=25):
+ for _ in range(iters):
+ p = hung(XV, XT@O); u, s, vt = np.linalg.svd(XT[p].T@XV); On = u@vt
+ if np.allclose(On, O, atol=1e-10): O = On; break
+ O = On
+ return hung(XV, XT@O)
+
+for SEED in (11, 23):
+ rng = np.random.default_rng(SEED); sigma = rng.permutation(N)
+ T = T0[np.ix_(sigma, sigma)]; truth = np.argsort(sigma)
+ acc = lambda p: float((np.asarray(p) == truth).mean())
+ Tt = torch.tensor(T, dtype=torch.float32, device=dev)
+ CONST = float((Tt*Tt).sum() + (Vt*Vt).sum())
+ def energy(p):
+ P = torch.as_tensor(np.asarray(p), dtype=torch.long, device=dev)
+ return (CONST - 2.0*float((Tt[P[:, None], P[None, :]]*Vt).sum()))/(N*(N-1))
+ def descend(p, steps=4000):
+ P = torch.as_tensor(np.ascontiguousarray(p), dtype=torch.long, device=dev)
+ return fast_pair_descent(Tt, Vt, P, steps).cpu().numpy()
+ wT, UT = np.linalg.eigh(T); wT = wT[::-1]; UT = UT[:, ::-1]
+ t0 = time.time(); sols = []
+ for r in (8, 10, 12, 14, 16):
+ XV = UV[:, :r]*np.sqrt(np.abs(wV[:r])); XT = UT[:, :r]*np.sqrt(np.abs(wT[:r]))
+ for _ in range(30):
+ p = icp(XV, XT, ortho_group.rvs(r, random_state=int(rng.integers(1 << 30))))
+ sols.append((energy(p), p, r))
+ sols.sort(key=lambda z: z[0])
+ print(f"\n##### hidden seed {SEED}: {len(sols)} ICP sols in {time.time()-t0:.0f}s, "
+ f"E(truth)={energy(truth):.4f}, best member E={sols[0][0]:.4f} acc={acc(sols[0][1]):.3f}", flush=True)
+ A = [descend(p) for _, p, _ in sols[:5]]
+ bA = min(A, key=energy)
+ print(f" ROUTE A (descend 5 lowest-E, blind pick): E={energy(bA):.4f} acc={acc(bA):.3f}", flush=True)
+
+ def expand(aL, aR):
+ pr = np.setdiff1d(np.arange(N), aL); prR = np.setdiff1d(np.arange(N), aR)
+ L = V[np.ix_(pr, aL)]; R = T[np.ix_(prR, aR)]
+ L = (L-L.mean(1, keepdims=True))/L.std(1, keepdims=True).clip(1e-9)
+ R = (R-R.mean(1, keepdims=True))/R.std(1, keepdims=True).clip(1e-9)
+ S = L@R.T/L.shape[1]; _, c = linear_sum_assignment(-S)
+ full = np.zeros(N, dtype=int); full[aL] = aR; full[pr] = prR[c]; return full
+ grid = []
+ for m in (10, 30, 60):
+ votes = np.zeros((N, N))
+ for _, p, _ in sols[:m]: votes[np.arange(N), p] += 1
+ conf = votes.max(1) - np.partition(votes, -2, axis=1)[:, -2]
+ order = np.argsort(-conf); pm = votes.argmax(1)
+ for K in (12, 25, 50, 100):
+ sel = order[:K]; part = pm[sel]; keep = np.zeros(len(sel), bool); seen = set()
+ for i, c in enumerate(part):
+ if c not in seen: seen.add(c); keep[i] = True
+ sel, part = sel[keep], part[keep]
+ prec = float((part == truth[sel]).mean())
+ e = expand(sel, part); pd = descend(e)
+ grid.append((energy(pd), acc(pd), m, K, prec, acc(e)))
+ print(f" m={m:2d} K={K:3d} prec={prec:.3f} expand={acc(e):.3f} -> descent acc={acc(pd):.3f} E={energy(pd):.4f}", flush=True)
+ grid.sort()
+ print(f" ROUTE B (blind pick by energy over the 12 cells): m={grid[0][2]} K={grid[0][3]} "
+ f"E={grid[0][0]:.4f} acc={grid[0][1]:.3f} [best cell by accuracy was {max(g[1] for g in grid):.3f}]", flush=True)
+ print(f" wall {time.time()-t0:.0f}s", flush=True)