|
2 | 2 |
|
3 | 3 | All notable changes to StatsPAI will be documented in this file. |
4 | 4 |
|
| 5 | +## [1.5.0] — 2026-04-21 — Interference / Conformal / Mendelian family consolidation |
| 6 | + |
| 7 | +Minor release. Three concurrent improvements to the interference, |
| 8 | +conformal causal inference, and Mendelian Randomization families: |
| 9 | +full-family documentation guides, unified dispatchers matching the |
| 10 | +`sp.synth` / `sp.decompose` / `sp.dml` pattern, and a targeted |
| 11 | +correctness audit that surfaced and fixed two silent-wrong-numbers |
| 12 | +issues. |
| 13 | + |
| 14 | +### Added — three new family guides (interference / conformal / MR) |
| 15 | + |
| 16 | +- `docs/guides/interference_family.md` — complete walkthrough of |
| 17 | + `sp.spillover`, `sp.network_exposure`, `sp.peer_effects`, |
| 18 | + `sp.network_hte`, `sp.inward_outward_spillover`, |
| 19 | + `sp.cluster_matched_pair`, `sp.cluster_cross_interference`, |
| 20 | + `sp.cluster_staggered_rollout`, `sp.dnc_gnn_did`. Decision tree |
| 21 | + covering partial / network / cluster-RCT designs with the 5 |
| 22 | + diagnostics every interference analysis should report (exposure |
| 23 | + balance, identification check for peer_effects, overlap for |
| 24 | + network_hte, parallel trends for staggered-cluster, sensitivity to |
| 25 | + exposure function). |
| 26 | +- `docs/guides/conformal_family.md` — complete walkthrough of |
| 27 | + `sp.conformal_cate`, `sp.weighted_conformal_prediction`, |
| 28 | + `sp.conformal_counterfactual`, `sp.conformal_ite_interval`, |
| 29 | + `sp.conformal_density_ite`, `sp.conformal_ite_multidp`, |
| 30 | + `sp.conformal_debiased_ml`, `sp.conformal_fair_ite`, |
| 31 | + `sp.conformal_continuous`, `sp.conformal_interference`. Clarifies |
| 32 | + the distinction between marginal and conditional coverage, with |
| 33 | + per-tool "when to use it" + how-to-read-disagreement guidance. |
| 34 | +- `docs/guides/mendelian_family.md` — complete walkthrough of all 17 |
| 35 | + MR functions (4 point estimators + 6 diagnostics + 3 multi-exposure |
| 36 | + extensions + instrument-strength F + 2 plots), organised around the |
| 37 | + IV1 / IV2 / IV3 assumption hierarchy. Ships the 4 sanity checks every |
| 38 | + MR analysis should report and a worked BMI → T2D example. |
| 39 | + |
| 40 | +Each guide is linked from `mkdocs.yml` under Guides and surfaces via |
| 41 | +`sp.search_functions()`. |
| 42 | + |
| 43 | +### Added — unified family dispatchers |
| 44 | + |
| 45 | +Three new top-level dispatchers mirroring the style of `sp.synth` / |
| 46 | +`sp.decompose` / `sp.dml`: |
| 47 | + |
| 48 | +- **`sp.mr(method=..., ...)`** — single entry point for the 17-function |
| 49 | + Mendelian Randomization family. Supports |
| 50 | + `method ∈ {"ivw", "egger", "median", "penalized_median", "mode", |
| 51 | + "simple_mode", "all", "mvmr", "mediation", "bma", "presso", "radial", |
| 52 | + "leave_one_out", "steiger", "heterogeneity", "pleiotropy_egger", |
| 53 | + "f_statistic", ...}` with aliases. kwargs pass through to the target |
| 54 | + function. `sp.mr_available_methods()` lists all aliases. |
| 55 | + |
| 56 | +- **`sp.conformal(kind=..., ...)`** — single entry point for the |
| 57 | + 10-function conformal causal inference family. Supports |
| 58 | + `kind ∈ {"cate", "counterfactual", "ite", "weighted", "density", |
| 59 | + "multidp", "debiased", "fair", "continuous", "interference", ...}`. |
| 60 | + `sp.conformal_available_kinds()` lists all aliases. |
| 61 | + |
| 62 | +- **`sp.interference(design=..., ...)`** — single entry point for the |
| 63 | + 9-function interference / spillover family. Supports |
| 64 | + `design ∈ {"partial", "network_exposure", "peer_effects", |
| 65 | + "network_hte", "inward_outward", "cluster_matched_pair", |
| 66 | + "cluster_cross", "cluster_staggered", "dnc_gnn", ...}`. |
| 67 | + `sp.interference_available_designs()` lists all aliases. |
| 68 | + |
| 69 | +All three dispatchers are registered with hand-written schemas so |
| 70 | +`sp.describe_function("mr")` / `"conformal"` / `"interference"` return |
| 71 | +agent-readable descriptions. 30 new tests in |
| 72 | +`tests/test_dispatchers_v150.py` guarantee the dispatcher path and the |
| 73 | +direct-call path produce byte-for-byte identical results. |
| 74 | + |
| 75 | +### ⚠️ Breaking — `sp.mr` is now a function, not a module alias |
| 76 | + |
| 77 | +Prior to v1.5.0 `sp.mr` was a reference to the `statspai.mendelian` |
| 78 | +submodule (`from . import mendelian as mr`), so `sp.mr.mr_ivw(...)` |
| 79 | +worked. v1.5.0 replaces this with the new **dispatcher function** |
| 80 | +`sp.mr(method=..., ...)`. |
| 81 | + |
| 82 | +**Migration**: code that previously wrote `sp.mr.mr_ivw(bx, by, sx, sy)` |
| 83 | +should use the top-level `sp.mr_ivw(bx, by, sx, sy)` (already exported |
| 84 | +in every prior version) or the new `sp.mr("ivw", beta_exposure=bx, ...)` |
| 85 | +dispatcher. The module is still accessible as `sp.mendelian` for users |
| 86 | +who were doing submodule-level introspection. |
| 87 | + |
| 88 | +Updated references: the only in-repo consumer of the old |
| 89 | +`sp.mr.mr_ivw` form was `tests/reference_parity/test_mr_parity.py`, |
| 90 | +which has been migrated to top-level calls. All external user code |
| 91 | +that already uses `sp.mr_ivw` / `sp.mendelian_randomization` / etc |
| 92 | +continues to work unchanged. |
| 93 | + |
| 94 | +### Fixed — silent wrong numbers (correctness audit) |
| 95 | + |
| 96 | +- **`sp.mr_egger` — slope inference used Normal, not t(n−2).** The |
| 97 | + companion `sp.mr_pleiotropy_egger` correctly used `t(n−2)` for the |
| 98 | + Egger intercept p-value, but `mr_egger` itself used `stats.norm.cdf` |
| 99 | + for both the slope p-value and the slope CI's critical value. This |
| 100 | + was anti-conservative at small `n_snps`: e.g. for `n_snps = 5` and a |
| 101 | + t-stat of 1.5, the Normal-based two-sided p is 0.134 whereas the |
| 102 | + correct t(3)-based p is 0.231. `mendelian_randomization(..., methods=["egger"])` |
| 103 | + inherited the bug through its internal call. The fix switches both the |
| 104 | + p-value and the CI critical value to `t(n−2)`. Regression guard in |
| 105 | + `tests/test_correctness_v150.py::TestMREggerUsesTDistribution`. |
| 106 | + For `n_snps ≥ 100` the change is numerically invisible (< 1e-3 in p). |
| 107 | + |
| 108 | +- **`sp.mr_presso` — MC p-value could equal exactly 0.** Both the |
| 109 | + global test p-value and the per-SNP outlier p-values used the raw |
| 110 | + `mean(null >= obs)` form, which collapses to `0.0` when the observed |
| 111 | + statistic exceeds every simulated null. An MC-estimated p-value |
| 112 | + cannot be zero — its true lower bound is `1 / (B + 1)`. The fix |
| 113 | + switches to the standard `(k + 1) / (B + 1)` convention (matching |
| 114 | + R's `MR-PRESSO` package). Downstream effect: reported p-values are |
| 115 | + now always strictly positive and in `[1/(B+1), 1]`, which prevents |
| 116 | + log-transforms and sensitivity analyses from silently producing |
| 117 | + `-inf`. Regression guard in |
| 118 | + `tests/test_correctness_v150.py::TestMRPressoMCPvalueConvention`. |
| 119 | + |
| 120 | +### Fixed — dead code |
| 121 | + |
| 122 | +- **`sp.network_exposure._ht_estimate`** contained a dimensionally |
| 123 | + inconsistent `var = ...` expression that was immediately overwritten |
| 124 | + by the conservative Aronow-Samii Theorem 1 bound `var_as = ...`. The |
| 125 | + dead line is removed; the reported SE is unchanged. |
| 126 | + |
| 127 | +### Fixed — registry coverage |
| 128 | + |
| 129 | +Five previously-exposed-but-unregistered family functions now surface |
| 130 | +in `sp.list_functions()` and have agent-readable schemas via |
| 131 | +`sp.describe_function()`: |
| 132 | + |
| 133 | +- `sp.network_exposure` (Aronow-Samii HT) |
| 134 | +- `sp.peer_effects` (Bramoullé-Djebbari-Fortin 2SLS) |
| 135 | +- `sp.weighted_conformal_prediction` (TBCR 2019 primitive) |
| 136 | +- `sp.conformal_counterfactual` (Lei-Candès Theorem 1) |
| 137 | +- `sp.conformal_ite_interval` (Lei-Candès Eq. 3.4 nested bound) |
| 138 | + |
| 139 | +### No other API changes |
| 140 | + |
| 141 | +Every other public signature is byte-for-byte identical to v1.4.2. |
| 142 | +Existing user code keeps working; upgrades reveal slightly wider Egger |
| 143 | +CIs at small `n_snps` and strictly positive `mr_presso` p-values. |
| 144 | + |
5 | 145 | ## [1.4.2] — 2026-04-21 — correctness patches + family guides |
6 | 146 |
|
7 | 147 | Patch release. No breaking changes; two silent-wrong-numbers bug |
|
0 commit comments