Commit d0bb6b2
linalg: scale-invariant rank detection + solve; fix rank-0 IndexError (both backends)
Repairs scale-sensitive rank handling in the shared OLS backend. A covariate on
a large raw scale (~1e8) inflated the pivoted-QR rank threshold (anchored to the
largest pivot diagonal) and false-dropped the intercept/treatment/interaction to
NaN on a full-rank design; the scipy.lstsq(cond=1e-7) solve likewise truncated
the small-scale direction, returning finite-but-wrong coefficients.
_detect_rank_deficiency now runs a raw pivoted QR first (preserving the
established drop-column selection for genuinely collinear designs) and only
adopts the equilibrated rank when the raw drop was scale-induced. The solve
equilibrates columns to unit 2-norm and unscales the coefficients. Mirrored in
rust/src/linalg.rs (unscale before fitted/vcov). Rank-0 designs now return
all-NaN cleanly (solve_ols) or raise a clear ValueError (solve_logit/poisson,
routed through CS pscore_fallback) instead of a cryptic IndexError.
No drop-order change for collinear designs; no-op for full-rank well-conditioned
designs (R-parity goldens unaffected). New regression tests in
tests/test_linalg.py::TestNumericalStability.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent b3dc8d0 commit d0bb6b2
7 files changed
Lines changed: 349 additions & 40 deletions
File tree
- diff_diff
- docs/methodology
- rust/src
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| 82 | + | |
82 | 83 | | |
83 | 84 | | |
84 | 85 | | |
| |||
0 commit comments