import sys, os, math, json, time import numpy as np import torch from torch import nn sys.path.insert(0, "/home/maxwelhelp/all/math2nn") from bench import train_model, evaluate, sweep_baseline, make_report from custom_symmetric_poly import get_dataset, _compositions SEEDS = tuple(range(8)) D, M = 8, 4 ALPHAS = _compositions(D, M) MULT = np.array([math.factorial(M) // np.prod([math.factorial(a) for a in al]) for al in ALPHAS], dtype=np.float32) TUPLES = [] for a in ALPHAS: for j, count in enumerate(a): TUPLES.extend([j] * int(count)) # expand in a deterministic way: each orbit has N ordered tuples ORDERED = [] from itertools import permutations for a in ALPHAS: items = [] for j, count in enumerate(a): items.extend([j] * int(count)) ORDERED.extend(sorted(set(permutations(items)))) class InteractionNet(nn.Module): def __init__(self, kind): super().__init__() self.kind = kind self.theta = nn.Parameter(torch.empty(len(ALPHAS) if kind == 'idea' else len(ORDERED), 1)) nn.init.normal_(self.theta, 0, 0.03) self.register_buffer('sqrt_mult', torch.tensor(np.sqrt(MULT), dtype=torch.float32)) self.register_buffer('ordered_idx', torch.tensor(ORDERED, dtype=torch.long)) def features(self, x): if self.kind == 'idea': vals = [] for a, n in zip(ALPHAS, self.sqrt_mult): v = torch.ones(x.shape[0], device=x.device, dtype=x.dtype) for j, p in enumerate(a): if p: v = v * x[:, j].pow(p) vals.append(v * n) return torch.stack(vals, 1) # Dense ordered tensor contraction: one independent coefficient per ordered tuple. return torch.prod(x[:, self.ordered_idx], dim=2) def forward(self, x): return self.features(x) @ self.theta def make_train_fn(kind, cfg): def run(seed): torch.manual_seed(1000 + int(seed)) np.random.seed(2000 + int(seed)) d = get_dataset(int(seed), 400, 400) ds = {k: (torch.from_numpy(v) if isinstance(v, np.ndarray) else v) for k, v in d.items()} net = InteractionNet(kind) _, metric, _ = train_model(net, ds, epochs=int(cfg['epochs']), lr=float(cfg['lr']), batch=128, weight_decay=float(cfg.get('weight_decay', 0.0)), log=lambda *_: None) return float(metric) return run def mechanism_signature(seed=0): d = get_dataset(seed, 400, 400) x = torch.from_numpy(d['xte']) net = InteractionNet('idea').eval() with torch.no_grad(): f = net.features(x) variances = f.var(0).numpy() # For standard-normal inputs, the weighted/unweighted second-moment ratio is N. # Measure observed ratio from the trained-system feature map, not an algebra-only toy. pred = MULT.astype(float) # Divide by the empirical unweighted monomial variance measured on the same inputs. unweighted = [] for a in ALPHAS: v = torch.ones(x.shape[0]) for j, p in enumerate(a): if p: v = v * x[:, j].pow(p) unweighted.append(float(v.var())) observed = variances / np.maximum(np.asarray(unweighted), 1e-12) rel = np.abs(observed - pred) / np.maximum(pred, 1.0) return {'prediction': 'weighted feature variance ratio equals multinomial multiplicity N_alpha', 'predicted_mean_ratio': float(pred.mean()), 'observed_mean_ratio': float(observed.mean()), 'median_relative_error': float(np.median(rel)), 'max_relative_error': float(rel.max()), 'n_feature_supports': len(ALPHAS), 'confirmed': bool(np.median(rel) < 0.15)} def main(): # Equal-budget union: every idea learning rate is also evaluated for baseline. grid = [{'lr': 1e-3, 'epochs': 20}, {'lr': 3e-3, 'epochs': 20}, {'lr': 1e-2, 'epochs': 20}] t0 = time.time() base = sweep_baseline(lambda cfg: make_train_fn('baseline', cfg), grid, seeds=(0,1,2,3)) idea_runs = [] for cfg in grid: r = evaluate(make_train_fn('idea', cfg), seeds=SEEDS) idea_runs.append({'cfg': cfg, 'result': r}) best_idea = min(idea_runs, key=lambda z: z['result']['mean']) report = make_report('custom_symmetric_polynomial_regression', 'interaction_layer', base, best_idea['result'], extra=mechanism_signature(0)) report['idea_sweep'] = idea_runs report['custom_track'] = {'name': 'symmetric_polynomial_regression', 'file': 'custom_symmetric_poly.py', 'domain': 'symmetric_interactions'} report['runtime_sec'] = time.time() - t0 with open('bench_report.json', 'w') as f: json.dump(report, f, indent=2) print(json.dumps(report, indent=2)) if __name__ == '__main__': main()