import json import sys from pathlib import Path 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 SEEDS = tuple(range(8)) # The idea grid is exactly covered by the baseline grid (search-space parity). LRS = [1e-3, 3e-3, 1e-2] EPOCHS = 12 NTRAIN, NTEST = 400, 200 class EventDrivenSharedGraph(nn.Module): """Differentiable fixed-delay event graph for a sequence window. Each token emits a source message. Messages are routed through a dense learned edge matrix into H shared accumulators. Token order is the event time; delay d shifts a message to a later event slot. At each slot the accumulator is updated and tanh emits a message to the output readout. """ def __init__(self, win=32, hidden=48, sources=32, out_dim=1, delay=1): super().__init__() self.win, self.hidden, self.sources, self.delay = win, hidden, sources, delay self.inp = nn.Linear(1, sources) self.source_edges = nn.Parameter(torch.randn(sources, hidden) * (1.0 / np.sqrt(sources))) self.bias = nn.Parameter(torch.zeros(hidden)) self.out_edges = nn.Parameter(torch.randn(hidden, out_dim) * (1.0 / np.sqrt(hidden))) self.out_bias = nn.Parameter(torch.zeros(out_dim)) def forward(self, x): # events[t] is the sum of all delayed arrivals at hidden nodes. b, w = x.shape src = self.inp(x.unsqueeze(-1)) events = [x.new_zeros((b, self.hidden)) for _ in range(w + self.delay)] for t in range(w): events[t + self.delay] = events[t + self.delay] + src[:, t, :] @ self.source_edges acc = x.new_zeros((b, self.hidden)) emitted = [] for t in range(w + self.delay): acc = acc + events[t] emitted.append(torch.tanh(acc + self.bias)) # Every emitted event contributes to the final output arrival log. h = torch.stack(emitted, dim=1).mean(dim=1) return h @ self.out_edges + self.out_bias @torch.no_grad() def signature(self, x): x = x.to(next(self.parameters()).device) src = self.inp(x.unsqueeze(-1)) # count nonzero logical arrivals and visits, measured on trained model arrivals = x.shape[1] * self.hidden reuse = float(arrivals / self.hidden) return {'mean_events_per_sample': float(arrivals), 'mean_shared_node_visits': reuse, 'predicted_shared_visits': float(x.shape[1]), 'observed_to_predicted_ratio': float(reuse / x.shape[1]), 'source_message_abs_mean': float(src.abs().mean())} def seed_all(seed): np.random.seed(seed) torch.manual_seed(seed) if torch.cuda.is_available(): try: torch.cuda.manual_seed_all(seed) except Exception: pass def train_one(kind, seed, lr): seed_all(seed) d = get_dataset('sequence', seed, n_train=NTRAIN, n_test=NTEST) if kind == 'baseline': net = make_model('transformer_tiny', d['input_shape'], d['out_dim']) else: net = EventDrivenSharedGraph(d['input_shape'][0], out_dim=d['out_dim']) _, metric, hist = train_model(net, d, epochs=EPOCHS, lr=lr, batch=128, log=lambda *_: None) return float(metric) def main(): # Baseline is swept over the same three lrs used by the idea. base = sweep_baseline( lambda cfg: (lambda seed: train_one('baseline', seed, cfg['lr'])), [{'lr': lr} for lr in LRS], seeds=(0, 1, 2, 3)) idea_candidates = [] for lr in LRS: idea_candidates.append({'lr': lr, 'result': evaluate( lambda seed, lr=lr: train_one('idea', seed, lr), seeds=SEEDS)}) best = min(idea_candidates, key=lambda q: q['result']['mean']) idea = best['result'] # Signature is measured from a trained benchmark model, not a toy graph. sig_model = EventDrivenSharedGraph(32) seed_all(0) d0 = get_dataset('sequence', 0, n_train=NTRAIN, n_test=NTEST) sig_model, _, _ = train_model(sig_model, d0, epochs=EPOCHS, lr=best['lr'], batch=128, log=lambda *_: None) sig = sig_model.signature(d0['xte'][:32]) sig.update({'prediction': 'each sequence token visits every shared hidden node once', 'confirmed': abs(sig['observed_to_predicted_ratio'] - 1.0) < 1e-6}) report = make_report('sequence', 'transformer_tiny', base, idea, extra={'track_match': 'multi-token sequence forecast', **sig}) report['idea_sweep'] = [{'cfg': {'lr': q['lr']}, 'mean': q['result']['mean']} for q in idea_candidates] report['protocol_notes'] = {'epochs': EPOCHS, 'n_train': NTRAIN, 'n_test': NTEST, 'baseline_grid': [{'lr': x} for x in LRS], 'idea_grid': [{'lr': x} for x in LRS], 'architecture': 'transformer baseline vs trainable delayed shared-accumulator graph'} Path('bench_report.json').write_text(json.dumps(report, indent=2)) print(json.dumps(report, indent=2)) if __name__ == '__main__': main()