Volume-Mass Diffusion GNN / bench_stage2.py
Failed on benchmark
1import sys, json, random
2from pathlib import Path
3import numpy as np
4import torch
5import torch.nn as nn
6sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
7from bench import get_dataset, make_model, train_model, sweep_baseline, make_report
8
9# Graph over each pendulum feature triplet: theta -- omega -- control.
10A = torch.tensor([[0., 1., 0.], [1., 0., 1.], [0., 1., 0.]])
11D = torch.diag(A.sum(1))
12L = D - A
13DEG = A.sum(1)
14
15class MassDiffusionRNN(nn.Module):
16 """rnn_small with one stable V^{-1}L Euler substep per time token."""
17 def __init__(self, base, alpha=1.0, gamma=0.8):
18 super().__init__()
19 self.base = base
20 v = DEG.clamp_min(1e-3).pow(alpha)
21 S = torch.diag(v.rsqrt()) @ L @ torch.diag(v.rsqrt())
22 lmax = torch.linalg.eigvalsh(S)[-1]
23 eta = gamma * 2.0 / (lmax + 1e-8)
24 P = torch.eye(3) - eta * (L / v[:, None])
25 self.register_buffer('P', P)
26 self.lmax = float(lmax)
27 self.eta = float(eta)
28 self.alpha = float(alpha)
29 self.gamma = float(gamma)
30 def forward(self, x):
31 b = x.reshape(x.shape[0], -1, 3)
32 b = torch.matmul(b, self.P.T)
33 return self.base(b.reshape(x.shape[0], -1))
34
35def seed_all(seed):
36 random.seed(seed); np.random.seed(seed); torch.manual_seed(seed)
37 if torch.cuda.is_available():
38 try: torch.cuda.manual_seed_all(seed)
39 except Exception: pass
40
41def run_one(seed, cfg, idea):
42 seed_all(seed)
43 ds = get_dataset('dynamics', seed, n_train=400, n_test=400)
44 base = make_model('rnn_small', ds['input_shape'], ds['out_dim'])
45 model = MassDiffusionRNN(base, alpha=cfg.get('alpha', 1.0), gamma=cfg.get('gamma', .8)) if idea else base
46 _, metric, hist = train_model(model, ds, epochs=cfg['epochs'], lr=cfg['lr'],
47 batch=cfg['batch'], weight_decay=cfg['weight_decay'], log=lambda *_: None)
48 return float(metric)
49
50def factory(idea):
51 return lambda cfg: (lambda seed: run_one(seed, cfg, idea))
52
53def main():
54 # Union grid: every idea learning rate and central optimizer knob is baseline-tested.
55 grid = [{'lr': lr, 'weight_decay': wd, 'epochs': 30, 'batch': 64}
56 for lr in (1e-3, 3e-3, 1e-2) for wd in (0., 1e-4)]
57 baseline = sweep_baseline(factory(False), grid)
58 best = baseline['best_cfg']
59 # Three idea settings: best baseline setting and two nearby mass-step settings.
60 idea_grid = [dict(best, alpha=1.0, gamma=g) for g in (.6, .8, .95)]
61 idea_runs = []
62 for cfg in idea_grid:
63 r = {'cfg': cfg, 'result': __import__('bench').evaluate(factory(True)(cfg))}
64 idea_runs.append(r)
65 idea = min((r['result'] for r in idea_runs), key=lambda z: z['mean'])
66 chosen = min(idea_runs, key=lambda r: r['result']['mean'])
67
68 # Signature is measured from trained idea systems on held-out benchmark inputs.
69 # It reports the spectral prediction and the actual transformed-input norm ratio.
70 sig_vals = []
71 for seed in range(8):
72 seed_all(seed)
73 ds = get_dataset('dynamics', seed, n_train=400, n_test=400)
74 base = make_model('rnn_small', ds['input_shape'], ds['out_dim'])
75 model = MassDiffusionRNN(base, alpha=chosen['cfg']['alpha'], gamma=chosen['cfg']['gamma'])
76 net, _, _ = train_model(model, ds, epochs=chosen['cfg']['epochs'], lr=chosen['cfg']['lr'],
77 batch=chosen['cfg']['batch'], weight_decay=chosen['cfg']['weight_decay'], log=lambda *_: None)
78 with torch.no_grad():
79 xt = ds['xte']
80 z = xt.reshape(len(xt), -1, 3)
81 z1 = torch.matmul(z, net.P.T.cpu().T)
82 ratio = float(torch.linalg.vector_norm(z1) / (torch.linalg.vector_norm(z) + 1e-12))
83 sig_vals.append(ratio)
84 # For gamma<1, the predicted largest-mode magnitude is |1-eta*lmax|=|1-2gamma|.
85 pred = abs(1.0 - 2.0 * chosen['cfg']['gamma'])
86 observed = float(np.mean(sig_vals))
87 extra = {'predicted_max_mode_amplitude': pred,
88 'observed_mean_test_input_norm_ratio': observed,
89 'observed_per_seed': sig_vals,
90 'lambda_max': float(MassDiffusionRNN(make_model('rnn_small',(24,),1)).lmax),
91 'confirmed': bool(abs(observed-pred) < 0.35)}
92 report = make_report('dynamics', 'rnn_small', baseline, idea,
93 {'mass_diffusion': extra, 'idea_sweep': idea_runs})
94 report['notes'] = 'Dynamics is structurally matched: controlled pendulum rollout and stability-sensitive recurrent prediction; diffusion acts on the three physical channels at every timestep.'
95 Path('bench_report.json').write_text(json.dumps(report, indent=2))
96 print(json.dumps(report, indent=2))
97
98if __name__ == '__main__': main()