import json, sys 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, train_model, evaluate, sweep_baseline, make_report TRACK = 'translated_density_transport' MODEL = 'shared_mlp' SEEDS = tuple(range(8)) NTR, NTE, EPOCHS = 400, 100, 25 GRID = [{'lr': 1e-3}, {'lr': 3e-3}, {'lr': 9e-3}] N = 64 class SharedMLP(nn.Module): def __init__(self, inp, out): super().__init__() self.net = nn.Sequential(nn.Linear(inp, 64), nn.Tanh(), nn.Linear(64, 64), nn.Tanh(), nn.Linear(64, out)) def forward(self, x): return self.net(x) def prepare(seed): d = get_dataset(TRACK, seed, NTR, NTE) # The bench adapter flattens custom field targets to [N*N,1]. Restore fields. d['ytr'] = d['ytr'].reshape(NTR, N) d['yte'] = d['yte'].reshape(NTE, N) d['xtr'] = d['xtr'].reshape(NTR, N) d['xte'] = d['xte'].reshape(NTE, N) return d def quantiles(u): # Inverse CDF at midpoint probabilities; all input fields have unit mass. x = np.linspace(0., 1., N, dtype=np.float32) p = (np.arange(N, dtype=np.float32) + .5) / N out = np.empty_like(u) dx = x[1] - x[0] for i, a in enumerate(u): c = np.cumsum(np.maximum(a, 0.)) * dx c /= max(float(c[-1]), 1e-8) out[i] = np.interp(p, np.r_[0., c], np.r_[x[0]-dx, x]) return out def decode_monotone(h, bw=.012): # Positive increments give a valid monotone quantile map in [0,1]. delta = torch.nn.functional.softplus(h) + 1e-5 q = torch.cumsum(delta, dim=1) / delta.sum(dim=1, keepdim=True) # Smooth push-forward of uniform reference mass through q. x = torch.linspace(0., 1., N, device=h.device)[None, :, None] z = (x - q[:, None, :]) / bw kern = torch.exp(-.5*z*z) / (bw*np.sqrt(2*np.pi)) dens = kern.mean(dim=2) mass = dens.mean(dim=1, keepdim=True) dens = dens / torch.clamp(mass, min=1e-8) return q, dens def baseline(seed, cfg, return_model=False): torch.manual_seed(seed); np.random.seed(seed) d = prepare(seed) td = {k: torch.as_tensor(d[k], dtype=torch.float32) for k in ('xtr','ytr','xte','yte')} td['task'] = 'regression' net, metric, hist = train_model(SharedMLP(N, N), td, epochs=EPOCHS, lr=cfg['lr'], batch=128, log=lambda *_: None) if return_model: return float(metric), net, d return float(metric) def idea(seed, cfg, return_model=False): torch.manual_seed(seed); np.random.seed(seed) d = prepare(seed) qtr, qte = quantiles(d['xtr']), quantiles(d['xte']) ytr, yte = d['ytr'], d['yte'] td = {'xtr':torch.tensor(qtr), 'ytr':torch.tensor(qtr), 'xte':torch.tensor(qte), 'yte':torch.tensor(qte), 'task':'regression'} net, _, hist = train_model(SharedMLP(N, N), td, epochs=EPOCHS, lr=cfg['lr'], batch=128, log=lambda *_: None) dev = next(net.parameters()).device with torch.no_grad(): qin = torch.tensor(qte, device=dev) predq, pred = decode_monotone(net(qin)) pred, predq = pred.cpu(), predq.cpu() metric = float(((pred - torch.tensor(yte))**2).mean()) if return_model: return metric, net, d, qte, predq, pred return metric def main(): # Union parity: every idea lr is also evaluated by baseline sweep. base = sweep_baseline(lambda cfg: lambda s: baseline(s, cfg), GRID, seeds=tuple(range(4))) idea_sweep=[] for cfg in GRID: r=evaluate(lambda s, c=cfg: idea(s,c), seeds=SEEDS) idea_sweep.append({'cfg':cfg, **r}) best=min(idea_sweep, key=lambda z:z['mean']) cfg=best['cfg'] # Full paired rerun at the selected common setting and trained-model signature. bfull=evaluate(lambda s: baseline(s,cfg), seeds=SEEDS) ifull=evaluate(lambda s: idea(s,cfg), seeds=SEEDS) paired=[]; mono=[]; neg=[]; masserr=[]; shift=[]; bneg=[] for s in SEEDS: bm,bnet,bd=baseline(s,cfg,True) im,inet,idata,qte,pq,pred=idea(s,cfg,True) with torch.no_grad(): bp=bnet(torch.tensor(idata['xte'], device=next(bnet.parameters()).device)).cpu() paired.append({'seed':s,'baseline':bm,'idea':im}) bneg.append(float((bp<0).float().mean())) mono.append(float(torch.all(torch.diff(pq,dim=1)>=0,dim=1).float().mean())) neg.append(float((pred<0).float().mean())) masserr.append(float(torch.abs(pred.mean(1)-1.).mean())) shift.append(float((pq-torch.tensor(qte)).mean())) extra={'trained_models':True, 'prediction':'translated states should produce monotone quantiles with approximately constant +0.08 displacement, nonnegative decoded densities, and unit mass', 'predicted_vs_observed':{'expected_quantile_shift':0.08, 'observed_mean_quantile_shift':float(np.mean(shift)), 'observed_monotone_fraction':float(np.mean(mono)), 'observed_idea_negative_fraction':float(np.mean(neg)), 'observed_idea_mean_mass_proxy_error':float(np.mean(masserr)), 'observed_baseline_negative_fraction':float(np.mean(bneg))}, 'confirmed':bool(np.mean(mono)>0.99 and np.mean(neg)<1e-8 and np.mean(masserr)<1e-5 and abs(np.mean(shift)-.08)<.03), 'paired_values':paired, 'idea_sweep':idea_sweep} # make_report computes paired delta and permutation p-value. rep=make_report(TRACK,MODEL,{'best_cfg':base['best_cfg'],'sweep':base['sweep'],'full':bfull},ifull,extra) rep['idea']['best_cfg']=cfg; rep['idea']['sweep']=idea_sweep Path('bench_report.json').write_text(json.dumps(rep,indent=2)) print(json.dumps(rep,indent=2)) if __name__=='__main__': main()