SPB Git

spb/wp26_uqo

Public
9.7 KB · 200 lines python
1# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca2"""Checks of the theory module against the closed forms stated in paper/sections/theory.tex and welfare.tex."""3from __future__ import annotations45import itertools67import numpy as np8import pytest910from agentic_valuation.theory import aggregation_sim as ag11from agentic_valuation.theory import welfare as wf1213SEED = 20261027141516# ----------------------------------------------------------------------------------------- Propositions 1–317@pytest.mark.parametrize("K,rho", [(1, 0.0), (3, 0.2), (12, 0.4), (12, 0.0), (48, 0.8), (6, -0.1)])18def test_closed_form_mse_matches_simulation(K, rho):19    rng = np.random.default_rng(SEED)20    closed = ag.mse_mean_closed_form(K, 1.0, rho)21    sim = ag.simulate_mean_mse(K, 1.0, rho, 200_000, rng)22    assert abs(sim - closed) < 0.02 * max(closed, 0.05) + 1e-3232425def test_exchangeable_equal_weights_are_optimal_and_floor():26    for K, rho in itertools.product([2, 5, 12], [0.0, 0.3, 0.7]):27        S = ag.exchangeable_cov(K, 1.0, rho)28        assert ag.mse_optimal(S) == pytest.approx(ag.mse_equal(S), rel=1e-10)29        assert ag.mse_equal(S) >= rho  # floor rho * sigma^2 is never beaten by K30    assert ag.mse_mean_closed_form(10_000, 1.0, 0.5) == pytest.approx(0.5, abs=1e-3)313233def test_general_sigma_optimal_dominates_equal_weights():34    rng = np.random.default_rng(SEED)35    for _ in range(20):36        A = rng.standard_normal((8, 8))37        S = A @ A.T + 0.1 * np.eye(8)38        assert ag.mse_optimal(S) <= ag.mse_equal(S) + 1e-1239    S = ag.exchangeable_cov(5, 2.0, 0.3)  # rows with equal sums: equality40    assert ag.mse_optimal(S) == pytest.approx(ag.mse_equal(S))414243def test_nested_common_model_closed_form():44    for n, M in [(12, 1), (6, 2), (4, 3), (1, 12)]:45        S = ag.nested_cov(n, M, 0.3, 0.4, 0.5)46        assert ag.mse_equal(S) == pytest.approx(ag.mse_nested_closed_form(n, M, 0.3, 0.4, 0.5))47    # more providers at fixed K lowers the pooled MSE; the common component is a floor48    vals = [ag.mse_nested_closed_form(12 // M, M, 0.3, 0.4, 0.5) for M in (1, 2, 3, 4, 6, 12)]49    assert all(a > b for a, b in zip(vals, vals[1:]))50    assert min(vals) >= 0.3**2515253# ----------------------------------------------------------------------------------------- Propositions 4, 754def test_opinion_only_deliberation_contracts_dispersion_and_keeps_mean():55    rng = np.random.default_rng(SEED)56    x0 = rng.standard_normal((50_000, 12)) * 0.257    lam = 0.458    for anchor in ("mean", "median"):59        traj = ag.deliberate_opinion_only(x0, 4, lam, anchor)60        d0 = traj[0].std(axis=1)61        for r in range(5):62            np.testing.assert_allclose(traj[r].std(axis=1), d0 * (1 - lam) ** r, rtol=1e-10)63            # the anchoring statistic is invariant across rounds64            stat = np.median if anchor == "median" else np.mean65            np.testing.assert_allclose(stat(traj[r], axis=1), stat(traj[0], axis=1), atol=1e-12)66        if anchor == "mean":67            np.testing.assert_allclose(traj[4].mean(axis=1), traj[0].mean(axis=1), atol=1e-12)68        else:  # exact formula: MSE_r = MSE_0 + (1 - (1-lam)^r)^2 (MSE_med - MSE_mean)69            m0 = np.mean(traj[0].mean(axis=1) ** 2)70            md = np.mean(np.median(traj[0], axis=1) ** 2)71            for r in range(5):72                mr = np.mean(traj[r].mean(axis=1) ** 2)73                assert mr == pytest.approx(m0 + (1 - (1 - lam) ** r) ** 2 * (md - m0), rel=0.02)74            assert md >= m0  # the median is dominated by the mean under Gaussian errors757677def test_information_conservation_and_over_shrinkage():78    sig2 = np.linspace(0.05, 0.4, 12) ** 279    S0 = np.diag(sig2)80    best0 = ag.mse_optimal(S0)81    prev = best082    for r in range(1, 6):83        Sr = ag.deliberation_cov(S0, r, 0.5)84        assert ag.mse_optimal(Sr) >= best0 - 1e-12  # no pool of round r beats the best round-0 pool85        iv = ag.mse_pool(ag.inverse_variance_weights(Sr), Sr)86        assert iv > prev  # strictly dominated and worsening (heterogeneous variances)87        prev = iv88    assert prev <= ag.mse_equal(S0) + 1e-12 and prev == pytest.approx(ag.mse_equal(S0), rel=0.05)899091def test_degroot_consensus_is_round0_mean_for_complete_topology():92    K, lam = 7, 0.393    W = (1 - lam) * np.eye(K) + lam * np.ones((K, K)) / K94    np.testing.assert_allclose(ag.degroot_consensus_weights(W), np.full(K, 1 / K), atol=1e-10)959697# ----------------------------------------------------------------------------------------- Proposition 698def test_evidence_exchange_closed_form_and_sign():99    rng = np.random.default_rng(SEED)100    K, J = 12, 28101    views = [np.array([0, 1, 2, 3, 4 + 2 * k, 5 + 2 * k]) for k in range(K)]102    comm = ag.EvidenceCommittee(J=J, views=views, providers=np.arange(K) % 3, tau2=np.full(K, 0.01),103                                sigma_c=0.05, sigma_u=0.12, sigma_g=0.04)104    full = ag.exchange_views(views)105    before, after = comm.mse_equal_closed_form(), comm.mse_equal_closed_form(full)106    assert after < before107    assert after == pytest.approx(0.05**2 + 0.12**2 / J + 0.04**2 / 3 + 0.01 / K)108    res = ag.simulate_deliberation(comm, 1, 0.0, 60_000, rng, exchange_at=1)109    assert res["mse_mean"][0] == pytest.approx(before, rel=0.03)110    assert res["mse_mean"][1] == pytest.approx(after, rel=0.03)111    # exchange restricted to the common view is vacuous112    same = ag.exchange_views(views, cited=[np.array([0, 1, 2, 3])] * K)113    assert comm.mse_equal_closed_form(same) == pytest.approx(before)114    # persuasion noise (independent of the errors) raises the MSE115    res2 = ag.simulate_deliberation(comm, 2, 0.3, 60_000, rng, anchor="mean", persuasion_sd=0.05)116    assert res2["mse_mean"][2] > res2["mse_mean"][0]117118119def test_partial_exchange_can_raise_mse():120    ex = ag.EvidenceCommittee(J=2, views=[np.array([0]), np.array([1]), np.array([1])], providers=np.zeros(3, int),121                              tau2=np.zeros(3), sigma_u=1.0)122    before = ex.mse_equal_closed_form()123    partial = ex.mse_equal_closed_form(views=[np.array([0, 1]), np.array([1]), np.array([1])])124    assert before == pytest.approx(5 / 9) and partial == pytest.approx(13 / 18) and partial > before125126127# ----------------------------------------------------------------------------------------- Proposition 8128def test_placebo_invariance_under_separability():129    rng = np.random.default_rng(SEED)130    K, J = 12, 28131    views = [np.array([0, 1, 2, 3, 4 + 2 * k, 5 + 2 * k]) for k in range(K)]132    tau2 = np.linspace(0.05, 0.3, K) ** 2133    comm = ag.EvidenceCommittee(J=J, views=views, providers=np.arange(K) % 3, tau2=tau2, sigma_c=0.05,134                                sigma_u=0.12, sigma_g=0.04)135    base = comm.mse_equal_closed_form()136    shocks = comm.draw_shocks(50_000, rng)137    x_id = comm.draw(50_000, rng, shocks=shocks)138    for _ in range(10):139        perm = rng.permutation(K)140        assert ag.placebo_mse(comm, perm) == pytest.approx(base)141        x_p = comm.draw(50_000, rng, tau2=tau2[perm], shocks=shocks)142        assert np.mean(x_p.mean(axis=1) ** 2) == pytest.approx(np.mean(x_id.mean(axis=1) ** 2), rel=0.03)143    # a prompt x view complementarity (variance depending on the match) breaks the invariance144    tau2_match = tau2.copy()145    tau2_match[0] = 1.0  # slot 0 mismatched prompt146    assert comm.mse_equal_closed_form(tau2=tau2_match) > base147148149# ----------------------------------------------------------------------------------------- Welfare150def test_failure_probability_monotone_for_financeable_matches():151    p = wf.WelfareParams()152    u_s = np.array([-0.10, -0.05, 0.0])  # financeable at the error-free appraisal (a < 0)153    prev = wf.failure_prob_given_seller(u_s, 0.0, 0.01, p)154    for s in np.linspace(0.02, 0.3, 15):155        cur = wf.failure_prob_given_seller(u_s, 0.0, float(s), p)156        assert np.all(cur >= prev - 1e-12)157        prev = cur158    # undervaluation bias raises, overvaluation lowers, the failure probability159    f_neg = wf.failure_prob_given_seller(u_s, -0.03, 0.1, p)160    f_pos = wf.failure_prob_given_seller(u_s, 0.03, 0.1, p)161    assert np.all(f_neg > f_pos)162    # a larger haircut raises it163    assert np.all(wf.failure_prob_given_seller(u_s, 0.0, 0.1, wf.with_params(p, h1=0.5)) >164                  wf.failure_prob_given_seller(u_s, 0.0, 0.1, p))165    # non-financeable matches (a > 0): noise lowers the failure probability166    u_hi = np.array([0.10])167    assert wf.failure_prob_given_seller(u_hi, 0.0, 0.2, p) < wf.failure_prob_given_seller(u_hi, 0.0, 0.05, p)168169170def test_market_simulation_failure_rate_increasing_in_s():171    rng = np.random.default_rng(SEED)172    p = wf.WelfareParams()173    shocks = wf.draw_market_shocks(100_000, rng)174    rates = [wf.simulate_market(0.0, s, p, 100_000, rng, shocks=shocks)["failure_rate"] for s in (0.02, 0.05, 0.1, 0.2)]175    assert all(a < b for a, b in zip(rates, rates[1:]))176    exact = wf.simulate_market(0.0, 0.1, p, 100_000, rng, shocks=shocks)177    assert exact["failure_rate"] == pytest.approx(exact["failure_rate_exact"], abs=0.01)178179180def test_expected_loss_increasing_in_bias_and_spread():181    p = wf.WelfareParams()182    els = [wf.expected_loss(b, 0.1, p) for b in (-0.05, 0.0, 0.05, 0.1)]183    assert all(a < b for a, b in zip(els, els[1:]))184    els = [wf.expected_loss(0.0, s, p) for s in (0.0, 0.05, 0.1, 0.2, 0.3)]185    assert all(a < b for a, b in zip(els, els[1:]))186187188def test_systemic_mean_flat_and_dispersion_increasing_in_rho():189    rng = np.random.default_rng(SEED)190    p = wf.WelfareParams()191    N, n = 30, 20_000192    shocks = {"C": rng.standard_normal(n), "E": rng.standard_normal((n, N))}193    res = [wf.systemic_loss(r, 0.0, 0.1, p, N, n, rng, shocks=shocks) for r in (0.0, 0.25, 0.5, 0.75, 1.0)]194    means = [x["mean"] for x in res]195    assert max(means) - min(means) < 0.03 * np.mean(means)196    sds = [x["sd"] for x in res]197    covs = [x["cov_pair"] for x in res]198    assert all(a < b for a, b in zip(sds, sds[1:]))199    assert all(a <= b + 1e-12 for a, b in zip(covs, covs[1:]))200
Files · b780d9effbc0d688f848faf14e38219f6088143b
Menu

Theme