Fix HC2 and HC2-BM finite variances at leverage one - #823
Conversation
Overall assessment: ✅ Looks goodExecutive summary
Methodology
Healthy-design HC2, HC3, Bell–McCaffrey/Satterthwaite, and CR2 formulas remain unchanged. Code QualityNo findings. The guard is centralized, inference uses existing NaN-safe paths, and LWDiD’s duplicate warning was removed without removing its influence safeguard ( Performance
MaintainabilityNo findings. The Python and Rust cutoffs are parity-tested, and the versioned native capability prevents stale kernels from silently retaining the old behavior. Tech DebtNo findings. The corresponding HC2 flooring item is appropriately removed from SecurityNo findings; no secrets, unsafe input handling, or security-sensitive changes identified. Documentation/TestsNo findings. The PR adds the required changelog fragment and covers inclusive cutoff behavior, over-one leverage, NaN inference propagation, rank reduction, zero-weight/frequency-weight parity, CR2 boundaries, LWDiD behavior, and legacy Rust fallback. Focused tests could not be executed in the review environment because NumPy and pytest are unavailable. |
Summary
HC2 and unweighted, unclustered HC2-BM could report finite variance for a perfectly leveraged observation by flooring
1 - h_ii. For example, a regression on[1, D]with only one observation whereD=1could retain finite covariance despite having no residual information for that observation.These paths now follow HC3's defensive cutoff: any effective leverage
h_ii >= 1 - 1e-8produces aUserWarning, entirely NaN covariance and requested degrees of freedom, and unavailable downstream inference. Identified coefficients, residuals, and fitted values are retained. Numerical leverage above one no longer substitutes HC1. Additional unweighted BM contrast calculations return NaNs silently, avoiding duplicate covariance diagnostics.Methodology references (required if estimator / math changes)
docs/methodology/REGISTRY.mdanddocs/methodology/variance-conventions.md.1 - 1e-8cutoff and suppression of the entire covariance/DOF vector are disclosed defensive choices, not a claim of identical reference behavior at every numerical boundary. Probability/analytical weights retain WLS leverage; frequency weights use each replicated row's leverage. Weighted or clustered HC2-BM retains its separate CR2 convention, including all-ones probability weights, so it can return finite covariance where an otherwise identical unweighted call returns NaN. Weighting is not a remedy for undefined inference. Healthy-design formulas remain unchanged.Validation
Tests added/updated:
tests/test_linalg_hc2_bm.py,tests/test_linalg.py,tests/test_rust_backend.py,tests/test_estimators_vcov_type.py, andtests/test_lwdid.py. Coverage includes inclusive cutoff and over-one cases, all-NaN inference with retained estimates, rank reduction, zero-weight deletion/replication parity, the all-ones-pweight CR2 boundary, native capability selection and fallbacks, and LWDiD warning/influence propagation.The final affected-suite runs used the same 28 files covering linalg, estimators/TWFE, LWDiD, weighted CR2, replicate weights, checked-in reference goldens, and documentation/tracking checks. They include all 18 pre-merge-helper-resolved suites and ran sequentially in the provisioned
.venv.maturin develop --release --features accelerate --offlinewith.venvactiveDIFF_DIFF_BACKEND=rustDIFF_DIFF_BACKEND=pythoncargo test --manifest-path rust/Cargo.toml --features accelerate --offlineruff check diff_diff tests;black --check diff_diff tests;cargo fmt --manifest-path rust/Cargo.toml --check;git diff --checkmypy diff_diffMypy exits 2 at
numpy/__init__.pyi:737: “Type statement is only supported in Python 3.12 and greater.” The frozen local environment is Python 3.14 with NumPy 2.5.2, while the repository targets Python 3.10 for mypy. This also reproduces on unchanged pinned source usingmypy --no-incremental --python-version 3.10 -c "import numpy". The frozen lint workflow pins NumPy 2.4.5 with Python 3.14/mypy 2.3.1. The local environment and project type-checking configuration remain unchanged; this is not a passing type check.Pre-merge Check D flags the existing
_compute_confidence_intervaldefinition atdiff_diff/linalg.py:5526. The function and entire suffix are byte-identical to pinnedmain, and existing arithmetic propagates NaNs.Backtest / simulation / notebook evidence: deterministic regression fixtures and checked-in reference goldens ran. Legacy-extension coverage simulates the prior successful flooring algorithm; no older compiled extension was loaded. No new backtest, live R/Stata reference run, notebook execution, or full Sphinx build was performed. The whole repository suite was not run; default
not slowselection excluded 41 tests per mode, and skipped paths include unavailable optional reference dependencies and Python-mode native-only tests.Security / privacy
Changelog
changelog.d/20260905-hc2-leverage-one.md(behavioral change, zero-weight handling, and LWDiD warning deduplication).