import sys,json,copy import numpy as np, torch import torch.nn as nn sys.path.insert(0,'/home/maxwelhelp/all/math2nn') import bench from bench import train_model,evaluate,sweep_baseline,make_report import simplicial_track TRACK='simplicial_node_classification' bench.data._CUSTOM_CACHE={TRACK:simplicial_track} def P_local(): n=24; A=np.zeros((n,n),dtype=np.float32) for j in range(n): A[j,j]=A[(j+1)%n,j]=A[(j+2)%n,j]=1 P=(A/A.sum(1)[:,None])@np.diag(1/A.sum(0))@A.T return (.9*P+.1*np.eye(n)).astype('float32') P=P_local() class Net(nn.Module): def __init__(self,alpha): super().__init__(); self.register_buffer('P',torch.tensor(P)); self.alpha=alpha self.a=nn.Linear(8,32); self.b=nn.Linear(32,2) def forward(self,x): n=self.P.shape[0]; R=torch.ones_like(self.P)/n Q=(1-self.alpha)*self.P+self.alpha*R h=torch.relu(self.a(x)); h=torch.einsum('ij,bjf->bif',Q,h); return self.b(h).transpose(1,2) def train(cfg,seed): torch.manual_seed(1000+seed); np.random.seed(1000+seed) ds=bench.get_dataset(TRACK,seed,400,100); net=Net(cfg['alpha']) _,metric,_=train_model(net,ds,epochs=cfg['epochs'],lr=cfg['lr'],batch=128,weight_decay=cfg['wd'],log=lambda *_:None) return metric def factory(cfg): return lambda seed: train(cfg,seed) def main(): # Union parity: both methods are evaluated on all lr/wd values and alpha values. grid=[{'alpha':a,'lr':lr,'wd':wd,'epochs':18} for lr in (0.003,0.01,0.03) for wd in (0.0,0.0001) for a in (0.0,0.1,0.3)] # Baseline method is alpha=0; central baseline knob wd is swept too. bgrid=[{k:v for k,v in c.items()} for c in grid if c['alpha']==0] base=sweep_baseline(factory,bgrid,seeds=(0,1,2,3)) best=base['best_cfg']; idea_cfgs=[] for a in (0.1,0.3,0.6): c=dict(best); c['alpha']=a; idea_cfgs.append(c) # Same-size idea sweep; alpha=.6 is nearby but also uses best baseline lr/wd. ir=[] for c in idea_cfgs: r=evaluate(factory(c)); ir.append({'cfg':c,'result':r}) chosen=min(ir,key=lambda z:z['result']['mean']); idea=chosen['result'] # Retrain/evaluate observed trained systems for signature on one paired seed. ds=bench.get_dataset(TRACK,0,400,100); x=ds['xte']; sig=[] for a in (0.0,0.1,0.3,0.6): torch.manual_seed(1000); net=Net(a); _,_,_=train_model(net,ds,epochs=best['epochs'],lr=best['lr'],batch=128,weight_decay=best['wd'],log=lambda *_:None) with torch.no_grad(): xdev=x.to(net.P.device); q=net.a(xdev); q=torch.einsum('ij,bjf->bif',((1-a)*net.P+a*torch.ones_like(net.P)/24),q) v=q-q.mean(1,keepdim=True); ratio=float(v.norm()/ (torch.relu(net.a(xdev))-torch.relu(net.a(xdev)).mean(1,keepdim=True)).norm()) sig.append({'alpha':a,'observed_nonconstant_norm_ratio':ratio,'predicted':1-a}) sigerr=max(abs(z['observed_nonconstant_norm_ratio']-z['predicted']) for z in sig) rep=make_report(TRACK,'custom_diffusion_node_net',base,idea,{'prediction':'teleportation contracts mean-zero feature modes by 1-alpha','rows':sig,'max_abs_error':sigerr,'confirmed':bool(sigerr<0.08),'idea_sweep':ir,'custom_track':{'name':TRACK,'file':'simplicial_track.py','domain':'graph-nn'}}) rep['idea_candidates']=ir with open('bench_report.json','w') as f: json.dump(rep,f,indent=2) print(json.dumps(rep,indent=2)) if __name__=='__main__': main()