ASSEMBLY-WP-0006-T03: global sensitivity on GADM-0.4
- gsa.py: any model (--model), parallel (--jobs), channel counters recalibrated per sample; reproduces the GADM-0.2 analysis exactly - GADM-0.4: additive at baseline, but governance levers (m, L, r_lin) interact strongly in the generative regime; commons investment now the top baseline driver; organism capability level confirmed as top uncertainty; explainability gains weight via power channels 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
This commit is contained in:
parent
2c6d6d3ec9
commit
ffff0b3a3c
7 changed files with 227 additions and 21 deletions
|
|
@ -19,7 +19,8 @@ simulation. No install step.
|
|||
```bash
|
||||
python3 -m unittest discover -s simulations/gadm02 # must pass before committing model changes
|
||||
cd simulations/gadm02 && python3 run_scenarios.py && python3 sensitivity.py
|
||||
cd simulations/gadm02 && python3 gsa.py # global sensitivity, ~5 min
|
||||
cd simulations/gadm02 && python3 -B gsa.py --jobs 8 # global sensitivity GADM-0.2, ~1.5 min
|
||||
cd simulations/gadm02 && python3 -B gsa.py --model gadm04 --jobs 8 # GADM-0.4, ~12 min
|
||||
cd simulations/gadm02 && python3 -B power_channels.py # GADM-0.3 power channels (T03 stage)
|
||||
cd simulations/gadm02 && python3 -B wealth.py # GADM-0.3 wealth vs. power
|
||||
cd simulations/gadm02 && python3 -B organism_grid.py # GADM-0.3 organism grid
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ the code is the reference if the two ever disagree.
|
|||
|
||||
**Status:** in development, exploratory. Implemented so far: durable human
|
||||
capability (§ 1, ASSEMBLY-WP-0006-T01), grounded organism depreciation and
|
||||
robustness checks (§ 2, T02).
|
||||
robustness checks (§ 2, T02), global sensitivity analysis (§ 3, T03).
|
||||
|
||||
GADM-0.4 builds on the GADM-0.3 cohort model (`GADM-0.3.md` § 5), which stays
|
||||
unchanged and reproducible in `gadm03c.py`. Time unit: one year.
|
||||
|
|
@ -106,10 +106,65 @@ Details and sources: `ParameterGrounding.md`.
|
|||
are essentially unchanged: L3 half-time 17 years, final ΔH working age +0.032;
|
||||
L1 ΔH −0.028 (ageing, strong mentoring) against −0.038 (stable, none).
|
||||
|
||||
---
|
||||
|
||||
## 3. Global sensitivity analysis (T03)
|
||||
|
||||
Same method as `GADM-0.2.md` § 5.1: Morris screening over all parameters, then
|
||||
Sobol indices (S1 Saltelli 2010 centred, ST Jansen, N = 256, bootstrap 95 %
|
||||
intervals) for the top 10 plus the reuse bundle; ±50 % ranges. New for
|
||||
GADM-0.4:
|
||||
|
||||
- the channel counters c_* are recalibrated for every sample (not varied), so
|
||||
each sample starts in equilibrium;
|
||||
- the switch `wealth_on` is fixed;
|
||||
- evaluation is parallel (`--jobs 8`). The parallel code reproduces the
|
||||
GADM-0.2 analysis exactly.
|
||||
|
||||
Full tables: `simulations/gadm02/results/gsa_gadm04.md`.
|
||||
|
||||
| | Baseline | Generative regime |
|
||||
|---|---|---|
|
||||
| Top ST | `IX` 0.25, `dO` 0.23, `q0` 0.10, `lX` 0.10, `dX` 0.08 | `dO` 0.17, `expl` 0.17, `m` 0.16, `IX` 0.13, `q0` 0.12 |
|
||||
| ΣS1 (analysed set) | 1.03 | 0.89 |
|
||||
| Reuse bundle ΣS1 / ΣST | 0.30 / 0.28 | 0.20 / 0.26 |
|
||||
| Morris σ ≥ μ* (strong interaction / non-linearity) | none | `L` (σ 0.051 vs. μ* 0.036), `r_lin` (0.043 vs. 0.042), `m` (0.057 vs. 0.062) |
|
||||
|
||||
For comparison, GADM-0.2: baseline top ST was `dO` 0.34, `p7` 0.13, `ell` 0.10;
|
||||
generative top ST was `m` 0.27, `L` 0.13, `ereg` 0.13; ΣS1 ≈ 1.04–1.06.
|
||||
|
||||
### 3.1 Readings
|
||||
|
||||
1. **Additivity holds at baseline, but breaks for the governance levers in
|
||||
the generative regime.** That is where the superlinear power channels
|
||||
operate near their thresholds. Lineage diffusion `L` acts almost entirely
|
||||
through interactions (S1 ≈ 0, ST 0.04), together with the lineage
|
||||
self-reinforcement `r_lin`: it matters where it pushes the lineage channel
|
||||
across its tipping point (GADM-0.3 P1/P2). One-at-a-time analysis is no
|
||||
longer adequate for these levers.
|
||||
2. **The commons cluster now drives the baseline.** Commons investment `IX`,
|
||||
enclosure `lX` and obsolescence `dX` together carry about 0.43 of total
|
||||
ST. In GADM-0.2, the reuse bundle carried about 0.11. Two things changed:
|
||||
durable capability makes X's contribution to formation persist, and the
|
||||
power channels make infrastructure control respond to X and standards. The
|
||||
bundle's effect is mostly **IX alone**, without interaction (ΣST ≈ ΣS1).
|
||||
So the GADM-0.2 correction ("reuse is not an interaction bundle") still
|
||||
stands.
|
||||
3. **The level of organism capability is a top uncertainty.** `dO` stays among
|
||||
the top two drivers in both scenarios (ST 0.17–0.23), even after grounding.
|
||||
Varying `dO` with investment held fixed moves the organism level, which is
|
||||
exactly the parameter `ParameterGrounding.md` lists as ungrounded. Grounding
|
||||
that level is the most valuable next empirical step.
|
||||
4. **Explainability gains weight in the generative regime** (ST 0.17, up from
|
||||
0.06 in GADM-0.2). With power channels, epistemic capacity Q does double
|
||||
duty: it checks the political channel and feeds the discovery counter. So
|
||||
`expl` acts on power as well as on knowledge.
|
||||
|
||||
## Reproduce
|
||||
|
||||
```bash
|
||||
python3 -B -m unittest discover -s simulations/gadm02
|
||||
cd simulations/gadm02 && python3 -B life_cycle.py --model gadm04
|
||||
cd simulations/gadm02 && python3 -B robustness.py # T02 robustness checks
|
||||
cd simulations/gadm02 && python3 -B gsa.py --model gadm04 --jobs 8 # T03, ~12 min on 8 cores
|
||||
```
|
||||
|
|
|
|||
|
|
@ -59,7 +59,8 @@ Readings:
|
|||
## Open grounding needs
|
||||
|
||||
1. The level of organism capability K_Ω (AI capital share, task-automation
|
||||
share).
|
||||
share). Confirmed as a top uncertainty by the GADM-0.4 Sobol analysis
|
||||
(`GADM-0.4.md` § 3, reading 3).
|
||||
2. The channel self-reinforcement r_k (persistence of market leadership,
|
||||
platform tipping evidence).
|
||||
3. The wealth → influence conversion k_w (political-economy evidence on
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ bytecode cache can mask same-size changes made within the same second.
|
|||
python3 -m unittest discover -s simulations/gadm02 # tests
|
||||
cd simulations/gadm02 && python3 run_scenarios.py # → results/summary.md + CSVs
|
||||
cd simulations/gadm02 && python3 sensitivity.py # → results/sensitivity.md
|
||||
cd simulations/gadm02 && python3 gsa.py # → results/gsa.md (~5 min; --quick)
|
||||
cd simulations/gadm02 && python3 -B gsa.py --jobs 8 # → results/gsa.md (--model gadm04 → gsa_gadm04.md; --quick)
|
||||
```
|
||||
|
||||
Results under `results/` are committed so that model documents can cite them.
|
||||
|
|
|
|||
|
|
@ -10,18 +10,32 @@ reading in models/GADM-0.2.md can be tested directly.
|
|||
Ranges: each parameter varies ±50 % around its scenario value; levers are
|
||||
clipped to [0, 1]; zero-valued parameters vary in [0, 0.1].
|
||||
|
||||
Usage: python3 simulations/gadm02/gsa.py [--quick]
|
||||
Usage: python3 -B simulations/gadm02/gsa.py [--quick] [--model gadm04] [--jobs 8]
|
||||
|
||||
For models with power channels (gadm03, gadm03c, gadm04), the channel counter
|
||||
coefficients c_* are not varied but recalibrated for every sample, so each
|
||||
sample starts in equilibrium; the wealth_on switch is not varied.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import multiprocessing as mp
|
||||
import random
|
||||
import statistics
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import gadm02 as g
|
||||
from scenarios import load_all
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
import gadm03 # noqa: E402
|
||||
from scenarios import load_all # noqa: E402
|
||||
|
||||
MODEL = sys.argv[sys.argv.index("--model") + 1] if "--model" in sys.argv else "gadm02"
|
||||
JOBS = int(sys.argv[sys.argv.index("--jobs") + 1]) if "--jobs" in sys.argv else 1
|
||||
g = importlib.import_module(MODEL)
|
||||
CHANNELS = "Pm" in g.STOCKS
|
||||
FIXED = {"wealth_on"} | ({c for c in gadm03.COEF.values()} if CHANNELS else set())
|
||||
|
||||
OUT = Path(__file__).resolve().parent / "results"
|
||||
LEVERS = {"m", "L", "lin_open", "std", "disc", "expl", "x_att", "auto", "dshare", "cp", "od"}
|
||||
|
|
@ -44,21 +58,52 @@ def model(base: dict, rng: dict, names: list[str], u: list[float], init: dict) -
|
|||
for k, x in zip(names, u):
|
||||
lo, hi = rng[k]
|
||||
p[k] = lo + x * (hi - lo)
|
||||
if CHANNELS:
|
||||
p.update(gadm03.calibrate(p))
|
||||
return g.simulate(p, init)[-1]["VFAC"]
|
||||
|
||||
|
||||
_CTX: dict = {}
|
||||
|
||||
|
||||
def _init(ctx: dict) -> None:
|
||||
_CTX.update(ctx)
|
||||
|
||||
|
||||
def _eval(u: list[float]) -> float:
|
||||
return model(_CTX["base"], _CTX["rng"], _CTX["names"], u, _CTX["init"])
|
||||
|
||||
|
||||
def evaluate(base, rng, names, init, points: list[list[float]]) -> list[float]:
|
||||
"""Evaluate many sample points, in parallel when --jobs > 1."""
|
||||
if JOBS <= 1:
|
||||
return [model(base, rng, names, u, init) for u in points]
|
||||
ctx = {"base": base, "rng": rng, "names": names, "init": init}
|
||||
with mp.Pool(JOBS, initializer=_init, initargs=(ctx,)) as pool:
|
||||
return pool.map(_eval, points, chunksize=8)
|
||||
|
||||
|
||||
def morris(base, init, rng, r, rnd):
|
||||
names = list(base)
|
||||
names = [n for n in base if n not in FIXED]
|
||||
k, delta = len(names), 2 / 3
|
||||
effects = {n: [] for n in names}
|
||||
for _ in range(r):
|
||||
trajectories = []
|
||||
for _ in range(r): # draw all trajectories first (same RNG order as before)
|
||||
x = [rnd.choice((0.0, 1 / 3)) for _ in range(k)]
|
||||
y = model(base, rng, names, x, init)
|
||||
for i in rnd.sample(range(k), k):
|
||||
order = rnd.sample(range(k), k)
|
||||
pts = [list(x)]
|
||||
for i in order:
|
||||
x[i] += delta
|
||||
y2 = model(base, rng, names, x, init)
|
||||
effects[names[i]].append((y2 - y) / delta)
|
||||
y = y2
|
||||
pts.append(list(x))
|
||||
trajectories.append((order, pts))
|
||||
flat = [pt for _o, pts in trajectories for pt in pts]
|
||||
ys = evaluate(base, rng, names, init, flat)
|
||||
pos = 0
|
||||
for order, pts in trajectories:
|
||||
y = ys[pos:pos + len(pts)]
|
||||
pos += len(pts)
|
||||
for j, i in enumerate(order):
|
||||
effects[names[i]].append((y[j + 1] - y[j]) / delta)
|
||||
res = []
|
||||
for n, ee in effects.items():
|
||||
res.append((n, statistics.fmean(abs(e) for e in ee), statistics.fmean(ee), statistics.pstdev(ee)))
|
||||
|
|
@ -70,11 +115,10 @@ def sobol(base, init, rng, names, n, rnd, boot=200):
|
|||
k = len(names)
|
||||
A = [[rnd.random() for _ in range(k)] for _ in range(n)]
|
||||
B = [[rnd.random() for _ in range(k)] for _ in range(n)]
|
||||
fA = [model(base, rng, names, a, init) for a in A]
|
||||
fB = [model(base, rng, names, b, init) for b in B]
|
||||
fAB = []
|
||||
for i in range(k):
|
||||
fAB.append([model(base, rng, names, A[j][:i] + [B[j][i]] + A[j][i + 1:], init) for j in range(n)])
|
||||
pts = A + B + [A[j][:i] + [B[j][i]] + A[j][i + 1:] for i in range(k) for j in range(n)]
|
||||
ys = evaluate(base, rng, names, init, pts)
|
||||
fA, fB = ys[:n], ys[n:2 * n]
|
||||
fAB = [ys[2 * n + i * n: 2 * n + (i + 1) * n] for i in range(k)]
|
||||
|
||||
def indices(idx):
|
||||
a = [fA[j] for j in idx]
|
||||
|
|
@ -107,6 +151,8 @@ def main() -> None:
|
|||
OUT.mkdir(exist_ok=True)
|
||||
scs = load_all()
|
||||
lines = [f"# {g.MODEL_VERSION} global sensitivity analysis", "",
|
||||
*([("Channel counters c_* are recalibrated per sample (not varied); "
|
||||
"wealth_on is fixed."), ""] if CHANNELS else []),
|
||||
"Generated by `simulations/gadm02/gsa.py`" + (" (--quick)" if quick else "") + ".",
|
||||
f"Output: final VFAC (t = 100 {g.TIME_UNIT}s). Parameters vary ±50 % around the scenario",
|
||||
"value (levers clipped to [0, 1]; zero-valued parameters in [0, 0.1]).",
|
||||
|
|
@ -141,7 +187,8 @@ def main() -> None:
|
|||
lines += ["", f"Sum of S1 over analysed parameters: {tot1:.3f}. Reuse bundle: "
|
||||
f"ΣS1 = {b1:.3f}, ΣST = {bt:.3f}.", ""]
|
||||
print(f"done {name}", file=sys.stderr)
|
||||
(OUT / ("gsa-quick.md" if quick else "gsa.md")).write_text("\n".join(lines))
|
||||
stem = "gsa" if MODEL == "gadm02" else f"gsa_{MODEL}"
|
||||
(OUT / (f"{stem}-quick.md" if quick else f"{stem}.md")).write_text("\n".join(lines))
|
||||
print("\n".join(lines))
|
||||
|
||||
|
||||
|
|
|
|||
102
simulations/gadm02/results/gsa_gadm04.md
Normal file
102
simulations/gadm02/results/gsa_gadm04.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# GADM-0.4-dev global sensitivity analysis
|
||||
|
||||
Channel counters c_* are recalibrated per sample (not varied); wealth_on is fixed.
|
||||
|
||||
Generated by `simulations/gadm02/gsa.py`.
|
||||
Output: final VFAC (t = 100 years). Parameters vary ±50 % around the scenario
|
||||
value (levers clipped to [0, 1]; zero-valued parameters in [0, 0.1]).
|
||||
Morris: 20 trajectories, 4 levels. Sobol: S1 Saltelli (2010, centred), ST Jansen, N = 256,
|
||||
bootstrap 95 % intervals, seed 20261012. Parameters outside the Sobol set stay at
|
||||
their scenario values.
|
||||
|
||||
μ* ranks importance. A σ that is large relative to μ* indicates
|
||||
interactions or non-linearity. ST − S1 is the share of variance due to
|
||||
interactions involving that parameter.
|
||||
|
||||
## Baseline (current trends)
|
||||
|
||||
### Morris screening (top 15)
|
||||
|
||||
| Rank | Parameter | μ* | μ | σ |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `IX` | 0.0520 | +0.0520 | 0.0119 |
|
||||
| 2 | `dO` | 0.0503 | +0.0503 | 0.0209 |
|
||||
| 3 | `q0` | 0.0410 | +0.0410 | 0.0185 |
|
||||
| 4 | `dX` | 0.0333 | -0.0333 | 0.0096 |
|
||||
| 5 | `n1` | 0.0328 | +0.0328 | 0.0122 |
|
||||
| 6 | `ell` | 0.0301 | -0.0301 | 0.0159 |
|
||||
| 7 | `lX` | 0.0275 | -0.0275 | 0.0061 |
|
||||
| 8 | `q2` | 0.0263 | -0.0263 | 0.0142 |
|
||||
| 9 | `g1` | 0.0260 | +0.0260 | 0.0070 |
|
||||
| 10 | `arec` | 0.0250 | +0.0250 | 0.0046 |
|
||||
| 11 | `b1` | 0.0225 | -0.0225 | 0.0049 |
|
||||
| 12 | `wL0` | 0.0216 | +0.0216 | 0.0080 |
|
||||
| 13 | `r_lin` | 0.0210 | -0.0210 | 0.0098 |
|
||||
| 14 | `aext` | 0.0198 | -0.0198 | 0.0059 |
|
||||
| 15 | `r_mem` | 0.0197 | -0.0197 | 0.0092 |
|
||||
|
||||
Reuse bundle Morris ranks: `rho0` 29, `IX` 1, `lin_open` 44, `std` 54
|
||||
|
||||
### Sobol indices
|
||||
|
||||
| Parameter | S1 | S1 95 % CI | ST | ST 95 % CI | ST − S1 |
|
||||
|---|---|---|---|---|---|
|
||||
| `IX` | 0.260 | [0.184, 0.355] | 0.249 | [0.214, 0.291] | -0.011 |
|
||||
| `dO` | 0.169 | [0.077, 0.246] | 0.228 | [0.182, 0.275] | +0.059 |
|
||||
| `q0` | 0.100 | [0.057, 0.150] | 0.097 | [0.082, 0.117] | -0.003 |
|
||||
| `lX` | 0.140 | [0.101, 0.189] | 0.096 | [0.078, 0.115] | -0.044 |
|
||||
| `dX` | 0.109 | [0.059, 0.162] | 0.079 | [0.066, 0.097] | -0.030 |
|
||||
| `n1` | 0.019 | [-0.037, 0.078] | 0.079 | [0.065, 0.098] | +0.060 |
|
||||
| `ell` | 0.070 | [0.027, 0.121] | 0.077 | [0.062, 0.097] | +0.007 |
|
||||
| `g1` | 0.030 | [-0.015, 0.068] | 0.070 | [0.056, 0.084] | +0.040 |
|
||||
| `arec` | 0.017 | [-0.028, 0.064] | 0.062 | [0.052, 0.073] | +0.045 |
|
||||
| `q2` | 0.067 | [0.031, 0.103] | 0.050 | [0.042, 0.059] | -0.016 |
|
||||
| `rho0` | 0.030 | [0.008, 0.048] | 0.017 | [0.014, 0.021] | -0.013 |
|
||||
| `std` | 0.009 | [-0.007, 0.024] | 0.006 | [0.004, 0.007] | -0.003 |
|
||||
| `lin_open` | 0.005 | [-0.006, 0.017] | 0.004 | [0.003, 0.005] | -0.001 |
|
||||
|
||||
Sum of S1 over analysed parameters: 1.025. Reuse bundle: ΣS1 = 0.304, ΣST = 0.277.
|
||||
|
||||
## Generative regime
|
||||
|
||||
### Morris screening (top 15)
|
||||
|
||||
| Rank | Parameter | μ* | μ | σ |
|
||||
|---|---|---|---|---|
|
||||
| 1 | `m` | 0.0624 | +0.0546 | 0.0565 |
|
||||
| 2 | `r_lin` | 0.0423 | -0.0419 | 0.0431 |
|
||||
| 3 | `IX` | 0.0410 | +0.0410 | 0.0288 |
|
||||
| 4 | `dO` | 0.0388 | +0.0388 | 0.0245 |
|
||||
| 5 | `n1` | 0.0365 | +0.0365 | 0.0175 |
|
||||
| 6 | `L` | 0.0361 | +0.0197 | 0.0508 |
|
||||
| 7 | `q0` | 0.0350 | +0.0350 | 0.0261 |
|
||||
| 8 | `expl` | 0.0347 | +0.0347 | 0.0156 |
|
||||
| 9 | `aX` | 0.0297 | +0.0297 | 0.0203 |
|
||||
| 10 | `dX` | 0.0292 | -0.0292 | 0.0173 |
|
||||
| 11 | `q1` | 0.0286 | +0.0286 | 0.0160 |
|
||||
| 12 | `q2` | 0.0275 | -0.0275 | 0.0142 |
|
||||
| 13 | `arec` | 0.0267 | +0.0267 | 0.0069 |
|
||||
| 14 | `ereg` | 0.0267 | +0.0267 | 0.0130 |
|
||||
| 15 | `g1` | 0.0265 | +0.0265 | 0.0086 |
|
||||
|
||||
Reuse bundle Morris ranks: `rho0` 26, `IX` 3, `lin_open` 31, `std` 22
|
||||
|
||||
### Sobol indices
|
||||
|
||||
| Parameter | S1 | S1 95 % CI | ST | ST 95 % CI | ST − S1 |
|
||||
|---|---|---|---|---|---|
|
||||
| `dO` | 0.147 | [0.076, 0.204] | 0.171 | [0.134, 0.203] | +0.023 |
|
||||
| `expl` | 0.165 | [0.103, 0.235] | 0.166 | [0.139, 0.196] | +0.000 |
|
||||
| `m` | 0.095 | [0.022, 0.182] | 0.156 | [0.092, 0.230] | +0.061 |
|
||||
| `IX` | 0.098 | [0.030, 0.152] | 0.129 | [0.102, 0.162] | +0.032 |
|
||||
| `q0` | 0.119 | [0.074, 0.192] | 0.118 | [0.096, 0.144] | -0.001 |
|
||||
| `n1` | 0.075 | [0.023, 0.127] | 0.092 | [0.076, 0.113] | +0.017 |
|
||||
| `std` | 0.051 | [0.009, 0.091] | 0.066 | [0.052, 0.079] | +0.015 |
|
||||
| `aX` | 0.044 | [0.009, 0.078] | 0.060 | [0.044, 0.075] | +0.016 |
|
||||
| `dX` | 0.043 | [-0.002, 0.095] | 0.057 | [0.047, 0.070] | +0.014 |
|
||||
| `r_lin` | 0.016 | [-0.014, 0.045] | 0.048 | [0.029, 0.070] | +0.032 |
|
||||
| `L` | -0.013 | [-0.047, 0.013] | 0.039 | [0.017, 0.071] | +0.052 |
|
||||
| `rho0` | 0.032 | [0.001, 0.061] | 0.038 | [0.030, 0.051] | +0.006 |
|
||||
| `lin_open` | 0.022 | [-0.004, 0.051] | 0.027 | [0.022, 0.035] | +0.006 |
|
||||
|
||||
Sum of S1 over analysed parameters: 0.894. Reuse bundle: ΣS1 = 0.203, ΣST = 0.262.
|
||||
|
|
@ -51,7 +51,7 @@ literature- or data-based ranges. Re-run the affected readings.
|
|||
|
||||
```task
|
||||
id: ASSEMBLY-WP-0006-T03
|
||||
status: progress
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "c33dd30b-a015-55d6-ba75-61dc3d7a1b49"
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue