import json, sys, random from pathlib import Path import numpy as np import torch from torch import nn ROOT = Path(__file__).resolve().parent sys.path.insert(0, '/home/maxwelhelp/all/math2nn') from bench import train_model, evaluate, sweep_baseline, make_report from bench.promote import smoke_test import holonomy_track SEEDS = tuple(range(8)) # Union of all step sizes is shared by both sides; nearby epoch settings are # likewise represented in the common grid. GRID = [ {'lr': 1e-3, 'epochs': 35, 'width': 48}, {'lr': 3e-3, 'epochs': 35, 'width': 48}, {'lr': 1e-2, 'epochs': 35, 'width': 48}, ] class BaseMatrixNet(nn.Module): def __init__(self, width=48, holonomy=False): super().__init__() self.holonomy = holonomy self.edge = nn.Linear(4, 4, bias=False) # same readout capacity and dimensions in both systems self.head = nn.Sequential(nn.Linear(4, width), nn.Tanh(), nn.Linear(width, 4)) def forward(self, x): z = self.edge(x.reshape(-1, 3, 4)).reshape(x.shape[0], 3, 2, 2) if self.holonomy: h = torch.eye(2, device=x.device, dtype=x.dtype).expand(x.shape[0], 2, 2).clone() for r in range(3): h = h @ z[:, r] pooled = h.reshape(x.shape[0], 4) else: pooled = z.sum(dim=1).reshape(x.shape[0], 4) return self.head(pooled) def commutator_signature(self, x): with torch.no_grad(): z = self.edge(x.reshape(-1, 3, 4)).reshape(x.shape[0], 3, 2, 2) ordered = z[:, 0] @ z[:, 1] @ z[:, 2] reversed_order = z[:, 2] @ z[:, 1] @ z[:, 0] return float((ordered - reversed_order).norm(dim=(1, 2)).mean().cpu()) def seed_all(seed): random.seed(seed); np.random.seed(seed); torch.manual_seed(seed) def train_one(kind, cfg, seed, return_net=False): seed_all(seed) d = holonomy_track.get_dataset(seed, 400, 200) net = BaseMatrixNet(width=cfg['width'], holonomy=(kind == 'idea')) td = dict(d) for k in ('xtr', 'ytr', 'xte', 'yte'): td[k] = torch.tensor(d[k], dtype=torch.float32) net, metric, hist = train_model(net, td, epochs=cfg['epochs'], lr=cfg['lr'], batch=128, weight_decay=1e-4, log=lambda *_: None) if metric is None: raise RuntimeError('training failed') if return_net: return metric, net, d return metric def main(): ok, msg = smoke_test(ROOT / 'holonomy_track.py', 'directed_path_holonomy') if not ok: raise RuntimeError('custom track smoke test failed: ' + msg) base = sweep_baseline(lambda cfg: lambda seed: train_one('baseline', cfg, seed), GRID) # Same three configs on idea side; report the best config selected on the # four-seed selection split, then evaluate it on all eight paired seeds. idea_trials = [] for cfg in GRID: r = evaluate(lambda seed, c=cfg: train_one('idea', c, seed), seeds=(0,1,2,3)) idea_trials.append({'cfg': cfg, 'mean': r['mean']}) best_cfg = min(idea_trials, key=lambda q: q['mean'])['cfg'] idea = evaluate(lambda seed: train_one('idea', best_cfg, seed), seeds=SEEDS) # Signature is behavior of trained networks, not an analytic identity. sig_vals = [] for s in SEEDS: _, bn, bd = train_one('baseline', base['best_cfg'], s, return_net=True) _, hn, hd = train_one('idea', best_cfg, s, return_net=True) bdev = next(bn.parameters()).device hdev = next(hn.parameters()).device xb = torch.tensor(bd['xte'], dtype=torch.float32, device=hdev) xb_base = torch.tensor(bd['xte'], dtype=torch.float32, device=bdev) with torch.no_grad(): y = torch.tensor(bd['yte'], dtype=torch.float32) pb = bn(xb_base).to('cpu'); ph = hn(xb).to('cpu') # prediction error on reversed input measures order sensitivity; # compare observed degradation to the model's ordinary test error. rev = xb.reshape(-1,3,4)[:, [2,1,0]].reshape(-1,12) obs = float(((ph - hn(rev).to('cpu'))**2).mean().sqrt()) pred_order = float(((ph - pb)**2).mean().sqrt()) sig_vals.append({'order_sensitivity': obs, 'system_gap': pred_order, 'baseline_test_rmse': float(((pb-y)**2).mean().sqrt()), 'idea_test_rmse': float(((ph-y)**2).mean().sqrt())}) signature = { 'prediction': 'ordered holonomy should retain order: reversal sensitivity exceeds additive baseline gap', 'observed_mean': {k: float(np.mean([v[k] for v in sig_vals])) for k in sig_vals[0]}, 'confirmed': bool(np.mean([v['order_sensitivity'] for v in sig_vals]) > np.mean([v['baseline_test_rmse'] for v in sig_vals])) } report = make_report('directed_path_holonomy', 'matched_matrix_mlp', base, idea, { 'custom_track': {'name': 'directed_path_holonomy', 'file': 'holonomy_track.py', 'domain': 'graph'}, 'idea_sweep': idea_trials, 'idea_best_cfg': best_cfg, 'mechanism_signature': signature, 'notes': 'Baseline and idea share edge encoder and head; only sum versus ordered matrix product differs.' }) Path('bench_report.json').write_text(json.dumps(report, indent=2, sort_keys=True)) print(json.dumps(report, sort_keys=True)) if __name__ == '__main__': main()