Skip to content

Commit 585e2e7

Browse files
docs(jss): refresh reviewer-facing counts + validation framing
Updates README/README_CN/index/dossier registry counts (1,020 functions, 81 submodules, 51 R-aligned Track A modules, 244 certified+validated symbols) and reframes the coverage grid as method-family breadth rather than a per-option validation certificate, pointing readers to validation_status. CHANGELOG records the rddensity escape hatch, the causal-forest AIPW correctness fix, the SCM-solver certification, and the regenerated Track A ledger under [Unreleased]. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a0f2033 commit 585e2e7

5 files changed

Lines changed: 79 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,56 @@ All notable changes to StatsPAI will be documented in this file.
44

55
## [Unreleased]
66

7+
### Added — RD density parity escape hatch
8+
9+
- **`sp.rddensity` now accepts side-specific manual bandwidths and an optional
10+
R reference backend.** The native dependency-light implementation remains
11+
the default, but `h=(h_left, h_right)` now records matched left/right
12+
bandwidths explicitly, and `backend="r"` delegates to
13+
`rddensity::rddensity` through `Rscript` when R plus the `rddensity` and
14+
`jsonlite` packages are installed. This does not pretend the native
15+
bandwidth selector is identical to the R package; it gives users and
16+
reviewers a clear path to canonical selector/test-statistic parity when
17+
that exact comparison matters. Guarded by `tests/test_rddensity_io.py`;
18+
the R backend test is skipped automatically when R is unavailable.
19+
20+
### ⚠️ Correctness fix — causal-forest ATE/ATT now doubly-robust (AIPW)
21+
22+
- **`CausalForest.average_treatment_effect` (and the `target_sample`
23+
`"all"`/`"treated"`/`"control"`/`"overlap"` aggregations) now returns
24+
the doubly-robust AIPW influence-function mean** — the estimand
25+
`grf::average_treatment_effect` reports — instead of a plug-in average
26+
of the regularisation-shrunk CATE predictions. The plug-in average
27+
overshoots the true effect (≈ 15 % on a clean-overlap DGP) and
28+
disagreed with `grf` by an order of magnitude on overlap-pathological
29+
samples. The AIPW score reuses the forest's own cross-fitted nuisances
30+
`m̂(X)=Ê[Y|X]`, `ê(X)=Ê[T|X]` and the CATE `τ̂(X)`:
31+
`Γ_i = τ̂ + (T−ê)/(ê(1−ê))·(Y − m̂ − (T−ê)τ̂)`. **Numbers change** for
32+
any code reading `average_treatment_effect(...)['estimate']`; the SE is
33+
now the influence-function SE `sd(Γ)/√n`. The plug-in `cf.ate()` /
34+
`cf.att()` convenience methods are unchanged and retained, but are no
35+
longer the validation estimand. Users who reported a causal-forest ATE
36+
from `average_treatment_effect` should re-run.
37+
- On a clean-overlap DGP (`e(X)∈[0.30,0.70]`, known ATE = 1) the AIPW
38+
ATE recovers the truth within 1.5 SE and agrees with `grf` at
39+
`rel = 0.037` (`z = 0.69` combined SE); the R-parity module
40+
`13_causal_forest` verdict moves from GAP (500 % band) to PASS
41+
(10 % band).
42+
- Guards: `tests/reference_parity/test_causal_forest_aipw_recovery.py`
43+
(recovery against truth, no R needed) and the tightened
44+
`tests/reference_parity/test_grf_parity.py` (combined-SE parity vs a
45+
committed `grf` fixture).
46+
- **Synthetic-control solver certified on identified problems.** Added
47+
`tests/reference_parity/test_scm_recovery.py` and the cross-language
48+
module `tests/r_parity/52_scm_unique`: on a DGP whose synthetic-control
49+
weights are uniquely identified (treated unit exactly a convex
50+
combination of donors in the pre-period), `sp.synth(method="classic")`
51+
recovers the exact weights and gap (pre-RMSE = 0) and agrees with
52+
`Synth::synth` to 0.7 %; the classical/augmented/generalised/MC
53+
variants each recover a known factor-model ATT within 3 %. No
54+
estimator numerics change — this is added validation isolating the
55+
Basque-data SCM gap (module `07`) as genuine weight non-uniqueness.
56+
757
### Added — MCP protocol modernization
858

959
- **Protocol version negotiation + bump to `2025-06-18`
@@ -53,15 +103,15 @@ All notable changes to StatsPAI will be documented in this file.
53103
than only frozen.
54104
- **`sp.validation_report(collect_tests=True)`** — shells out to
55105
`pytest --collect-only` and returns the authoritative, parametrize-expanded
56-
parity test counts (114 reference-parity, 50 external-parity, 12 coverage
57-
Monte Carlo on the 1.16.0 tree); a regression test pins those three to the
106+
parity test counts (124 reference-parity, 50 external-parity, 12 coverage
107+
Monte Carlo on the current source snapshot); a regression test pins those three to the
58108
JSS manuscript headline so a parity test added/removed without updating the
59109
paper fails CI. Default `validation_report()` path is unchanged (fast,
60110
metadata-only).
61111
- **Strictness-tier breakdown in the Track A parity tables
62112
(`tests/r_parity/compare.py`)** — each module is classified by its
63113
registered point-estimate tolerance into machine / iterative / moderate /
64-
methodological tiers (6 / 26 / 9 / 9 on the 50 R-rendered modules), shown
114+
methodological tiers (6 / 26 / 10 / 9 on the 51 R-joined modules), shown
65115
in the Markdown ledger and the LaTeX appendix caption so a machine-precision
66116
match is not flattened together with a deliberately loose
67117
methodological-difference tolerance.
@@ -82,13 +132,15 @@ All notable changes to StatsPAI will be documented in this file.
82132

83133
### Changed — Track A R golden values regenerated under the locked environment
84134

85-
- The 50 committed `results/*_R.json` were regenerated under R 4.5.2 with
135+
- The current R parity ledger covers 51 `results/*_R.json` modules under R 4.5.2 with
86136
the `renv.lock` package set so each is self-describing. The only material
87137
numerical movement is `07_scm` (classical SCM), whose headline estimate
88138
shifted by 2.18e-8 — L-BFGS-B / Apple-Accelerate-BLAS non-associativity,
89-
far inside that module's 0.20 SCM non-uniqueness parity tolerance. All
90-
cross-language verdicts are unchanged (42 PASS / 10 GAP). This is a
91-
reference-fixture refresh, not a `statspai` estimator-output change.
139+
far inside that module's 0.20 SCM non-uniqueness parity tolerance. The
140+
ledger also adds `52_scm_unique`, an identified synthetic-control DGP that
141+
separates solver correctness from the Basque non-uniqueness gap. This is
142+
primarily a reference-fixture and evidence refresh, not a broad
143+
`statspai` estimator-output change.
92144
- The 44 committed `results/*_Stata.json` were likewise refreshed to embed
93145
the engine `provenance` block; their numbers are **unchanged** (every
94146
module reproduces at exactly 0 under the locked Stata 18 MP environment).

README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
[![status](https://joss.theoj.org/papers/9f1c837b1b1df7adfcdd538c3698e332/status.svg)](https://joss.theoj.org/papers/9f1c837b1b1df7adfcdd538c3698e332)
1515
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.19933900.svg)](https://doi.org/10.5281/zenodo.19933900)
1616

17-
StatsPAI is the **first agent-native** Python platform for causal inference and applied econometrics. One `import`, **1,000+ registered functions** across **80 submodules** (live count: `python scripts/registry_stats.py`), covering the complete empirical research workflow — from classical econometrics to cutting-edge ML/AI causal methods to publication-ready tables in Word, Excel, and LaTeX.
17+
StatsPAI is an **agent-native** Python platform for causal inference and applied econometrics. One `import`, **1,000+ registered functions** across **80+ submodules** (live count: `python scripts/registry_stats.py`), covering the empirical research workflow — from classical econometrics to ML/AI causal methods to publication-ready tables in Word, Excel, and LaTeX.
1818

19-
**Built for AI agents**: every function returns structured result objects with machine-readable schemas (`list_functions()`, `describe_function()`, `function_schema()`) and is numerically validated against R and Stata reference implementations — purpose-built for LLM-driven research workflows while remaining fully ergonomic for human researchers.
19+
**Built for AI agents**: every registered function has machine-readable discovery metadata (`list_functions()`, `describe_function()`, `function_schema()`), and parity-backed functions expose an explicit `validation_status` so agents and humans can distinguish certified numerical evidence from API-stable breadth.
2020

2121
It brings R's [Causal Inference Task View](https://cran.r-project.org/web/views/CausalInference.html) (fixest, did, rdrobust, gsynth, DoubleML, MatchIt, CausalImpact, ...) and Stata's core econometrics commands into a single, consistent Python API.
2222

@@ -115,7 +115,7 @@ Every result object exposes the same interface — `.summary()` / `.tidy()` / `.
115115

116116
## 📊 Causal Inference Coverage at a Glance
117117

118-
StatsPAI's focus is **causal inference** — and on this axis we aim to be the most complete single package in any language. "Stata" = official + major SSC packages. "R" = CRAN. "sm+lm" = statsmodels + linearmodels.
118+
StatsPAI's focus is **causal inference**. The grid below summarizes method-family breadth; it is not a validation certificate for every option. "Stata" = official + major SSC packages. "R" = CRAN. "sm+lm" = statsmodels + linearmodels.
119119

120120
| Method family | Stata | R | sm+lm | DoubleML | **StatsPAI** |
121121
| ------------------------------------------------------------- | :---: | :---: | :---: | :---: | :---: |
@@ -137,7 +137,7 @@ StatsPAI's focus is **causal inference** — and on this axis we aim to be the m
137137

138138
**Legend**: 🏆 most complete across ecosystems · ✅ full coverage · ⚠️ partial / scattered / single algorithm · ❌ not available.
139139

140-
**StatsPAI at a glance**: 1,018 registered functions in the live agent registry · 80 submodules · ~249k LOC (core) + ~86k LOC (tests). All four numbers are reproducible from the canonical generator (`python scripts/registry_stats.py`); the per-module table in [`docs/stats.md`](docs/stats.md) is regenerated from the same script. For the full coverage matrix (23 method families) and cross-ecosystem line-count comparison, see [`docs/stats.md`](docs/stats.md).
140+
**StatsPAI at a glance**: 1,020 registered functions in the live agent registry · 81 submodules · 266k LOC (core) + 93k LOC (tests). All four numbers are reproducible from the canonical generator (`python scripts/registry_stats.py`); the per-module table in [`docs/stats.md`](docs/stats.md) is regenerated from the same script. For the full coverage matrix (23 method families) and cross-ecosystem line-count comparison, see [`docs/stats.md`](docs/stats.md).
141141

142142
**Validation tiers matter**: `stability="stable"` means the public API is SemVer-stable; it does not by itself mean R/Stata/paper parity. Use `sp.list_functions(validation_status="certified")` for cross-language or published-reference evidence, and inspect `sp.describe_function(name)["limitations"]` before production use. See [`docs/guides/stability.md`](docs/guides/stability.md).
143143

@@ -146,8 +146,9 @@ StatsPAI's focus is **causal inference** — and on this axis we aim to be the m
146146
StatsPAI 1.16.0 corrects the `sp.qreg` Powell sandwich standard error (was off
147147
by √n) and rebuilds `sp.xtabond` Arellano–Bond difference GMM to match Stata to
148148
machine precision — both flagged ⚠️ **Correctness**, so re-run affected
149-
analyses. The Track A cross-language parity harness grows from 36 to 50
150-
R-aligned modules (Stata reference for 43 of them), and the JOSS
149+
analyses. The Track A cross-language parity harness grows from 36 to 51
150+
R-aligned modules (Stata reference for 43 of them, plus one
151+
Py-Stata-only `xtabond` migration check), and the JSS
151152
reviewer-facing docs are refreshed. Full notes in
152153
[`CHANGELOG.md`](CHANGELOG.md) under `[1.16.0]`.
153154

@@ -428,7 +429,7 @@ StatsPAI 1.4.0 is Sprint 2 of the 知识地图 v3 roadmap. Closes the four secon
428429
| **Particle-filter assimilation** | **`sp.assimilation.particle_filter`** — bootstrap-SIR particle filter with systematic resampling (Gordon-Salmond-Smith 1993; Douc-Cappé 2005). Non-Gaussian priors, heavy-tailed observation noise, nonlinear dynamics via pluggable callbacks. Agrees with exact Kalman to ~0.003 under Gaussian DGPs. **`sp.assimilative_causal(..., backend='particle')`** routes the end-to-end wrapper. |
429430
| **Documentation (v3 frontier guides)** | `docs/guides/synth_experimental.md` (Abadie-Zhao inverse-SC workflow), `docs/guides/harvest_did.md` (Borusyak-Hull-Jaravel harvesting DID), `docs/guides/assimilative_ci.md` (Nature Comms 2026 streaming CI, Kalman + particle backends). Wired into `mkdocs.yml` nav. |
430431
| **v1.3 stable foundation (carried forward)** | 11 2025-2026 frontier methods from Sprint 1: `synth_experimental_design`, `rdrobust(..., bootstrap='rbc')`, `evidence_without_injustice`, `target_trial.to_paper(fmt='jama'/'bmj')`, `harvest_did`, `bcf_ordinal`, `bcf_factor_exposure`, `causal_mas`, `shift_share_political`, `causal_kalman`. All v1.0 capstone surfaces (`sp.bridge`, `sp.fairness`, `sp.surrogate`, `sp.epi`, `sp.longitudinal`, `sp.question`, full MR suite, TARGET checklist) remain intact. |
431-
| **Agent-native platform** | `sp.list_functions()` / `sp.describe_function()` / `sp.function_schema()` expose OpenAI/Anthropic tool-calling schemas for 1,018 registered public functions. 145 curated or explicitly inherited `FunctionSpec` entries carry at least one of assumptions, preconditions, failure modes, limitations, `typical_n_min`, and validation tiers for the flagship surface. `sp.agent.mcp_server` MCP scaffold lets external LLMs call every StatsPAI function via natural-language tool invocation. |
432+
| **Agent-native platform** | `sp.list_functions()` / `sp.describe_function()` / `sp.function_schema()` expose OpenAI/Anthropic tool-calling schemas for 1,020 registered public functions. 362 curated or explicitly inherited `FunctionSpec` entries carry at least one of assumptions, preconditions, failure modes, limitations, `typical_n_min`, and validation tiers for the flagship surface. `validation_status` distinguishes certified/validated evidence from API-stable breadth. `sp.agent.mcp_server` MCP scaffold lets external LLMs call every StatsPAI function via natural-language tool invocation. |
432433
| **CI/CD hygiene** | `tabulate` hard-dep from v1.3.0 carried forward. Deflaked `test_forest_ate_recovers_average_tau` by seeding the forest explicitly (`random_state=0`, `n_estimators=300`, larger `n`). 2 699+ tests passing across all OS × Python matrix entries. |
433434

434435
**Previously in v0.9.2 — Decomposition Analysis**: **18 first-class decomposition methods across 13 modules (~6,200 LOC, 54 tests)**, unified under `sp.decompose(method=...)`. Mean (Blinder-Oaxaca/Gelbach/Fairlie/Bauer-Sinning/Yun), distributional (RIF/FFL/DFL/Machado-Mata/Melly/CFM), inequality (Theil/Atkinson/Dagum/Shapley/Lerman-Yitzhaki), demographic (Kitagawa/Das-Gupta), and causal (gap_closing/mediation_decompose/disparity_decompose). Closed-form influence functions for Theil/Atkinson, weighted O(n log n) Dagum Gini, cross-method consistency checks.
@@ -653,7 +654,7 @@ wrappers, no runtime dependencies on upstream DID packages.
653654
| --- | --- | --- |
654655
| `rdrobust()` | Sharp/Fuzzy RD with robust bias-corrected inference | Calonico, Cattaneo & Titiunik (2014) |
655656
| `rdplot()` | RD visualization with binned scatter ||
656-
| `rddensity()` | McCrary density manipulation test | McCrary (2008) |
657+
| `rddensity()` | CJM density manipulation test; optional `backend="r"` bridge to `rddensity::rddensity` | Cattaneo, Jansson & Ma (2020) |
657658
| `rdmc()` | Multi-cutoff RD | Cattaneo et al. (2024) |
658659
| `rdms()` | Geographic / multi-score RD | Keele & Titiunik (2015) |
659660
| `rkd()` | Regression Kink Design | Card et al. (2015) |

README_CN.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ StatsPAI 聚焦**因果推断**——在这条主线上,我们的目标是成
4646

4747
**图例**:🏆 跨生态最完整 · ✅ 完整覆盖 · ⚠️ 部分 / 分散 / 单算法 · ❌ 无。
4848

49-
**StatsPAI 一句话概览**:live agent registry 中有 1,018 个注册函数 · 80 个子模块 · ~249k 行核心代码 + ~86k 行测试。这四个数字都可以由唯一的生成器 (`python scripts/registry_stats.py`) 现场复算;[`docs/stats.md`](docs/stats.md) 中的按模块拆分表也由同一个脚本回写。完整覆盖矩阵(23 个方法家族)以及跨生态行数对比,详见 [`docs/stats.md`](docs/stats.md)
49+
**StatsPAI 一句话概览**:live agent registry 中有 1,020 个注册函数 · 81 个子模块 · 266k 行核心代码 + 93k 行测试。这四个数字都可以由唯一的生成器 (`python scripts/registry_stats.py`) 现场复算;[`docs/stats.md`](docs/stats.md) 中的按模块拆分表也由同一个脚本回写。完整覆盖矩阵(23 个方法家族)以及跨生态行数对比,详见 [`docs/stats.md`](docs/stats.md)。这些覆盖数字描述 API 广度,不等同于每个函数都有 R/Stata 数值验证;生产使用请查看 `validation_status`
5050

5151
**📦 v1.16.0(2026-05-29)— 正确性修复与跨语言对齐扩展**
5252

5353
StatsPAI 1.16.0 修正了 `sp.qreg` Powell 三明治标准误(此前差一个 √n 因子),
5454
并重写 `sp.xtabond` Arellano–Bond 差分 GMM 使其与 Stata 机器精度一致——
5555
两者均标 ⚠️ **正确性**,相关分析请重跑。Track A 跨语言对齐夹具从 36 个
56-
扩展到 50 个 R 对齐模块(其中 43 个有 Stata 参考),并刷新了 JOSS
56+
扩展到 51 个 R 对齐模块(其中 43 个有 Stata 参考,另有 1 个
57+
Py-Stata-only 的 `xtabond` 迁移检查),并刷新了 JSS
5758
reviewer 相关文档。完整发布说明见 [`CHANGELOG.md`](CHANGELOG.md)
5859
`[1.16.0]`
5960

@@ -240,7 +241,7 @@ StatsPAI 1.4.0 是知识地图 v3 路线图的 Sprint 2,关闭了 Sprint 1 末
240241
| **粒子滤波同化** | **`sp.assimilation.particle_filter`** — 带系统重采样的 bootstrap-SIR 粒子滤波(Gordon-Salmond-Smith 1993;Douc-Cappé 2005)。非高斯先验、重尾观测噪声、非线性动力学通过可插拔回调实现。高斯 DGP 下与精确 Kalman 一致到 ~0.003。**`sp.assimilative_causal(..., backend='particle')`** 端到端 wrapper 路由到粒子滤波。 |
241242
| **文档(v3 前沿指南)** | `docs/guides/synth_experimental.md`(Abadie-Zhao 反向 SC 流程)、`docs/guides/harvest_did.md`(Borusyak-Hull-Jaravel harvest DID)、`docs/guides/assimilative_ci.md`(Nature Comms 2026 流式 CI,Kalman + 粒子后端)。已挂到 `mkdocs.yml` 导航。 |
242243
| **v1.3 稳定基础(延续)** | Sprint 1 的 11 个 2025-2026 前沿方法:`synth_experimental_design``rdrobust(..., bootstrap='rbc')``evidence_without_injustice``target_trial.to_paper(fmt='jama'/'bmj')``harvest_did``bcf_ordinal``bcf_factor_exposure``causal_mas``shift_share_political``causal_kalman`。所有 v1.0 capstone 面(`sp.bridge``sp.fairness``sp.surrogate``sp.epi``sp.longitudinal``sp.question`、MR 全家桶、TARGET 清单)保持不变。 |
243-
| **Agent 平台** | `sp.list_functions()` / `sp.describe_function()` / `sp.function_schema()` 为 1,018 个注册公共函数提供 OpenAI/Anthropic tool-calling schema。145 个已策划或显式继承的 `FunctionSpec` 条目携带假设、前置条件、失败模式、局限、`typical_n_min` 或 validation tier。`sp.agent.mcp_server` MCP 脚手架让外部 LLM 可自然语言调用每个函数。 |
244+
| **Agent 平台** | `sp.list_functions()` / `sp.describe_function()` / `sp.function_schema()` 为 1,020 个注册公共函数提供 OpenAI/Anthropic tool-calling schema。362 个已策划或显式继承的 `FunctionSpec` 条目携带假设、前置条件、失败模式、局限、`typical_n_min` 或 validation tier`validation_status` 明确区分 certified / validated / api_stable,避免把 schema 覆盖误读成数值验证`sp.agent.mcp_server` MCP 脚手架让外部 LLM 可自然语言调用每个函数。 |
244245
| **CI/CD 卫生** | v1.3.0 的 `tabulate` 硬依赖延续。通过显式播种(`random_state=0``n_estimators=300`、扩 `n`)修复 `test_forest_ate_recovers_average_tau` flake。2 699+ 测试在所有 OS × Python matrix 上通过。 |
245246

246247
**v0.6 新功能**`sp.interactive(fig)` —— 类似 Stata Graph Editor 的 WYSIWYG 图表编辑器,支持 29 种学术主题、实时预览、自动生成可复现代码。
@@ -339,7 +340,7 @@ StatsPAI **不是** R 的 wrapper。我们从原始论文独立重新实现每
339340
| --- | --- | --- |
340341
| `rdrobust()` | 尖锐/模糊 RD,稳健偏差校正推断 | Calonico, Cattaneo & Titiunik (2014) |
341342
| `rdplot()` | RD 可视化(分箱散点图) ||
342-
| `rddensity()` | McCrary 密度操纵检验 | McCrary (2008) |
343+
| `rddensity()` | CJM 密度操纵检验;可选 `backend="r"` 桥接 `rddensity::rddensity` | Cattaneo, Jansson & Ma (2020) |
343344

344345
### 因果推断 — 匹配与再加权
345346

0 commit comments

Comments
 (0)