assembly-economics/simulations/gadm02/world_scenarios.py

135 lines
6.8 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.

"""Global and local-in-context scenarios; hypothesis re-test (ASSEMBLY-WP-0011-T04/T05).
World: US (operators' home, calibrated, J3-like levers), EU (calibrated, J1),
AR (calibrated, J2) and a J4 reference bloc "J4" (US calibrated start with J4
levers and its own operators; it competes for the frontier only).
Context scenarios (rest of the world as seen by EU and AR; T04):
X1 US-led concentrated frontier current levers
X2 open-lineage world US moves to rights-first (J1) levers
X3 accelerating frontier US and J4 with self-enhancement (selfenh 3, B4-like)
X4 fragmented blocs hosts' frontier access 0.2, foreign shares halved
Hypotheses (owner, research/ScenarioBandMethod.md § 4):
H1 "Europe lags": EU adoption below US and AR; is lagging worse for output or VFAC?
H2 "Argentina may thrive": AR output and VFAC above EU?
Local vs. global: EU run alone against a US context path taken from a US-only
run, compared with the global run. The approximation fails when the host feeds
back on the home (spillover, frontier).
Usage: python3 -B simulations/gadm02/world_scenarios.py
"""
from __future__ import annotations
import sys
from pathlib import Path
sys.dont_write_bytecode = True
import cross_border as cb # noqa: E402
import gadm04 as g # noqa: E402
import world as w # noqa: E402
from jurisdictions import ARCHETYPES # noqa: E402
OUT = Path(__file__).resolve().parent / "results"
J1 = cb.J1
J4 = {k: v for k, v in ARCHETYPES["J4"][1].items() if k != "perm"}
CONTEXTS = {
"X1 US-led concentrated frontier": {},
"X2 open-lineage world": {"us": J1},
"X3 accelerating frontier": {"us": {"selfenh": 3.0}, "j4": {"selfenh": 3.0}},
"X4 fragmented blocs": {"kappa": 0.2, "fscale": 0.5},
}
NAMES = ("US", "EU", "AR", "J4")
def build(cal: dict, ctx: dict, sent: float) -> dict:
us_p, us_s = cal["US"]
eu_p, eu_s = cal["EU"]
ar_p, ar_s = cal["AR"]
kap = ctx.get("kappa", cb.KAPPA)
fs = ctx.get("fscale", 1.0)
f_eu = {c: v * fs for c, v in cb.F["EU"].items()}
f_ar = {c: v * fs for c, v in cb.F["AR"].items()}
return {
"US": w.make({**us_p, **ctx.get("us", {}), "sent": sent}, us_s),
"EU": w.make({**eu_p, "sent": sent}, eu_s, home="US", f=f_eu, kappa=kap),
"AR": w.make({**ar_p, "sent": sent}, ar_s, home="US", f=f_ar, kappa=kap),
# WP-0016-T03: the J4 bloc starts from its own calibrated state (China data, calibrate_data "CN")
"J4": w.make({**cal["CN"][0], **ctx.get("j4", {}), "sent": sent, "sig_gain": 0.5, "leg_lag": 10.0},
cal["CN"][1]) if "CN" in cal else
w.make({**us_p, **J4, **ctx.get("j4", {}), "sent": sent, "sig_gain": 0.5, "leg_lag": 10.0}, us_s),
}
def summ(rows: list[dict]) -> dict:
f = rows[-1]
y0 = rows[0]["Ystar"] # output relative to the jurisdiction's own start (levels are not comparable)
return {"Ko10": rows[10]["Ko"], "Y25": sum(r["Ystar"] for r in rows[:26]) / 26 / y0,
"Y100": sum(r["Ystar"] for r in rows) / len(rows) / y0, "V100": f["VFAC"], "P": f["P"],
"G": f["G"], "A": f["A"], "Sig": f["Sig"]}
def main() -> None:
OUT.mkdir(exist_ok=True)
cal = cb.base()
lines = [f"# {g.MODEL_VERSION}: global scenarios with cross-border coupling", "",
"Generated by `simulations/gadm02/world_scenarios.py` (ASSEMBLY-WP-0011-T04/T05).",
"Calibration v0.4 starts; operators' home US; J4 reference bloc competes for the frontier.",
"Ȳ = mean potential output Y* relative to the jurisdiction's own Y* at t 0 (1.00 = no growth);",
"output levels are normalized model units and not comparable across jurisdictions. P floor 0.70.", ""]
res = {}
for cname, ctx in CONTEXTS.items():
for lp, sent in (("off", 0.0), ("on", 1.0)):
rows = w.simulate_world(build(cal, ctx, sent))
for n in NAMES:
res[(cname, lp, n)] = summ(rows[n])
for cname in CONTEXTS:
lines += [f"## {cname}", "",
"| Loop | Jurisdiction | K_Ω t10 | Ȳ 0–25 / 0–100 | VFAC t100 | P t100 | G | A | Σ end |",
"|---|---|---|---|---|---|---|---|---|"]
for lp in ("off", "on"):
for n in NAMES:
r = res[(cname, lp, n)]
lines.append(f"| {lp} | {n} | {r['Ko10']:.2f} | {r['Y25']:.2f} / {r['Y100']:.2f} | {r['V100']:.3f}"
f" | {r['P']:.2f} | {r['G']:.2f} | {r['A']:.2f} | {r['Sig']:.2f} |")
lines.append("")
lines += ["## Hypothesis re-test", "",
"| Context | Loop | H1 EU adoption lags US and AR (K_Ω t10) | EU − US: ΔȲ 0–25 / ΔVFAC | "
"H2 AR − EU: ΔȲ 0–25 / ΔȲ 0–100 / ΔVFAC | H2 holds? |", "|---|---|---|---|---|---|"]
for cname in CONTEXTS:
for lp in ("off", "on"):
eu, us, ar = (res[(cname, lp, n)] for n in ("EU", "US", "AR"))
lags = eu["Ko10"] < us["Ko10"] and eu["Ko10"] < ar["Ko10"]
h2 = ar["Y100"] > eu["Y100"] and ar["V100"] > eu["V100"]
lines.append(f"| {cname} | {lp} | {'yes' if lags else 'no'} ({eu['Ko10']:.2f} vs {us['Ko10']:.2f}, "
f"{ar['Ko10']:.2f}) | {eu['Y25'] - us['Y25']:+.3f} / {eu['V100'] - us['V100']:+.3f} | "
f"{ar['Y25'] - eu['Y25']:+.3f} / {ar['Y100'] - eu['Y100']:+.3f} / {ar['V100'] - eu['V100']:+.3f}"
f" | {'yes' if h2 else 'no'} |")
lines += ["", "## Local-in-context vs. global (EU, context X1, loop off)", "",
"EU simulated alone against a US path from a US-only run, compared with the full global run.", "",
"| Setting | EU P t100 local | global | EU K_Ω t25 local | global | max |ΔP| over time |",
"|---|---|---|---|---|---|"]
for label, eu_access, sigma in (("no spillover", None, None),
("strong EU access rule, spillover σ 0.5", cb.STRONG, {"Pd": 0.5, "Pmem": 0.5})):
wd = build(cal, {}, 0.0)
wd["EU"]["access"] = {c: 0.0 for c in w.COUPLED} | (eu_access or {})
glob = w.simulate_world(wd, record_every=1, sigma=sigma)
us_only = w.simulate_world({"US": wd["US"], "J4": wd["J4"]}, record_every=1)
loc = w.simulate_world({"US": w.context(us_only["US"], wd["US"]["p"]),
"J4": w.context(us_only["J4"], wd["J4"]["p"]), "EU": wd["EU"]},
record_every=1, sigma=sigma)
dmax = max(abs(a["P"] - b["P"]) for a, b in zip(loc["EU"], glob["EU"]))
lines.append(f"| {label} | {loc['EU'][-1]['P']:.3f} | {glob['EU'][-1]['P']:.3f} | {loc['EU'][250]['Ko']:.3f}"
f" | {glob['EU'][250]['Ko']:.3f} | {dmax:.3f} |")
lines.append("")
(OUT / "world_scenarios.md").write_text("\n".join(lines))
print("\n".join(lines))
if __name__ == "__main__":
main()