fix(imputation): LSMR singular-variance fallback — no dense normal-matrix materialization#657
Conversation
Overall Assessment✅ Looks good. No unmitigated P0/P1 findings. Executive Summary
MethodologyFinding M1 — P3 informational
Code QualityFinding C1 — P3 informational
PerformanceNo findings.
MaintainabilityNo blocking findings.
Tech DebtFinding T1 — P3 informational, tracked
SecurityNo findings.
Documentation/TestsFinding D1 — P3 informational
|
…ted parity oracle
M1: REGISTRY ImputationDiD passages (v_it derivation note + sparse
variance solver note) now describe the sparse LSMR fallback (certified
istop set {0,1,2,4,5}, uncertified -> full NaN inference tuple, dense
lstsq removed) instead of the pre-#657 dense-lstsq contract.
C1: five stale 'dense lstsq' comments/docstrings in imputation.py
updated to the LSMR contract.
D1: weighted singular-system parity test added — null-space components
of (A_0'[W]A_0) live in null(sqrt(W) A_0), so the WEIGHTED projection
W_0 A_0 z (what the weighted estimator consumes) must match the dense
oracle even where the unweighted projection need not; includes
zero-weight (subpopulation) rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3fd25bb to
6b45c59
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good. No unmitigated P0/P1 findings. Executive Summary
MethodologyNo blocking findings.
Code QualityFinding C1 — P3 informational
PerformanceNo findings.
MaintainabilityNo blocking findings.
Tech DebtFinding T1 — P3 informational, tracked
SecurityNo findings.
Documentation/TestsFinding D1 — P3 informational, tracked
Finding D2 — P3 informational
|
…ted parity oracle
M1: REGISTRY ImputationDiD passages (v_it derivation note + sparse
variance solver note) now describe the sparse LSMR fallback (certified
istop set {0,1,2,4,5}, uncertified -> full NaN inference tuple, dense
lstsq removed) instead of the pre-#657 dense-lstsq contract.
C1: five stale 'dense lstsq' comments/docstrings in imputation.py
updated to the LSMR contract.
D1: weighted singular-system parity test added — null-space components
of (A_0'[W]A_0) live in null(sqrt(W) A_0), so the WEIGHTED projection
W_0 A_0 z (what the weighted estimator consumes) must match the dense
oracle even where the unweighted projection need not; includes
zero-weight (subpopulation) rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6b45c59 to
1aa0424
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good. No unmitigated P0/P1 findings. Executive Summary
MethodologyNo blocking findings.
Code QualityNo findings.
PerformanceNo findings.
MaintainabilityNo findings.
Tech DebtNo blocking findings.
SecurityNo findings.
Documentation/TestsFinding D1 — P3 informational
|
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good. No unmitigated P0/P1 findings. Executive Summary
MethodologyNo findings.
Code QualityNo findings.
PerformanceNo findings.
MaintainabilityNo findings.
Tech Debt
SecurityNo findings.
Documentation/Tests
Validation not run: |
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good. No unmitigated P0/P1 findings. Executive Summary
MethodologyNo findings.
Code QualityNo findings.
PerformanceNo findings.
MaintainabilityNo findings.
Tech Debt
SecurityNo findings.
Documentation/TestsNo blocking findings.
Validation attempted but not run: |
…ted parity oracle
M1: REGISTRY ImputationDiD passages (v_it derivation note + sparse
variance solver note) now describe the sparse LSMR fallback (certified
istop set {0,1,2,4,5}, uncertified -> full NaN inference tuple, dense
lstsq removed) instead of the pre-#657 dense-lstsq contract.
C1: five stale 'dense lstsq' comments/docstrings in imputation.py
updated to the LSMR contract.
D1: weighted singular-system parity test added — null-space components
of (A_0'[W]A_0) live in null(sqrt(W) A_0), so the WEIGHTED projection
W_0 A_0 z (what the weighted estimator consumes) must match the dense
oracle even where the unweighted projection need not; includes
zero-weight (subpopulation) rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3ba44a6 to
3db3920
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
…trix materialization The sparse-factorization fallback called np.linalg.lstsq((A0'[W]A0).toarray(), ...) — an O((U+T+K)^2) dense materialization and OOM risk on large panels (triggered only when the sparse factorization fails, e.g. rank-deficient Omega_0). Both fallback sites now solve via scipy.sparse.linalg.lsmr with no densification. Solver choice provably cannot change the estimator: least-squares solutions of the singular system differ only by null(sqrt(W) A_0) components, which the downstream projection v = -[W_0] A_0 z annihilates — locked by a dense-lstsq-oracle parity test on a genuinely singular system plus a no-densify spy test through the full fit. Warning text updated (tests adjusted). The analogous TwoStageDiD dense fallbacks are multi-RHS with coefficient-level consumers where the invariance argument does not transfer — tracked as a new TODO row rather than blindly swapped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ted parity oracle
M1: REGISTRY ImputationDiD passages (v_it derivation note + sparse
variance solver note) now describe the sparse LSMR fallback (certified
istop set {0,1,2,4,5}, uncertified -> full NaN inference tuple, dense
lstsq removed) instead of the pre-#657 dense-lstsq contract.
C1: five stale 'dense lstsq' comments/docstrings in imputation.py
updated to the LSMR contract.
D1: weighted singular-system parity test added — null-space components
of (A_0'[W]A_0) live in null(sqrt(W) A_0), so the WEIGHTED projection
W_0 A_0 z (what the weighted estimator consumes) must match the dense
oracle even where the unweighted projection need not; includes
zero-weight (subpopulation) rows.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t docstring
Review D1 (informational, concrete fix): the docstring said "istop
outside {0,1,2}" but the certified set is {0,1,2,4,5} (istop 4/5
machine-precision analogues were added in review round 3). Wording only;
the test drives istop=7, which is uncertified under either set.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lbd6nqWmg4snvvBmegwqiw
Review P3: the uncertified solve raises _LSMRUnconvergedError; it is the variance boundary (_compute_conservative_variance) that returns NaN. Wording only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lbd6nqWmg4snvvBmegwqiw
3db3920 to
05c4b71
Compare
|
🔁 AI review rerun (requested by @igerber) Head SHA: Overall Assessment✅ Looks good — no unmitigated P0/P1 findings. Executive Summary
Methodology
Code Quality
Performance
Maintainability
Tech Debt
Security
Documentation/Tests
|
Summary
np.linalg.lstsq((A₀'[W]A₀).toarray(), …)— anO((U+T+K)²)dense materialization, triggered when the sparse factorization fails (e.g. rank-deficient Ω₀). Both fallback sites now solve viascipy.sparse.linalg.lsmrwith no dense materialization (locked by a spy test that poisons.toarraythrough a full fit).null(√W·A₀)components, which the downstream projectionv = −[W₀]A₀zannihilates — locked by a dense-lstsq-oracle parity test on a genuinely singular system.istop ∈ {0,1,2,4,5}counts as certified (4/5 are SciPy's machine-precision analogues of 1/2 — round-3 P1); an uncertified stop gets one retry with an uncapped condition limit, then raises a private_LSMRUnconvergedErrorcaught at the variance boundary and converted to a full NaN inference tuple. Raising rather than returning NaN matters: the missing-FEnan_to_numin the psi product would launder a NaN vector into zeros and a finite, wrong variance (round-2 P0) — a fit-level regression test forcesistop=7end-to-end and asserts NaN SE/t/p/CI with a finite point estimate.TwoStageDiDdense fallbacks are multi-RHS with coefficient-level consumers, where the null-space-invariance argument does not transfer — tracked as a new TODO row with the analysis needed, not blindly swapped.Methodology references (required if estimator / math changes)
atol=1e-14, uncertified solves fail closed to NaNValidation
tests/test_imputation.py::TestLSMRFallbackParity(5 tests: singular-oracle parity, no-densify spy, unconverged raise, fit-level NaN inference, istop-4/5 acceptance); warning-text assertions updated in 2 files. Suites green: 179 imputation + methodology-imputation; two_stage untouched (126).Security / privacy
Generated with Claude Code