Skip to content

Commit 578460e

Browse files
igerberclaude
andcommitted
Address PR #372 R6 review (1 P2): backfill HeterogeneousAdoptionDiD across all canonical surfaces
P2 - HeterogeneousAdoptionDiD shipped (Phases 1a-3 + 3-followup + 4.5 A/B/C0 merged via PRs #366/#367) but missing from the new authoritative documentation surfaces this PR establishes - README.md ## Estimators catalog: add one-line entry after ContinuousDiD (de Chaisemartin, Ciccia, D'Haultfoeuille & Knau 2026; alias HAD). - diff_diff/guides/llms.txt ## Estimators: add matching one-liner. - .claude/commands/docs-check.md required-estimators table: add HAD row pointing at had.rst as the API target. - docs/references.rst: add new "Heterogeneous Adoption (No-Untreated Designs)" sub-section with the de Chaisemartin et al. (2026) arXiv:2405.04465v6 citation. - docs/api/index.rst: add HeterogeneousAdoptionDiD to estimators autosummary; add HeterogeneousAdoptionDiDResults + HeterogeneousAdoptionDiDEventStudyResults to results autosummary. - docs/api/had.rst (NEW): autoclass page for the three classes with a brief intro and a "When to use HAD" note pointing at sibling estimators (ContinuousDiD for never-treated controls, dCDH for binary reversible). - docs/doc-deps.yaml: add diff_diff/had.py + diff_diff/had_pretests.py source mappings (REGISTRY methodology, had.rst api_reference, README catalog, references.rst, llms.txt). The llms-full.txt mapping is intentionally omitted with a comment - that section is deferred to the Phase 5 follow-up tracked in TODO.md. TODO.md: narrow the Phase 5 entry from "llms.txt updates" to "llms-full.txt HeterogeneousAdoptionDiD section" since the catalog one-liner, API page, and bibliography landed here. practitioner_next_steps() integration and tutorial notebook remain deferred. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5afdc04 commit 578460e

8 files changed

Lines changed: 103 additions & 1 deletion

File tree

.claude/commands/docs-check.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ The following estimators/features MUST have documentation:
4848
| EfficientDiD | "EfficientDiD" | "EfficientDiD" | efficient_did.rst | "Multi-Period and Staggered" |
4949
| StackedDiD | "StackedDiD" | "StackedDiD" | stacked_did.rst | "Multi-Period and Staggered" |
5050
| ContinuousDiD | "ContinuousDiD" | "ContinuousDiD" | continuous_did.rst | "Multi-Period and Staggered" |
51+
| HeterogeneousAdoptionDiD | "HeterogeneousAdoptionDiD" | "HeterogeneousAdoptionDiD" | had.rst | "Heterogeneous Adoption (No-Untreated Designs)" |
5152
| TripleDifference | "TripleDifference" | "TripleDifference" | triple_diff.rst | "Triple Difference" |
5253
| StaggeredTripleDifference | "StaggeredTripleDifference" | "StaggeredTripleDifference" | staggered.rst | "Triple Difference" |
5354
| WooldridgeDiD | "WooldridgeDiD" | "WooldridgeDiD" | wooldridge_etwfe.rst | "Multi-Period and Staggered" |

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ Full guide: `diff_diff.get_llm_guide("practitioner")`.
110110
- [SyntheticDiD](https://diff-diff.readthedocs.io/en/stable/api/estimators.html) - Synthetic DiD combining standard DiD and synthetic control for few treated units
111111
- [TripleDifference](https://diff-diff.readthedocs.io/en/stable/api/triple_diff.html) - triple difference (DDD) estimator for designs requiring two criteria for treatment eligibility
112112
- [ContinuousDiD](https://diff-diff.readthedocs.io/en/stable/api/continuous_did.html) - Callaway, Goodman-Bacon & Sant'Anna (2024) continuous treatment DiD with dose-response curves
113+
- [HeterogeneousAdoptionDiD](https://diff-diff.readthedocs.io/en/stable/api/had.html) - de Chaisemartin, Ciccia, D'Haultfœuille & Knau (2026) for **no-untreated-unit panels** where every unit receives a strictly positive heterogeneous dose; local-linear estimator targeting the Weighted Average Slope (WAS) at the support boundary, with multi-period event-study extension. Alias `HAD`.
113114
- [StackedDiD](https://diff-diff.readthedocs.io/en/stable/api/stacked_did.html) - Wing, Freedman & Hollingsworth (2024) stacked DiD with Q-weights and sub-experiments
114115
- [EfficientDiD](https://diff-diff.readthedocs.io/en/stable/api/efficient_did.html) - Chen, Sant'Anna & Xie (2025) efficient DiD with optimal weighting for tighter SEs
115116
- [TROP](https://diff-diff.readthedocs.io/en/stable/api/trop.html) - Triply Robust Panel estimator (Athey et al. 2025) with nuclear norm factor adjustment

TODO.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Deferred items from PR reviews that were not addressed before merge.
105105
| `HeterogeneousAdoptionDiD` Phase 3 R-parity: Phase 3 ships coverage-rate validation on synthetic DGPs (not tight point parity against `chaisemartin::stute_test` / `yatchew_test`). Tight numerical parity requires aligning bootstrap seed semantics and `B` across numpy/R and is deferred. | `tests/test_had_pretests.py` | Phase 3 | Low |
106106
| `HeterogeneousAdoptionDiD` Phase 3 nprobust bandwidth for Stute: some Stute variants on continuous regressors use nprobust-style optimal bandwidth selection. Phase 3 uses OLS residuals from a 2-parameter linear fit (no bandwidth selection). nprobust integration is a future enhancement; not in paper scope. | `diff_diff/had_pretests.py::stute_test` | Phase 3 | Low |
107107
| `HeterogeneousAdoptionDiD` Phase 4: Pierce-Schott (2016) replication harness; reproduce paper Figure 2 values and Table 1 coverage rates. | `benchmarks/`, `tests/` | Phase 2a | Low |
108-
| `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 |
108+
| `HeterogeneousAdoptionDiD` Phase 5: `practitioner_next_steps()` integration, tutorial notebook, and `llms-full.txt` HeterogeneousAdoptionDiD section (preserving UTF-8 fingerprint). README catalog + bundled `llms.txt` entry + `docs/api/had.rst` + `docs/references.rst` citation landed in PR #372 docs refresh. | `diff_diff/practitioner.py`, `tutorials/`, `diff_diff/guides/llms-full.txt` | Phase 2a | Low |
109109
| `HeterogeneousAdoptionDiD` time-varying dose on event study: Phase 2b REJECTS panels where `D_{g,t}` varies within a unit for `t >= F` (the aggregation uses `D_{g, F}` as the single regressor for all horizons, paper Appendix B.2 constant-dose convention). A follow-up PR could add a time-varying-dose estimator for these panels; current behavior is front-door rejection with a redirect to `ChaisemartinDHaultfoeuille`. | `diff_diff/had.py::_validate_had_panel_event_study` | Phase 2b | Low |
110110
| `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 |
111111
| SyntheticDiD: bootstrap cross-language parity anchor against R's default `synthdid::vcov(method="bootstrap")` (refit; rebinds `opts` per draw) or Julia `Synthdid.jl::src/vcov.jl::bootstrap_se` (refit by construction). Same-library validation (placebo-SE tracking, AER §6.3 MC truth) is in place; a cross-language anchor is desirable to bolster the methodology contract. Julia is the cleanest target — minimal wrapping work and refit-native vcov. Tolerance target: 1e-6 on Monte Carlo samples (different BLAS + RNG paths preclude 1e-10). The R-parity fixture from the previous release was deleted because it pinned the now-removed fixed-weight path. | `benchmarks/R/`, `benchmarks/julia/`, `tests/` | follow-up | Low |

diff_diff/guides/llms.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Full practitioner guide: call `diff_diff.get_llm_guide("practitioner")`
6161
- [SyntheticDiD](https://diff-diff.readthedocs.io/en/stable/api/estimators.html): Synthetic DiD combining standard DiD and synthetic control methods for few treated units
6262
- [TripleDifference](https://diff-diff.readthedocs.io/en/stable/api/triple_diff.html): Triple difference (DDD) estimator for designs requiring two criteria for treatment eligibility
6363
- [ContinuousDiD](https://diff-diff.readthedocs.io/en/stable/api/continuous_did.html): Callaway, Goodman-Bacon & Sant'Anna (2024) continuous treatment DiD with dose-response curves
64+
- [HeterogeneousAdoptionDiD](https://diff-diff.readthedocs.io/en/stable/api/had.html): de Chaisemartin, Ciccia, D'Haultfœuille & Knau (2026) for designs where **no unit remains untreated** (heterogeneous adoption / HAD); local-linear estimator targeting Weighted Average Slope (WAS) at the support boundary, with multi-period event-study extension. Alias `HAD`.
6465
- [StackedDiD](https://diff-diff.readthedocs.io/en/stable/api/stacked_did.html): Wing, Freedman & Hollingsworth (2024) stacked DiD with Q-weights and sub-experiments
6566
- [EfficientDiD](https://diff-diff.readthedocs.io/en/stable/api/efficient_did.html): Chen, Sant'Anna & Xie (2025) efficient DiD with optimal weighting for tighter SEs
6667
- [TROP](https://diff-diff.readthedocs.io/en/stable/api/trop.html): Triply Robust Panel estimator (Athey et al. 2025) with nuclear norm factor adjustment

docs/api/had.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Heterogeneous Adoption Difference-in-Differences
2+
================================================
3+
4+
Estimator for designs where **no unit remains untreated** at the post period
5+
- every unit `g` receives a strictly positive heterogeneous dose `D_{g,2} > 0`,
6+
and there is no genuinely untreated control group to anchor a standard DiD
7+
contrast.
8+
9+
This module implements the methodology from de Chaisemartin, Ciccia,
10+
D'Haultfœuille & Knau (2026), "Difference-in-Differences Estimators When No
11+
Unit Remains Untreated" (arXiv:2405.04465v6), which:
12+
13+
1. **Targets the Weighted Average Slope (WAS)** as the identified parameter
14+
when no untreated comparison group exists (paper Equation 2).
15+
2. **Uses local-linear regression at the support boundary** to estimate the
16+
slope of the dose-outcome relationship at the lowest observed dose.
17+
3. **Provides bias-corrected confidence intervals** (Calonico, Cattaneo & Titiunik
18+
2014 / `nprobust`-style) and HC2 / Bell-McCaffrey small-sample SEs.
19+
4. **Extends to multi-period event-study settings** (paper Appendix B.2),
20+
producing per-horizon WAS effects with correlated standard errors and
21+
sup-t bands.
22+
23+
.. note::
24+
25+
**When to use HAD.** Use ``HeterogeneousAdoptionDiD`` when your panel has
26+
no untreated unit at any treatment period (e.g. universal-rollout policies,
27+
industry-wide tariff changes) but treatment intensity varies across units.
28+
For panels with a never-treated control group and continuous treatment,
29+
use :class:`~diff_diff.ContinuousDiD` instead. For binary reversible
30+
treatments, use :class:`~diff_diff.ChaisemartinDHaultfoeuille`.
31+
32+
HeterogeneousAdoptionDiD
33+
------------------------
34+
35+
.. autoclass:: diff_diff.HeterogeneousAdoptionDiD
36+
:members:
37+
:undoc-members:
38+
:show-inheritance:
39+
40+
HeterogeneousAdoptionDiDResults
41+
-------------------------------
42+
43+
Single-period results container for ``HeterogeneousAdoptionDiD`` estimation.
44+
45+
.. autoclass:: diff_diff.HeterogeneousAdoptionDiDResults
46+
:members:
47+
:undoc-members:
48+
:show-inheritance:
49+
50+
HeterogeneousAdoptionDiDEventStudyResults
51+
-----------------------------------------
52+
53+
Multi-period event-study results container for the Appendix B.2 extension.
54+
55+
.. autoclass:: diff_diff.HeterogeneousAdoptionDiDEventStudyResults
56+
:members:
57+
:undoc-members:
58+
:show-inheritance:

docs/api/index.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Core estimator classes for DiD analysis:
2424
diff_diff.TripleDifference
2525
diff_diff.TROP
2626
diff_diff.ContinuousDiD
27+
diff_diff.HeterogeneousAdoptionDiD
2728
diff_diff.EfficientDiD
2829
diff_diff.TwoStageDiD
2930
diff_diff.WooldridgeDiD
@@ -56,6 +57,8 @@ Result containers returned by estimators:
5657
diff_diff.TROPResults
5758
diff_diff.ContinuousDiDResults
5859
diff_diff.DoseResponseCurve
60+
diff_diff.HeterogeneousAdoptionDiDResults
61+
diff_diff.HeterogeneousAdoptionDiDEventStudyResults
5962
diff_diff.EfficientDiDResults
6063
diff_diff.EDiDBootstrapResults
6164
diff_diff.TwoStageDiDResults

docs/doc-deps.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,37 @@ sources:
361361
section: "Varying Spending Levels"
362362
type: user_guide
363363

364+
# ── HeterogeneousAdoptionDiD (HAD) ──────────────────────────────────
365+
366+
diff_diff/had.py:
367+
drift_risk: medium
368+
docs:
369+
- path: docs/methodology/REGISTRY.md
370+
section: "HeterogeneousAdoptionDiD"
371+
type: methodology
372+
- path: docs/api/had.rst
373+
type: api_reference
374+
- path: README.md
375+
section: "Estimators (one-line catalog entry)"
376+
type: user_guide
377+
- path: docs/references.rst
378+
type: user_guide
379+
- path: diff_diff/guides/llms.txt
380+
section: "Estimators"
381+
type: user_guide
382+
# Note: llms-full.txt does not yet have a HeterogeneousAdoptionDiD section
383+
# (deferred to TODO.md Phase 5 follow-up); the dependency mapping will be
384+
# added when that section lands.
385+
386+
diff_diff/had_pretests.py:
387+
drift_risk: medium
388+
docs:
389+
- path: docs/methodology/REGISTRY.md
390+
section: "HeterogeneousAdoptionDiD"
391+
type: methodology
392+
- path: docs/api/had.rst
393+
type: api_reference
394+
364395
# ── SyntheticDiD ───────────��───────────────────────────────────────
365396

366397
diff_diff/synthetic_did.py:

docs/references.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ Continuous Treatment DiD
165165

166166
Primary source for ATT(d), ACRT, dose-response curves, and B-spline flexibility implemented in our ``ContinuousDiD`` class.
167167

168+
Heterogeneous Adoption (No-Untreated Designs)
169+
---------------------------------------------
170+
171+
- **de Chaisemartin, C., Ciccia, D., D'Haultfœuille, X., & Knau, F. (2026).** "Difference-in-Differences Estimators When No Unit Remains Untreated." *arXiv preprint* arXiv:2405.04465v6. https://arxiv.org/abs/2405.04465
172+
173+
Primary source for the Weighted Average Slope (WAS) estimator and its multi-period event-study extension implemented in our ``HeterogeneousAdoptionDiD`` class. Targets settings where every unit receives a strictly positive heterogeneous dose at treatment time, using local-linear regression at the support boundary.
174+
168175
Power Analysis
169176
--------------
170177

0 commit comments

Comments
 (0)