summaryrefslogtreecommitdiff
path: root/logs/control_probe.py
diff options
context:
space:
mode:
authorYurenHao0426 <Blackhao0426@gmail.com>2026-08-01 19:32:58 -0500
committerYurenHao0426 <Blackhao0426@gmail.com>2026-08-01 19:32:58 -0500
commitef104fe4f07713bf11f266d2954b7446f176f8ae (patch)
tree0ad9a26ca870bac83d3f509ec46be5b2f4048f9a /logs/control_probe.py
parentde827a42e10ede662f4bd2893c4f8b6d54be45dc (diff)
Record the matching battery: fifteen solvers, one amplifier
Adds MATCHING_RESULTS.md. Entropic GW annealed and PATH both reach 0.961 on the reference field, above the 0.958 the project's own pipeline reached after months, and neither had been run. GW reaches 0.881 on the rank-8 field where GRAMPA reaches 0.076 -- which retires the morning's rank-ladder conclusion, since that ladder was run entirely with GRAMPA and GRAMPA degrades on clustered eigenvalues. The hard instance yields to amplification rather than a better solver: descent multiplies a partial answer by about four, so the job is to feed it a start that is 10-20% correct rather than to replace it. Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'logs/control_probe.py')
-rw-r--r--logs/control_probe.py93
1 files changed, 93 insertions, 0 deletions
diff --git a/logs/control_probe.py b/logs/control_probe.py
new file mode 100644
index 0000000..170d731
--- /dev/null
+++ b/logs/control_probe.py
@@ -0,0 +1,93 @@
+"""Control: is the FGW warm-homotopy win driven by TLB *information*, or by any
+linear term at all (generic symmetry-breaking)? Three linear costs:
+
+ tlb -- the proposed distance-profile cost
+ tlbperm -- the same matrix with its columns randomly permuted: identical
+ value distribution, correspondence information destroyed
+ rand -- iid uniform noise cost
+
+Plus 3 trials, cold and warm sweeps, blind selection by E(P).
+"""
+from __future__ import annotations
+import json, sys, time
+sys.path.insert(0, "/home/yurenh2/emm")
+import numpy as np, ot, torch
+from scipy.optimize import linear_sum_assignment
+from worldalign.synth_fast_gate import ClosedFormEnergy, fast_pair_descent
+from worldalign.synth_triangle_gate import standardized
+
+DEV = torch.device("cuda:1")
+ALPHAS = [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95, 1.0]
+
+
+def standardise(m):
+ mask = ~np.eye(len(m), dtype=bool); v = m[mask]
+ o = (m - v.mean()) / v.std(); np.fill_diagonal(o, 0.0); return o
+
+
+def tlb(V, S):
+ n = len(V); a = np.sort(V, 1); b = np.sort(S, 1)
+ return (a * a).sum(1)[:, None] / n + (b * b).sum(1)[None, :] / n - 2 * (a @ b.T) / n
+
+
+def main():
+ path = "/home/yurenh2/emm/artifacts/synth_v1/omit_size.pt"
+ st = torch.load(path, map_location="cpu", weights_only=False)
+ V = standardise(st["visual_field"].double().numpy())
+ T = standardise(st["text_field"].double().numpy())
+ n = len(V); w = ot.unif(n)
+ rows = []
+ for trial in range(3):
+ rng = np.random.default_rng(trial); hid = rng.permutation(n)
+ S = T[np.ix_(hid, hid)]
+ Vg = standardized(torch.from_numpy(V).to(DEV)).double()
+ Sg = standardized(torch.from_numpy(S).to(DEV)).double()
+ en = ClosedFormEnergy(Sg, Vg, 1.0, 0.0, 64)
+ et = float(en.energy(torch.from_numpy(
+ np.ascontiguousarray(np.argsort(hid))).to(DEV)[None])[0])
+
+ def acc(c): return float((hid[c] == np.arange(n)).mean())
+
+ def refine(c):
+ f = fast_pair_descent(Sg, Vg,
+ torch.from_numpy(np.ascontiguousarray(c)).to(DEV),
+ 600).cpu().numpy()
+ e = float(en.energy(torch.from_numpy(np.ascontiguousarray(f)).to(DEV)[None])[0])
+ return e, acc(f)
+
+ base = tlb(V, S)
+ shuf = np.random.default_rng(100 + trial).permutation(n)
+ variants = {
+ "tlb": base / base.max(),
+ "tlbperm": base[:, shuf] / base.max(),
+ "rand": np.random.default_rng(200 + trial).random((n, n)),
+ }
+ for vname, M in variants.items():
+ for mode in ("cold", "warm"):
+ G = np.outer(w, w); recs = []
+ for a in ALPHAS:
+ g0 = None if mode == "cold" else G
+ G2 = ot.gromov.fused_gromov_wasserstein(
+ M, V, S, w, w, "square_loss", alpha=a, G0=g0,
+ max_iter=200, tol_rel=1e-9)
+ if mode == "warm":
+ G = G2
+ _, cols = linear_sum_assignment(-G2)
+ e, ar = refine(cols)
+ recs.append({"alpha": a, "raw": acc(cols), "refined": ar, "E": e})
+ best = min(recs, key=lambda x: x["E"])
+ row = {"trial": trial, "cost": vname, "mode": mode,
+ "E_truth": et, "blind_refined": best["refined"],
+ "blind_alpha": best["alpha"], "blind_E": best["E"],
+ "oracle_refined": max(x["refined"] for x in recs),
+ "curve": [round(x["refined"], 4) for x in recs]}
+ rows.append(row)
+ print(json.dumps(row), flush=True)
+ scratch = ("/tmp/claude-1273071/-home-yurenh2-emm/"
+ "bb97201d-d9b5-46e6-82fc-981d37e7ab98/scratchpad/")
+ json.dump(rows, open(scratch + "control.json", "w"))
+ print(json.dumps({"done": True}))
+
+
+if __name__ == "__main__":
+ main()