Heterogeneous Adoption Difference-in-Differences ================================================ Estimator for designs where **no unit remains untreated** at the post period. Every unit `g` is exposed to treatment at the same single date but adoption intensity (dose) varies across units; there is no genuinely untreated control group to anchor a standard DiD contrast. This module implements the methodology from de Chaisemartin, Ciccia, D'Haultfœuille & Knau (2026), "Difference-in-Differences Estimators When No Unit Remains Untreated" (arXiv:2405.04465v6), which: 1. **Targets WAS or WAS_{d̲} depending on design path:** Design 1' (the QUG / Quasi-Untreated-Group case with ``d̲ = 0``) identifies the Weighted Average Slope (WAS, paper Equation 2); Design 1 (no QUG, ``d̲ > 0``) identifies ``WAS_{d̲}`` under Assumption 6, or sign identification only under Assumption 5 (neither additional assumption is testable via pre-trends). The shipped result classes expose ``target_parameter == "WAS"`` versus ``"WAS_d_lower"`` so callers can key on the resolved estimand. 2. **Estimates the target via local-linear regression at the dose support boundary**, with three concrete fit paths: ``continuous_at_zero`` for Design 1', and ``continuous_near_d_lower`` or ``mass_point`` for Design 1 (auto-detected from the dose distribution). 3. **Provides bias-corrected confidence intervals** ported from the ``nprobust`` machinery for the continuous-dose paths, and a structural-residual 2SLS sandwich for the mass-point path. 4. **Extends to multi-period event-study settings** (paper Appendix B.2), restricting staggered-timing panels to the last-treatment cohort (which retains never-treated units as comparisons) with pointwise per-horizon CIs. .. note:: **When to use HAD.** Use ``HeterogeneousAdoptionDiD`` when your panel has no untreated unit at the post period (e.g. universal-rollout policies, industry-wide tariff changes) but treatment intensity varies across units. For panels with a never-treated control group and continuous treatment, use :class:`~diff_diff.ContinuousDiD` instead. For binary reversible treatments, use :class:`~diff_diff.ChaisemartinDHaultfoeuille`. .. note:: **Inference contract.** Per-horizon CIs are always pointwise. There are three SE regimes selected by call site: - **Unweighted** - continuous paths use the CCT-2014 weighted-robust SE from the in-house ``lprobust`` port; the mass-point path uses a structural-residual 2SLS sandwich. No cross-horizon covariance. - **``weights=np.ndarray`` shortcut (deprecated)** - continuous paths reuse the CCT-2014 SE; the mass-point path uses an analytical weighted 2SLS sandwich (``classical`` / ``hc1``; CR1 when ``cluster=`` is supplied, except ``cluster=`` + ``aggregate="event_study"`` + ``cband=True`` is rejected outright regardless of ``vcov_type`` per the cluster-combination deviation below; ``hc2`` / ``hc2_bm`` raise ``NotImplementedError`` pending a 2SLS-specific leverage derivation). Yields ``variance_formula="pweight"`` / ``"pweight_2sls"``. - **``survey_design=SurveyDesign(weights="col", ...)``** (canonical; accepts strata / PSU / FPC) - both paths compose Binder (1983) Taylor-series linearization with ``df_survey`` threaded into ``safe_inference``. Yields ``variance_formula="survey_binder_tsl"`` / ``"survey_binder_tsl_2sls"``. The two weighted paths currently produce different SE families on this estimator (CCT-2014 / 2SLS pweight-sandwich vs Binder-TSL); the deprecated ``weights=`` and ``survey=`` aliases will be removed in the next minor release, at which point the long-term unification onto a single SE contract under ``survey_design=`` lands. (Tracked in ``TODO.md``; the deprecation warning emitted by ``HeterogeneousAdoptionDiD.fit`` spells the migration out per call site.) On array-in HAD pretest helpers (``stute_test``, ``yatchew_hr_test``, ``stute_joint_pretest``) the pweight-only shortcut is ``survey_design=make_pweight_design(weights)``; data-in surfaces use ``survey_design=SurveyDesign(weights="col_name", ...)`` against ``data`` instead. ``qug_test`` is the exception: the QUG step has no survey-aware migration target (Phase 4.5 C0 decision; see methodology REGISTRY) and permanently raises ``NotImplementedError`` on any of ``survey_design=`` / ``survey=`` / ``weights=``. The composite workflow ``did_had_pretest_workflow`` handles this by skipping QUG under survey/weighted dispatch and emitting a ``UserWarning``. A simultaneous confidence band (sup-t) is available only on the **weighted event-study path** via ``cband=True``. Joint cross-horizon analytical covariance is not computed in this release; tracked in ``TODO.md``. **Mass-point ``vcov_type="classical"`` deviation.** The mass-point ``survey_design=SurveyDesign(...)`` paths (static and event-study) and the deprecated ``weights=`` + ``aggregate="event_study"`` + ``cband=True`` path reject ``vcov_type="classical"`` with ``NotImplementedError``. The per-unit 2SLS influence function returned by the mass-point fit is HC1-scaled so that ``compute_survey_if_variance`` and the sup-t bootstrap target ``V_HC1`` consistently; mixing it with a classical analytical SE would silently report a ``V_HC1``-targeted variance under a ``classical`` label. Use ``vcov_type="hc1"`` or set ``robust=True`` explicitly (the constructor default ``robust=False`` maps to ``vcov_type="classical"``, which triggers the guard); a classical-aligned IF derivation is queued for a follow-up PR. **Mass-point cluster-combination deviation.** On ``design="mass_point"``, two clustered weighted paths are rejected outright regardless of ``vcov_type``: - ``survey_design=SurveyDesign(...)`` + ``cluster=`` (static and event-study): the survey path composes Binder-TSL variance, which would silently override the CR1 cluster-robust sandwich. Workarounds: ``cluster=`` alone (unweighted CR1), or ``weights=`` + ``cluster=`` (weighted-CR1 pweight sandwich), or ``survey_design=`` alone (Binder-TSL). Combined cluster-robust + survey inference is queued for a follow-up PR. - Deprecated ``weights=`` shortcut + ``cluster=`` + ``aggregate="event_study"`` + ``cband=True``: the sup-t bootstrap normalizes HC1-scale perturbations by the CR1 analytical SE, mixing variance families. Workarounds: pass ``cband=False`` (keeps weighted-CR1 per-horizon), or drop ``cluster=`` (keeps weighted-HC1 sup-t). .. tip:: For an end-to-end walkthrough of the survey-aware HAD workflow on a BRFSS-shape stratified household-survey panel - including the now- supported ``SurveyDesign(strata=...)`` path through the Stute pretest family (lifted in PR #432, 2026-05) - see `Tutorial 22: Survey-Weighted HAD <../tutorials/22_had_survey_design.ipynb>`_. HeterogeneousAdoptionDiD ------------------------ .. autoclass:: diff_diff.HeterogeneousAdoptionDiD :no-index: :members: :undoc-members: :show-inheritance: HeterogeneousAdoptionDiDResults ------------------------------- Single-period results container for ``HeterogeneousAdoptionDiD`` estimation. .. autoclass:: diff_diff.HeterogeneousAdoptionDiDResults :no-index: :members: :undoc-members: :show-inheritance: HeterogeneousAdoptionDiDEventStudyResults ----------------------------------------- Multi-period event-study results container for the Appendix B.2 extension. .. autoclass:: diff_diff.HeterogeneousAdoptionDiDEventStudyResults :no-index: :members: :undoc-members: :show-inheritance: HAD Pretests ------------ Diagnostic pretests for the HAD identification assumptions from de Chaisemartin et al. (2026). The composite orchestrator :func:`~diff_diff.did_had_pretest_workflow` is a diagnostic battery only - it does NOT pick the HAD design path (``continuous_at_zero`` / ``continuous_near_d_lower`` / ``mass_point``); that is auto-detected inside :meth:`HeterogeneousAdoptionDiD.fit` from the dose support. The workflow has two explicit modes selected by the caller via the ``aggregate=`` kwarg: ``aggregate="overall"`` (default, two-period first-differenced sample) runs single-period tests; ``aggregate="event_study"`` (multi-period panel with three or more periods) runs joint multi-period tests. Both modes return a unified :class:`~diff_diff.HADPretestReport`. .. autofunction:: diff_diff.did_had_pretest_workflow .. autoclass:: diff_diff.HADPretestReport :no-index: :members: :undoc-members: :show-inheritance: Single-period tests (``aggregate="overall"``) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autofunction:: diff_diff.qug_test .. autofunction:: diff_diff.stute_test .. autofunction:: diff_diff.yatchew_hr_test .. autoclass:: diff_diff.QUGTestResults :no-index: :members: :undoc-members: :show-inheritance: .. autoclass:: diff_diff.StuteTestResults :no-index: :members: :undoc-members: :show-inheritance: .. autoclass:: diff_diff.YatchewTestResults :no-index: :members: :undoc-members: :show-inheritance: Joint multi-period tests (``aggregate="event_study"``) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. autofunction:: diff_diff.stute_joint_pretest .. autofunction:: diff_diff.joint_pretrends_test .. autofunction:: diff_diff.joint_homogeneity_test .. autoclass:: diff_diff.StuteJointResult :no-index: :members: :undoc-members: :show-inheritance: