Add unified LOOCV for TROP joint method with Rust acceleration#113
Add unified LOOCV for TROP joint method with Rust acceleration#113
Conversation
Implements proper leave-one-out cross-validation for the TROP joint method, matching the twostep method's approach per the paper's Equation 5. Adds Rust backend acceleration for parallel LOOCV grid search and bootstrap variance estimation (5-15x speedup). Changes: - rust/src/trop.rs: Add loocv_grid_search_joint(), bootstrap_trop_variance_joint(), and supporting joint model fitting functions - diff_diff/trop.py: Update _fit_joint() to use LOOCV, add _loocv_score_joint() Python fallback, integrate Rust acceleration for bootstrap variance - diff_diff/_backend.py: Export new Rust functions - docs/methodology/REGISTRY.md: Document unified LOOCV approach for joint method - tests/: Add comprehensive tests for joint method LOOCV and Rust/Python parity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
Methodology
Documentation/Tests
Code Quality
Performance
Maintainability
Tech Debt
Security
Overall Assessment: Executive Summary
|
Address P1 review feedback: - P1-2: Align nuclear-norm threshold scaling by using eta * lambda_nn for soft-threshold SVD step in Python (matching Rust implementation) - P1-1: Add comprehensive NaN handling in _compute_joint_weights, _solve_joint_no_lowrank, and _solve_joint_with_lowrank Add tests for NaN handling parity between backends. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
Open Questions
Overall Assessment
Executive Summary
|
- Fix _solve_joint_with_lowrank to mask delta for NaN observations (ensures NaN Y values don't contribute to gradient step) - Fix jackknife to use true leave-one-out via weight zeroing (removes incorrect imputation with column means) - Handle units with no valid pre-period data by setting delta_unit=0 (previously got max weight due to dist=0) - Document simultaneous adoption assumption for joint method variance - Correct notebook weight normalization statement (not "sum to one") - Add tests for true NaN exclusion and jackknife variation behavior Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
Open Questions
Overall Assessment Executive Summary
|
Address PR #113 Round 3 feedback: - rust/src/trop.rs: Change dist from 0.0 to INFINITY when n_valid=0 so delta_unit = exp(-inf) = 0.0 (zero weight) instead of exp(0) = 1.0 (max weight). This matches Python behavior. - tests/test_rust_backend.py: Add two parity tests: - test_trop_joint_no_valid_pre_unit_gets_zero_weight - test_trop_joint_nan_exclusion_rust_python_parity Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall assessment: Executive summary
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
…ix NaN handling - Add staggered adoption check in _fit_joint() that raises ValueError when units are first treated at different periods - Fix Rust solve_joint NaN weight masking: observations with NaN outcomes now get zero effective weight instead of having values imputed to 0.0 - Fix Rust average_treated initialization: use NaN instead of 0.0 so periods with all-NaN treated data are excluded from unit distance - Update methodology registry to reflect enforced simultaneous adoption - Add test_joint_rejects_staggered_adoption test Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall assessment: Executive Summary
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
…-pre NaN tests - Fix Python valid_count to use np.isfinite(diff) instead of np.isfinite(Y) When average_treated[t] is NaN, the period should be excluded from both numerator and denominator of RMSE distance calculation - Add test_joint_treated_pre_nan_handling to test_trop.py - Add test_trop_joint_treated_pre_nan_rust_python_parity to test_rust_backend.py - Fix Rust docstring for loocv_grid_search_joint (was incorrectly describing "two-stage coordinate descent", now correctly says "parallel grid search") Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall assessment:
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
…st reloads - Fix simultaneous-adoption check to use observed periods only, avoiding false positives on unbalanced panels where missing entries were filled as 0 - Add zero-weight guard in Python joint solver matching Rust's behavior - Fix backend parity tests to properly reload trop module using sys.modules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall assessment: Executive Summary
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
Fix ValueError in joint method when control_obs exceeds max_loocv_samples without Rust backend. np.random.choice cannot directly sample from a list of tuples - now samples indices first, then indexes into the list (matching the pattern already used in the twostep method). Add test to verify Python-only joint LOOCV subsampling works correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
/ai-review |
3 similar comments
|
/ai-review |
|
/ai-review |
|
/ai-review |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment: ✅ Looks good Executive Summary
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
Update version numbers in: - diff_diff/__init__.py - pyproject.toml - rust/Cargo.toml Add CHANGELOG entry for v2.1.9 documenting TROP joint method improvements including unified LOOCV with Rust acceleration and various Rust/Python parity fixes from PR #113. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
loocv_grid_search_joint())bootstrap_trop_variance_joint())_loocv_score_joint()) when Rust unavailable_fit_joint()and_bootstrap_variance_joint()to use Rust when availableMethodology references (required if estimator / math changes)
Validation
tests/test_trop.py: Addedtest_joint_loocv_selects_from_grid,test_joint_loocv_score_internaltests/test_rust_backend.py: AddedTestTROPJointRustBackendclass (4 tests) andTestTROPJointRustVsNumpyclass (2 tests)cargo checkpasses)Security / privacy
Generated with Claude Code