import sys, json, math, random 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, sweep_baseline, make_report from bench.protocol import DEFAULT_SEEDS def seed_all(s): random.seed(s); np.random.seed(s); torch.manual_seed(s) def controller_rate(h, alpha, eps, hc, delta): z = max(-60., min(60., (h-hc)/delta)) b = 1./(1.+math.exp(-z)) return alpha*(eps+(1.-eps)*b) def flat_state(model): return torch.cat([p.detach().reshape(-1) for p in model.parameters()]) def assign_state(model, v): k=0 with torch.no_grad(): for p in model.parameters(): n=p.numel(); p.copy_(v[k:k+n].view_as(p)); k += n def predict(model, theta, x): # functional_call avoids mutating the center while scoring each candidate. from torch.func import functional_call params={n:v for (n,_),v in zip(model.named_parameters(), split_state(model, theta))} return functional_call(model, params, (x,)) def split_state(model, theta): out=[]; k=0 for p in model.parameters(): n=p.numel(); out.append(theta[k:k+n].view_as(p)); k += n return out def run(seed, mode, cfg, signature=False): seed_all(seed) ds=get_dataset('tabular', seed, n_train=400, n_test=400) device=torch.device('cuda' if torch.cuda.is_available() else 'cpu') try: model=make_model('mlp_tiny', tuple(ds['xtr'].shape[1:]), 1).to(device) x=ds['xtr'].to(device); y=ds['ytr'].to(device).view(-1,1) xt=ds['xte'].to(device); yt=ds['yte'].to(device).view(-1,1) theta=flat_state(model) # Same number of candidate evaluations and perturbation scale for both methods. N=16; sigma=cfg['sigma']; tau=cfg['tau0']; T=cfg['steps'] alpha=cfg['alpha']; eps=cfg['epsilon']; hc=cfg['hc']; delta=cfg['delta'] hs=[]; rates=[]; ess=[]; losses=[] for _ in range(T): noise=torch.randn((N,theta.numel()), device=device) cand=theta[None,:] + sigma*noise vals=[] for i in range(N): pred=predict(model,cand[i],x) vals.append(((pred-y)**2).mean()) lv=torch.stack(vals) use_tau=tau z=-(lv-lv.min())/max(use_tau,1e-8) w=torch.softmax(torch.clamp(z,-80,0),dim=0) H=-(w*torch.log(w.clamp_min(1e-12))).sum() h=float(H.item()/math.log(N)); e=float(torch.exp(H).item()) theta=(w[:,None]*cand).sum(0) if mode=='feedback': r=controller_rate(h,alpha,eps,hc,delta) else: r=alpha tau *= math.exp(-r) hs.append(h); rates.append(r); ess.append(e); losses.append(float(((predict(model,theta,x)-y)**2).item())) assign_state(model,theta) with torch.no_grad(): metric=float(((model(xt)-yt)**2).mean().item()) if signature: high=np.mean([r for h,r in zip(hs,rates) if h>hc+2*delta]) if any(h>hc+2*delta for h in hs) else float('nan') low=np.mean([r for h,r in zip(hs,rates) if hcfg['hc']+2*cfg['delta']]) if any(h>cfg['hc']+2*cfg['delta'] for h in hs) else float('nan'); lo=np.mean([r for h,r in zip(hs,rates) if h