summaryrefslogtreecommitdiff
path: root/logs
diff options
context:
space:
mode:
Diffstat (limited to 'logs')
-rw-r--r--logs/amp_probe.log10
-rw-r--r--logs/amp_probe.py166
-rw-r--r--logs/amplify.log6
-rw-r--r--logs/amplify160.log7
-rw-r--r--logs/amplify640.log6
-rw-r--r--logs/battery2.log3
6 files changed, 198 insertions, 0 deletions
diff --git a/logs/amp_probe.log b/logs/amp_probe.log
new file mode 100644
index 0000000..6ee836b
--- /dev/null
+++ b/logs/amp_probe.log
@@ -0,0 +1,10 @@
+REF grampa 0.0195 umeyama 0.0078 chance 0.0039
+trial0 mf scale=0 raw mean 0.0205 max 0.0234 | refined mean 0.0220 max 0.0273 | sharp 0.851 (79.4s)
+trial0 iid scale=0.0001 raw mean 0.0200 max 0.0234 | refined mean 0.0190 max 0.0234 | sharp 0.851 (27.0s)
+trial0 iid scale=0.01 raw mean 0.0200 max 0.0234 | refined mean 0.0215 max 0.0312 | sharp 0.852 (95.5s)
+trial0 iid scale=0.1 raw mean 0.0205 max 0.0234 | refined mean 0.0200 max 0.0273 | sharp 0.851 (277.2s)
+trial0 iid scale=0.3 raw mean 0.0127 max 0.0195 | refined mean 0.0151 max 0.0234 | sharp 0.783 (116.4s)
+trial0 iid scale=1 raw mean 0.0151 max 0.0195 | refined mean 0.0142 max 0.0195 | sharp 0.857 (141.2s)
+trial0 iid scale=3 raw mean 0.0083 max 0.0195 | refined mean 0.0088 max 0.0156 | sharp 0.810 (130.5s)
+trial0 iid scale=10 raw mean 0.0093 max 0.0156 | refined mean 0.0107 max 0.0234 | sharp 0.840 (331.4s)
+trial0 iid scale=100 raw mean 0.0107 max 0.0117 | refined mean 0.0117 max 0.0195 | sharp 0.859 (202.1s)
diff --git a/logs/amp_probe.py b/logs/amp_probe.py
new file mode 100644
index 0000000..2969348
--- /dev/null
+++ b/logs/amp_probe.py
@@ -0,0 +1,166 @@
+"""Adversarial probe of the Onsager-corrected Sinkhorn (adaptive-TAP) proposal.
+
+Iteration under test: h = beta * V mu T - b_t * mu_prev ; mu = Sinkhorn(exp(h))
+b_t = 0 recovers naive mean field, which is algebraically identical to the PGD
+form of entropic Gromov-Wasserstein already measured at 0.034 on omit-size.
+
+We sweep the Onsager coefficient over ten orders of magnitude so the verdict
+does not depend on getting the R-transform prescription exactly right: if no
+scalar b works, no principled derivation of b can rescue it.
+"""
+import json, sys, time
+import numpy as np
+import torch
+
+sys.path.insert(0, "/home/yurenh2/emm")
+from worldalign.synth_fast_gate import fast_pair_descent
+from worldalign.spectral_match import grampa, umeyama
+
+DEV = torch.device("cuda:0")
+DT = torch.float64
+
+
+def standardise(M):
+ m = ~np.eye(len(M), dtype=bool)
+ v = M[m]
+ out = (M - v.mean()) / v.std()
+ np.fill_diagonal(out, 0.0)
+ return out
+
+
+def sinkhorn_log(h, iters=60):
+ """Doubly stochastic (row and col sums 1) projection of exp(h), batched."""
+ lg = h - h.amax(dim=(-2, -1), keepdim=True)
+ for _ in range(iters):
+ lg = lg - torch.logsumexp(lg, dim=-1, keepdim=True)
+ lg = lg - torch.logsumexp(lg, dim=-2, keepdim=True)
+ return torch.exp(lg)
+
+
+def hutch_chi(h, mu, eps=1e-3, iters=60):
+ """(1/N^2) tr(d Sinkhorn / d h) by a random probe."""
+ Z = torch.randn_like(h)
+ mu2 = sinkhorn_log(h + eps * Z, iters)
+ return ((Z * (mu2 - mu)).sum(dim=(-2, -1)) / (eps * h.shape[-1] ** 2))
+
+
+def r_transform(spec_prod, chi_grid):
+ """Numerical R-transform of an empirical spectrum: invert Stieltjes."""
+ # G(z) = mean 1/(z - l); R(g) = G^{-1}(g) - 1/g
+ lo, hi = spec_prod.max() * 1.0001, spec_prod.max() * 1e6
+ out = []
+ for g in chi_grid:
+ a, b = lo, hi
+ for _ in range(200):
+ m = 0.5 * (a + b)
+ if np.mean(1.0 / (m - spec_prod)) > g:
+ a = m
+ else:
+ b = m
+ z = 0.5 * (a + b)
+ out.append(z - 1.0 / g)
+ return np.array(out)
+
+
+def run_amp(V, T, betas, b_scale, mode, restarts, iters_per_beta, seed,
+ spec_prod=None, damping=0.0):
+ N = V.shape[-1]
+ g = torch.Generator(device=DEV).manual_seed(seed)
+ mu = torch.rand(restarts, N, N, generator=g, device=DEV, dtype=DT) * 0.01
+ mu = sinkhorn_log(mu)
+ mu_prev = torch.full_like(mu, 1.0 / N)
+ traj = []
+ for beta in betas:
+ for _ in range(iters_per_beta):
+ drive = beta * torch.matmul(torch.matmul(V, mu), T)
+ if mode == "mf":
+ b = torch.zeros(restarts, device=DEV, dtype=DT)
+ else:
+ chi = hutch_chi(drive, mu)
+ if mode == "iid":
+ coef = beta * beta * N # sum_j V_ij^2 <T^2> ~ N
+ elif mode == "ri":
+ cg = np.clip(chi.abs().cpu().numpy(), 1e-12, None)
+ coef = torch.tensor(
+ r_transform(spec_prod * beta, cg), device=DEV, dtype=DT)
+ b = b_scale * coef * chi
+ h = drive - b.view(-1, 1, 1) * mu_prev
+ new = sinkhorn_log(h)
+ if damping:
+ new = (1 - damping) * new + damping * mu
+ mu_prev, mu = mu, new
+ if not torch.isfinite(mu).all():
+ return None, traj
+ traj.append(float(mu.amax(dim=-1).mean()))
+ return mu, traj
+
+
+def score(mu, hidden, V_gpu, Tsh_gpu, N, refine=True):
+ from scipy.optimize import linear_sum_assignment
+ accs, refs = [], []
+ for b in range(mu.shape[0]):
+ _, col = linear_sum_assignment(-mu[b].cpu().numpy())
+ a = float((hidden[col] == np.arange(N)).mean())
+ accs.append(a)
+ if refine:
+ fin = fast_pair_descent(
+ Tsh_gpu, V_gpu,
+ torch.from_numpy(np.ascontiguousarray(col)).to(DEV), 600)
+ refs.append(float((hidden[fin.cpu().numpy()] == np.arange(N)).mean()))
+ return accs, refs
+
+
+def main():
+ d = torch.load("/home/yurenh2/emm/artifacts/synth_v1/omit_size.pt",
+ map_location="cpu", weights_only=False)
+ V = standardise(d["visual_field"].double().numpy())
+ T = standardise(d["text_field"].double().numpy())
+ N = len(V)
+ lv = np.linalg.eigvalsh(V)
+ results = []
+
+ for trial in range(2):
+ rng = np.random.default_rng(trial)
+ hidden = rng.permutation(N)
+ Tsh = standardise(T[np.ix_(hidden, hidden)])
+ lt = np.linalg.eigvalsh(Tsh)
+ spec_prod = np.outer(lv, lt).ravel() / N # operator scale of V (x) T / N
+ Vg = torch.from_numpy(V).to(DEV).to(DT)
+ Tg = torch.from_numpy(Tsh).to(DEV).to(DT)
+
+ # sanity: GRAMPA / Umeyama reference on this exact instance
+ if trial == 0:
+ gp = grampa(V, Tsh, 1.0)
+ um = umeyama(V, Tsh)
+ print("REF grampa %.4f umeyama %.4f chance %.4f"
+ % (float((hidden[gp] == np.arange(N)).mean()),
+ float((hidden[um] == np.arange(N)).mean()), 1 / N), flush=True)
+
+ betas_ladder = list(np.geomspace(0.02, 2.0, 20))
+ for mode in ("mf", "iid", "ri"):
+ scales = [0.0] if mode == "mf" else [1e-4, 1e-2, 0.1, 0.3, 1.0, 3.0, 10.0, 100.0]
+ for sc in scales:
+ t0 = time.time()
+ mu, traj = run_amp(Vg, Tg, betas_ladder, sc, mode, 8, 25, 100 + trial,
+ spec_prod=spec_prod)
+ if mu is None:
+ print("trial%d %-4s scale=%-8g DIVERGED after %d beta steps (%.1fs)"
+ % (trial, mode, sc, len(traj), time.time() - t0), flush=True)
+ results.append(dict(trial=trial, mode=mode, scale=sc,
+ diverged=True, betas_done=len(traj)))
+ continue
+ accs, refs = score(mu, hidden, Vg, Tg, N)
+ print("trial%d %-4s scale=%-8g raw mean %.4f max %.4f | refined mean %.4f max %.4f | sharp %.3f (%.1fs)"
+ % (trial, mode, sc, np.mean(accs), np.max(accs),
+ np.mean(refs), np.max(refs), traj[-1], time.time() - t0),
+ flush=True)
+ results.append(dict(trial=trial, mode=mode, scale=sc, diverged=False,
+ raw=accs, refined=refs, sharpness=traj[-1]))
+
+ with open("/home/yurenh2/emm/logs/amp_probe.json", "w") as f:
+ json.dump(results, f, indent=1)
+ print("DONE")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/logs/amplify.log b/logs/amplify.log
index f397d0a..f775312 100644
--- a/logs/amplify.log
+++ b/logs/amplify.log
@@ -1,2 +1,8 @@
=== omit-size (N=256, chance=0.0039)
+ trial 0: ladders=[0.242, 0.152, 0.207] picked-by-energy=0.2070
+ trial 1: ladders=[0.02, 0.059, 0.035] picked-by-energy=0.0195
+ trial 2: ladders=[0.277, 0.273, 0.016] picked-by-energy=0.2734
+ MEAN selected-by-energy=0.1667 (oracle ladder choice would give 0.1927)
+
+=== synth-full (N=256, chance=0.0039)
diff --git a/logs/amplify160.log b/logs/amplify160.log
new file mode 100644
index 0000000..c51c9f0
--- /dev/null
+++ b/logs/amplify160.log
@@ -0,0 +1,7 @@
+
+=== omit-size-pool160 (N=256, chance=0.0039)
+ trial 0: ladders=[0.383, 0.395] picked-by-energy=0.3945
+ trial 1: ladders=[0.641, 0.68] picked-by-energy=0.6797
+ trial 2: ladders=[0.012, 0.449] picked-by-energy=0.4492
+ MEAN selected-by-energy=0.5078 (oracle ladder choice would give 0.5078)
+{"done": true}
diff --git a/logs/amplify640.log b/logs/amplify640.log
new file mode 100644
index 0000000..f2e8354
--- /dev/null
+++ b/logs/amplify640.log
@@ -0,0 +1,6 @@
+
+=== omit-size-pool640 (N=256, chance=0.0039)
+ trial 0: ladders=[0.562, 0.293] picked-by-energy=0.5625
+ trial 1: ladders=[0.727, 0.461] picked-by-energy=0.7266
+ MEAN selected-by-energy=0.6445 (oracle ladder choice would give 0.6445)
+{"done": true}
diff --git a/logs/battery2.log b/logs/battery2.log
index a0b3a2a..baae871 100644
--- a/logs/battery2.log
+++ b/logs/battery2.log
@@ -28,3 +28,6 @@
=== synth-full-BOUND0.99 (N=256, chance=0.0039)
GW x12 restarts raw=0.3698 refined=0.3971 (12.3s)
GW kl-loss raw=0.0846 refined=0.0885 (13.9s)
+ entropic GW deep anneal raw=0.0078 refined=0.0443 (824.6s)
+ semirelaxed GW raw=0.0221 refined=0.0365 (60.7s)
+{"done": true}