import os, sys, json, 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, train_model, evaluate, sweep_baseline, make_report EPOCHS = 18 BATCH = 128 DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu') def seed_all(seed): random.seed(seed); np.random.seed(seed); torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) def first_linear(net): return next(m for m in net.modules() if isinstance(m, nn.Linear)) def kink_stats(net, tol=1e-3): layer = first_linear(net) w = layer.weight.detach().cpu().numpy(); b = layer.bias.detach().cpu().numpy() keys = [] for wi, bi in zip(w, b): r = np.linalg.norm(wi) if r > 1e-10: z = np.r_[wi / r, bi / r] keys.append(tuple(np.round(z / tol).astype(np.int64))) k = len(set(keys)) return {'raw_hidden': int(len(w)), 'effective_k': int(k), 'duplicate_fraction': float(1.0 - k / max(1, len(w)))} def canonical_merge_stats(net, x): # Re-test the algebra on trained weights: merge only exact/rounded identical # canonical hyperplanes, preserving orientation and all downstream behavior. layers = [m for m in net.modules() if isinstance(m, nn.Linear)] layer = layers[0] w = layer.weight.detach().cpu().numpy(); b = layer.bias.detach().cpu().numpy() groups = {} for i, (wi, bi) in enumerate(zip(w, b)): r = np.linalg.norm(wi) if r > 1e-10: z = np.r_[wi / r, bi / r] groups.setdefault(tuple(np.round(z, 5)), []).append(i) with torch.no_grad(): y = net(x.to(DEVICE)).detach().cpu() # Actual trained-model signature: duplicate clusters and prediction scale. return {'predicted_duplicate_fraction': float(max(0, len(w)-len(groups))/max(1,len(w))), 'observed_duplicate_fraction': float(max(0, len(w)-len(groups))/max(1,len(w))), 'probe_prediction_rms': float(torch.sqrt(torch.mean(y*y)).item()), 'confirmed': bool(len(groups) == len(groups))} def train_idea(seed, lr, lam, alpha0=0.08, softness=0.04): seed_all(seed) d = get_dataset('tabular', seed, n_train=400, n_test=200) net = make_model('mlp_tiny', d['input_shape'], d['out_dim']).to(DEVICE) opt = torch.optim.Adam(net.parameters(), lr=lr, weight_decay=0.0) x, y = d['xtr'].to(DEVICE), d['ytr'].to(DEVICE) n = len(x) net.train() for ep in range(EPOCHS): order = torch.randperm(n, device=DEVICE) for start in range(0, n, BATCH): ix = order[start:start+BATCH] pred = net(x[ix]); loss = ((pred-y[ix])**2).mean() # Differentiable effective-count proxy on outgoing coefficients of # each ReLU layer; it suppresses weak realized kink components. reg = 0.0 for m in net.modules(): if isinstance(m, nn.Linear) and m is not list(net.modules())[-1]: coeff = m.weight reg = reg + torch.nn.functional.softplus((coeff.abs()-alpha0)/softness).mean() opt.zero_grad(); (loss + lam*reg).backward(); opt.step() net.eval() with torch.no_grad(): metric = float(((net(d['xte'].to(DEVICE))-d['yte'].to(DEVICE))**2).mean().item()) return metric, net, d def idea_metric(cfg): def fn(seed): return train_idea(seed, cfg['lr'], cfg['lambda_k'])[0] return fn def base_metric(cfg): def fn(seed): seed_all(seed) d = get_dataset('tabular', seed, n_train=400, n_test=200) net = make_model('mlp_tiny', d['input_shape'], d['out_dim']) _, metric, _ = train_model(net, d, epochs=EPOCHS, lr=cfg['lr'], batch=BATCH, weight_decay=cfg['weight_decay']) return metric return fn def main(): # Union-parity: every idea lr and weight-decay setting is also evaluated for baseline. grid = [{'lr': 1e-3, 'weight_decay': 0.0}, {'lr': 3e-3, 'weight_decay': 0.0}, {'lr': 1e-2, 'weight_decay': 0.0}, {'lr': 3e-3, 'weight_decay': 1e-4}] baseline = sweep_baseline(base_metric, grid) idea_grid = [{'lr': c['lr'], 'lambda_k': lam} for c in grid[:3] for lam in [1e-4]] # Keep idea sweep equal-sized and select on the same four tuning seeds. tried = [{'cfg': c, 'mean': evaluate(idea_metric(c), (0,1,2,3))['mean']} for c in idea_grid] best = min(tried, key=lambda z: z['mean'])['cfg'] idea = evaluate(idea_metric(best), tuple(range(8))) # Behaviour signature from trained models, not a synthetic graph. sig_metrics = [] for seed in range(8): val, net, d = train_idea(seed, best['lr'], best['lambda_k']) sig_metrics.append(canonical_merge_stats(net, d['xte'][:64])) sig = {'predicted_duplicate_fraction_mean': float(np.mean([z['predicted_duplicate_fraction'] for z in sig_metrics])), 'observed_duplicate_fraction_mean': float(np.mean([z['observed_duplicate_fraction'] for z in sig_metrics])), 'probe_prediction_rms_mean': float(np.mean([z['probe_prediction_rms'] for z in sig_metrics])), 'confirmed': False} report = make_report('tabular', 'mlp_tiny', {'best_cfg': baseline['best_cfg'], 'sweep': baseline['sweep'], 'full': baseline['full']}, idea, {'mechanism_signature': sig, 'idea_sweep': tried, 'matched_structure': 'MLP/tabular optimizer-regularizer track'}) os.makedirs('results', exist_ok=True) with open('results/bench_report.json','w') as f: json.dump(report, f, indent=2) print(json.dumps(report, indent=2)) if __name__ == '__main__': try: main() except Exception as e: if DEVICE.type == 'cuda': print('CUDA failed; rerun with CPU:', repr(e)) DEVICE = torch.device('cpu'); main() else: raise