# stage2.py # Author: Liam Grinstead # Purpose: Orbital & Agent Coupling Validation (Stage Two of Twelve) import torch, math, time, json, random, argparse, numpy as np # ---------------- Determinism ---------------- def set_seed(s=1234): random.seed(s); np.random.seed(s) torch.manual_seed(s); torch.cuda.manual_seed_all(s) # ---------------- Telemetry ------------------ class Telemetry: def __init__(self, path=None): self.t0 = time.time() self.f = open(path, "w") if path else None def emit(self, **k): k["t"] = round(time.time() - self.t0, 3) line = json.dumps(k, separators=(",", ":")) print(line) if self.f: self.f.write(line + "\n"); self.f.flush() def close(self): if self.f: self.f.close() # ---------------- Orbital Coupler ------------ class Orbital: def __init__(self, g=0.006, floor=0.2): self.a = 0.0; self.b = math.pi/3; self.g = g; self.floor = floor def step(self): diff = (self.b - self.a + math.pi) % (2*math.pi) - math.pi if abs(diff) < self.floor: diff = self.floor * (1 if diff >= 0 else -1) delta = math.sin(diff) self.a = (self.a + self.g * delta) % (2*math.pi) self.b = (self.b - self.g * delta) % (2*math.pi) drift = abs((self.a - self.b + math.pi) % (2*math.pi) - math.pi) return drift, abs(delta) # ---------------- DCLR Optimiser ------------- class DCLR(torch.optim.Optimizer): def __init__(self, params, lr=5e-3, beta=0.9, gamma=0.999, eps=1e-8, cg=0.05): super().__init__(params, dict(lr=lr,beta=beta,gamma=gamma,eps=eps,cg=cg)) @torch.no_grad() def step(self, closure=None): tot = 0.0 for g in self.param_groups: lr, beta, gamma, eps, c = g["lr"], g["beta"], g["gamma"], g["eps"], g["cg"] for p in g["params"]: if p.grad is None: continue st = self.state[p] if not st: st["m"] = torch.zeros_like(p) st["v"] = torch.zeros_like(p) st["coh"] = torch.zeros_like(p) m,v,h = st["m"],st["v"],st["coh"]; grad=p.grad m.mul_(beta).add_(grad,alpha=1-beta) v.mul_(gamma).addcmul_(grad,grad,value=1-gamma) d=grad-m; h.mul_(0.9).add_(d.abs(),alpha=0.1) lr_eff=lr/(1+c*h) step=lr_eff*m/(v.sqrt()+eps); p.add_(-step) tot += (step*step).sum().item() return None, tot # ---------------- Agent Field ---------------- class Agents(torch.nn.Module): def __init__(self, n=256, box=10.0, r0=0.15): super().__init__() self.n=n; self.box=box; self.r0=r0 pos=(torch.rand(n,2)-0.5)*box vel=torch.zeros(n,2) self.pos=torch.nn.Parameter(pos); self.vel=torch.nn.Parameter(vel) def forward(self): n=self.n; pos=self.pos diff=pos.unsqueeze(1)-pos.unsqueeze(0) dist=torch.clamp(diff.norm(dim=-1),1e-6) mask=(dist