import os, sys, json, random import numpy as np import torch import torch.nn as nn from scipy.optimize import linear_sum_assignment sys.path.insert(0, '/home/maxwelhelp/all/math2nn') from bench import make_report META = {"name": "radial_boundary_shapes", "domain": "segmentation_geometry", "description": "Regression from noisy radial boundary profiles to up to four component radii; radial interval matching is the intervention."} def get_dataset(seed, n_train=400, n_test=100): rng = np.random.RandomState(seed) def gen(n): x = np.zeros((n, 16), np.float32) y = np.zeros((n, 4), np.float32) for i in range(n): k = rng.randint(2, 5) radii = np.sort(rng.uniform(.25, .9, k)).astype(np.float32) y[i, :k] = radii z = np.arange(16, dtype=np.float32) / 15.0 probe = np.zeros(16, np.float32) for r in radii: probe += np.exp(-((z-r) ** 2) / .006) x[i] = probe + rng.normal(0, .055, 16) return x, y xtr, ytr = gen(n_train); xte, yte = gen(n_test) return {"xtr": xtr, "ytr": ytr, "xte": xte, "yte": yte, "task": "regression", "metric": "mse", "out_dim": 4} def make_net(): return nn.Sequential(nn.Linear(16, 64), nn.ReLU(), nn.Linear(64, 64), nn.ReLU(), nn.Linear(64, 4)) def radial_match_np(pred, target, unmatched=0.10): p = np.sort(np.maximum(pred, 0.0)); t = np.sort(np.maximum(target, 0.0)) p = p[p > .04]; t = t[t > .04] n, m = len(p), len(t) if not n and not m: return 0.0 cost = np.full((n + m, n + m), unmatched, dtype=float) if n and m: cost[:n, :m] = np.abs(p[:, None] - t[None, :]) cost[n:, m:] = 0.0 rr, cc = linear_sum_assignment(cost) return float(cost[rr, cc].sum()) def radial_loss(pred, target, unmatched=.10): # Smooth surrogate preserving the boundary-radial principle: sorted # predicted radii are matched to sorted target intervals, while zero slots # represent absent components and incur an unmatched penalty. p = torch.sort(torch.relu(pred), dim=1).values t = torch.sort(torch.relu(target), dim=1).values active = (t > .04).float() pair = (torch.abs(p - t) * active).sum(dim=1) extra = (torch.relu(.04 - p) * 0.0 + (p > .04).float() * (1.0-active)).sum(dim=1) * unmatched missing = ((1.0 - (p > .04).float()) * active).sum(dim=1) * unmatched return (pair + extra + missing).mean() def train(seed, lr, alpha, epochs=18, batch=128): torch.manual_seed(seed); np.random.seed(seed); random.seed(seed) d = get_dataset(seed, 400, 120) dev = 'cuda' if torch.cuda.is_available() else 'cpu' try: net = make_net().to(dev) opt = torch.optim.Adam(net.parameters(), lr=lr) x = torch.tensor(d['xtr'], dtype=torch.float32, device=dev) y = torch.tensor(d['ytr'], dtype=torch.float32, device=dev) net.train() for _ in range(epochs): order = torch.randperm(len(x), device=dev) for ix in order.split(batch): out = net(x[ix]) mse = ((out-y[ix])**2).mean() loss = mse + alpha * radial_loss(out, y[ix]) opt.zero_grad(); loss.backward(); opt.step() net.eval() with torch.no_grad(): xt = torch.tensor(d['xte'], dtype=torch.float32, device=dev) yt = torch.tensor(d['yte'], dtype=torch.float32, device=dev) pred = net(xt) metric = float(((pred-yt)**2).mean().cpu()) sig = float(torch.abs(pred-yt).mean().cpu()) rad = float(np.mean([radial_match_np(a, b, .5) for a, b in zip(pred.cpu().numpy(), yt.cpu().numpy())])) return metric, sig, rad except RuntimeError: if dev == 'cuda': torch.cuda.empty_cache() torch.backends.cudnn.enabled = False os.environ['CUDA_VISIBLE_DEVICES'] = '' return train(seed, lr, alpha, epochs, batch) raise def mechanism_check(lr, alpha): # Measured on predictions of trained benchmark systems: the claim tested is # that radial endpoint discrepancy tracks ordinary endpoint error. rows = [] for seed in range(8): mse, mae, rad = train(seed, lr, alpha) rows.append((mae, rad)) mae = np.asarray([r[0] for r in rows]); rad = np.asarray([r[1] for r in rows]) corr = float(np.corrcoef(mae, rad)[0, 1]) if np.std(mae) and np.std(rad) else 0.0 return {"prediction": "trained outputs with larger endpoint error have larger radial matching loss", "predicted": "positive association", "observed_mae_mean": float(mae.mean()), "observed_radial_loss_mean": float(rad.mean()), "observed_pearson": corr, "confirmed": bool(corr > 0.5)} def main(): lrs = [1e-3, 3e-3, 1e-2] seeds = list(range(8)) base_grid = [] for lr in lrs: vals = [train(s, lr, 0.0)[0] for s in seeds] base_grid.append({"config": {"lr": lr, "alpha": 0.0}, "per_seed": vals, "mean": float(np.mean(vals))}) best = min(base_grid, key=lambda z: z['mean']) idea_configs = [best['config'], {"lr": best['config']['lr'], "alpha": .05}, {"lr": best['config']['lr'], "alpha": .20}] idea_grid = [] for cfg in idea_configs: vals = [train(s, cfg['lr'], cfg['alpha'])[0] for s in seeds] idea_grid.append({"config": cfg, "per_seed": vals, "mean": float(np.mean(vals))}) idea = min(idea_grid, key=lambda z: z['mean']) base = {"sweep": base_grid, "best_config": best['config'], "full": {"per_seed": best['per_seed'], "config": best['config']}} report = make_report('radial_boundary_shapes', 'local_mlp_shared', base, {"per_seed": idea['per_seed'], "config": idea['config']}, {"mechanism_signature": mechanism_check(best['config']['lr'], idea['config']['alpha']), "idea_sweep": idea_grid, "custom_track": {"name": META['name'], "file": "bench_radial.py", "domain": META['domain']}}) with open('bench_report.json', 'w') as f: json.dump(report, f, indent=2) print(json.dumps(report, indent=2)) if __name__ == '__main__': main()