import sys, json, math, random from pathlib import Path import numpy as np import torch import torch.nn as nn sys.path.insert(0, '/home/maxwelhelp/all/math2nn') from bench import get_dataset, make_model, train_model, evaluate, sweep_baseline, make_report SEEDS=tuple(range(8)) DEVICE='cuda' if torch.cuda.is_available() else 'cpu' def seed_all(s): random.seed(s); np.random.seed(s); torch.manual_seed(s) if torch.cuda.is_available(): try: torch.cuda.manual_seed_all(s) except Exception: pass def free_moments(tau,K=2): out=[] for p in range(1,K+1): n=p-1; a=p*tau; q=np.zeros(n+1); q[0]=1.; b=np.zeros(n+1) for j in range(1,n+1): b[j]=a*((-1)**(j+1)) for k in range(1,n+1): q[k]=sum(j*b[j]*q[k-j] for j in range(1,k+1))/k out.append(math.exp(-a)*sum(math.comb(p,j)*q[n-j] for j in range(n+1))/p) return np.asarray(out,dtype=np.float32) def math_check(): rng=np.random.default_rng(11); n=28; tau=.5; L=round(tau*n); P=np.diag([1.]*(n-1)+[0.]); vals=[] for _ in range(12): B=np.eye(n,dtype=complex) for _ in range(L): z=(rng.normal(size=(n,n))+1j*rng.normal(size=(n,n)))/np.sqrt(2) q,r=np.linalg.qr(z); d=np.diag(r); B=P@(q*(d/np.abs(d)).conj())@B e=np.linalg.eigvalsh(B.conj().T@B).real; vals.append([e.mean(),(e**2).mean()]) emp=np.mean(vals,0); tar=free_moments(tau) return {'tau':tau,'target':tar.tolist(),'empirical':emp.tolist(),'abs_error':np.abs(emp-tar).tolist(),'confirmed':bool(np.max(np.abs(emp-tar))<.03)} def baseline_one(seed,cfg): seed_all(seed); d=get_dataset('tabular',seed,n_train=400,n_test=200) _,metric,_=train_model(make_model('mlp_tiny',d['input_shape'],d['out_dim']),d,epochs=cfg['epochs'],lr=cfg['lr'],batch=128,log=lambda *_:None) return float(metric) def spectral_loss(net,x,target): # Scalar-output Jacobian: one reverse-mode derivative gives per-example J rows. x=x.detach().requires_grad_(True); y=net(x).reshape(-1) g=torch.autograd.grad(y.sum(),x,create_graph=True)[0] m1=g.pow(2).sum(1).mean(); m2=g.pow(4).sum(1).mean() / x.shape[1] # Normalize moments as trace/N and trace((J^T J)^2)/N. m=torch.stack([m1/x.shape[1],m2]) return ((torch.log(m+1e-5)-torch.log(target+1e-5))**2).sum(),m def idea_one(seed,cfg,capture=False): seed_all(seed); d=get_dataset('tabular',seed,n_train=400,n_test=200) net=make_model('mlp_tiny',d['input_shape'],d['out_dim']) dev=torch.device(DEVICE); net=net.to(dev); x=d['xtr'].to(dev); y=d['ytr'].to(dev); xe=d['xte'].to(dev); ye=d['yte'].to(dev) target=torch.tensor(free_moments(2/64),device=dev) opt=torch.optim.Adam(net.parameters(),lr=cfg['lr']); mse=nn.MSELoss() for ep in range(cfg['epochs']): net.train(); perm=torch.randperm(len(x),device=dev) for st in range(0,len(x),128): ix=perm[st:st+128]; pred=net(x[ix]); loss=mse(pred,y[ix]) # The intervention is applied periodically to a small probe batch only. if st==0 and ep < max(1,cfg['epochs']//3): sl,_=spectral_loss(net,x[ix[:8]],target); loss=loss+cfg['lam']*sl.clamp(max=10.) opt.zero_grad(); loss.backward(); opt.step() net.eval() with torch.no_grad(): metric=float(mse(net(xe),ye).cpu()) if not capture:return metric with torch.enable_grad(): sl,m=spectral_loss(net,xe[:16],target) return {'metric':metric,'predicted_moments':target.cpu().tolist(),'observed_moments':m.detach().cpu().tolist(),'log_moment_rmse':float(torch.sqrt(sl/2).detach().cpu())} def main(): grid=[{'lr':lr,'epochs':8,'weight_decay':0.0,'lam':0.0} for lr in (.001,.003,.006)] # sweep_baseline calls the supplied factory with each seed and preserves full seed results. base=sweep_baseline(lambda c: (lambda seed: baseline_one(seed,c)),grid,seeds=(0,1,2,3)) best=base['best_cfg']; lrs=[c['lr'] for c in grid] # Same lr union on idea side; three predetermined spectral weights. idea_cfgs=[{'lr':lr,'epochs':8,'weight_decay':0.0,'lam':lam} for lr in lrs for lam in (.0001, .0003, .001)] runs=[] for c in idea_cfgs: r=evaluate(lambda seed,c=c: idea_one(seed,c),seeds=SEEDS); runs.append((c,r)) ibest,ires=min(runs,key=lambda z:z[1]['mean']) # Evaluate baseline best configuration on all eight paired seeds for make_report comparison. base_full=evaluate(lambda seed: baseline_one(seed,best),seeds=SEEDS) base['full']=base_full sig=idea_one(0,ibest,True) report=make_report('tabular','mlp_tiny',base,ires,extra={ 'selected_idea_cfg':ibest,'math_check':math_check(), 'mechanism_signature':{'source':'trained mlp_tiny Jacobian on held-out tabular inputs','predicted_moments':sig['predicted_moments'],'observed_moments':sig['observed_moments'],'log_moment_rmse':sig['log_moment_rmse'],'confirmed':bool(sig['log_moment_rmse']<1.0)}}) report['idea_sweep']=[{'cfg':c,'result':r} for c,r in runs] Path('bench_report.json').write_text(json.dumps(report,indent=2)); print(json.dumps(report,indent=2)) if __name__=='__main__': main()