assembly-economics/simulations/gadm02/nonrival.py
tegwick e05c8f100f ASSEMBLY-WP-0007 active, T01 done: GADM-0.5-dev rival/non-rival output (phi_r, xn); commons feed carries the non-rival effect, no genuine decoupling (BR-010)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 14913@bnt-lap001
Assistant-Session: add7ef69-b901-4756-9a67-34cf1125d96f
2026-10-13 23:07:39 +02:00

95 lines
4.6 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.

"""Rival vs. non-rival output across the adoption band (ASSEMBLY-WP-0007-T01).
Tests (models/GADM-Sectors-Scope.md § 1):
1. Throughput decoupling: can VFAC rise while physical throughput falls?
2. Does the generative regime's ecological cost shrink when its growth is mostly
non-rival?
Variants: GADM-0.4 (phi_r 1), phi_r 0.5 and 0.2 with commons feed xn 0.3, and phi_r 0.2
without commons feed (xn 0), at band anchors B1-B4, baseline and generative regime.
Usage: python3 -B simulations/gadm02/nonrival.py
"""
from __future__ import annotations
import sys
from pathlib import Path
sys.dont_write_bytecode = True
import band_probe as bp # noqa: E402
import gadm04 as g # noqa: E402
import gadm05 as g5 # noqa: E402
import grounding as gr # noqa: E402
from scenarios import load_all # noqa: E402
OUT = Path(__file__).resolve().parent / "results"
ANCHORS = list(gr.BAND)
VARIANTS = {"GADM-0.4 (φ 1)": {"phi_r": 1.0, "xn": 0.0}, "φ 0.5, xn 0.3": {"phi_r": 0.5, "xn": 0.3},
"φ 0.2, xn 0.3": {"phi_r": 0.2, "xn": 0.3}, "φ 0.2, xn 0": {"phi_r": 0.2, "xn": 0.0}}
def decoupling(rows: list[dict], p: dict, step: int = 5) -> float:
"""Share of 5-year windows in which VFAC rises while physical throughput falls."""
hits = n = 0
for i in range(0, len(rows) - step, step):
a, b = rows[i], rows[i + step]
n += 1
hits += b["VFAC"] > a["VFAC"] and g5.throughput(b, p) < g5.throughput(a, p)
return hits / n
def main() -> None:
OUT.mkdir(exist_ok=True)
scs = load_all()
var = {a: gr.variant(gr.BAND[a]) for a in ANCHORS}
res = {}
for a in ANCHORS:
for sc in ("baseline", "generative_regime"):
for vn, vp in VARIANTS.items():
p = {**scs[sc]["params"], **var[a][0], **vp}
rows = g.simulate(p, {**scs[sc]["init"], **var[a][1]})
f = rows[-1]
res[(a, sc, vn)] = {"V": f["VFAC"], "E": f["E"], "X": f["X"], "Y": f["Ystar"],
"yn": f["Yn"] / f["Ystar"] if f["Ystar"] else 0.0,
"thr": g5.throughput(f, p), "dec": decoupling(rows, p)}
lines = ["# GADM-0.5-dev: rival vs. non-rival output across the band", "",
"Generated by `simulations/gadm02/nonrival.py` (ASSEMBLY-WP-0007-T01). t = 100. Y_n share =",
"non-rival share of output. Decoupling = share of 5-year windows with VFAC rising while physical",
"throughput (e1 K_phys dem thr + e2 K_Ω) falls.", ""]
for sc in ("baseline", "generative_regime"):
lines += [f"## {scs[sc]['title']}", "",
"| Anchor | Variant | VFAC | E | X | Y* | Y_n share | throughput | decoupling |",
"|---|---|---|---|---|---|---|---|---|"]
for a in ANCHORS:
for vn in VARIANTS:
r = res[(a, sc, vn)]
lines.append(f"| {a} | {vn} | {r['V']:.3f} | {r['E']:.2f} | {r['X']:.2f} | {r['Y']:.3f} | "
f"{r['yn']:.2f} | {r['thr']:.4f} | {r['dec']:.2f} |")
lines.append("")
readings = []
for sc in ("baseline", "generative_regime"):
title = scs[sc]["title"]
readings.append((f"ΔVFAC φ 0.2/xn 0.3 vs. GADM-0.4: {title}",
[res[(a, sc, "φ 0.2, xn 0.3")]["V"] - res[(a, sc, "GADM-0.4 (φ 1)")]["V"] for a in ANCHORS]))
readings.append((f"ΔE φ 0.2/xn 0.3 vs. GADM-0.4: {title}",
[res[(a, sc, "φ 0.2, xn 0.3")]["E"] - res[(a, sc, "GADM-0.4 (φ 1)")]["E"] for a in ANCHORS]))
readings.append((f"ΔVFAC from commons feed alone (xn 0.3 vs. 0 at φ 0.2): {title}",
[res[(a, sc, "φ 0.2, xn 0.3")]["V"] - res[(a, sc, "φ 0.2, xn 0")]["V"] for a in ANCHORS]))
gen_vs_base = [res[(a, "generative_regime", "φ 0.2, xn 0.3")]["V"] - res[(a, "baseline", "φ 0.2, xn 0.3")]["V"]
for a in ANCHORS]
old_gap = [res[(a, "generative_regime", "GADM-0.4 (φ 1)")]["V"] - res[(a, "baseline", "GADM-0.4 (φ 1)")]["V"]
for a in ANCHORS]
readings.append(("generative − baseline VFAC, GADM-0.5 (φ 0.2, xn 0.3)", gen_vs_base))
readings.append(("generative − baseline VFAC, GADM-0.4", old_gap))
lines += ["## Readings and robustness class", "", "| Reading | " + " | ".join(ANCHORS) + " | class |",
"|---|" + "---|" * (len(ANCHORS) + 1)]
for name, vals in readings:
lines.append(f"| {name} | " + " | ".join(f"{v:+.3f}" for v in vals) + f" | {bp.classify(vals, 'sign')} |")
lines.append("")
(OUT / "nonrival.md").write_text("\n".join(lines))
print("\n".join(lines))
if __name__ == "__main__":
main()