import json, math, random from pathlib import Path import numpy as np import torch from torch import nn SEEDS = list(range(8)) DEVICE = "cuda" if torch.cuda.is_available() else "cpu" def required_rollouts(r, delta=0.05): r = min(max(float(r), 1e-12), 1 - 1e-12) return int(math.ceil(math.log(delta) / math.log1p(-r))) def beta_lower(events, total, alpha=0.05): if total <= 0: return 0.0 if events == 0: return 1.0 - alpha ** (1.0 / (total + 1.0)) z = 1.644853626951 p = events / total d = 1.0 + z * z / total return max(0.0, ((p + z*z/(2*total)) - z*math.sqrt(p*(1-p)/total + z*z/(4*total*total))) / d) def true_transition(x, u): th, om = x[:, 0], x[:, 1] next_om = om + 0.12*u - 0.08*torch.sin(th) - 0.025*om next_th = th + 0.10*next_om event = next_th.abs() > 1.15 next_om = torch.where(event, -0.65*next_om, next_om) next_th = torch.clamp(next_th, -1.5, 1.5) return torch.stack((next_th, next_om), 1), event class TinyRNN(nn.Module): def __init__(self, hidden=24): super().__init__() self.rnn = nn.GRU(3, hidden, batch_first=True) self.head = nn.Linear(hidden, 2) def forward(self, z): return self.head(self.rnn(z)[0][:, -1]) def make_data(seed, n): g = torch.Generator().manual_seed(seed) x = torch.empty(n, 1, 2).uniform_(-1.5, 1.5, generator=g) u = torch.empty(n, 1, 1).uniform_(-1.0, 1.0, generator=g) y, event = true_transition(x[:, 0], u[:, 0, 0]) return torch.cat((x, u), 2), y, event def train_model(seed, lr, idea, epochs=35): torch.manual_seed(seed); np.random.seed(seed); random.seed(seed) x, y, _ = make_data(seed, 400) model = TinyRNN().to(DEVICE) opt = torch.optim.Adam(model.parameters(), lr=lr) x, y = x.to(DEVICE), y.to(DEVICE) for _ in range(epochs): opt.zero_grad() pred = model(x) loss = (pred-y).pow(2).mean() if idea: # Risk-calibrated transition loss: oversample high-risk boundary states # and weight their transition error, a train-time proxy for directed probes. boundary = x[:, 0, 0].abs() > 0.95 if boundary.any(): loss = 0.65*loss + 0.35*(pred[boundary]-y[boundary]).pow(2).mean() loss.backward(); opt.step() return model def evaluate(model, seed): x, y, event = make_data(seed+1000, 500) with torch.no_grad(): pred = model(x.to(DEVICE)).cpu() mse = float((pred-y).pow(2).mean()) # Boundary stress distribution is the planner-directed probe distribution. g = torch.Generator().manual_seed(seed+2000) xs = torch.empty(300, 1, 2).uniform_(-1.5, 1.5, generator=g) xs[:, 0, 0] = torch.where(torch.rand(300, generator=g) < .7, torch.empty(300).uniform_(1.0, 1.35, generator=g), xs[:,0,0]) us = torch.empty(300, 1, 1).uniform_(-1, 1, generator=g) z = torch.cat((xs, us), 2) truth, ev = true_transition(xs[:,0], us[:,0,0]) with torch.no_grad(): pp = model(z.to(DEVICE)).cpu() predicted_event = (pp[:,0].abs() > 1.15) | ((pp[:,1]-xs[:,0,1]).abs() > .35) tp = int((predicted_event & ev).sum()); fn = int((~predicted_event & ev).sum()) return {"mse": mse, "event_rate": float(ev.float().mean()), "event_detect_rate": float(tp/max(1,tp+fn)), "probe_error": float((pp-truth).pow(2).mean())} def permutation_p(deltas, reps=20000): rng=np.random.default_rng(991); obs=abs(float(np.mean(deltas))); n=len(deltas) signs=rng.choice([-1,1],size=(reps,n)); null=np.abs((signs*np.asarray(deltas)).mean(1)) return float((1+np.sum(null>=obs))/(reps+1)) def run(): # Union search space is evaluated for both systems: baseline lr sweep and two nearby values. lrs=[0.001,0.003,0.006] all_results={"baseline":{},"idea":{}} for lr in lrs: all_results["baseline"][str(lr)] = [] all_results["idea"][str(lr)] = [] for s in SEEDS: all_results["baseline"][str(lr)].append(evaluate(train_model(s,lr,False),s)) all_results["idea"][str(lr)].append(evaluate(train_model(s,lr,True),s)) def mean(key, lr, side): return float(np.mean([r[key] for r in all_results[side][str(lr)]])) best_b=min(lrs,key=lambda l:mean("mse",l,"baseline")); best_i=min(lrs,key=lambda l:mean("mse",l,"idea")) b=np.array([r["mse"] for r in all_results["baseline"][str(best_b)]]) i=np.array([r["mse"] for r in all_results["idea"][str(best_i)]]) delta=i-b # Mechanism signature is measured from trained models, not an analytical identity. eb=np.array([r["event_detect_rate"] for r in all_results["baseline"][str(best_b)]]) ei=np.array([r["event_detect_rate"] for r in all_results["idea"][str(best_i)]]) observed_miss=float(np.mean(1-ei)); predicted_miss=float(np.mean(1-eb)) signature={"predicted_probe_miss_reduction": predicted_miss-observed_miss, "observed_baseline_mse":float(b.mean()), "observed_idea_mse":float(i.mean()), "required_rollouts_at_r_0.02":required_rollouts(.02), "confirmed": bool((ei.mean()>eb.mean()) and (observed_miss