Skip to content

Commit f691af9

Browse files
committed
fix: reject sub-ulp pscore_trim whose upper clip bound rounds to 1.0 (PR #795 review P1)
1 parent d70113c commit f691af9

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

diff_diff/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,15 @@ def validate_pscore_trim(value: Any) -> float:
533533
raise ValueError(f"pscore_trim must be in (0, 0.5), got {value}") from None
534534
if not np.isfinite(coerced) or not 0 < coerced < 0.5:
535535
raise ValueError(f"pscore_trim must be in (0, 0.5), got {value}")
536+
# A trim below half an ulp of 1.0 makes the upper clip bound
537+
# 1 - trim round to exactly 1.0 in binary64, so np.clip would retain
538+
# pscore == 1 and 1/(1-p) weights could divide by zero - the same
539+
# disabled-overlap-guard failure trim=0 is rejected for.
540+
if 1.0 - coerced == 1.0:
541+
raise ValueError(
542+
f"pscore_trim must be in (0, 0.5) and large enough that "
543+
f"1 - pscore_trim < 1 in float64, got {value}"
544+
)
536545
return coerced
537546

538547

tests/test_continuous_did.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1709,8 +1709,23 @@ def test_pscore_trim_never_stores_zero_after_coercion(self):
17091709
try:
17101710
r = validate_pscore_trim(x)
17111711
except ValueError:
1712-
continue # underflowed to 0.0 and was rejected - correct
1712+
continue # underflowed / sub-ulp and was rejected - correct
17131713
assert type(r) is float and 0.0 < r < 0.5
1714+
# The derived upper clip bound must remain strictly below 1.
1715+
assert 1.0 - r < 1.0
1716+
1717+
def test_pscore_trim_sub_ulp_rejected(self):
1718+
"""Binary64 cancellation guard (CI review): a positive trim below
1719+
half an ulp of 1.0 makes 1 - trim round to exactly 1.0, so np.clip
1720+
would retain pscore == 1 - reject it like trim=0."""
1721+
from diff_diff.utils import validate_pscore_trim
1722+
1723+
assert 1.0 - 1e-20 == 1.0 # the failure mode being guarded
1724+
for bad in (1e-20, 5e-17, 2.0**-54):
1725+
with pytest.raises(ValueError, match="pscore_trim must be in"):
1726+
validate_pscore_trim(bad)
1727+
r = validate_pscore_trim(2.0**-52) # representable: 1 - 2**-52 < 1
1728+
assert 1.0 - r < 1.0
17141729

17151730
def test_pscore_trim_huge_int_raises_valueerror(self):
17161731
"""An out-of-float-range Python int raises the documented ValueError,

0 commit comments

Comments
 (0)