Skip to content

Commit f6e0cc4

Browse files
fix(ci): support scikit-learn 1.9 (LassoCV n_alphas removal)
scikit-learn 1.7 deprecated LassoCV's `n_alphas` in favour of an integer `alphas`, and 1.9 removed it — `LassoCV(n_alphas=20)` now raises TypeError at construction. The CI matrix resolves to scikit-learn 1.9.0, breaking `sp.tmle(method='hal')` (HALRegressor) and `sp.rd_flex(learner='lasso')`. Add a version-robust shim `compat.sklearn.lasso_cv_alphas_kwargs(n)` that emits {"alphas": n} on sklearn >= 1.7 and {"n_alphas": n} on older releases; route both call sites through it. Alpha count unchanged (20 / 50) — no numerical effect. Verified: 8 HAL tests + 6 rd_flex tests green on local sklearn 1.6.1; version-logic asserted across 1.6/1.7/1.8/1.9; flake8 hard + baseline and mypy baseline gates pass locally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 812bb42 commit f6e0cc4

4 files changed

Lines changed: 50 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,22 @@ All notable changes to StatsPAI will be documented in this file.
2323

2424
### Fixed
2525

26+
- **CI: scikit-learn 1.9 compatibility — `LassoCV(n_alphas=...)` removal.**
27+
scikit-learn 1.7 deprecated the `n_alphas` argument of the coordinate-descent
28+
CV estimators (`LassoCV`/`ElasticNetCV`/…) in favour of passing an integer to
29+
`alphas`, and 1.9 removed it outright — constructing `LassoCV(n_alphas=20)`
30+
now raises `TypeError: LassoCV.__init__() got an unexpected keyword argument
31+
'n_alphas'`. The `CI/CD Pipeline` matrix resolves to scikit-learn 1.9.0, so
32+
`sp.tmle(method='hal')` (`HALRegressor`) and `sp.rd_flex(learner='lasso')`
33+
both failed at construction (`tests/test_hal_tmle.py`,
34+
`tests/test_estimator_provenance_round5.py::TestHalTmleProvenance`,
35+
`tests/test_low_cov_battery.py::test_hal_regressor_predicts_finite`). A new
36+
version-robust shim `statspai.compat.sklearn.lasso_cv_alphas_kwargs(n)` emits
37+
`{"alphas": n}` on scikit-learn >= 1.7 and `{"n_alphas": n}` on older
38+
releases; both call sites now route through it. The number of path alphas
39+
(20 for HAL, 50 for `rd_flex`) is unchanged — no numerical effect. Verified
40+
on the local scikit-learn 1.6.1 pin (8 HAL tests + 6 `rd_flex` tests green)
41+
and by version-logic assertion across 1.6/1.7/1.8/1.9.
2642
- **CI: `schemas/functions.json` no longer drifts by pandas version.** The
2743
auto-registered schema export stringified parameter annotations via
2844
`str(typing.Optional[pandas.DataFrame])`, which pandas 3.0 renders as

src/statspai/compat/sklearn.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,35 @@ def _check_sklearn():
4141
)
4242

4343

44+
def lasso_cv_alphas_kwargs(n_alphas: int) -> dict:
45+
"""Version-robust keyword for the number of CV path alphas.
46+
47+
scikit-learn 1.7 deprecated the ``n_alphas`` argument of the
48+
coordinate-descent CV estimators (``LassoCV`` / ``ElasticNetCV`` / ...)
49+
in favour of passing an integer to ``alphas``; ``n_alphas`` was removed
50+
outright in 1.9 (raising ``TypeError`` on construction). Older versions
51+
only understand ``n_alphas``. Return whichever keyword the installed
52+
scikit-learn accepts so call sites stay portable across the boundary.
53+
54+
Parameters
55+
----------
56+
n_alphas : int
57+
Number of alphas to evaluate along the regularisation path.
58+
59+
Returns
60+
-------
61+
dict
62+
``{"alphas": n}`` on scikit-learn >= 1.7, else ``{"n_alphas": n}``.
63+
"""
64+
from packaging.version import parse as _parse
65+
66+
import sklearn
67+
68+
if _parse(sklearn.__version__).release[:2] >= (1, 7):
69+
return {"alphas": int(n_alphas)}
70+
return {"n_alphas": int(n_alphas)}
71+
72+
4473
class SklearnOLS(BaseEstimator, RegressorMixin):
4574
"""
4675
Sklearn-compatible OLS with robust/clustered standard errors.

src/statspai/rd/rd_flex.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,9 @@ def _make_learner(name: str, random_state: Optional[int]):
287287
return RidgeCV(alphas=np.logspace(-3, 3, 21))
288288
if name == "lasso":
289289
from sklearn.linear_model import LassoCV
290-
return LassoCV(cv=5, n_alphas=50, random_state=random_state,
291-
max_iter=10_000)
290+
from ..compat.sklearn import lasso_cv_alphas_kwargs
291+
return LassoCV(cv=5, random_state=random_state,
292+
max_iter=10_000, **lasso_cv_alphas_kwargs(50))
292293
except ImportError as exc: # pragma: no cover
293294
raise ImportError(
294295
"rd_flex requires scikit-learn. Install with: pip install scikit-learn"

src/statspai/tmle/hal_tmle.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,11 +176,12 @@ def fit(self, X, y):
176176
B, anchors = _hal_basis(X, anchors=None,
177177
max_anchors_per_col=self.max_anchors_per_col)
178178
from sklearn.linear_model import Lasso, LassoCV
179+
from ..compat.sklearn import lasso_cv_alphas_kwargs
179180
if self.lambda_ is None:
180181
cv = int(max(2, min(self.cv, max(2, len(y) // 20))))
181182
model = LassoCV(
182183
cv=cv, random_state=self.random_state,
183-
n_alphas=20, max_iter=5000,
184+
max_iter=5000, **lasso_cv_alphas_kwargs(20),
184185
)
185186
else:
186187
model = Lasso(alpha=self.lambda_, max_iter=5000,

0 commit comments

Comments
 (0)