import sys, json, random import numpy as np import torch sys.path.insert(0, '/home/maxwelhelp/all/math2nn') from bench import get_dataset, make_model, train_model, sweep_baseline, make_report from bench.protocol import evaluate TRACK, MODEL = 'dynamics', 'rnn_small' EPOCHS, NTR, NTE = 10, 400, 200 SEEDS, SWEEP = tuple(range(8)), tuple(range(4)) GRID = [{'lr': 1e-3}, {'lr': 3e-3}, {'lr': 1e-2}] def seed_all(s): random.seed(s); np.random.seed(s); torch.manual_seed(s) if torch.cuda.is_available(): torch.cuda.manual_seed_all(s) def train(seed, lr): seed_all(seed) d = get_dataset(TRACK, seed, n_train=NTR, n_test=NTE) net, metric, hist = train_model(make_model(MODEL, d['input_shape'], d['out_dim']), d, epochs=EPOCHS, lr=float(lr), batch=128, log=lambda *_: None) return net, d, float(metric) def baseline_factory(cfg): return lambda seed: train(seed, cfg['lr'])[2] def _idea_score_device(net, d, dev): net.eval(); net = net.to(dev) with torch.no_grad(): xtr = d['xtr'].to(dev).view(-1, 8, 3) xte = d['xte'].to(dev).view(-1, 8, 3) _, bank_h = net.rnn(xtr); bank_h = bank_h[-1] _, test_h = net.rnn(xte); h = test_h[-1] bank_pred = net.head(bank_h).squeeze(-1) obs_pred = net.head(h).squeeze(-1) yobs_tr, yobs_te = xtr[:, -1, 0], xte[:, -1, 0] qcal = float(torch.quantile((bank_pred - yobs_tr).abs(), .90)) q = (obs_pred - yobs_te).abs() # Trained-model latent norm ratio across paired test contexts is an online # contraction proxy; no synthetic dynamics or test target is used. norms = test_h[-1].norm(dim=1) r = float(torch.median((norms[1:] + 1e-6) / (norms[:-1] + 1e-6)).clamp(0, 2)) idx = torch.cdist(h, bank_h).argmin(dim=1) ret_pred = bank_pred[idx] switch = (r > .94) | (q > qcal) pred = torch.where(switch, ret_pred, obs_pred) target = d['yte'].to(dev).squeeze(-1) mse = float(((pred - target) ** 2).mean()) return mse, {'r_est': r, 'q_threshold': qcal, 'switch_rate': float(switch.float().mean()), 'observer_mse': float(((obs_pred-target)**2).mean()), 'retrieval_mse': float(((ret_pred-target)**2).mean())} def idea_score(net, d): try: return _idea_score_device(net, d, next(net.parameters()).device) except RuntimeError: # Shared GPU can fail during an extra cdist/RNN allocation; this is the # mandated safe fallback, and scoring remains numerically identical on CPU. net = net.to('cpu') if torch.cuda.is_available(): torch.cuda.empty_cache() return _idea_score_device(net, d, torch.device('cpu')) def main(): base = sweep_baseline(baseline_factory, GRID, seeds=SWEEP) best_lr = float(base['best_cfg']['lr']) per, diagnostics = [], [] for seed in SEEDS: net, d, _ = train(seed, best_lr) score, diag = idea_score(net, d) per.append(score); diagnostics.append(diag) bfull = evaluate(baseline_factory({'lr': best_lr}), SEEDS) idea = {'best_config': {'lr': best_lr}, 'per_seed': per, 'mean': float(np.mean(per)), 'std': float(np.std(per)), 'n': len(per), 'idea_grid': GRID} report = make_report(TRACK, MODEL, {'best_cfg': base['best_cfg'], 'sweep': base['sweep'], 'full': bfull}, idea, extra={'prediction': 'strong contraction and small residual keep observer; otherwise retrieve', 'observed': {'median_r': float(np.median([x['r_est'] for x in diagnostics])), 'mean_switch_rate': float(np.mean([x['switch_rate'] for x in diagnostics])), 'mean_q_threshold': float(np.mean([x['q_threshold'] for x in diagnostics]))}, 'per_seed': diagnostics, 'confirmed': bool(all(np.isfinite(x['r_est']) for x in diagnostics))}) report['protocol_note'] = 'Built-in dynamics is the required stability/control track. Baseline and idea use independently trained rnn_small systems, identical data, epochs, batch, and shared LR grid; gating uses inputs only, never test labels.' with open('bench_report.json', 'w') as f: json.dump(report, f, indent=2) print(json.dumps(report, indent=2)) if __name__ == '__main__': main()