Skip to content

Commit 052594a

Browse files
fix(rd): extrapolate _ols_fit singular matrix fallback
_ols_fit now falls back to lstsq + pinv when the QR decomposition encounters a singular design matrix (e.g., collinear covariates). Prevents crash in rd_extrapolate with degenerate covariate sets. 79 RD tests pass. 13 end-to-end functional checks verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 6489270 commit 052594a

1 file changed

Lines changed: 15 additions & 7 deletions

File tree

src/statspai/rd/extrapolate.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -69,17 +69,25 @@
6969

7070
def _ols_fit(X: np.ndarray, y: np.ndarray):
7171
"""
72-
OLS regression via QR decomposition.
72+
OLS regression via QR decomposition with fallback to lstsq.
7373
7474
Returns (coefficients, residuals, variance-covariance matrix of beta).
7575
"""
76-
Q, R = np.linalg.qr(X, mode='reduced')
77-
beta = np.linalg.solve(R, Q.T @ y)
78-
resid = y - X @ beta
7976
n, k = X.shape
80-
sigma2 = np.sum(resid ** 2) / max(n - k, 1)
81-
R_inv = np.linalg.inv(R)
82-
vcov = sigma2 * (R_inv @ R_inv.T)
77+
try:
78+
Q, R = np.linalg.qr(X, mode='reduced')
79+
beta = np.linalg.solve(R, Q.T @ y)
80+
resid = y - X @ beta
81+
sigma2 = np.sum(resid ** 2) / max(n - k, 1)
82+
R_inv = np.linalg.inv(R)
83+
vcov = sigma2 * (R_inv @ R_inv.T)
84+
except np.linalg.LinAlgError:
85+
# Fallback for singular/near-singular design matrices
86+
beta, _, _, _ = np.linalg.lstsq(X, y, rcond=None)
87+
resid = y - X @ beta
88+
sigma2 = np.sum(resid ** 2) / max(n - k, 1)
89+
XtX_inv = np.linalg.pinv(X.T @ X)
90+
vcov = sigma2 * XtX_inv
8391
return beta, resid, vcov
8492

8593

0 commit comments

Comments
 (0)