assembly-economics/simulations/gadm02/sensitivity.py
tegwick 0e372398bf Implement ASSEMBLY-WP-0004: GADM-0.2 model, simulation, scenarios, sensitivity
- models/GADM-CLD.md: causal-loop diagram, loops, 49 signed links
- models/GADM-0.2.md: organism extensions (K split, attention headroom,
  selection channel O, membrane/lineage levers, participation claims),
  equations, findings
- simulations/gadm02: stdlib simulation, scenario runner, OAT sensitivity,
  unit tests; results committed
- scenarios/: six GADM-0.1 regimes + baseline + two organism-axis variants
- metrics/MetricsCatalog.md, models/GADM-0.3-Candidates.md,
  references/LiteratureComparison.md
- ASSEMBLY-WP-0005 (proposed, residual): GADM-0.3

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 1494246@bnt-lap001
Assistant-Session: caf6de6f-f9e4-47ce-a584-49baa271fa4b
2026-10-12 00:43:53 +02:00

60 lines
2.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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()