Spectral-gap adaptive halting / smoke.py
Failed on benchmark
1import sys, json, math, random
2from pathlib import Path
3import numpy as np
4import torch
5import torch.nn as nn
6
7sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
8from bench import get_dataset, train_model, make_report
9
10SEEDS = tuple(range(1))
11LRS = (1e-3, 3e-3, 1e-2)
12CAPS = (2, 3, 4)
13EPOCHS = 1
14NTRAIN, NTEST = 400, 200
15
16class RefinementNet(nn.Module):
17 """Independently trained tied-GRU refinement system with optional halting."""
18 def __init__(self, input_shape, out_dim, cap=4, adaptive=False, delta=.01,
19 residual_tol=.025, tau_threshold=3.0):
20 super().__init__()
21 inp = int(np.prod(input_shape))
22 self.enc = nn.Sequential(nn.Linear(inp, 32), nn.Tanh())
23 self.cell = nn.GRUCell(32, 32)
24 self.head = nn.Linear(32, out_dim)
25 self.cap, self.adaptive = cap, adaptive
26 self.delta, self.residual_tol, self.tau_threshold = delta, residual_tol, tau_threshold
27 self.last_stats = {}
28
29 def forward(self, x):
30 a = self.enc(x)
31 b = x.shape[0]
32 h = torch.zeros(b, 32, device=x.device, dtype=x.dtype)
33 active = torch.ones(b, dtype=torch.bool, device=x.device)
34 stopped = torch.zeros(b, dtype=torch.bool, device=x.device)
35 all_r, all_tau, all_lam = [], [], []
36 for t in range(self.cap):
37 hnew = self.cell(a, h)
38 r = (hnew - h).norm(dim=1) / (hnew.norm(dim=1) + 1e-6)
39 # A directional finite-difference JVP of the tied block.
40 with torch.no_grad():
41 v = torch.ones_like(hnew) / math.sqrt(hnew.shape[1])
42 eps = 1e-3
43 hp = self.cell(a.detach(), hnew.detach() + eps * v)
44 hm = self.cell(a.detach(), hnew.detach() - eps * v)
45 jv = (hp - hm) / (2 * eps)
46 lam = jv.norm(dim=1) / (v.norm(dim=1) + 1e-8)
47 tau = math.pi / torch.clamp(1.0 - lam, min=self.delta)
48 if self.adaptive and t > 0:
49 good = active & (r < self.residual_tol) & (tau < self.tau_threshold) & (lam < 1.0)
50 stopped = stopped | good
51 active = active & ~good
52 h = torch.where(active[:, None], hnew, h)
53 all_r.append(r.detach()); all_tau.append(tau.detach()); all_lam.append(lam.detach())
54 # If no early exit occurred, actual iterations equal cap; otherwise t+1.
55 # Reconstruct first stopping index from the recorded residual/lambda traces.
56 rs, ts, ls = torch.stack(all_r), torch.stack(all_tau), torch.stack(all_lam)
57 if self.adaptive:
58 it = torch.full((b,), self.cap, dtype=torch.float32, device=x.device)
59 for t in range(1, self.cap):
60 good = (rs[t] < self.residual_tol) & (ts[t] < self.tau_threshold) & (ls[t] < 1.0)
61 it = torch.where((it == self.cap) & good, torch.tensor(float(t + 1), device=x.device), it)
62 else:
63 it = torch.full((b,), float(self.cap), device=x.device)
64 self.last_stats = {'iterations': it.cpu().tolist(), 'residual': rs.cpu().tolist(),
65 'tau': ts.cpu().tolist(), 'lambda': ls.cpu().tolist()}
66 return self.head(h)
67
68
69def seed_all(seed):
70 random.seed(seed); np.random.seed(seed); torch.manual_seed(seed)
71 if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)
72
73
74def run_one(seed, cfg, adaptive):
75 seed_all(seed)
76 d = get_dataset('dynamics', seed, n_train=NTRAIN, n_test=NTEST)
77 net = RefinementNet(d['input_shape'], d['out_dim'], cap=cfg['cap'], adaptive=adaptive,
78 delta=.01, residual_tol=cfg['rtol'], tau_threshold=cfg['tau'])
79 net, metric, _ = train_model(net, d, epochs=EPOCHS, lr=cfg['lr'], batch=128)
80 return (float(metric), net.last_stats) if net is not None else (float('nan'), {})
81
82
83def eval_cfg(cfg, adaptive, seeds=SEEDS):
84 vals, stats = [], []
85 for s in seeds:
86 m, st = run_one(s, cfg, adaptive); vals.append(m); stats.append(st)
87 return {'per_seed': vals, 'mean': float(np.nanmean(vals)), 'cfg': cfg, 'stats': stats}
88
89
90def main():
91 tried = []
92 for lr in LRS:
93 for cap in CAPS:
94 cfg = {'lr': lr, 'cap': cap, 'rtol': .025, 'tau': 3.0}
95 r = eval_cfg(cfg, False, seeds=range(1))
96 tried.append({'cfg': cfg, 'mean': r['mean'], 'per_seed': r['per_seed']})
97 finite = [x for x in tried if np.isfinite(x['mean'])]
98 best = min(finite, key=lambda z: z['mean'])['cfg']
99 base = eval_cfg(best, False, SEEDS)
100 idea_cfgs = [best,
101 {'lr': 1e-3 if best['lr'] != 1e-3 else 3e-3, 'cap': best['cap'], 'rtol': .025, 'tau': 3.0},
102 {'lr': best['lr'], 'cap': 4 if best['cap'] != 4 else 3, 'rtol': .025, 'tau': 3.0}]
103 ideas = [eval_cfg(c, True, SEEDS) for c in idea_cfgs]
104 idea = min(ideas, key=lambda z: z['mean'])
105 pred, obs = [], []
106 for st in idea['stats']:
107 if st:
108 pred.extend(np.asarray(st['tau']).reshape(-1).tolist())
109 obs.extend(np.asarray(st['iterations']).reshape(-1).tolist())
110 pred, obs = np.asarray(pred), np.asarray(obs)
111 corr = float(np.corrcoef(pred, obs)[0, 1]) if len(pred) > 1 and np.std(pred) > 0 and np.std(obs) > 0 else 0.0
112 signature = {'quantity': 'trained-model predicted local tau vs observed adaptive iterations',
113 'n_examples': int(len(pred)), 'predicted_tau_mean': float(pred.mean()) if len(pred) else None,
114 'observed_iterations_mean': float(obs.mean()) if len(obs) else None,
115 'correlation': corr, 'confirmed': bool(len(pred) >= 100 and corr > 0.3)}
116 report = make_report('dynamics', 'rnn_small', {'best_cfg': best, 'sweep': tried, 'full': base}, idea,
117 extra=signature)
118 report['idea_alternatives'] = ideas
119 report['notes'] = 'Matched dynamics track; fixed and adaptive tied-GRU systems trained independently with train_model and identical shared hyperparameters.'
120 Path('bench_report.json').write_text(json.dumps(report, indent=2, allow_nan=False))
121 print(json.dumps(report, indent=2))
122
123if __name__ == '__main__': main()