Skip to content

feat(aggregation): EfficientDiD post-fit aggregate() replays the bootstrap on bootstrapped fits (M-023) - #783

Merged
igerber merged 3 commits into
mainfrom
feat/edid-bootstrap-replay
Aug 20, 2026
Merged

feat(aggregation): EfficientDiD post-fit aggregate() replays the bootstrap on bootstrapped fits (M-023)#783
igerber merged 3 commits into
mainfrom
feat/edid-bootstrap-replay

Conversation

@igerber

@igerber igerber commented Aug 20, 2026

Copy link
Copy Markdown
Owner

Summary

  • Post-fit aggregate('event_study') / aggregate('group') now work on bootstrapped EfficientDiD fits by REPLAYING the fit-time multiplier bootstrap from a kit-retained BootstrapReplaySpec (the CS mechanism from feat(aggregation): CS post-fit aggregate() replays the multiplier bootstrap on bootstrapped fits (M-020) #780, transplanted): the RNG state is snapshotted at weight-stream construction and carried BY VALUE, so percentile se/CI/t match a fit-time fit(aggregate=...) aggregation to BLAS reassociation (~1 ULP, assert_allclose — never bit-identity; percentile p-values are count statistics compared at 2/n_bootstrap). seed=None fits replay, pickles carry the state, and post-fit set_params/attribute mutation cannot alter a replay.
  • Branch-aware backend stamp with a fail-closed gate: plain/cluster/unstratified-survey fits stamp the live Rust/NumPy weight backend (the two generators produce different draws from the same RNG state); stratified-survey, census-FPC, and single-PSU degenerate fits are provably backend-independent and stamp "portable". Replays under a mismatched backend and pre-replay legacy pickles fail closed with explicit remedies. Replays re-emit the fit-time bootstrap warnings; the 'simple'/'total' relays stay silent and unchanged.
  • The ES/group percentile-override appliers moved verbatim from staggered_bootstrap.py to diff_diff.bootstrap_utils (a structural Protocol for the container contract) and are now shared by the CS and EfficientDiD fit paths and replays — one implementation, no twin drift. The move was gated by a pre/post oracle on the unmodified tree (bit-identical) plus a committed t == effect/se regression pin.
  • Fixed (pre-existing): the EDiD bootstrap ES prep keyed horizons by raw t - g while the analytical aggregator buckets by int(t - g), so on fractional-period panels a strict sub-aggregate's percentile inference was attached to the pooled analytical row (the balance_e anchor filter had the same hole). All three keying sites now use the analytical expression — a no-op on integer-period panels. Companions: n_groups counts DISTINCT cohorts per bucket (was a cell count that over-counted on fractional panels; this also moves the ES n column on analytical fractional fits), and every aggregation that truncation-buckets a fractional horizon (fit-time, post-fit, replay, hausman_pretest) now emits a UserWarning pointing at the new REGISTRY truncation Note.
  • Ripples: practitioner_next_steps advises the post-fit route on bootstrapped EDiD fits; to_dataframe/absent-surface hints trued up; DiagnosticReport needs no change (EDiD never routes through the derived-ES resolver — its PT check is the Hausman pretest).

Methodology references (required if estimator / math changes)

  • Method name(s): EfficientDiD (Chen, Sant'Anna & Xie 2025) — multiplier-bootstrap inference and event-study/group aggregation; no estimator, EIF, or identification change. The replay re-runs the existing engine with the fit-captured RNG state.
  • Paper / source link(s): Chen, Sant'Anna & Xie (2025), per docs/methodology/REGISTRY.md EfficientDiD section; bootstrap pattern follows Callaway & Sant'Anna (2021) / the R did package as already documented.
  • Any intentional deviations from the source (and why): the fractional-period int(t - g) truncation-bucketing convention (pre-existing on the analytical path; the bootstrap prep is now aligned to it) is recorded as a new REGISTRY - **Note:** covering the double-width bucket 0, the PT-Post reference collision, and the cell-mass within-bucket weighting, with a runtime UserWarning. The M-023/M-120/M-020 ledger notes were amended in the same diff; the cross-estimator survey_metadata.df_survey provenance residual is filed as a new TODO row.

Validation

  • Tests added/updated: tests/test_aggregate_contract.py (new TestEfficientBootstrapReplay, TestEfficientBootstrapReplayDesigns, TestEfficientFractionalPeriods — parity vs the NATIVE fit-time surface across plain/cluster/weights-only-survey/stratified/FPC/census-FPC/single-PSU/anticipation/alpha/mammen/covariate/PT-Post/balance_e/fractional designs, fail-closed backend + legacy gates, warning re-emission, seedless idempotence, pickle round-trip, mutation immunity, spec-carrying retention), tests/test_efficient_did.py (committed t == effect/se recompute pin; comment trueups), tests/test_practitioner.py (post-fit advice pin).
  • Backtest / simulation / notebook evidence (if applicable): pre/post helper-relocation oracle on the unmodified tree — fit-time ES/group/gt t_stat payloads bit-identical. Fractional-period behavior verified live (prep keys = analytical int buckets; off-grid onsets carry percentile inference post-fix; replay parity at 1e-13 on the fractional fixture). Tutorial 15 markdown-only edits (the notebook is committed output-free; no execution step by design).
  • Suites run: aggregate-contract (406), EDiD (203/7/51), CS bootstrap subsets + staggered aggregation, practitioner (115), diagnostic/business report (365), bootstrap chunking/utils (62/35), guides + doc snippets (202), v4 matrix + naming guard (288), survey phase3/phase6 subsets; black/ruff clean; mypy zero errors.

Security / privacy

  • Confirm no secrets/PII in this PR: Yes

…strap on bootstrapped fits (M-023)

Transplant the CS BootstrapReplaySpec mechanism (#780) onto EfficientDiD:
post-fit aggregate('event_study'/'group') on a bootstrapped fit now REPLAYS
the fit-time multiplier bootstrap from the kit-retained RNG state instead of
raising NotImplementedError. Percentile se/CI/t match a fit-time aggregation
to ~1 ULP (BLAS reassociation; p-values compared at 2/n_bootstrap); seed=None
fits replay; pickles carry the state; post-fit set_params/mutation cannot
alter a replay. The spec stamps the weight backend at capture (portable for
stratified-survey / census-FPC / single-PSU degenerate branches); replays
under a different Rust/NumPy backend and pre-replay legacy pickles fail
closed. Replays re-emit the fit-time bootstrap warnings; the simple/total
relays stay silent.

The ES/group percentile-override appliers moved verbatim to
diff_diff.bootstrap_utils (shared by the CS and EDiD fit paths and replays;
oracle-verified bit-inert, plus a committed t == effect/se regression pin).

Fixed: the EDiD bootstrap ES prep keyed horizons by raw t - g while the
analytical aggregator buckets by int(t - g) — on fractional-period panels a
strict sub-aggregate's percentile inference was attached to the pooled row
(balance_e anchor filter included). All three keying sites now use the
analytical expression (a no-op on integer panels); n_groups counts DISTINCT
cohorts per bucket; fractional truncation-bucketing now emits a UserWarning,
and a new REGISTRY truncation Note documents the full convention (double-width
bucket 0, the PT-Post reference collision, cell-mass weighting).

Docs/ledger: M-023/M-120/M-020 notes + code_refs, REGISTRY EDiD Note (b)/(c)
+ truncation Note + weight-backend/dCDH-M-026 trueups, migration-4.0,
v4-design, all three llms guides, tutorial 15 prose, practitioner advice,
CHANGELOG (Added + Fixed). TODO row retired; new df_survey-provenance
cross-estimator row added.

Tests: TestEfficientBootstrapReplay + Designs + TestEfficientFractionalPeriods
(parity vs the native fit-time surface across plain/cluster/survey/stratified/
FPC/census-FPC/single-PSU/anticipation/alpha/mammen/covariate/PT-Post/
fractional designs; fail-closed gates; warning re-emission; retention with a
spec-carrying kit), practitioner + t-recompute pins.
@github-actions

Copy link
Copy Markdown

Overall assessment

Blocker — one P0 inference bug.

Executive summary

  • Bootstrap replay otherwise follows Chen–Sant’Anna–Xie Theorem 4.1 and the Registry contract.
  • P0: census-FPC zero-weight draws can leak tiny positive SEs and finite inference through floating-point roundoff.
  • Fractional-period bucketing is a documented Registry deviation and therefore P3 informational.
  • Survey-df provenance debt is tracked in TODO.md.

Methodology

  • [Newly identified] Severity: P0 — Degenerate census-FPC inference can appear valid.
    Impact: Census FPC produces all-zero multiplier blocks, hence a constant bootstrap distribution. Computing np.std on constant nonzero levels can yield a tiny positive value through mean-subtraction roundoff, bypassing se <= 0; replay can then publish a huge t-statistic, finite p-value, and point CI instead of all-NaN inference. The new test checks only replay parity, so matching invalid fit-time output passes. See diff_diff/bootstrap_chunking.py:L205-L230, diff_diff/efficient_did_bootstrap.py:L251-L308, diff_diff/bootstrap_utils.py:L369-L384, and tests/test_aggregate_contract.py:L2657-L2672.
    Concrete fix: Detect exact constant bootstrap distributions before np.std—in both scalar and batch helpers—and return NaN for SE, p-value, and CI. Alternatively, early-return the all-NaN bootstrap container whenever the survey generator is deterministically zero.

  • Severity: P3 — Documented fractional-period deviation.
    Impact: int(t-g) differs from the paper’s exact-horizon ES equations and can pool pre/post horizons, but this is explicitly documented with a warning in docs/methodology/REGISTRY.md:L1641; it is not a defect under the review rules.
    Concrete fix: None required.

Code Quality

No additional findings.

Performance

  • Severity: P3 — Informational.
    Impact: Each replay costs O(n_bootstrap × n_units × n_gt). This is explicitly documented and intentional.
    Concrete fix: None required.

Maintainability

No findings.

Tech Debt

  • Severity: P3 — Tracked in TODO.md.
    Impact: Survey replay containers retain analytical scalar df_survey provenance beside percentile inference. Per-row inference fields are correctly cleared. See TODO.md:L39.
    Concrete fix: Resolve through the tracked cross-estimator follow-up.

Security

No findings or apparent secrets.

Documentation/Tests

  • Severity: P2 — Group replay CI parity is untested.
    Impact: The parity helper checks ATT, SE, t, and p, but not group confidence intervals. See tests/test_aggregate_contract.py:L2453-L2463.
    Concrete fix: Compare both group CI bounds against fit_time.group_effects[g]["conf_int"].

Validation was limited to static inspection and AST parsing; the environment lacks pytest and NumPy.

Path to Approval

  1. Add an exact constant-distribution guard to scalar and batch bootstrap-stat helpers so all inference fields become NaN.
  2. Strengthen the census-FPC test to assert NaN for SE, t, p, and both CI bounds on fit-time and replayed event-study/group surfaces.

… group CI replay parity

CI review P0 (verified by execution): census-FPC zero-weight draws leave
every multiplier-bootstrap replicate at the original effect, and np.std of
a constant non-zero level can return a tiny positive value from
mean-subtraction roundoff (~2.8e-17 at level 0.1), slipping past the
se <= 0 guard and publishing an astronomically large finite t with a
degenerate point CI. The shared percentile-statistic helpers (scalar and
batch, consumed by every multiplier-bootstrap engine) now detect
max(draws) == min(draws) and return the full NaN inference tuple with the
existing zero-SE RuntimeWarning. Exact check, no tolerance: genuinely
varying draws are unaffected.

Tests: direct scalar/batch guard pins (constant-nonzero NaNs, near-constant
unaffected, healthy batch column intact); the census-FPC replay arm now
asserts full-NaN se/t/p/CI on BOTH the fit-time and replayed ES + group
surfaces; group replay parity now also compares both CI bounds (CI review
P2). REGISTRY Survey-Aware Bootstrap Note + CHANGELOG Fixed entry added.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 0ad910d8fad959cade9dc6d3f746fc77cb5cd6fd


Overall assessment

Looks good — no unmitigated P0/P1 findings.

Executive summary

  • The previous census-FPC P0 is resolved with scalar and batch constant-distribution guards.
  • Tests now verify complete NaN inference and group CI replay parity.
  • EfficientDiD replay preserves the existing Registry-documented multiplier-bootstrap methodology.
  • Fractional-period bucketing is an explicitly documented deviation.
  • Survey-df provenance debt remains tracked in TODO.md.

Methodology

  • Severity: P3 — Documented fractional-period deviation.
    Impact: int(t-g) can pool exact horizons, including pre/post values, but the behavior and warning are explicitly documented in docs/methodology/REGISTRY.md:L1641.
    Concrete fix: None required.

The replay is consistent with the bootstrap approach documented in docs/methodology/REGISTRY.md:L1545-L1554 and fails closed on incompatible backends in diff_diff/efficient_did_results.py:L346-L400.

Code Quality

No findings. The previous P0 is addressed in both helpers at diff_diff/bootstrap_utils.py:L369-L385 and diff_diff/bootstrap_utils.py:L465-L502.

Performance

  • Severity: P3 — Informational.
    Impact: Each replay costs O(n_bootstrap × n_units × n_gt) with no memoization, as documented in diff_diff/efficient_did_results.py:L381-L400.
    Concrete fix: None required.

Maintainability

No findings. Shared percentile-override helpers remove CS/EDiD implementation duplication.

Tech Debt

  • Severity: P3 — Tracked in TODO.md.
    Impact: Survey replay containers retain scalar analytical df_survey provenance beside percentile inference; row-level df remains NaN. See TODO.md:L39.
    Concrete fix: Resolve through the tracked cross-estimator follow-up.

Security

No findings or apparent secrets.

Documentation/Tests

No findings. Group CI parity is covered at tests/test_aggregate_contract.py:L2453-L2469; census-FPC full-NaN inference is covered at tests/test_aggregate_contract.py:L2663-L2698.

Focused tests could not run because pytest and NumPy are unavailable. AST parsing of the changed Python files passed.

@igerber igerber added the ready-for-ci Triggers CI test workflows label Aug 20, 2026
…ate rows (CI OpenBLAS failure)

CI failed on ubuntu/windows/pure-python (macOS green) in the census-FPC
replay arm: the zero-weight fused GEMM leaves every replicate row of the
per-cell draw matrix IDENTICAL and exactly at the original effects, but the
SECOND-STAGE re-aggregation matvecs (EDiD overall/ES/group; CS group) can
reduce different row positions in different BLAS orders - identical rows
in, rows differing by ~1 ULP out - producing a not-exactly-constant derived
distribution whose ~1e-17 std slips past the zero/constant guards (observed
se=1.37e-17 on ubuntu; Accelerate happens to reduce all rows identically,
which is why macOS passed).

Fix in both engines: when the replicate rows are identical (zero
information), compute each second-stage reduction ONCE and broadcast, so
the derived distributions are exactly constant on every platform and the
existing zero/constant NaN guards fire deterministically. The fused-GEMM
cell/overall/ES columns are exact (0*x sums are order-independent) and need
no handling; CS's exposure was its group matvec (the same class,
pre-existing since the census-FPC path shipped).

No behavior change off the degenerate path: the broadcast branch requires
bitwise-identical replicate rows, unreachable with any varying draw.
@github-actions

Copy link
Copy Markdown

🔁 AI review rerun (requested by @igerber)

Head SHA: 9a522dd89d743e019608d35e016718ae014e3cba


Overall assessment

Looks good — no unmitigated P0/P1 findings.

Executive summary

  • The previous census-FPC P0 is fully resolved for scalar, batch, and second-stage aggregate inference.
  • EfficientDiD replay uses the fit-captured multiplier-bootstrap state and fails closed for incompatible backends.
  • Replay methodology matches Chen–Sant’Anna–Xie’s permitted multiplier bootstrap and the Registry contract.
  • Fractional-period bucketing is an explicitly documented deviation with runtime warnings.
  • Survey-df provenance debt remains tracked in TODO.md.

Methodology

  • Severity: P3 — Documented bootstrap implementation choice.
    Impact: EfficientDiD event-study/group replay regenerates the existing multiplier bootstrap rather than changing the estimator or variance target. The paper permits multiplier bootstrap inference (docs/methodology/papers/chen-santanna-xie-2025-review.md:L136-L140), and the implementation choice is documented in docs/methodology/REGISTRY.md:L1545-L1554. Replay and backend guards are implemented at diff_diff/efficient_did_results.py:L347-L444.
    Concrete fix: None required.

  • Severity: P3 — Documented fractional-period deviation.
    Impact: int(t-g) can pool exact horizons, including pre/post values, but the convention, weighting, reference collision, and warning are explicitly documented in docs/methodology/REGISTRY.md:L1641.
    Concrete fix: None required.

Code Quality

No findings. The prior P0 is addressed by:

  • Full-NaN constant-distribution guards in diff_diff/bootstrap_utils.py:L369-L385 and L465-L502.
  • Exact broadcasting of degenerate second-stage reductions in diff_diff/efficient_did_bootstrap.py:L310-L379 and diff_diff/staggered_bootstrap.py:L599-L631.
  • Shared inference appliers using safe_inference_batch() at diff_diff/bootstrap_utils.py:L996-L1057.

Performance

  • Severity: P3 — Informational.
    Impact: Each replay costs O(n_bootstrap × n_units × n_gt) without memoization, as documented in diff_diff/efficient_did_results.py:L381-L387 and the Registry.
    Concrete fix: None required.

Maintainability

No findings. Moving both percentile-override paths into shared helpers removes CS/EDiD implementation duplication.

Tech Debt

  • Severity: P3 — Tracked in TODO.md.
    Impact: Survey replay containers retain the scalar analytical df_survey metadata beside percentile inference, while row-level df remains correctly NaN. See TODO.md:L39.
    Concrete fix: Resolve through the tracked cross-estimator follow-up.

Security

No findings or apparent secrets. Replay retention uses factorized design arrays rather than raw unit labels.

Documentation/Tests

No findings. Census-FPC full-NaN behavior is covered at tests/test_aggregate_contract.py:L2663-L2698; group replay checks SE, t, p-value, and CI at tests/test_aggregate_contract.py:L2453-L2469.

Runtime tests could not be executed because NumPy, pandas, and pytest are unavailable. AST parsing of the 15 affected Python/test files passed.

@igerber
igerber merged commit 4acd102 into main Aug 20, 2026
33 checks passed
@igerber
igerber deleted the feat/edid-bootstrap-replay branch August 20, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-ci Triggers CI test workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant