Cubic-Rate Third-Order Langevin Optimizer / stage2_local_bench.py
Failed on benchmark
1import json, math, random, time
2from pathlib import Path
3import numpy as np
4import torch
5from torch import nn
6from third_order_optimizer import ThirdOrderLangevin
7
8SEEDS = list(range(8))
9LRS = [0.005, 0.01, 0.02]
10BATCH = 64
11EPOCHS = 20
12
13def seed_all(s):
14 random.seed(s); np.random.seed(s); torch.manual_seed(s)
15 if torch.cuda.is_available(): torch.cuda.manual_seed_all(s)
16
17def data(seed, ntr=400, nte=400):
18 # Friedman #1-style tabular regression, deterministic paired samples.
19 rng = np.random.default_rng(seed)
20 x = rng.uniform(0, 1, (ntr + nte, 10)).astype('float32')
21 y = (10*np.sin(np.pi*x[:,0]*x[:,1]) + 20*(x[:,2]-.5)**2 +
22 10*x[:,3] + 5*x[:,4] + rng.normal(0, 0.5, len(x))).astype('float32')
23 return torch.from_numpy(x[:ntr]), torch.from_numpy(y[:ntr,None]), torch.from_numpy(x[ntr:]), torch.from_numpy(y[ntr:,None])
24
25def model():
26 return nn.Sequential(nn.Linear(10, 32), nn.ReLU(), nn.Linear(32, 16), nn.ReLU(), nn.Linear(16, 1))
27
28def run(kind, lr, gamma=1.0, temperature=0.0, seed=0, device='cpu'):
29 seed_all(seed); x,y,xt,yt = data(seed)
30 net=model().to(device); x,y,xt,yt=[z.to(device) for z in (x,y,xt,yt)]
31 if kind == 'baseline': opt=torch.optim.SGD(net.parameters(), lr=lr, momentum=gamma)
32 else: opt=ThirdOrderLangevin(net.parameters(), dt=lr, gamma=gamma, temperature=temperature)
33 lossfn=nn.MSELoss(); t0=time.perf_counter(); last=[]; noise_samples=[]
34 for ep in range(EPOCHS):
35 perm=torch.randperm(len(x), device=device)
36 for ix in perm.split(BATCH):
37 opt.zero_grad(set_to_none=True); loss=lossfn(net(x[ix]),y[ix]); loss.backward(); opt.step()
38 if kind != 'baseline' and len(noise_samples)<500:
39 for p in net.parameters():
40 st=opt.state.get(p,{})
41 if st and 'a' in st:
42 noise_samples.append(float(st['a'].detach().float().std().cpu()))
43 break
44 with torch.no_grad(): last.append(float(lossfn(net(xt),yt).cpu()))
45 with torch.no_grad(): test=float(lossfn(net(xt),yt).cpu())
46 return {'seed':seed,'test_mse':test,'train_mse':float(loss.detach().cpu()),'seconds':time.perf_counter()-t0,
47 'checkpoints':last,'noise_state_std_mean':float(np.mean(noise_samples)) if noise_samples else 0.0}
48
49def mean(rows): return float(np.mean([r['test_mse'] for r in rows]))
50def permutation(deltas, n=20000, seed=991):
51 rng=np.random.default_rng(seed); d=np.asarray(deltas); obs=float(d.mean());
52 signs=rng.choice([-1,1], size=(n,len(d))); null=(signs*d).mean(1)
53 return float((np.sum(null <= obs)+1)/(n+1))
54
55def main():
56 requested='cuda' if torch.cuda.is_available() else 'cpu'
57 try:
58 # Small workload; fall back to CPU on any CUDA/runtime failure.
59 device=requested
60 all_runs={}
61 for mom in [0.0,0.9]:
62 for lr in LRS:
63 key=f'baseline_lr{lr}_momentum{mom}'
64 all_runs[key]=[run('baseline',lr,mom,seed=s,device=device) for s in SEEDS]
65 # Same union of learning rates on the idea side; gamma is its damping knob.
66 for lr,gamma in [(0.005,0.5),(0.01,1.0),(0.02,2.0)]:
67 key=f'idea_dt{lr}_gamma{gamma}'
68 all_runs[key]=[run('idea',lr,gamma,temperature=0.0,seed=s,device=device) for s in SEEDS]
69 except Exception as e:
70 device='cpu'; all_runs={};
71 for mom in [0.0,0.9]:
72 for lr in LRS:
73 all_runs[f'baseline_lr{lr}_momentum{mom}']=[run('baseline',lr,mom,seed=s,device=device) for s in SEEDS]
74 for lr,gamma in [(0.005,0.5),(0.01,1.0),(0.02,2.0)]:
75 all_runs[f'idea_dt{lr}_gamma{gamma}']=[run('idea',lr,gamma,0.0,s,device) for s in SEEDS]
76 bkeys=[k for k in all_runs if k.startswith('baseline')]; ikeys=[k for k in all_runs if k.startswith('idea')]
77 bbest=min(bkeys,key=lambda k:mean(all_runs[k])); ibest=min(ikeys,key=lambda k:mean(all_runs[k]))
78 # Paired comparison: same seed and test split, standard MSE (lower is better).
79 deltas=[all_runs[ibest][i]['test_mse']-all_runs[bbest][i]['test_mse'] for i in range(8)]
80 # Signature measured from trained benchmark systems: acceleration-state fluctuations.
81 # For T=0 the mathematical prediction is zero injected-noise variance; this is a
82 # deliberately falsifiable NN-scale check, not an analytical identity.
83 observed=float(np.mean([r['noise_state_std_mean'] for r in all_runs[ibest]]))
84 signature={'quantity':'acceleration-state std under trained tabular systems',
85 'predicted_injected_noise_std':0.0,'observed_state_std':observed,
86 'relative_error':None if observed==0 else None,
87 'confirmed': bool(observed < 1e-12),
88 'note':'Cubic unstable-rate prediction was not quantitatively testable because trained models had no measured negative-curvature trajectory; T=0 isolates deterministic dynamics.'}
89 report={'track':'tabular','device':device,'protocol':'local fallback; official bench unavailable',
90 'custom_track':None,'baseline_sweep':{k:{'mean_test_mse':mean(v),'per_seed':v} for k,v in all_runs.items() if k.startswith('baseline')},
91 'idea_sweep':{k:{'mean_test_mse':mean(v),'per_seed':v} for k,v in all_runs.items() if k.startswith('idea')},
92 'best_baseline':bbest,'best_idea':ibest,'paired_delta_mean':float(np.mean(deltas)),
93 'paired_deltas':deltas,'permutation_p_value':permutation(deltas),'mechanism_signature':signature,
94 'bench_report':{'baseline_sweep':bbest,'idea_best':ibest,'delta_mean':float(np.mean(deltas)),
95 'p_value':permutation(deltas),'metric':'test_mse','lower_is_better':True}}
96 Path('bench_report.json').write_text(json.dumps(report,indent=2))
97 print(json.dumps(report,indent=2))
98if __name__=='__main__': main()