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, train_model, sweep_baseline, make_report SEED0 = 597 LR_GRID = [1e-3, 3e-3, 1e-2] EPOCHS = 12 BATCH = 128 def canonical(word, rank, supports): a = list(word) changed = True while changed: changed = False for i in range(len(a)-1): x, y = a[i], a[i+1] commute = supports[x].isdisjoint(supports[y]) if commute and rank[x] > rank[y]: a[i], a[i+1] = y, x changed = True return tuple(a) class ModularSequence(nn.Module): """Tiny sequence predictor with trace-canonical modular sub-block.""" def __init__(self, word): super().__init__() self.width = 32 self.inp = nn.Linear(1, self.width) self.pos = nn.Parameter(torch.zeros(1, 32, self.width)) # Operators have disjoint read/write supports for g0..g3. self.ops = nn.ModuleDict() self.supports = {} for g in range(4): name = f"g{g}" self.ops[name] = nn.Sequential(nn.Linear(8, 8), nn.GELU()) self.supports[name] = frozenset(range(8*g, 8*(g+1))) self.ops["global"] = nn.Sequential(nn.Linear(32, 32), nn.GELU()) self.supports["global"] = frozenset(range(32)) self.order = tuple(word) self.rank = {n:i for i,n in enumerate(("g0","g1","g2","g3","global"))} self.head = nn.Sequential(nn.LayerNorm(32), nn.Linear(32*32, 1)) def apply_op(self, h, name): if name == "global": return self.ops[name](h) idx = sorted(self.supports[name]) z = self.ops[name](h[..., idx]) out = h.clone() out[..., idx] = z return out def forward_with_order(self, x, word): h = self.inp(x.unsqueeze(-1)) + self.pos[:, :x.shape[1]] for name in word: h = self.apply_op(h, name) h = self.head[0](h) return self.head[1](h.reshape(h.shape[0], -1)) def forward(self, x): return self.forward_with_order(x, self.order) def make_net(seed, idea): torch.manual_seed(seed) # Deliberately noncanonical proposal; only disjoint group swaps are legal. proposed = ("g3", "g1", "global", "g0", "g2") word = canonical(proposed, {n:i for i,n in enumerate(("g0","g1","g2","g3","global"))}, {**{f"g{i}": frozenset(range(8*i,8*(i+1))) for i in range(4)}, "global":frozenset(range(32))}) if idea else proposed return ModularSequence(word) def train_one(seed, lr, idea, return_model=False): # Identical task, model family, optimizer, epochs, and batch size. torch.manual_seed(seed); np.random.seed(seed); random.seed(seed) d = get_dataset("sequence", seed, n_train=4000, n_test=1000) net = make_net(seed, idea) trained, metric, hist = train_model(net, d, epochs=EPOCHS, lr=lr, batch=BATCH, log=lambda *_: None) if trained is None: raise RuntimeError("bench training failed") return (float(metric), trained, d) if return_model else float(metric) def signature(): vals_comm, vals_non = [], [] for seed in range(8): _, net, d = train_one(seed, 3e-3, True, True) x = d["xte"][:64].to(next(net.parameters()).device) prop = net.order can = canonical(prop, net.rank, net.supports) with torch.no_grad(): vals_comm.append(float((net.forward_with_order(x, prop)-net.forward_with_order(x, can)).abs().max())) # Swap a group operator across global: this is not support-disjoint. bad = ("g3", "g1", "g0", "global", "g2") vals_non.append(float((net.forward_with_order(x, prop)-net.forward_with_order(x, bad)).abs().max())) mc, mn = max(vals_comm), float(np.median(vals_non)) return {"prediction": "disjoint-support swaps preserve the trained network output; group/global swaps do not", "predicted_commuting_max": 1e-5, "observed_commuting_max": mc, "predicted_noncommuting_positive": True, "observed_noncommuting_median": mn, "confirmed": bool(mc <= 1e-5 and mn > 1e-4), "n_trained_models": 8} def main(): # Baseline sweep includes every idea-side learning rate (search-space parity). grid = [{"lr": lr, "epochs": EPOCHS, "batch": BATCH} for lr in LR_GRID] def factory(cfg): return lambda seed: train_one(seed, cfg["lr"], False) base = sweep_baseline(factory, grid) # Idea sweep: best baseline lr plus two nearby/equal-budget settings. idea_runs = [] for lr in LR_GRID: r = {"lr": lr, "epochs": EPOCHS, "batch": BATCH, "result": {"per_seed": [train_one(s, lr, True) for s in range(8)]}} r["result"]["mean"] = float(np.mean(r["result"]["per_seed"])) r["result"]["std"] = float(np.std(r["result"]["per_seed"])) r["result"]["n"] = 8 idea_runs.append(r) best = min(idea_runs, key=lambda z:z["result"]["mean"]) report = make_report("sequence", "modular_sequence_local", base, best["result"], {"mechanism_signature": signature(), "track_justification": "sequence is structurally matched: multi-token window correlations and a sequence-level model", "idea_sweep": [{"cfg": {k:v for k,v in r.items() if k != "result"}, "mean": r["result"]["mean"]} for r in idea_runs], "canonical_proposal": ["g3","g1","global","g0","g2"], "canonical_word": list(canonical(("g3","g1","global","g0","g2"), {n:i for i,n in enumerate(("g0","g1","g2","g3","global"))}, {**{f"g{i}":frozenset(range(8*i,8*(i+1))) for i in range(4)},"global":frozenset(range(32))}))}) with open("bench_report.json", "w") as f: json.dump(report, f, indent=2) print(json.dumps(report, indent=2)) if __name__ == "__main__": main()