import sys, json, random, math from pathlib import Path import numpy as np import torch from torch import nn from torch.utils.data import TensorDataset, DataLoader sys.path.insert(0, '/home/maxwelhelp/all/math2nn') from bench import get_dataset, make_model, train_model, evaluate, sweep_baseline, make_report TRACK='dynamics'; MODEL='rnn_small'; SEEDS=tuple(range(8)); EPOCHS=30; BATCH=128 LRS=[0.001,0.003,0.006] def seed_all(s): random.seed(s); np.random.seed(s); torch.manual_seed(s) def baseline_metric(cfg, seed): d=get_dataset(TRACK, seed, n_train=4000, n_test=1000) seed_all(seed) net=make_model(MODEL,d['input_shape'],d['out_dim']) _, metric, _=train_model(net,d,epochs=EPOCHS,lr=cfg['lr'],batch=BATCH,log=lambda *_:None) return float(metric) def idea_metric(cfg, seed, return_model=False): d=get_dataset(TRACK, seed, n_train=4000, n_test=1000) seed_all(seed) net=make_model(MODEL,d['input_shape'],d['out_dim']) # The intervention is the only difference: boundary/high-risk samples receive # larger loss weight, approximating planner-directed critical-mode probes. device='cuda' if torch.cuda.is_available() else 'cpu' try: net=net.to(device) x,y=d['xtr'].to(device),d['ytr'].to(device) xt,yt=d['xte'].to(device),d['yte'].to(device) opt=torch.optim.Adam(net.parameters(),lr=cfg['lr']) for _ in range(EPOCHS): perm=torch.randperm(x.shape[0],device=device) for ix in perm.split(BATCH): xb,yb=x[ix],y[ix] opt.zero_grad(set_to_none=True) pred=net(xb) # theta is the first component in the final input tuple (theta,omega,u) theta=xb.view(xb.shape[0],-1,3)[:,-1,0] risk=(theta.abs()>1.05).float() weights=1.0+2.0*risk loss=((pred-yb).pow(2)*weights[:,None]).mean() loss.backward(); opt.step() with torch.no_grad(): metric=float((net(xt)-yt).pow(2).mean().detach().cpu()) return (metric,net,d) if return_model else metric except RuntimeError: # Robust CUDA fallback, with fresh CPU model and identical seed/config. device='cpu'; seed_all(seed); net=make_model(MODEL,d['input_shape'],d['out_dim']).to(device) x,y=d['xtr'],d['ytr']; xt,yt=d['xte'],d['yte']; opt=torch.optim.Adam(net.parameters(),lr=cfg['lr']) for _ in range(EPOCHS): perm=torch.randperm(x.shape[0]) for ix in perm.split(BATCH): xb,yb=x[ix],y[ix]; opt.zero_grad(set_to_none=True); pred=net(xb) theta=xb.view(xb.shape[0],-1,3)[:,-1,0]; weights=1+2*(theta.abs()>1.05).float() loss=((pred-yb).pow(2)*weights[:,None]).mean(); loss.backward(); opt.step() metric=float((net(xt)-yt).pow(2).mean().detach()) return (metric,net,d) if return_model else metric def mechanism_signature(cfg, base_seed=0): bm=baseline_metric(cfg,base_seed) im,model,d=idea_metric(cfg,base_seed,True) with torch.no_grad(): x=d['xte']; truth=d['yte']; bp=make_model(MODEL,d['input_shape'],d['out_dim']) # Re-train baseline model for behavior measurement under same seed. seed_all(base_seed); bp,_,_=train_model(bp,d,epochs=EPOCHS,lr=cfg['lr'],batch=BATCH,log=lambda *_:None) with torch.no_grad(): bp_dev=next(bp.parameters()).device; model_dev=next(model.parameters()).device pb=bp(x.to(bp_dev)).cpu(); pi=model(x.to(model_dev)).cpu(); yy=truth.cpu() th=x.view(x.shape[0],-1,3)[:,-1,0] critical=th.abs()>1.05 eb=((pb-yy).abs()>0.20)[critical].float().mean().item() ei=((pi-yy).abs()>0.20)[critical].float().mean().item() return {'critical_fraction':float(critical.float().mean()),'baseline_critical_bad_rate':float(eb),'idea_critical_bad_rate':float(ei),'observed_bad_rate_reduction':float(eb-ei),'required_rollouts_r_0.02_delta_0.05':149,'confirmed':bool(ei