Spectral Hamiltonian Neuron / stage2_bench.py
Mechanism confirmed, baseline not beaten
1import os, sys, json
2import numpy as np
3import torch
4import torch.nn as nn
5
6sys.path.insert(0, "/home/maxwelhelp/all/math2nn")
7from bench import get_dataset, train_model, evaluate, sweep_baseline, make_report
8
9TRACK = "dynamics"
10EPOCHS = 18
11LRS = [1e-3, 3e-3, 1e-2]
12D = 4
13
14
15def fixed_ops():
16 # Hermitian Pauli interactions on two latent qubits; X and Z terms do not commute.
17 I = torch.eye(2, dtype=torch.float32)
18 X = torch.tensor([[0., 1.], [1., 0.]])
19 Y = torch.tensor([[0., -1.], [1., 0.]]) # real representation of iY
20 Z = torch.tensor([[1., 0.], [0., -1.]])
21 kron = torch.kron
22 return torch.stack([kron(X, I), kron(Z, X), kron(Y, Y), kron(Z, Z)])
23
24
25class MatchedRNN(nn.Module):
26 def __init__(self, idea, seed):
27 super().__init__()
28 # This is the bench rnn_small backbone, kept identical between systems.
29 self.rnn = nn.GRU(3, 64, batch_first=True)
30 self.proj = nn.Linear(64, D, bias=False)
31 g = torch.Generator().manual_seed(10000 + int(seed))
32 with torch.no_grad():
33 self.proj.weight.copy_(torch.randn(D, 64, generator=g) / np.sqrt(64))
34 for p in self.proj.parameters():
35 p.requires_grad_(False)
36 self.idea = idea
37 if idea:
38 self.theta = nn.Parameter(torch.randn(D, generator=g) * 0.15)
39 self.register_buffer("ops", fixed_ops())
40 else:
41 self.readout = nn.Linear(D, 1, bias=False)
42
43 def forward(self, x):
44 seq = x.view(x.shape[0], -1, 3)
45 _, h = self.rnn(seq)
46 z = self.proj(h[-1])
47 if not self.idea:
48 return self.readout(z)
49 # rho is a pure-state density matrix made from the shared latent feature.
50 q = z / (torch.linalg.vector_norm(z, dim=1, keepdim=True) + 1e-6)
51 rho = q.unsqueeze(2) * q.unsqueeze(1)
52 H = torch.einsum("j,jab->ab", self.theta, self.ops)
53 lam, U = torch.linalg.eigh(H)
54 A = (U * torch.tanh(lam).unsqueeze(0)) @ U.T
55 pred = torch.einsum("bij,ji->b", rho, A)
56 return pred.unsqueeze(1)
57
58
59def run_one(idea, lr, seed, keep_model=False):
60 torch.manual_seed(7000 + int(seed))
61 np.random.seed(7000 + int(seed))
62 ds = get_dataset(TRACK, int(seed), n_train=400, n_test=400)
63 model = MatchedRNN(idea, seed)
64 net, metric, hist = train_model(model, ds, epochs=EPOCHS, lr=lr, batch=128, log=lambda *_: None)
65 if net is None:
66 raise RuntimeError("bench training failed")
67 return (float(metric), net, ds) if keep_model else float(metric)
68
69
70def make_train(idea, cfg=None):
71 cfg = cfg or {"lr": 3e-3, "epochs": EPOCHS}
72 return lambda seed: run_one(idea, float(cfg["lr"]), seed)
73
74
75def behavior_signature(idea_res):
76 # Re-test the claimed nonlinear expressivity on predictions of trained systems.
77 vals = []
78 nonlinear = []
79 comm = fixed_ops()
80 comm_norm = float(torch.linalg.matrix_norm(comm[0] @ comm[1] - comm[1] @ comm[0]))
81 for seed in range(8):
82 metric, net, ds = run_one(True, idea_res["cfg"]["lr"], seed, True)
83 net = net.to("cpu")
84 with torch.no_grad():
85 p = net(ds["xte"]).squeeze().numpy()
86 # Shared latent coordinates are reconstructed from the trained backbone.
87 seq = ds["xte"].view(len(ds["xte"]), -1, 3)
88 _, h = net.rnn(seq)
89 z = net.proj(h[-1]).numpy()
90 X = np.column_stack([np.ones(len(z)), z])
91 fit = X @ np.linalg.lstsq(X, p, rcond=None)[0]
92 nl = float(np.sqrt(np.mean((p - fit) ** 2)))
93 vals.append(float(metric)); nonlinear.append(nl)
94 return {
95 "prediction": "noncommuting Hamiltonian terms should create a measurable nonlinear readout",
96 "commutator_frobenius": comm_norm,
97 "trained_idea_test_mse_mean": float(np.mean(vals)),
98 "trained_idea_nonlinear_residual_mean": float(np.mean(nonlinear)),
99 "confirmed": bool(comm_norm > 1e-6 and np.mean(nonlinear) > 1e-4),
100 }
101
102
103def main():
104 # Baseline and idea use the same union of step sizes, satisfying search parity.
105 grid = [{"lr": lr, "epochs": EPOCHS} for lr in LRS]
106 base = sweep_baseline(lambda cfg: make_train(False, cfg), grid)
107 idea_trials = []
108 best = None
109 for cfg in grid:
110 r = evaluate(make_train(True, cfg))
111 idea_trials.append({"cfg": cfg, **r})
112 if best is None or r["mean"] < best["mean"]:
113 best = {"cfg": cfg, **r}
114 sig = behavior_signature(best)
115 report = make_report(TRACK, "rnn_small_matched_spectral_head", base, best,
116 {"mechanism_signature": sig,
117 "idea_sweep": idea_trials,
118 "structural_match": "dynamics: controlled pendulum rollout"})
119 report["budget"] = {"epochs": EPOCHS, "n_train": 400, "n_test": 400, "seeds": 8}
120 with open("bench_report.json", "w") as f:
121 json.dump(report, f, indent=2)
122 print(json.dumps(report, indent=2))
123
124
125if __name__ == "__main__":
126 main()