You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replaces the "paper's own §4 RCS DGPs are NOT replicated" caveat with the
real thing, scoped by five dual-review rounds that executed the candidate
designs:
- §4.2.2 (kernel-estimation design, arXiv v3 p. 19) replicated verbatim in
tests/test_methodology_dml_did.py: an exact-draw-order frame generator; a
DGP-shape pin covering the distributions, all three innovation scales
(0.1/0.2/0.3), stationary-sampling checks (T independent of (D, X)), the
design's built-in confounded unadjusted contrast (-> theta0 + 1 = 4, its
covariate-adjustment point), and both correct-specification facts (true
propensity sigmoid(X - 1/2) by Bayes; true l20 = 0.25 X); seed-pinned
recovery at both paper Ns (sieve + linear at N=500, linear at N=200, each
with a 4*se gate, a seed-pin absolute bound, and a discriminating
comparison requiring the adjusted estimate to beat the unadjusted
contrast); and a slow MC coverage class (the file's third), seeds offset
from the incumbent streams. Fits run under a strict warning gate that
tolerates exactly the documented panel=False Assumption 2.3 UserWarning
and the occasional propensity-trimming UserWarning (rare fitted-tail
excursions on the unbounded-X design) and fails on anything else.
"N(0, 0.1)" adopted as VARIANCE 0.1 with the ambiguity recorded.
- §4.2.1 (ML-estimation design) extracted into the paper review but
documented as NOT replicable with the bundled unpenalized learners:
order-of-magnitude noise at (N=500, p=100) (SE ~4-10, EPV ~1.4-1.6 vs the
solve_logit threshold 10, fitted out-of-fold clipping ~5-29% of rows -
estimated propensities; true-propensity tail mass is only ~0.06%), and a
fail-closed linear-outcome rank deficiency at (N=200, p=100) even under an
oracle propensity (ridge/sieve fit there). TODO row narrowed accordingly
(penalized propensity learner + p-robust outcome learner); the review
gains the full §4.2 parameterizations, a ninth suspected-typo entry (the
printed-but-unused beta_0, with the trend-cancellation note), and a Gaps
entry for the paper's own regularity-condition quirks (Gaussian kernel vs
Assumption 3.3; unbounded-index propensities vs a.s. strict overlap
3.2(a) in BOTH §4.2 designs).
- REGISTRY: Case 2 checklist bullet updated + a standalone replication-scope
Note (native learners stand in for kernel first stages; overlap quirk;
§4.2.1 evidence). dr_scores test comment updated (parameterization now
extracted; §4.2.2 replicated). CHANGELOG Added entry.
Verified: default lane + both existing suites green on Rust and pure-Python
backends; the new MC coverage class green on both (200 reps Rust / 22-rep
reduced band pure-Python); the seed-pinned recovery values reproduce the
review measurements exactly (att 2.7581/2.9054/2.5580, unadjusted contrast
4.008/3.978/3.617).
| Replicate Chang (2020) §4's own RCS simulation DGPs (pp. 17-21, "fully specified" per the paper review) as recovery/coverage fixtures for the `DMLDiD(panel=False)` lane — the shipped tests use a library-authored RCS design (documented in the REGISTRY checklist caveat); needs the paper PDF to extract the parameterization |`tests/test_methodology_dml_did.py`, `docs/methodology/papers/chang-2020-review.md`| DML PR-B2 | Mid | Low |
82
+
| Replicate Chang (2020) §4.2.1 ML-design RCS DGP — requires a penalized (Lasso-class) propensity learner or a maintainer fixture via the duck-typed `propensity_learner=` object route; native logit+linear verified pure noise at (N=500, p=100) across ~20 review seeds (att ~ −23..+16, SE ~4-10, EPV ~1.4-1.6 vs threshold 10, fitted out-of-fold clipping ~5-29%, mean ~14%); at (N=200, p=100) `outcome_learner="linear"` fails closed on control-fold rank deficiency even under an oracle propensity (use ridge/sieve there); §4.2.2 is replicated |`tests/test_methodology_dml_did.py`, `diff_diff/_learners.py`| DML PR-B2 | Mid | Low |
83
83
| Optional scheduled end-to-end execution gate for the MMM tutorials (29/30): a cron-only workflow (or extension of `mmm-interop.yml`) that executes both notebooks in isolated exact-pin environments, so a stale/invalid committed posterior cannot stay green indefinitely - today the hybrid posture (deliberate: notebooks execute locally with committed outputs; CI smoke-tests the exporters without sampling; drift tests pin source + committed-output needles) leaves the MCMC claims un-re-executed in CI |`.github/workflows/mmm-interop.yml`, `docs/tutorials/29_mmm_calibration_pymc.ipynb`, `docs/tutorials/30_mmm_calibration_meridian.ipynb`| mmm-interop | Mid | Low |
84
84
| Committed `fixest::feols` event-study golden for TWFE `event_study=True` (within + pooled specs, unbalanced + covariate panels, matched CR1 cluster convention, per-period effects + vcov block) - the in-suite gates are shared-core cross-checks (TWFE-within == MPD-absorb, pooled == MPD bit-exact), so a defect common to the shared core would pass; the live-R harness (`benchmarks/R/benchmark_multiperiod.R`, `feols(y ~ treated * time_f \| unit)`) validated the within design in `docs/benchmarks.rst` but is not a committed regression test - follow the `fixest_did_twfe_golden.json` committed-golden pattern (pytest.skip when absent) |`tests/test_fixest_did_twfe_parity.py`, `benchmarks/R/`| 3(a) R2 | Mid | Medium |
85
85
| Type-blind `n_bootstrap` acceptance in already-validated estimators - HAD bool (`isinstance(..., int)` passes `True`, runs as 1 replicate), dCDH bool+float (its bare `< 0` check passes both `True` and `2.5`), TROP float (`2.5` passes the `>= 2` floor), SyntheticDiD float under all three variance methods + bool/negative under jackknife (its floor check is skipped there) - align these local checks with the `utils.validate_n_bootstrap` type guard (M-081 kept them out of the sweep: it scoped to previously-UNvalidated estimators only) |`diff_diff/had.py`, `diff_diff/chaisemartin_dhaultfoeuille.py`, `diff_diff/trop.py`, `diff_diff/synthetic_did.py`| 2(d) PR-B | Quick | Low |
Copy file name to clipboardExpand all lines: docs/methodology/REGISTRY.md
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3129,6 +3129,8 @@ the finite-dimensional `p_0` is handled by the variance correction below.
3129
3129
at 0.0 observed diff under identical folds and sklearn learners).
3130
3130
- R / Stata: none — the paper ships no companion package.
3131
3131
3132
+
- **Note:** Chang §4.2 simulation-DGP replication scope (PR-B2). The §4.2.2 kernel-design RCS DGP (arXiv v3 p. 19) is replicated verbatim in `tests/test_methodology_dml_did.py`, estimated with the library's NATIVE learners rather than the paper's Gaussian-kernel first stages (no kernel first stage is bundled); the fixture is correctly specified on both nuisances (true propensity `sigmoid(X − 1/2)` by Bayes' rule; true `ℓ₂₀ = 0.25·X`), so it validates paper-DGP faithfulness — the discriminating assertion compares against the design's own confounded unadjusted contrast (→ θ₀ + 1), not against a misspecified learner. The design's unbounded X means the a.s. strict-overlap Assumption 3.2(a) holds for no fixed κ > 0 (mass outside [0.01, 0.99] ≈ 2.1e-5 — immaterial at the fixture Ns; the same quirk applies more strongly to §4.2.1, tail ≈ 0.06%). The §4.2.1 ML design is NOT replicable with the bundled unpenalized learners: at (N=500, p=100) the native logit+linear stack is order-of-magnitude noise across seeds (SE ~4-10; `solve_logit` EPV ~1.4-1.6 vs threshold 10; fitted out-of-fold clipping ~5-29% of rows — estimated propensities, not true ones, whose out-of-[0.01, 0.99] mass is ≈0.06%), and at (N=200, p=100) the linear outcome learner fails closed on control-fold rank deficiency even under an oracle propensity (ridge/sieve fit there) — tracked in TODO.md (penalized propensity learner + p-robust outcome learner).
3133
+
3132
3134
**Requirements checklist (shipped Case 1 panel + Case 2 RCS staggered lanes):**
- [x] Normal-approximation inference via `safe_inference()`
3141
3143
- [x] Validation: 2-period DoubleMLDID + staggered per-cell DoubleMLDIDBinary parity spikes (version-pinned, committed, golden literals consumed in-tests) + oracle-nuisance closed-form equivalence + degenerate-cell hand-pipeline equivalence (rtol 1e-14 — BLAS reduces differently-laid-out inputs in platform-dependent order, so bit identity does not hold cross-platform) + Monte Carlo coverage sanity
3142
-
- [x] Case 2 (repeated cross sections): Equation 3.2 score + λ-corrected Theorem 2 variance — SHIPPED as `DMLDiD(panel=False)` (`chang_rcs_score` / `chang_rcs_lambda_slope` / `chang_rcs_score_augmented`; equation-level fixtures, oracle closed forms, derivative-identity checks, DR both directions, characterization spike, MC coverage). The paper's own §4RCS simulation DGPs are NOT replicated (tracked TODO row — needs the paper PDF pp. 17-21); the shipped recovery tests use a library-authored RCS design.
3144
+
- [x] Case 2 (repeated cross sections): Equation 3.2 score + λ-corrected Theorem 2 variance — SHIPPED as `DMLDiD(panel=False)` (`chang_rcs_score` / `chang_rcs_lambda_slope` / `chang_rcs_score_augmented`; equation-level fixtures, oracle closed forms, derivative-identity checks, DR both directions, characterization spike, MC coverage). The paper's own §4.2.2 kernel-design RCS DGP is replicated (`tests/test_methodology_dml_did.py` "Chang Sec. 4.2.2" section: DGP-shape pin, seed-pinned recovery at both paper Ns with a discriminating comparison against the design's confounded unadjusted contrast, slow MC coverage — see the §4.2 replication Note below); the §4.2.1 ML design is not replicable with the bundled learners (narrowed TODO.md row).
3143
3145
- [ ] Case 3 (multilevel treatment): deferred (`DEFERRED.md`; implementation-required overlap conditions per the paper review's Case 3 caution)
Copy file name to clipboardExpand all lines: docs/methodology/papers/chang-2020-review.md
+6-2Lines changed: 6 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -192,7 +192,7 @@ items remain open, tracked in DEFERRED.md):
192
192
-[x] Normal-approximation inference via `safe_inference()` (no-design fits; survey/bare-cluster fits use finite-df t inference via `df=df_survey` — library extension, not from the paper)
193
193
-[ ] Multilevel treatment (Case 3): open — DEFERRED
194
194
-[ ] Case 3 guards: open — DEFERRED (see the Case 3 caution above)
195
-
-[x] Validation (Case 1): `doubleml.DoubleMLDID` (2-period) + `DoubleMLDIDBinary` (staggered per-cell, end-to-end public fit) parity spikes, doubleml==0.11.4 pinned, golden literals in-repo; [x] Case 2 equation-level fixtures — SHIPPED (closed-form/oracle fixtures, derivative-identity checks, DR both directions, `DoubleMLDIDCSBinary` characterization spike — no parity oracle exists). CAVEAT: the paper's own §4 RCS simulation DGPs (pp. 17-21) are NOT replicated — the shipped recovery/coverage tests use a library-authored RCS design; replication is a tracked TODO.md row (needs the paper PDF); [] Case 3 fixtures — open
195
+
- [x] Validation (Case 1): `doubleml.DoubleMLDID` (2-period) + `DoubleMLDIDBinary` (staggered per-cell, end-to-end public fit) parity spikes, doubleml==0.11.4 pinned, golden literals in-repo; [x] Case 2 equation-level fixtures — SHIPPED (closed-form/oracle fixtures, derivative-identity checks, DR both directions, `DoubleMLDIDCSBinary` characterization spike — no parity oracle exists). [x] §4.2.2 kernel-design RCS DGP replicated (`tests/test_methodology_dml_did.py`, "Chang Sec. 4.2.2" section: DGP-shape pin, seed-pinned recovery at both paper Ns with a discriminating comparison against the design's own confounded unadjusted contrast, slow MC coverage; the library's native learners stand in for the paper's Gaussian-kernel first stages, and the fixture is correctly specified on BOTH nuisances, so it adds paper-DGP faithfulness, not plug-in-bias reproduction). The §4.2.1 ML-design DGP is extracted below but NOT replicable with the bundled unpenalized learners — narrowed TODO.md row (penalized propensity learner + ridge/sieve outcome learner required); [ ] Case 3 fixtures — open
196
196
197
197
---
198
198
@@ -244,6 +244,8 @@ items remain open, tracked in DEFERRED.md):
244
244
7.**Case 2 response in the Lasso penalty-loading recipe (appendix "The Lasso Penalty", p. 26):** the recipe literally prints "Let `y_i` denote `Y_i(1) - Y_i(0)` or `(T_i - λ̂_k)`" — the Case 2 response omits the `Y_i` multiplier (verified against the PDF page 2026-08-22), while the modified-Lasso objective it feeds (p. 12) minimizes over `(T_i - λ̂_k)·Y_i - q_i'β`. Loadings computed on a bare `(T_i - λ̂_k)` would produce a different first-stage estimator; implement loadings on the objective's response, `(T_i - λ̂_k)·Y_i`, pending comparison with the published article.
245
245
8.**`ȳ_k` population/denominator inconsistency (p. 26):**`ȳ_k = M^{-1} Σ_{i∈I_k^c} y_i` is defined as the mean over the FULL auxiliary sample (size `M`), while both loading sums run over the untreated subset `I_kz^c` with normalizer `1/M_k` (verified against the PDF page 2026-08-22). Whether the centering mean was intended over `I_k^c` or `I_kz^c` is not resolvable from the arXiv text; record the choice made at implementation time and compare with the published article.
246
246
247
+
9.**§4.2.1's printed-but-unused `β_0` (p. 19):** the RCS ML simulation section prints "where `β_0 = γ_0 + 0.5`" although `β_0` appears in none of its equations (`Y⁰(0) = 1 + ε_1` as printed), while §4.1.1 (p. 18) and §4.3.1 (p. 20) print `Y⁰(0) = X'β_0 + ε_1` in the parallel position — a suspected dropped `X'β_0` term; not resolvable from the arXiv text. Either reading leaves §4.2.1's parallel trends intact (the level term cancels in the recursive trend `Y⁰(1) = Y⁰(0) + 1 + ε_2`) and `ℓ_20` constant in X, so the library's §4.2.1 non-replicability conclusion (above) is robust to the ambiguity; check the published version.
248
+
247
249
**Contradiction — where `p̂_k` (and `λ̂_k`) is computed (algorithms vs proofs):**
248
250
- Both printed algorithm statements agree with each other and were verified against the PDF pages directly (2026-08-22): main-text Algorithm 1 (p. 11) prints `p̂_k = (1/n) Σ_{i∈I_k^c} D_i` and the appendix multilevel algorithm (p. 25, step 2) prints `p̂_w = (1/n) Σ_{i∈I_k^c} D_i` — in both cases a `1/n` normalizer (n = |I_k|) over the **auxiliary sample `I_k^c`** (size `M = N - n`), which is not a valid mean of anything as printed (typo 1 above).
249
251
- The proofs of Theorems 1-2, however, use `p̂_k = E_{n,k}[D]` (fold mean over the **main fold `I_k`**, pp. 31, 33, 37), i.e. `p̂_k - p_0 = E_{n,k}[D - p_0]`, which is what generates the `G_1p0 (D - p_0)` influence-correction term in the variance algebra.
@@ -258,6 +260,8 @@ items remain open, tracked in DEFERRED.md):
258
260
- Multilevel treatment: variance estimator and full theorem statements are not printed in the reviewed text (Theorems 2/4 cover Cases 1-2 only; multilevel results "can be proven using the same arguments", p. 13). The analogy `Ĝ_wp = -θ̃_w/p̂_w` is plausible but NOT stated in the paper.
259
261
- Theorem 2 proof for the repeated-cross-sections case concludes within the reviewed range (pp. 51-56); no gaps remain in the proofs of Theorems 1-4 as reviewed, but the appendix's stated Lasso-penalty constants `c`, `γ`, `B` have no defaults (p. 26).
260
262
- No clustering, no bootstrap, no missing-data handling, no aggregation across multilevel treatment arms.
263
+
- The §4 simulation designs violate the paper's own regularity conditions in two symmetric ways: the Gaussian kernel used for the kernel-estimation variants violates Assumption 3.3's compact-support requirement (see the Tuning Parameters kernel row), and BOTH §4.2 propensities are unbounded-Gaussian-index logistics, so the a.s. strict-overlap Assumption 3.2(a) (`Pr(κ ≤ g_0(X) ≤ 1−κ) = 1` for fixed `κ > 0`) holds for NO fixed κ — §4.2.1's tail mass outside [0.01, 0.99] is ≈0.06% (`X'γ_0 ~ N(0.685, 1.21²)`), §4.2.2's ≈2.1e-5 (immaterial at the simulated Ns, but a theory/design inconsistency worth recording).
261
264
- Empirical application (Section 5, pp. 21-22, repeated cross sections via Eqs. 2.2/3.2): Sequeira (2016) tariff/bribery data, N = 1084. Table 1 exact estimate (SE) pairs (verified against the PDF 2026-08-22): Sequeira (2016) TWFE -2.928 (0.944); Abadie kernel -7.986 (3.028); orthogonal θ̃ kernel -8.670 (3.643); Abadie Lasso -7.499 (2.746); orthogonal θ̃ Lasso -9.191 (4.854) — usable as a rough replication target only if the Sequeira data is obtainable.
- Simulation DGPs (Section 4, pp. 17-21) are fully specified and are the recommended validation fixtures: e.g. 4.1.1 repeated outcomes ML: `N ∈ {200, 500}`, `p ∈ {100, 300}`, `X ~ N(0, I_p)`, `γ_0 = (1, 1/2, 1/3, 1/4, 1/5, 0, ...)`, logistic PS, `β_0 = γ_0 + 0.5`, `θ_0 = 3`, errors N(0, 0.1) (variance/SD as printed "N(0,0.1)" — ambiguous as printed; the shipped §4.2.2 fixtures ADOPT the variance-0.1 reading, `σ = √0.1`, stated in the fixture comment); 4.3.1 multilevel: `W ∈ {0,1,2}`, shares (0.3, 0.3, 0.4), `θ_10 = 3`, `θ_20 = 6`.
266
+
- §4.2 repeated-cross-section DGPs (pp. 18-20, extracted 2026-08-29 for the PR-B2 fixtures). **§4.2.1 (ML estimation):** `N ∈ {200, 500}`, `p ∈ {100, 300}`, `X_i ~ N(0.3·1, I_p)` (note the 0.3 mean, unlike §4.1.1), same `γ_0`, logistic PS `P(D=1|X) = sigmoid(X'γ_0)`; as printed `Y⁰(0) = 1 + ε_1`, `Y⁰(1) = Y⁰(0) + 1 + ε_2`, `Y¹(1) = θ_0 + Y⁰(1) + ε_3`, `θ_0 = 3`, `T ~ Bernoulli(0.5)`, observed `Y = Y(0) + T(Y(1) − Y(0))` (see suspected-typo 9 on the printed-but-unused `β_0`). Under our native unpenalized learners this design is NOT estimable: at (N=500, p=100) the logit+linear stack is pure noise across ~20 review seeds (att ~ −23..+16, SE ~4-10; `solve_logit`'s EPV guard fires every fold at EPV ~1.4-1.6 vs threshold 10; FITTED out-of-fold clipping ~5-29% of rows across seeds, mean ~14% — the estimated-propensity leg of strict overlap; TRUE-propensity mass outside [0.01, 0.99] is only ≈0.06%, `X'γ_0 ~ N(0.685, 1.21²)`), and at (N=200, p=100) `outcome_learner="linear"` fails closed on control-fold rank deficiency even under an oracle propensity (ridge/sieve fit there). Reproduce by generating the equations above at (500, 100) with `DMLDiD(panel=False)` and native learners over any batch of ~20 seeds — the qualitative behavior is seed-robust; the ranges are deliberately qualitative, not seed-pinned. **§4.2.2 (kernel estimation):** `N ∈ {200, 500}`, `D ~ Bernoulli(0.5)`, scalar `X | D ~ N(D, 1)`, `Y⁰(0) = ε_1`, `Y⁰(1) = Y⁰(0) + X + ε_2`, `Y¹(1) = θ_0 + Y⁰(1) + ε_3`, `θ_0 = 3`, `T ~ Bernoulli(0.5)`, same observation rule. True propensity `= sigmoid(X − 1/2)` by Bayes' rule; true `ℓ_20(X) = λ_0(1−λ_0)·X = 0.25·X`. The trend `X + ε_2` with group-imbalanced X violates UNCONDITIONAL parallel trends by construction (the unadjusted 2×2 contrast converges to `θ_0 + 1 = 4`) — the design's covariate-adjustment point. Replicated verbatim in `tests/test_methodology_dml_did.py`.
263
267
- Finding to preserve for docs: in the repeated-cross-section simulations the orthogonal estimator appears well centered on the truth but visibly NOISIER than Abadie's plug-in at small N (Figures 9-14) — expect larger SEs. Orthogonality removes first-order nuisance-estimation bias (an asymptotic property under the Theorem 1 rates); it does NOT guarantee finite-sample unbiasedness, and histogram centering in simulations cannot establish it.
0 commit comments