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
135 lines
6.8 KiB
Python
135 lines
6.8 KiB
Python
"""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()
|