1import numpy as np, pandas as pd, pytest2from wp35.measurement.irt import fit_2pl3from wp35.econometrics.common import holm, fold_win_share, oos_loglik_binomial4from wp35.simulations.model import Params, simulate, shannon_lengths, zipf_dist, entropy_bits567def test_irt_recovers_ordering_with_missing_items():8 rng = np.random.default_rng(1); N, J = 400, 129 theta = rng.normal(size=N); b = np.linspace(-1.5, 1.5, J); a = np.full(J, 1.5)10 P = 1 / (1 + np.exp(-a * (theta[:, None] - b[None, :]))); X = (rng.random((N, J)) < P).astype(float)11 X[rng.random((N, J)) < 0.3] = np.nan # 30% missing at random, never recoded as 012 f = fit_2pl(X)13 ok = ~np.isnan(f["eap"])14 assert np.corrcoef(f["eap"][ok], theta[ok])[0, 1] > 0.815 assert np.corrcoef(f["b"], b)[0, 1] > 0.9161718def test_holm_and_win_share():19 adj = holm({"a": 0.01, "b": 0.04, "c": 0.20})20 assert adj["a"] == pytest.approx(0.03) and adj["b"] == pytest.approx(0.08) and adj["c"] == pytest.approx(0.20)21 cv = pd.DataFrame({"model": ["m"] * 3 + ["b"] * 3, "fold": [1, 2, 3] * 2, "oos_loglik": [-1, -2, -3, -1.5, -2.5, -2.5]})22 w = fold_win_share(cv, "m", "b"); assert w["folds"] == 3 and w["win_share"] == pytest.approx(2 / 3)23 assert oos_loglik_binomial([1, 0], [0.5, 0.5]) == pytest.approx(np.log(0.5))242526def test_shannon_code_lengths_satisfy_kraft_and_bounds():27 for z in [0.0, 1.0, 2.0]:28 p = zipf_dist(64, z); L = shannon_lengths(p); H = entropy_bits(p)29 assert np.sum(2.0 ** (-L)) <= 1 + 1e-9 # Kraft30 assert H <= (p * L).sum() < H + 1 # Theorem 1313233def test_simulation_is_deterministic_and_measures_shock_penalty():34 a = simulate(Params(N=100, regime="norms", seed=3)); b = simulate(Params(N=100, regime="norms", seed=3))35 assert a["success_rate"] == b["success_rate"] and a["bits_per_task"] == b["bits_per_task"]36 s = simulate(Params(N=100, regime="norms", seed=3, shock_type="distribution", shock_period=20, shock_kl_target=2.0, adapt_delay=1000))37 extra_per_recipient = (s["bits_per_task_post"] - s["bits_per_task_pre"]) / (s["k"] - 1)38 assert abs(extra_per_recipient - s["kl_q_p_bits"]) < 1.0 # Theorem 3 within ±1 bit39 assert 0 <= s["success_rate"] <= 1 and s["msgs_per_task"] > 0404142def test_central_regime_saturates_when_capacity_below_load():43 low = simulate(Params(N=200, regime="central", seed=0, admin_capacity_ratio=0.25))44 high = simulate(Params(N=200, regime="central", seed=0, admin_capacity_ratio=4.0))45 assert low["success_rate"] < high["success_rate"] and low["fail_latency_share"] > high["fail_latency_share"]46