1"""SIMULATED — factorial experiments + ablations for H3. Usage: python -m wp35.simulations.run_experiments [--quick]2Design (pre-declared here before running): N x network x bandwidth x plan-entropy x shock x regime x seeds, plus an3admin-capacity factor for the two hierarchical regimes. Outputs results/simulated/runs_main.parquet, runs_ablation.parquet.4"""5from __future__ import annotations6import itertools, sys, time7from multiprocessing import Pool8from pathlib import Path9import pandas as pd10from wp35.simulations.model import Params, simulate1112ROOT = Path(__file__).resolve().parents[3]; OUT = ROOT / "results" / "simulated"; OUT.mkdir(parents=True, exist_ok=True)13SEEDS = list(range(8))14REGIMES = ["none", "norms", "records", "central", "federated"]151617def main_design(quick=False):18 Ns = [50, 200, 800] if not quick else [50, 200]19 cells = []20 for N, net, bw, zipf, shock, reg, seed in itertools.product(Ns, ["lattice", "smallworld", "random"], [2, 8], [0.0, 1.0], ["none", "distribution", "network"], REGIMES, SEEDS if not quick else SEEDS[:2]):21 base = dict(N=N, network=net, bandwidth=bw, zipf=zipf, shock_type=shock, shock_period=20 if shock != "none" else -1, regime=reg, seed=seed, T=40)22 if reg in ("central", "federated"):23 for ratio in [0.5, 2.0]:24 cells.append(Params(**base, admin_capacity_ratio=ratio))25 else:26 cells.append(Params(**base, admin_capacity_ratio=2.0))27 return cells282930def ablation_design(quick=False):31 cells = []32 seeds = SEEDS if not quick else SEEDS[:2]33 base = dict(N=200, network="lattice", bandwidth=8, zipf=1.0, T=40)34 for seed in seeds:35 for reg in REGIMES:36 r = 2.037 cells.append(Params(**base, regime=reg, seed=seed, admin_capacity_ratio=r, shock_type="none", shock_period=-1)) # reference38 # records without verification39 cells.append(Params(**base, regime="records", seed=seed, verify_records=False, admin_capacity_ratio=2.0))40 # code adaptation speed after a distribution shock41 for ad in [0, 10, 1000]:42 cells.append(Params(**base, regime="norms", seed=seed, shock_type="distribution", shock_period=20, adapt_delay=ad, shock_kl_target=2.0, admin_capacity_ratio=2.0))43 # horizon (break-even, Theorem 2) and learning cost44 for T in [10, 40, 160]:45 for lb in [0.0, 200.0, 1000.0]:46 for reg in ["none", "norms"]:47 cells.append(Params(**{**base, "T": T}, regime=reg, seed=seed, learn_bits=lb, admin_capacity_ratio=2.0))48 # heterogeneity of information (eps) for none vs norms49 for eps in [0.05, 0.25, 0.6]:50 for reg in ["none", "norms"]:51 cells.append(Params(**base, regime=reg, seed=seed, eps=eps, admin_capacity_ratio=2.0))52 # admin capacity sweep (phase diagram, Theorem 5) and branching53 for ratio in [0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 4.0]:54 for reg in ["central", "federated"]:55 cells.append(Params(**base, regime=reg, seed=seed, admin_capacity_ratio=ratio))56 for b in [4, 16]:57 cells.append(Params(**base, regime="federated", seed=seed, branching=b, admin_capacity_ratio=1.0))58 # plan-set size / entropy sweep (coding gain, Theorem 1)59 for s_, z in [(16, 0.0), (64, 0.0), (256, 0.0), (64, 0.5), (64, 1.5), (64, 2.5)]:60 for reg in ["none", "norms"]:61 cells.append(Params(**{**base, "zipf": z}, regime=reg, seed=seed, s=s_, admin_capacity_ratio=2.0))62 return cells636465def _run(P):66 return simulate(P)676869def run(cells, path, n_jobs=12):70 t0 = time.time()71 with Pool(n_jobs) as pool:72 rows = pool.map(_run, cells, chunksize=16)73 df = pd.DataFrame(rows); df.to_parquet(path, index=False)74 print(f"{path.name}: {len(df)} runs in {time.time() - t0:.1f}s")75 return df767778def main():79 quick = "--quick" in sys.argv80 main_cells = main_design(quick); abl = ablation_design(quick)81 print("main design cells:", len(main_cells), "| ablation cells:", len(abl))82 run(main_cells, OUT / ("runs_main_quick.parquet" if quick else "runs_main.parquet"))83 run(abl, OUT / ("runs_ablation_quick.parquet" if quick else "runs_ablation.parquet"))848586if __name__ == "__main__":87 main()88