You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -42,49 +42,11 @@ Target: < 1000 lines per module for maintainability.
42
42
43
43
All 7 t_stat locations fixed (diagnostics.py, sun_abraham.py, triple_diff.py) -- all now use `np.nan` or `np.isfinite()` guards. Fixed in PR #118 and follow-up PRs.
44
44
45
-
**Remaining nuance**: `diagnostics.py:785`still has `se = ... else 0.0` for the SE variable itself (not t_stat). The downstream t_stat line correctly returns `np.nan`, so inference is safe, but the SE value of 0.0 is technically incorrect for an undefined SE.
45
+
~~**Remaining nuance**: `diagnostics.py:785`SE = 0.0~~ — ✅ Fixed: SE now returns `np.nan` when undefined, and all downstream inference uses `safe_inference()`.
46
46
47
-
### Migrate Existing Inference Call Sites to `safe_inference()`
47
+
### ~~Migrate Existing Inference Call Sites to `safe_inference()`~~ -- DONE
48
48
49
-
`safe_inference()` was added to `diff_diff/utils.py` to compute t_stat, p_value, and CI together with a NaN gate at the top. It is now the prescribed pattern for all new code (see CLAUDE.md design pattern #7). However, ~26 existing inline inference computations across 12 files have **not** been migrated yet.
**Note**: This command has one false positive (`utils.py:178`, inside the `safe_inference()` body) and misses multi-line expressions (e.g., `sun_abraham.py:660-661`). The table above is the authoritative list.
74
-
75
-
**Migration pattern:**
76
-
```python
77
-
# Before (inline, error-prone)
78
-
t_stat = effect / se if se >0else0.0
79
-
p_value = compute_p_value(t_stat)
80
-
ci = compute_confidence_interval(effect, se, alpha)
81
-
82
-
# After (NaN-safe, consistent)
83
-
from diff_diff.utils import safe_inference
84
-
t_stat, p_value, ci = safe_inference(effect, se, alpha=alpha, df=df)
85
-
```
86
-
87
-
**Priority**: Medium — the NaN-handling table above covers the worst cases (those using `0.0`). The remaining sites may use partial guards but should still be migrated for consistency and to prevent regressions.
49
+
✅ All ~32 inline inference call sites migrated to `safe_inference()` across 11 source files: `estimators.py`, `sun_abraham.py`, `staggered.py`, `staggered_aggregation.py`, `triple_diff.py`, `imputation.py`, `two_stage.py`, `diagnostics.py`, `synthetic_did.py`, `trop.py`, `utils.py`. Two sites left as-is with comments: `diagnostics.py:665` (permutation-based p_value) and `linalg.py:1310` (deliberately uses ±inf for zero-SE).
88
50
89
51
---
90
52
@@ -96,17 +58,17 @@ Deferred items from PR reviews that were not addressed before merge.
96
58
97
59
| Issue | Location | PR | Priority |
98
60
|-------|----------|----|----------|
99
-
| TwoStageDiD & ImputationDiD bootstrap hardcodes Rademacher only; no `bootstrap_weights` parameter unlike CallawaySantAnna |`two_stage.py:1860`, `imputation.py:2363`|#156, #141| Medium |
100
-
| TwoStageDiD GMM score logic duplicated between analytic/bootstrap with inconsistent NaN/overflow handling |`two_stage.py:1454-1784`|#156| Medium |
101
-
| ImputationDiD weight construction duplicated between aggregation and bootstrap (drift risk) -- has explicit code comment acknowledging duplication |`imputation.py:1777-1786`, `imputation.py:2216-2221`|#141| Medium |
102
-
| ImputationDiD dense `(A0'A0).toarray()` scales O((U+T+K)^2), OOM risk on large panels |`imputation.py:1564`|#141| Medium |
61
+
| TwoStageDiD & ImputationDiD bootstrap hardcodes Rademacher only; no `bootstrap_weights` parameter unlike CallawaySantAnna |`two_stage_bootstrap.py`, `imputation_bootstrap.py`|#156, #141| Medium |
62
+
| TwoStageDiD GMM score logic duplicated between analytic/bootstrap with inconsistent NaN/overflow handling |`two_stage.py`, `two_stage_bootstrap.py`|#156| Medium |
63
+
| ImputationDiD weight construction duplicated between aggregation and bootstrap (drift risk) -- has explicit code comment acknowledging duplication |`imputation.py`, `imputation_bootstrap.py`|#141| Medium |
64
+
| ImputationDiD dense `(A0'A0).toarray()` scales O((U+T+K)^2), OOM risk on large panels |`imputation.py`|#141| Medium |
103
65
104
66
#### Performance
105
67
106
68
| Issue | Location | PR | Priority |
107
69
|-------|----------|----|----------|
108
-
| TwoStageDiD per-column `.toarray()` in loop for cluster scores |`two_stage.py:1766-1767`|#156| Medium |
109
-
| ImputationDiD event-study SEs recompute full conservative variance per horizon (should cache A0/A1 factorization) |`imputation.py:1772-1804`|#141| Low |
70
+
| TwoStageDiD per-column `.toarray()` in loop for cluster scores |`two_stage_bootstrap.py`|#156| Medium |
71
+
| ImputationDiD event-study SEs recompute full conservative variance per horizon (should cache A0/A1 factorization) |`imputation.py`|#141| Low |
| SunAbraham deprecated `min_pre_periods`/`min_post_periods` still in `fit()` docstring |`sun_abraham.py:458-487`|#153| Low |
@@ -209,19 +171,9 @@ Spurious RuntimeWarnings ("divide by zero", "overflow", "invalid value") are emi
209
171
- Occurs in IPW and DR estimation methods with covariates
210
172
- Related to logistic regression overflow in edge cases (separate from BLAS bug)
211
173
212
-
### Fix Plan (follow-up PR)
213
-
214
-
Replace `@` operator with `np.dot()` at affected call sites. `np.dot()` bypasses the ufunc FPE dispatch layer and produces identical results with zero spurious warnings on M4.
215
-
216
-
**Affected files and lines:**
217
-
-`linalg.py`: 332, 690, 704, 829, 1463
218
-
-`staggered.py`: 78, 85, 102, 860, 1024-1025
219
-
-`triple_diff.py`: 301, 307, 323
220
-
-`utils.py`: 515
221
-
-`imputation.py`: 1253, 1301, 1602, 1662
222
-
-`trop.py`: 1098
174
+
### ~~Fix Plan (follow-up PR)~~ -- DONE
223
175
224
-
**Regression test:** Assert no RuntimeWarnings from `solve_ols()` with n ≥ 500 on all platforms.
176
+
✅ Replaced `@` operator with `np.dot()` at all 19 affected call sites across 6 files: `linalg.py` (5), `staggered.py` (5), `triple_diff.py` (3), `utils.py` (1), `imputation.py` (4), `trop.py` (1). Regression test added in `test_linalg.py::TestNoDotRuntimeWarnings`.
225
177
226
178
**Long-term:** Revert to `@` operator when numpy ≥ 2.3 becomes the minimum supported version.
0 commit comments