Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f875f55
Phase 2a: HeterogeneousAdoptionDiD class (single-period)
igerber Apr 20, 2026
77b3466
Enforce d_lower == d.min() contract on HAD mass-point path
igerber Apr 20, 2026
687a615
Clarify continuous-path mass-point-guard test docstring
igerber Apr 20, 2026
8c38f07
Fix P0 HAD continuous estimator formula + P1 validator + docs
igerber Apr 20, 2026
c320dde
Address PR #346 CI review round 2: 2 P1s + 1 P2
igerber Apr 20, 2026
ddc09e4
Address PR #346 CI review round 3: P1 period inference + P2 summary l…
igerber Apr 20, 2026
e622d60
Address PR #346 CI review round 4: P1 d_lower > 0 on Design 1 paths
igerber Apr 20, 2026
2884c1d
Address PR #346 CI review round 5: P1 reciprocal mass_point regime guard
igerber Apr 20, 2026
afa0f6c
Address PR #346 CI review round 6: P1 panel-only note + P3 theorem refs
igerber Apr 20, 2026
792997d
Address PR #346 CI review round 7: P1 defer cluster validation + P3 r…
igerber Apr 21, 2026
1eda614
Address PR #346 CI review round 8: P1 snap d_lower + P2 row-level val…
igerber Apr 21, 2026
a3553ed
Address PR #346 CI review round 9: P2 robust warning + P3 docstring
igerber Apr 21, 2026
98b8201
Address PR #346 CI review round 10: P1 sklearn-compatible get/set_params
igerber Apr 21, 2026
c675051
Address PR #346 CI review round 11: P2 atomic set_params + P3 doc exa…
igerber Apr 21, 2026
07ab30f
Address PR #346 CI review round 12: P1 reject nonzero d_lower on cont…
igerber Apr 21, 2026
2f6e1d0
Address PR #346 CI review round 13: P1 reject non-finite d_lower + P3…
igerber Apr 22, 2026
22d7f65
Address PR #346 CI review round 14: P3 tighten NaN-contract wording
igerber Apr 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,16 @@ Deferred items from PR reviews that were not addressed before merge.
| `bias_corrected_local_linear`: support `weights=` once survey-design adaptation lands. nprobust's `lprobust` has no weight argument so there is no parity anchor; derivation needed. | `diff_diff/local_linear.py`, `diff_diff/_nprobust_port.py::lprobust` | Phase 1c | Medium |
| `bias_corrected_local_linear`: support multi-eval grid (`neval > 1`) with cross-covariance (`covgrid=TRUE` branch of `lprobust.R:253-378`). Not needed for HAD but useful for multi-dose diagnostics. | `diff_diff/_nprobust_port.py::lprobust` | Phase 1c | Low |
| Clustered-DGP parity: Phase 1c's DGP 4 uses manual `h=b=0.3` to sidestep an nprobust-internal singleton-cluster bug in `lpbwselect.mse.dpi`'s pilot fits. Once nprobust ships a fix (or we derive one independently), add a clustered-auto-bandwidth parity test. | `benchmarks/R/generate_nprobust_lprobust_golden.R` | Phase 1c | Low |
| `HeterogeneousAdoptionDiD` Phase 2b: multi-period event-study extension (Appendix B.2). `aggregate="event_study"` raises `NotImplementedError` in Phase 2a; Phase 2b will aggregate per-cohort first-differences into an event-study result with `.event_study_effects` / `.event_study_se` result fields. | `diff_diff/had.py`, `tests/test_had.py` | Phase 2a | Medium |
| `HeterogeneousAdoptionDiD`: survey-design integration (`survey=SurveyDesign(...)`). Currently raises `NotImplementedError`. Requires Taylor-linearization of the β-scale rescaling and replicate-weight-compatible 2SLS variance on the mass-point path. | `diff_diff/had.py` | Phase 2a | Medium |
| `HeterogeneousAdoptionDiD`: `weights=` support. Deferred jointly with survey integration. nprobust's `lprobust` has no weight argument so the nonparametric continuous path needs a derivation; the 2SLS mass-point path needs weighted-sandwich parity. | `diff_diff/had.py` | Phase 2a | Medium |
| `HeterogeneousAdoptionDiD` mass-point: `vcov_type in {"hc2", "hc2_bm"}` raises `NotImplementedError` pending a 2SLS-specific leverage derivation. The OLS leverage `x_i' (X'X)^{-1} x_i` is wrong for 2SLS; the correct finite-sample correction uses `x_i' (Z'X)^{-1} (...) (X'Z)^{-1} x_i`. Needs derivation plus an R / Stata (`ivreg2 small robust`) parity anchor. | `diff_diff/had.py::_fit_mass_point_2sls` | Phase 2a | Medium |
| `HeterogeneousAdoptionDiD` continuous paths: thread `cluster=` through `bias_corrected_local_linear` (Phase 1c's wrapper already supports cluster; Phase 2a ignores it with a `UserWarning` on the continuous path to keep scope tight). | `diff_diff/had.py`, `diff_diff/local_linear.py` | Phase 2a | Low |
| `HeterogeneousAdoptionDiD` Phase 3: `qug_test()`, `stute_test()`, `yatchew_hr_test()` pre-test diagnostics (paper Section 3.3). Composite helper `did_had_pretest_workflow()`. Not part of Phase 2a scope. | `diff_diff/had.py`, new module | Phase 2a | Medium |
| `HeterogeneousAdoptionDiD` Phase 4: Pierce-Schott (2016) replication harness; reproduce paper Figure 2 values and Table 1 coverage rates. | `benchmarks/`, `tests/` | Phase 2a | Low |
| `HeterogeneousAdoptionDiD` Phase 5: `practitioner_next_steps()` integration, tutorial notebook, and `llms.txt` updates (preserving UTF-8 fingerprint). | `diff_diff/practitioner.py`, `tutorials/`, `diff_diff/guides/` | Phase 2a | Low |
| `HeterogeneousAdoptionDiD` staggered-timing reduction: Phase 2a requires exactly 2 time periods and raises on `>2` periods with or without `first_treat_col`. A "last-cohort subgroup" reduction scheme (slice to max-cohort's 2-period window) could lift this in a targeted follow-up PR before full Phase 2b multi-period aggregation. | `diff_diff/had.py::_validate_had_panel` | Phase 2a | Low |
| `HeterogeneousAdoptionDiD` repeated-cross-section support: paper Section 2 defines HAD on panel OR repeated cross-section, but Phase 2a is panel-only. RCS inputs (disjoint unit IDs between periods) are rejected by the balanced-panel validator with the generic "unit(s) do not appear in both periods" error. A follow-up PR will add an RCS identification path based on pre/post cell means (rather than unit-level first differences), with its own validator and a distinct `data_mode` / API surface. | `diff_diff/had.py::_validate_had_panel`, `diff_diff/had.py::_aggregate_first_difference` | Phase 2a | Medium |

#### Performance

Expand Down
9 changes: 9 additions & 0 deletions diff_diff/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@
triangular_kernel,
uniform_kernel,
)
from diff_diff.had import (
HeterogeneousAdoptionDiD,
HeterogeneousAdoptionDiDResults,
)
from diff_diff.estimators import (
DifferenceInDifferences,
MultiPeriodDiD,
Expand Down Expand Up @@ -252,6 +256,7 @@
EDiD = EfficientDiD
ETWFE = WooldridgeDiD
DCDH = ChaisemartinDHaultfoeuille
HAD = HeterogeneousAdoptionDiD

__version__ = "3.2.0"
__all__ = [
Expand Down Expand Up @@ -431,6 +436,10 @@
# Bias-corrected local-linear (Phase 1c for HeterogeneousAdoptionDiD)
"BiasCorrectedFit",
"bias_corrected_local_linear",
# HeterogeneousAdoptionDiD (Phase 2a)
"HeterogeneousAdoptionDiD",
"HeterogeneousAdoptionDiDResults",
"HAD",
# Datasets
"load_card_krueger",
"load_castle_doctrine",
Expand Down
Loading
Loading