Differentiable Persistence Landscape Layer / stage2_bench.py

Failed on benchmark

Raw ⬇ ZIP
  1import json, random, sys
  2import numpy as np
  3import torch
  4from torch import nn
  5
  6sys.path.insert(0, "/home/maxwelhelp/all/math2nn")
  7from bench import train_model, evaluate, sweep_baseline, make_report, get_dataset as bench_get_dataset
  8# registered track supplied by bench
  9
 10SEEDS = tuple(range(8))
 11LRS = [1e-3, 3e-3, 1e-2]
 12EPOCHS = 28
 13NTR, NTE = 400, 200
 14
 15class PersistenceLandscape(nn.Module):
 16    def __init__(self, n_grid=48, levels=4):
 17        super().__init__()
 18        self.levels = levels
 19        self.register_buffer("grid", torch.linspace(0., 1., n_grid))
 20
 21    def forward(self, x):
 22        b, d = x[..., 0:1], x[..., 1:2]
 23        t = self.grid.view(1, 1, -1)
 24        v = torch.relu(torch.minimum(t - b, d - t))
 25        vals, _ = torch.topk(v, k=min(self.levels, v.shape[1]), dim=1)
 26        if vals.shape[1] < self.levels:
 27            z = torch.zeros(vals.shape[0], self.levels - vals.shape[1], vals.shape[2], device=x.device)
 28            vals = torch.cat([vals, z], dim=1)
 29        return vals.clamp_min(0.)
 30
 31
 32def seed_all(seed):
 33    random.seed(seed); np.random.seed(seed); torch.manual_seed(seed)
 34    if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed)
 35
 36
 37def as_tensors(ds):
 38    return {**ds,
 39      "xtr": torch.as_tensor(ds["xtr"], dtype=torch.float32),
 40      "ytr": torch.as_tensor(ds["ytr"], dtype=torch.long),
 41      "xte": torch.as_tensor(ds["xte"], dtype=torch.float32),
 42      "yte": torch.as_tensor(ds["yte"], dtype=torch.long)}
 43
 44
 45def representation(ds, kind):
 46    q = as_tensors(ds)
 47    if kind == "baseline":
 48        q["xtr"] = q["xtr"].flatten(1)
 49        q["xte"] = q["xte"].flatten(1)
 50        q["input_shape"] = q["xtr"].shape[1:]
 51    else:
 52        layer = PersistenceLandscape()
 53        with torch.no_grad():
 54            q["xtr"] = layer(q["xtr"]).flatten(1)
 55            q["xte"] = layer(q["xte"]).flatten(1)
 56        q["input_shape"] = q["xtr"].shape[1:]
 57    return q
 58
 59
 60def run(kind, lr, seed, return_model=False):
 61    seed_all(seed)
 62    ds = representation(bench_get_dataset("persistence_diagrams", seed=seed, n_train=NTR, n_test=NTE), kind)
 63    model = nn.Sequential(nn.Linear(int(np.prod(ds["input_shape"])), 64), nn.ReLU(), nn.Linear(64, 2))
 64    net, metric, hist = train_model(model, ds, epochs=EPOCHS, lr=lr, batch=128, log=lambda *a, **k: None)
 65    if return_model:
 66        return float(metric), net, ds
 67    return float(metric)
 68
 69
 70def base_factory(cfg):
 71    return lambda seed: run("baseline", float(cfg["lr"]), seed)
 72
 73
 74def idea_factory(cfg):
 75    return lambda seed: run("idea", float(cfg["lr"]), seed)
 76
 77
 78def mechanism_signature():
 79    seed_all(9001)
 80    ds = bench_get_dataset("persistence_diagrams", seed=9001, n_train=16, n_test=16)
 81    x = torch.as_tensor(ds["xte"], dtype=torch.float32)
 82    layer = PersistenceLandscape()
 83    a, b = x[:8], x[:8].clone()
 84    b[:, :, 0] += 0.006; b[:, :, 1] += 0.006
 85    with torch.no_grad():
 86        la, lb = layer(a), layer(b)
 87    observed = float(torch.sqrt(torch.mean((la-lb)**2)))
 88    tent_a = torch.relu(torch.minimum(layer.grid.view(1,1,-1)-a[...,0:1], a[...,1:2]-layer.grid.view(1,1,-1)))
 89    tent_b = torch.relu(torch.minimum(layer.grid.view(1,1,-1)-b[...,0:1], b[...,1:2]-layer.grid.view(1,1,-1)))
 90    matched = float(torch.sqrt(torch.mean((tent_a-tent_b)**2)))
 91    ratio = observed / matched if matched else 0.0
 92    return {"prediction":"landscape perturbation does not exceed matched tent perturbation",
 93            "predicted_ratio_bound":1.0, "observed_ratio":ratio,
 94            "observed_landscape_rms":observed, "matched_tent_rms":matched,
 95            "confirmed": bool(np.isfinite(ratio) and ratio <= 1.02)}
 96
 97
 98def main():
 99    grid = [{"lr": lr} for lr in LRS]
100    base = sweep_baseline(base_factory, grid, seeds=(0,1,2,3))
101    idea_trials = [{"cfg": c, "result": evaluate(idea_factory(c), SEEDS)} for c in grid]
102    best = min(idea_trials, key=lambda z: z["result"]["mean"])
103    rep = make_report("persistence_diagrams", "mlp_tiny", base, best["result"], {
104        "custom_track": {"name":"persistence_diagrams", "file":"persistence_track.py", "domain":"topological_representation"},
105        "idea_config": best["cfg"], "idea_sweep": idea_trials,
106        "mechanism_signature": mechanism_signature()
107    })
108    rep["mechanism_signature"] = rep.pop("mechanism_signature")
109    with open("bench_report.json", "w") as f: json.dump(rep, f, indent=2)
110    print(json.dumps(rep, indent=2))
111
112if __name__ == "__main__": main()