Skip to content

Commit 60b0e0c

Browse files
igerberclaude
andcommitted
Address PR #355 R6 P3: refresh three stale REGISTRY bullets post-PR #352
Three places in REGISTRY.md still described the pre-PR-#352 behavior where SDID survey bootstrap raised NotImplementedError, contradicting the new support matrix and survey-composition note landed earlier in this PR: - §SyntheticDiD bootstrap bullet (paper-faithful refit text): the "Composed with any survey design ... raises NotImplementedError" trailer is updated to describe weighted-FW dispatch under the survey + bootstrap composition note. - §SyntheticDiD requirements checklist bullet for "Bootstrap: paper- faithful Algorithm 2 step 2": "Survey designs raise NotImplementedError" trailer is updated to describe the hybrid pairs-bootstrap + Rao-Wu rescaling composition. - §Rao-Wu Rescaled Bootstrap "intentionally excluded" note: rewritten to add SDID to the list via its hybrid composition, with the precise distinction from standalone Rao-Wu (SunAbraham / TROP) and a pointer to §SyntheticDiD for the objective form and argmin-set caveat. Remaining NotImplementedError mentions in REGISTRY.md §SyntheticDiD (L1554-L1555) correctly describe the placebo / jackknife + strata/PSU/ FPC methodology gap that is out of scope for this PR and tracked in TODO.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 88ad1cc commit 60b0e0c

1 file changed

Lines changed: 11 additions & 9 deletions

File tree

docs/methodology/REGISTRY.md

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,7 @@ Convergence criterion: stop when objective decrease < min_decrease² (default mi
15051505

15061506
R-parity rationale: `synthdid_estimate()` (synthdid.R) stores `update.omega = TRUE` in `attr(estimate, "opts")`, and `vcov.R::bootstrap_sample` rebinds those `opts` inside its `do.call` back into `synthdid_estimate`, so the renormalized ω passed via `weights$omega` is used as Frank-Wolfe initialization (the `sum_normalize` helper in R's source explicitly says so). The Python path threads the same warm-start via `compute_sdid_unit_weights(..., init_weights=...)` and `compute_time_weights(..., init_weights=...)`. The FW objective is strictly convex on the simplex (quadratic loss + ζ² ridge on simplex), so warm- and cold-start converge to the same global minimum given enough iterations; warm-start matters in practice because the 100-iter first pass then sparsification is path-dependent on draws where the pre-sparsify budget is tight. Cross-language SE parity at bit tolerance is not claimed — different BLAS / RNG paths — but the procedure matches R's default bootstrap shape at the algorithm level, and Python-only bit-identity on non-survey data is asserted via `TestScaleEquivariance::test_baseline_parity_small_scale[bootstrap]` at `rel=1e-14`.
15071507

1508-
Expected wall-clock ~5–30× slower per fit than placebo (panel-size dependent; Frank-Wolfe second-pass can hit its 10K-iter cap on larger panels; warm-start plumbing closes the gap vs cold-start, which would be closer to 10–30× on these DGPs). Per-draw Frank-Wolfe non-convergence UserWarnings are suppressed inside the loop and aggregated into a single summary warning emitted after the loop when the share of valid bootstrap draws with any non-convergence event (counted once per draw — each draw runs Frank-Wolfe once for ω and once for λ, and any of those calls firing a non-convergence warning trips the draw) exceeds 5% of `n_successful`. Composed with any survey design (including pweight-only) this path raises `NotImplementedError` in the current release — see the survey-regression Note below for scope and the deferred-composition sketch.
1508+
Expected wall-clock ~5–30× slower per fit than placebo (panel-size dependent; Frank-Wolfe second-pass can hit its 10K-iter cap on larger panels; warm-start plumbing closes the gap vs cold-start, which would be closer to 10–30× on these DGPs). Per-draw Frank-Wolfe non-convergence UserWarnings are suppressed inside the loop and aggregated into a single summary warning emitted after the loop when the share of valid bootstrap draws with any non-convergence event (counted once per draw — each draw runs Frank-Wolfe once for ω and once for λ, and any of those calls firing a non-convergence warning trips the draw) exceeds 5% of `n_successful`. Composed with survey designs (pweight-only OR strata/PSU/FPC) this path uses the weighted Frank-Wolfe kernel and the per-draw dispatch described in the "Note (survey + bootstrap composition)" below.
15091509

15101510
- Alternative: Jackknife variance (matching R's `synthdid::vcov(method="jackknife")`)
15111511
Implements Algorithm 3 from Arkhangelsky et al. (2021):
@@ -1621,7 +1621,7 @@ Convergence criterion: stop when objective decrease < min_decrease² (default mi
16211621
- [x] Sparsification: v[v <= max(v)/4] = 0; v = v/sum(v)
16221622
- [x] Placebo SE formula: sqrt((r-1)/r) * sd(placebo_estimates)
16231623
- [x] Placebo SE: re-estimates omega and lambda per replication (matching R's update.omega=TRUE, update.lambda=TRUE)
1624-
- [x] Bootstrap: paper-faithful Algorithm 2 step 2 — re-estimates ω̂_b and λ̂_b per draw via two-pass sparsified Frank-Wolfe on the resampled panel using the fit-time normalized-scale zeta. Matches R's default `synthdid::vcov(method="bootstrap")` (which rebinds `attr(estimate, "opts")` so the renormalized ω serves only as Frank-Wolfe initialization). Survey designs raise `NotImplementedError`; Rao-Wu + refit composition is tracked in TODO.md and sketched in the deferred-composition Note above.
1624+
- [x] Bootstrap: paper-faithful Algorithm 2 step 2 — re-estimates ω̂_b and λ̂_b per draw via two-pass sparsified Frank-Wolfe on the resampled panel using the fit-time normalized-scale zeta. Matches R's default `synthdid::vcov(method="bootstrap")` (which rebinds `attr(estimate, "opts")` so the renormalized ω serves only as Frank-Wolfe initialization). Survey designs (pweight-only AND strata/PSU/FPC) are supported via the weighted-FW + hybrid pairs-bootstrap + Rao-Wu rescaling composition described in the "Note (survey + bootstrap composition)" above (PR #352).
16251625
- [x] Jackknife SE: fixed weights, LOO all units, formula `sqrt((n-1)/n * sum((u-ubar)^2))`
16261626
- [x] Jackknife: NaN SE for single treated or single nonzero-weight control
16271627
- [x] Jackknife: analytical p-value (not empirical)
@@ -2930,13 +2930,15 @@ ContinuousDiD, EfficientDiD):
29302930
Rescaled weight: `w*_i = w_i * (n_h / m_h) * r_hi` where `r_hi` = count of PSU *i* drawn.
29312931
- **Note:** FPC enters through the resample size `m_h`, not as a post-hoc scaling factor.
29322932
When `f_h >= 1` (census stratum), observations keep original weights (zero variance).
2933-
- **Note:** SyntheticDiD is intentionally excluded from this list. Paper-faithful
2934-
refit bootstrap (Arkhangelsky et al. 2021 Algorithm 2 step 2) re-estimates ω̂
2935-
and λ̂ via Frank-Wolfe on each draw; composing that with Rao-Wu rescaled
2936-
weights requires a weighted-FW derivation that is not yet implemented (sketch
2937-
in §SyntheticDiD survey-regression Note; tracked in TODO.md). The previous
2938-
SyntheticDiD Rao-Wu path composed fixed-ω with rescaled weights, which was
2939-
not paper-faithful and was removed.
2933+
- **Note:** SyntheticDiD joins this list via a **hybrid** pairs-bootstrap +
2934+
Rao-Wu rescaling (PR #352). Unlike SunAbraham / TROP, which use standalone
2935+
Rao-Wu (resample PSUs within strata and rescale weights), SDID first
2936+
performs unit-level pairs-bootstrap (`boot_idx = rng.choice(n_total)`) and
2937+
then slices Rao-Wu rescaled weights over the resampled units, passing them
2938+
into a **weighted Frank-Wolfe** re-estimation of ω̂ and λ̂ per draw. The
2939+
full objective and argmin-set caveat live in §SyntheticDiD "Note (survey +
2940+
bootstrap composition)"; the previous fixed-ω-and-rescaled-weights path
2941+
was removed in PR #351 and replaced with this weighted-FW derivation.
29402942
- **Note:** Bootstrap paths support all three `lonely_psu` modes: `"remove"`, `"certainty"`,
29412943
and `"adjust"`. For `"adjust"`, singleton PSUs from different strata are pooled into a
29422944
combined pseudo-stratum and weights are generated for the pooled group. This is the

0 commit comments

Comments
 (0)