Skip to content

Commit a520c52

Browse files
igerberclaude
andcommitted
Round-11 CI P3: branch sentinel-mass error wording on replicate vs TSL
The shared terminal-missingness ValueError in `_survey_se_from_group_if` said "Analytical survey SE" and attributed the failure to "within- group-varying PSU" — misleading for replicate ATT users, since replicate designs carry no PSU structure and still hit the same guard. Branch the wording on `resolved.uses_replicate_variance`: - Replicate ATT: "Rao-Wu replicate-weight ATT variance" + note that replicate ATT unconditionally uses the cell-period allocator per the Class A contract, PSU is not involved. - Binder TSL: keeps the original "Analytical Binder TSL survey SE" wording + the within-group-varying-PSU trigger explanation. Shared mechanic and pre-processing workaround kept the same. The regression tests still match on "no positive-weight observations" which appears in both branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ae58385 commit a520c52

1 file changed

Lines changed: 37 additions & 13 deletions

File tree

diff_diff/chaisemartin_dhaultfoeuille.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5958,20 +5958,44 @@ def _survey_se_from_group_if(
59585958
if leaked.size > 0 and bool(
59595959
np.any(np.abs(leaked) > 1e-12)
59605960
):
5961+
# Branch the wording on replicate-vs-TSL so replicate
5962+
# ATT users debugging this error are not misdirected
5963+
# to "within-group-varying PSU" (replicate designs
5964+
# have no PSU structure). Core mechanics and the
5965+
# pre-processing workaround are shared.
5966+
is_replicate = bool(
5967+
getattr(resolved, "uses_replicate_variance", False)
5968+
)
5969+
path_name = (
5970+
"Rao-Wu replicate-weight ATT variance"
5971+
if is_replicate
5972+
else "Analytical Binder TSL survey SE"
5973+
)
5974+
trigger_detail = (
5975+
"terminal missingness on a replicate-weight "
5976+
"design (replicate ATT unconditionally uses the "
5977+
"cell-period allocator per the Class A contract; "
5978+
"PSU structure is not involved)"
5979+
if is_replicate
5980+
else "terminal missingness combined with within-"
5981+
"group-varying PSU (the Binder TSL cell-period "
5982+
"allocator cannot allocate leaked mass to any "
5983+
"observation)"
5984+
)
59615985
raise ValueError(
5962-
"Analytical survey SE cannot be computed on this "
5963-
"panel: cohort-recentered IF mass landed on (g, t) "
5964-
"cells with no positive-weight observations "
5965-
"(W_{g, t} = 0). This typically occurs when "
5966-
"terminal missingness combines with within-group-"
5967-
"varying PSU: _cohort_recenter_per_period subtracts "
5968-
"column means across the full period grid, so a "
5969-
"group with no observation at period t acquires "
5970-
"non-zero centered mass there, which the cell-level "
5971-
"analytical expansion cannot allocate to any "
5972-
"observation. Pre-process the panel to remove "
5973-
"terminal missingness (drop late-exit groups or "
5974-
"trim to a balanced sub-panel) before fitting."
5986+
f"{path_name} cannot be computed on this panel: "
5987+
"cohort-recentered IF mass landed on (g, t) cells "
5988+
"with no positive-weight observations "
5989+
f"(W_{{g, t}} = 0). This typically occurs with "
5990+
f"{trigger_detail}: _cohort_recenter_per_period "
5991+
"subtracts cohort column means across the full "
5992+
"period grid, so a group with no observation at "
5993+
"period t acquires non-zero centered mass there, "
5994+
"which the cell-period allocator cannot allocate "
5995+
"to any observation. Pre-process the panel to "
5996+
"remove terminal missingness (drop late-exit "
5997+
"groups or trim to a balanced sub-panel) before "
5998+
"fitting."
59755999
)
59766000
# Lookup U_centered_per_period and W_cell per row.
59776001
u_obs_cell = np.zeros(w_eff.shape[0], dtype=np.float64)

0 commit comments

Comments
 (0)