Skip to content

Commit 32f2b2b

Browse files
igerberclaude
andcommitted
Address PR #208 review: cap SyntheticDiD treatment_fraction to fix zero-power bug
Add _sdid_dgp_kwargs that caps treatment_fraction at 0.4 so the placebo variance method has enough pseudo-controls (n_control > n_treated). Add regression test for the default SyntheticDiD path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d79b042 commit 32f2b2b

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

diff_diff/power.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ def _factor_dgp_kwargs(
109109
)
110110

111111

112+
def _sdid_dgp_kwargs(
113+
n_units: int,
114+
n_periods: int,
115+
treatment_effect: float,
116+
treatment_fraction: float,
117+
treatment_period: int,
118+
sigma: float,
119+
) -> Dict[str, Any]:
120+
# SyntheticDiD placebo variance requires n_control > n_treated;
121+
# cap at 40% treated to ensure adequate pseudo-controls.
122+
safe_fraction = min(treatment_fraction, 0.4)
123+
return _factor_dgp_kwargs(
124+
n_units=n_units,
125+
n_periods=n_periods,
126+
treatment_effect=treatment_effect,
127+
treatment_fraction=safe_fraction,
128+
treatment_period=treatment_period,
129+
sigma=sigma,
130+
)
131+
132+
112133
def _ddd_dgp_kwargs(
113134
n_units: int,
114135
n_periods: int,
@@ -315,7 +336,7 @@ def _get_registry() -> Dict[str, _EstimatorProfile]:
315336
),
316337
"SyntheticDiD": _EstimatorProfile(
317338
default_dgp=generate_factor_data,
318-
dgp_kwargs_builder=_factor_dgp_kwargs,
339+
dgp_kwargs_builder=_sdid_dgp_kwargs,
319340
fit_kwargs_builder=_sdid_fit_kwargs,
320341
result_extractor=_extract_simple,
321342
min_n=30,

tests/test_power.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
_extract_staggered,
4141
_factor_dgp_kwargs,
4242
_get_registry,
43+
_sdid_dgp_kwargs,
4344
_staggered_dgp_kwargs,
4445
_staggered_fit_kwargs,
4546
_trop_fit_kwargs,
@@ -707,6 +708,7 @@ def test_dgp_kwargs_builders_return_dicts(self):
707708
_basic_dgp_kwargs,
708709
_staggered_dgp_kwargs,
709710
_factor_dgp_kwargs,
711+
_sdid_dgp_kwargs,
710712
_ddd_dgp_kwargs,
711713
]:
712714
result = builder(**params)
@@ -969,6 +971,21 @@ def test_synthetic_did(self):
969971
)
970972
self._assert_valid_result(result, "SyntheticDiD")
971973

974+
@pytest.mark.slow
975+
def test_synthetic_did_default_fraction(self):
976+
"""Default treatment_fraction=0.5 must not produce zero power."""
977+
result = simulate_power(
978+
SyntheticDiD(),
979+
n_units=50,
980+
n_periods=6,
981+
treatment_period=3,
982+
n_simulations=10,
983+
seed=42,
984+
progress=False,
985+
)
986+
self._assert_valid_result(result, "SyntheticDiD")
987+
assert result.power > 0, "Default SyntheticDiD path gave zero power"
988+
972989

973990
# ---------------------------------------------------------------------------
974991
# simulate_mde tests

0 commit comments

Comments
 (0)