feat(dml): DMLDiD survey-design and cluster support on both lanes (DML survey PR) - #796
Conversation
…pletes) (#797) Reverts the v1.12 bump from #791. Since v1.12, heavy review runs on Linux never return after the codex turn completes: the action passes the runner's stdout/stderr descriptors to the whole codex process tree, and a surviving descendant keeps them open, so the step idles until timeout-minutes kills the job with the verdict already written (openai/codex-action#150). The CLI-side mitigation (codex 0.150.x) did not resolve it for this repo's drop-sudo runs; the action-side fix (openai/codex-action#151) is unmerged. Both attempts of the PR #796 review died this way (attempt 1 killed at the 60-minute timeout, attempt 2 stuck identically after the analysis finished). Also adds a dependabot ignore for openai/codex-action 1.12 so the broken version is not re-proposed; drop the ignore once the fixed release ships.
…nippet Address PR #796 review feedback: - describe_target_parameter: split the declared-survey marker (survey_metadata) from bare-cluster provenance (df_inference) so an unweighted panel=False cluster= fit is described with the fixed cohort-row-mass RCS wording, not "SURVEY cohort masses" (its synthesized all-ones weights reduce exactly to the fixed row masses). - RCS dropped-observations warning keys its weighting note on survey_metadata for the same reason. - practitioner learner-sensitivity snippet: include the fit's actual cluster= (from cluster_name) whenever populated, so a survey_design-without-PSU + cluster= fit's refit keeps PSU-cohesive folds and clustered inference instead of silently dropping them. - docs survey matrix: DMLDiD Strata/PSU/FPC cell now states the support level — "Full (TSL; df = n_PSU - n_strata)". - regression tests for all of the above (TestClusterReportingProvenance).
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0 or P1 findings. Executive Summary
Methodology
No undocumented methodology mismatch, missing assumption guard, or incorrect SE formula found. Code QualityNo findings. New inference uses PerformanceNo findings. Survey variance and bootstrap reuse existing shared IF/PSU machinery. MaintainabilityNo findings. Survey resolution, aggregation, bootstrap, and variance logic delegate to established shared helpers. Tech Debt
These do not block approval. SecurityNo findings. No secrets were introduced, and foreign learner representations/errors are sanitized before persistence. Documentation/TestsNo findings. Coverage includes both estimator lanes, weight invariance, FPC, direct survey-variance checks, zero-weight cells, PSU folds, bootstrap, df propagation, and aggregation. Static syntax validation passed; runtime tests could not be executed because the review environment lacks pytest/numpy. |
…L survey PR)
survey_design= (pweight full-design TSL: weights/strata/PSU/FPC) on
fit() and cluster= on the constructor, on the panel AND declared-RCS
lanes. Declared designs weight the moment kernels (Hajek p-hat /
lambda-hat / theta, weighted G_2lambda) and pass sample_weight into the
nuisance learners (user learner objects must accept it by keyword -
TypeError up front otherwise); cross-fitting switches to PSU-cohesive
folds when the PSU is strictly coarser than the sampling unit, with the
explicit Chang I_kz^c fold-composition guard on the RCS lane, an
effective-fold-count reduction to n_psu (warned, provenance on
DMLDiDResults.effective_n_folds / summary / to_dict / repr) when
2 <= n_psu < n_folds, and a stratified-fold fallback only for the
single-PSU design (whose clustered variance is NaN either way).
Per-cell SEs are PSU-gated exactly like CallawaySantAnna
(_cluster_robust_se_from_per_gt_if; NaN propagates as the deliberate
unidentified-variance signal on a RETAINED cell); analytical inference
uses df = n_PSU - n_strata t-statistics; the survey multiplier
bootstrap and <2-PSU NaN contracts activate through the inherited CS
machinery, and aggregation kits now carry the producer's bootstrap
label so post-fit replays brand warnings with the fitting estimator
(the _BOOTSTRAP_LABEL ClassVar becomes a plain annotation across its
five hosts). Bare cluster= keeps the kernels unweighted - it drives
folds, variance, and df_inference only. zero_weight_mass joins the
DMLDiD skip vocabulary; universal-base reference cells gate on the
weighted cohort mass; aggregate('total') fails closed on
declared-survey fits (is_survey_fit kit gate).
A documented LIBRARY EXTENSION of Chang (2020)'s i.i.d. theory
(REGISTRY DMLDiD survey Notes; Theorem 2's coverage claim does not
carry over on the weighted-lambda RCS lane). Replicate-weight designs
fail closed (TODO.md Actionable row). No v4 ledger row: additive
kwargs, no behavior change on existing calls (precedent check
recorded). Downstream consumer surfaces synced: reporting helpers'
target-parameter description, MMM scale hint, practitioner
learner-sensitivity template, BusinessReport narratives,
DiagnosticReport design_effect roster comment, variance-conventions
inventory (CI-pinned doc/test pair), survey-theory rosters, guides
(llms/full/autonomous/practitioner incl. the signature-parity blocks),
choosing_estimator + practitioner_decision_tree + survey-roadmap +
migration-4.0 + v4-design prose, api/dml_did.rst, doc-deps.yaml
mappings.
Tests: new tests/test_survey_dml.py (62 tests - invariant battery,
per-cell SE == compute_survey_if_variance cross-checks, cluster-wiring
branches, fold cohesion/reduction/boundary pins, spy-learner
weight-threading, zero_weight_mass skips, survey bootstrap + replay
label + legacy-kit fallback, rejections, df threading/rendering,
aggregation masses incl. the >2^53 cohort-key regression);
weighted-slope tests in test_dr_scores.py (uniform == unweighted,
finite-difference identity, validation rejections on the public AND
internal entries); guide signature pins extended; existing DMLDiD/CS
suites pass unchanged (no-survey fits bit-identical).
…nippet Address PR #796 review feedback: - describe_target_parameter: split the declared-survey marker (survey_metadata) from bare-cluster provenance (df_inference) so an unweighted panel=False cluster= fit is described with the fixed cohort-row-mass RCS wording, not "SURVEY cohort masses" (its synthesized all-ones weights reduce exactly to the fixed row masses). - RCS dropped-observations warning keys its weighting note on survey_metadata for the same reason. - practitioner learner-sensitivity snippet: include the fit's actual cluster= (from cluster_name) whenever populated, so a survey_design-without-PSU + cluster= fit's refit keeps PSU-cohesive folds and clustered inference instead of silently dropping them. - docs survey matrix: DMLDiD Strata/PSU/FPC cell now states the support level — "Full (TSL; df = n_PSU - n_strata)". - regression tests for all of the above (TestClusterReportingProvenance).
…reached 1e-8 by 0.5%)
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0 or P1 findings. One P2 provenance issue remains. Executive Summary
Methodology
No undocumented methodology mismatch, missing assumption check, or incorrect variance/SE formula found. Code Quality
PerformanceNo findings. MaintainabilityNo findings. The implementation appropriately reuses shared survey, aggregation, bootstrap, and inference helpers. Tech Debt
SecurityNo findings. No secrets or unsafe persistence of user learner objects were identified. Documentation/TestsNo additional findings. Coverage includes both estimator lanes, weight invariance, direct survey-variance checks, zero-weight cells, clustered folds, bootstrap behavior, df propagation, and aggregation masses. |
Address the P2 from the PR #796 review rerun: resolve() rescales pweights to mean 1, but both DMLDiD survey_metadata recompute sites (the cluster-inject path and the unit-level recompute) passed the resolved weights as compute_survey_metadata's raw_weights, so sum_weights and weight_range reported normalized-scale values (scale-invariant fields - effective_n, design_effect, df - were unaffected; estimates/inference untouched). Fix: capture the raw per-obs design weights once at fit, collapse them per unit (groupby-first, all_units order) on the panel lane, and pass raw to both sites. Rename the misnamed unused _survey_weights_raw unpack (it holds resolved/normalized weights) to _survey_weights_resolved. Raw-scale metadata pins added for panel, RCS, and injected-cluster fits (the module fixtures' uniform(0.5, 2.0) weights are non-unit-scale). The same normalized-weights pattern pre-exists in CS (staggered.py:2412), the staggered DDD engine (_staggered_triple_diff_engine.py:299), and ContinuousDiD (continuous_did.py:1107) - tracked as a TODO.md Actionable Backlog row rather than expanding this PR across three more estimator families mid-review.
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0 or P1 findings. Executive Summary
Methodology
No undocumented methodology mismatch, missing assumption check, or incorrect variance/SE formula found. Code QualityNo findings. The prior normalized-weight metadata issue is fixed in PerformanceNo findings. MaintainabilityNo findings. The implementation reuses shared survey, aggregation, bootstrap, and safe-inference helpers. Tech Debt
SecurityNo findings. Foreign learner objects and their potentially sensitive error representations are not persisted. Documentation/TestsNo findings. Coverage includes both estimator lanes, raw-weight metadata, weighting invariants, direct variance-kernel checks, PSU folds, zero-weight cells, bootstrap behavior, finite-df inference, and aggregation masses. |
Summary
DMLDiDgains full survey-design and cluster support on BOTH lanes:survey_design=onfit()(pweight-only full-design TSL — weights/strata/PSU/FPC) andcluster=on the constructor (coarser-than-unit clustering).p̂/λ̂/θ̂, weightedĜ₂λ(aweights=extension of the_dr_scoresRCS slope family, bit-identical when absent),sample_weightinto both nuisance learners (user learner objects must accept it by keyword — up-frontTypeErrorotherwise), and weighted influence-function payloads.assign_folds(cluster_ids=)), with an explicit ChangI_kz^cfold-composition guard on the RCS lane, an effective-fold-count reduction ton_psu(warned;effective_n_foldsprovenance on results/summary/to_dict/repr) when2 ≤ n_psu < n_folds, and a stratified-fold fallback only for single-PSU designs (whose clustered variance is NaN either way)._cluster_robust_se_from_per_gt_if; NaN propagates as the deliberate unidentified-variance signal on a RETAINED cell); analytical inference usesdf = n_PSU − n_stratat-statistics; the survey multiplier bootstrap and <2-PSU NaN contracts activate via the inherited CS mixins._BOOTSTRAP_LABELClassVar → plain annotation across its five hosts).cluster=keeps the moment kernels unweighted (folds/variance/df_inferenceonly);zero_weight_massjoins the DMLDiD skip vocabulary; universal-base reference cells gate on the weighted cohort mass;aggregate('total')fails closed on declared-survey fits.Methodology references (required if estimator / math changes)
docs/methodology/REGISTRY.md"DMLDiD" survey Notes;docs/methodology/survey-theory.md.Ĝ₂λplug-in (Theorem 2's coverage claim does not carry over on the weighted-λ RCS lane), PSU-cohesive folds replacing the D/D×T fold-stratification MECHANISM (Chang's fold-composition REQUIREMENT preserved by an explicit guard), anddf = n_PSU − n_stratat-inference. No external oracle exists (DoubleML has no survey support; Rdid::is survey-naive) — validation is the library's survey invariant battery plus directcompute_survey_if_variancekernel cross-checks.Validation
tests/test_survey_dml.py(new, 62 tests: invariant battery — uniform-weights ≡ unweighted, scale invariance, FPC-shrinks-SE; per-cell SE ==compute_survey_if_variance; cluster-wiring branches; fold cohesion/reduction/boundary pins; spy-learner weight-threading;zero_weight_mass; survey bootstrap + replay-label + legacy-kit fallback; rejections; df threading/rendering; aggregation masses incl. a >2⁵³ cohort-key regression),tests/test_dr_scores.py(weighted-slope family),tests/test_dml_did.py(cluster mutation-roster case),tests/test_guides.py(fit-signature pin),tests/test_variance_conventions.py(inventory rows, CI-pinned with the doc twin).Security / privacy