Legendre Feasibility Layer / bench_legendre.py
Mechanism confirmed, baseline not beaten
1import sys, json, time
2import numpy as np
3import torch
4import torch.nn as nn
5sys.path.insert(0, '/home/maxwelhelp/all/math2nn')
6from bench import get_dataset, make_model, sweep_baseline, make_report
7
8TRACK='directional_polytope'; MODEL='mlp_tiny'
9SEEDS=tuple(range(8)); BOUNDS=(-1.0,1.0)
10
11def legendre_box(z, eps=0.1, lo=-1., hi=1.):
12 # argmin z*w + eps[-log(w-lo)-log(hi-w)] independently.
13 # Solves z + eps[-1/(w-lo)+1/(hi-w)] = 0 in closed form.
14 a = z
15 L = hi-lo
16 disc = torch.sqrt((a*L)**2 + 4.0*eps*eps)
17 # numerically stable positive root for s=w-lo: a*s^2 + ...
18 # equivalent centered solution: q=w-(lo+hi)/2, q = (eps*a? )
19 # stationarity with center c and radius r: z + eps*(-1/(r+q)+1/(r-q))=0
20 # => z*(r^2-q^2)+2 eps*q=0; q=(sqrt(eps^2+z^2 r^2)-eps)/z
21 r=L/2; c=(lo+hi)/2
22 root=torch.sqrt(eps*eps + (a*r)**2)
23 q=torch.where(a.abs()>1e-8, -a*r*r/(root+eps), torch.zeros_like(a))
24 return (c+q).clamp(min=lo+1e-7,max=hi-1e-7)
25
26def train(seed, lr, kind, penalty=1.0, eps=0.1, epochs=20, return_net=False):
27 torch.manual_seed(seed); np.random.seed(seed)
28 d=get_dataset(TRACK, seed, n_train=400, n_test=200)
29 net=make_model(MODEL,d['input_shape'],d['out_dim'])
30 opt=torch.optim.Adam(net.parameters(),lr=lr)
31 xtr,ytr,xte,yte=d['xtr'],d['ytr'],d['xte'],d['yte']
32 if d.get('out_dim', 1) > 1 and ytr.shape[0] != xtr.shape[0]:
33 ytr=ytr.reshape(xtr.shape[0], d['out_dim']); yte=yte.reshape(xte.shape[0], d['out_dim'])
34 for ep in range(epochs):
35 p=torch.randperm(len(xtr)); net.train()
36 for i in range(0,len(xtr),128):
37 ix=p[i:i+128]; raw=net(xtr[ix])
38 if kind=='idea': out=legendre_box(raw,eps)
39 else: out=raw
40 loss=((out-ytr[ix])**2).mean()
41 if kind=='baseline':
42 loss=loss + penalty*(torch.relu(out-1).square()+torch.relu(-1-out).square()).mean()
43 opt.zero_grad(); loss.backward(); opt.step()
44 net.eval()
45 with torch.no_grad():
46 raw=net(xte); out=legendre_box(raw,eps) if kind=='idea' else raw
47 metric=((out-yte)**2).mean().item()
48 if return_net: return metric,net,d
49 return metric
50
51def base_factory(cfg):
52 return lambda seed: train(seed,cfg['lr'],'baseline',cfg['penalty'])
53def idea_factory(cfg):
54 return lambda seed: train(seed,cfg['lr'],'idea',eps=cfg['eps'])
55
56def signature():
57 metric,net,d=train(0,0.01,'idea',eps=.1,return_net=True)
58 z=net(d['xte'][:1]).detach().requires_grad_(True)
59 w=legendre_box(z,.1)
60 # diagonal observed J via autograd and analytic barrier inverse Hessian
61 obs=[]
62 for j in range(w.shape[1]):
63 obs.append(torch.autograd.grad(w[0,j],z,retain_graph=True)[0][0,j].item())
64 zz=z.detach()[0]; ww=legendre_box(zz,.1); h=.1*((1/(ww+1)**2)+(1/(1-ww)**2))
65 pred=(-1/h).numpy(); obs=np.array(obs)
66 rel=float(np.max(np.abs(obs-pred)/(np.abs(pred)+1e-12)))
67 return {'quantity':'diagonal Jacobian of trained feasibility head', 'predicted_mean_abs':float(np.mean(np.abs(pred))), 'observed_mean_abs':float(np.mean(np.abs(obs))), 'max_relative_error':rel, 'confirmed':bool(rel<1e-5)}
68
69def main():
70 # Union parity: all lr and baseline method knobs used by either side are swept here.
71 grid=[{'lr':lr,'penalty':p} for lr in (.003,.01,.03) for p in (0.0,1.0,10.0)]
72 t=time.time(); base=sweep_baseline(base_factory,grid,seeds=(0,1,2,3))
73 # explicit idea 3-config sweep at the same lr union; report best by 4-seed mean
74 igrid=[{'lr':lr,'eps':e} for lr,e in ((base['best_cfg']['lr'],.05),(base['best_cfg']['lr'],.1),(base['best_cfg']['lr'],.2))]
75 itried=[]
76 for cfg in igrid:
77 vals=[idea_factory(cfg)(s) for s in (0,1,2,3)]
78 itried.append({'cfg':cfg,'mean':float(np.mean(vals))})
79 ibest=min(itried,key=lambda x:x['mean'])['cfg']
80 idea={'best_cfg':ibest,'sweep':itried,'full':__import__('bench').evaluate(idea_factory(ibest),SEEDS)}
81 rep=make_report(TRACK,MODEL,base,idea['full'],signature())
82 rep['idea_sweep']=idea['sweep']; rep['idea_best_cfg']=idea['best_cfg']
83 rep['runtime_seconds']=time.time()-t; rep['protocol_note']='Registered directional_polytope; same MLP, end-to-end trained systems; box log barrier feasibility.'
84 print(json.dumps(rep,indent=2))
85 open('bench_report.json','w').write(json.dumps(rep,indent=2))
86if __name__=='__main__': main()