All scripts are designed to be run from the repository root with the
virtual environment activated (see README.md). Each script is
deterministic given the seed set baked into the code; outputs land in
papers/data/ (JSON) and papers/figures/ (PNG + PDF).
Headline numbers in the paper and the scripts that produce them:
| Paper result | Script | Seed(s) | Output |
|---|---|---|---|
| OpEff divergence (-19.2% at freq = 1/year) | papers/run_threat_freq_sweep.py |
0–99 paired | papers/data/threat_freq_sweep.json |
| Per-control emergent vs. analytical OpEff | papers/run_paper_experiments.py --exp 1 |
0–29 | papers/data/exp1_opeff_divergence.json |
| Remediation backlog phase transition (5–20 hrs/mo) | papers/run_paper_experiments.py --exp 2 |
50 | papers/data/exp2_backlog_dynamics.json |
| Remediation backlog with mean ± p5/p95 band (N seeds) | papers/run_paper_experiments.py --exp 2 --seeds 500 |
0–499 | papers/data/exp2_backlog_dynamics_n500.json |
| Cascading monitoring-failure run + narrative | papers/run_paper_experiments.py --exp 3 |
50 | papers/data/exp3_cascading_variance.json |
| Weak vs. medium scenario (N = 100) | papers/run_scenario_comparison.py |
0–99 paired | papers/data/scenario_comparison.json |
Figures:
| Figure | Script | Data source |
|---|---|---|
| fig1 — budget sensitivity | papers/generate_plots.py |
computed inline |
| fig2 — loss distribution | papers/generate_plots.py |
computed inline |
| fig3 — control state distribution timeline (Seed 50) | papers/generate_plots.py |
exp2_backlog_dynamics.json (b=40 run) |
| fig4 — contact funnel | papers/generate_plots.py |
computed inline |
| fig5 — OpEff divergence (analytical vs emergent) | papers/generate_plots.py |
exp1_opeff_divergence.json |
| fig6 — backlog dynamics (mean + p5/p95 band) | papers/generate_plots.py |
exp2_backlog_dynamics_n{N}.json if present, else exp2_backlog_dynamics.json |
| fig7 — cascade timeline | papers/generate_plots.py |
exp3_cascading_variance.json |
# 1. Run the three main experiments (~30 min on a laptop for --exp 1 alone at N=30;
# --exp 2 is fast; --exp 3 runs a 5-year horizon and is the longest).
python papers/run_paper_experiments.py --exp all
# 2. Run the threat-frequency sweep that produces the -19.2% headline number.
python papers/run_threat_freq_sweep.py
# 3. Run the scenario comparison.
python papers/run_scenario_comparison.py
# 4. Generate all figures.
python papers/generate_plots.py- The model uses a single seed passed to
FAIRCAMModel(..., seed=k). Per-agent PRNG stream isolation is handled bysrc.data.streamed_rng.StreamedRNG, so results are stable across runs for a given seed. run_paper_experiments.pypins seeds 0–29 for Experiment 1 and seed 50 for Experiments 2 and 3. Experiment 2 can also be run over N seeds (0..N-1) via--seeds Nto produce a smoothed fig6 with mean and p5/p95 uncertainty band; this writesexp2_backlog_dynamics_n{N}.jsonalongside (not replacing) the single-seed output.run_threat_freq_sweep.pyandrun_scenario_comparison.pyuse paired seeds 0–99 (same seed at each frequency/scenario level for variance reduction).- The
fig6_backlog_dynamics.{png,pdf}PDFs shipped in this repo were generated from--seeds 500(seeds 0–499) on the authors' hardware; a fresh run with the same seed range reproduces them bit-identically.
- Personnel-dynamics-driven results. The open-core
PersonnelIntegrationis a no-op stub. Any result that depends on satisficing, CVF, or social contagion cannot be reproduced here. The paper runs with personnel dynamics disabled. - Results that depend on extended calibration data not shipped here (DSC
survey coefficients, industry reference baselines). The loss-magnitude
tables shipped in
calibration/loss_tables/are the empirically calibrated values used in the paper.