"""One-at-a-time sensitivity of final VFAC to every parameter (±20 %). Usage: python3 simulations/gadm02/sensitivity.py """ from __future__ import annotations from pathlib import Path import gadm02 as g from scenarios import load_all OUT = Path(__file__).resolve().parent / "results" LEVERS = {"m", "L", "lin_open", "std", "disc", "expl", "x_att", "auto", "dshare", "cp", "od"} DELTA = 0.20 TOP = 15 def final_vfac(params: dict, init: dict) -> float: return g.simulate(params, init)[-1]["VFAC"] def analyse(sc: dict) -> list[tuple[str, float, float, float]]: base_p = {**g.PARAMS, **sc["params"]} v0 = final_vfac(base_p, sc["init"]) res = [] for k, val in base_p.items(): lo, hi = val * (1 - DELTA), val * (1 + DELTA) if k in LEVERS: hi = min(hi, 1.0) if val == 0: lo, hi = 0.0, 0.05 d_lo = final_vfac({**base_p, k: lo}, sc["init"]) - v0 d_hi = final_vfac({**base_p, k: hi}, sc["init"]) - v0 res.append((k, d_lo, d_hi, max(abs(d_lo), abs(d_hi)))) res.sort(key=lambda r: -r[3]) return v0, res def main() -> None: OUT.mkdir(exist_ok=True) scs = load_all() lines = [f"# {g.MODEL_VERSION} sensitivity (one-at-a-time, ±{DELTA:.0%})", "", "Generated by `simulations/gadm02/sensitivity.py`. ΔVFAC is the change in", "final VFAC (t = 100) when one parameter moves down or up by 20 %, all others", "fixed. Levers in [0, 1] are capped at 1. Zero-valued parameters are tested at", "0.05. One-at-a-time analysis misses interactions.", ""] for name in ("baseline", "generative_regime"): v0, res = analyse(scs[name]) rank = {r[0]: i + 1 for i, r in enumerate(res)} lines += [f"## {scs[name]['title']} (VFAC = {v0:.3f})", "", "| Rank | Parameter | ΔVFAC at −20 % | ΔVFAC at +20 % |", "|---|---|---|---|"] lines += [f"| {i + 1} | `{k}` | {lo:+.4f} | {hi:+.4f} |" for i, (k, lo, hi, _) in enumerate(res[:TOP])] lines += ["", f"Reuse coefficient `rho0` ranks {rank['rho0']} of {len(res)}.", ""] (OUT / "sensitivity.md").write_text("\n".join(lines)) print("\n".join(lines)) if __name__ == "__main__": main()