Source code for skein_glm.adaptive

"""Adaptive {lasso, MCP, SCAD} estimators (M6.x).

Two-stage procedure introduced by Zou (2006) for adaptive lasso, extended
here to MCP and SCAD:

1. **Pilot fit.** Run a plain lasso path (MCP at γ → ∞) on `(X, y)` and
   pick β at a position `pilot_position` along the path (default
   `'mid'` — the middle of the auto-generated λ-grid). Other options:
   `'last'` (smallest λ, closest to OLS), or an integer index.
2. **Adaptive weights.** Compute per-feature `w_j = 1 / max(|β_pilot[j]|,
   eps_pilot)^η`. Larger pilot magnitudes ⇒ smaller penalty weight ⇒ less
   shrinkage on truly active features. The exponent `η` (default 1.0)
   controls how aggressively the weights pull toward unbiased estimates.
3. **Final fit.** Run the chosen final penalty (Lasso / MCP / SCAD) on
   `(X, y)` with these adaptive weights. The path is the **final**
   estimator's path, λ-decreasing.

The pilot fit is on the **full** data (not per-fold) so the CV variants
keep their statistical guarantees: the pilot weights are an
input-data-based summary, the per-fold fit only re-runs the final
adaptive solve on each train split.

This is the headline reason the per-feature weight axis exists in
skein — the underlying solvers all accept `weights=` already, so
adaptive estimators are pure composition with no Rust changes.
"""

from __future__ import annotations

from typing import Any, Literal

import numpy as np
from numpy.typing import NDArray
from sklearn.base import BaseEstimator, ClassifierMixin, RegressorMixin
from sklearn.model_selection import KFold

from skein_glm.cv import (
    _CoxPathCVMixin,
    _LogisticPathCVMixin,
    _PoissonPathCVMixin,
)
from skein_glm.estimators import (
    CoxMCPPathRegressor,
    CoxSCADPathRegressor,
    GroupLassoPathRegressor,
    GroupMCPPathRegressor,
    GroupSCADPathRegressor,
    LogisticLassoPathRegressor,
    LogisticMCPPathRegressor,
    LogisticSCADPathRegressor,
    MCPPathRegressor,
    PoissonLassoPathRegressor,
    PoissonMCPPathRegressor,
    PoissonSCADPathRegressor,
    SCADPathRegressor,
    _is_sparse,
)

PilotPosition = Literal["mid", "last"]


def _validate_pilot_position(p: Any, n_lambdas: int) -> int:
    """Resolve `pilot_position` to a concrete index in `[0, n_lambdas)`."""
    if isinstance(p, str):
        if p == "mid":
            return n_lambdas // 2
        if p == "last":
            return n_lambdas - 1
        raise ValueError(
            f"pilot_position must be 'mid', 'last', or int; got {p!r}"
        )
    if isinstance(p, (int, np.integer)):
        idx = int(p)
        if not 0 <= idx < n_lambdas:
            raise ValueError(
                f"pilot_position {idx} out of range for n_pilot_lambdas={n_lambdas}"
            )
        return idx
    raise TypeError(
        f"pilot_position must be 'mid', 'last', or int; got {type(p).__name__}"
    )


def _adaptive_weights(
    beta_pilot: NDArray[np.float64],
    eta: float,
    eps_pilot: float,
) -> NDArray[np.float64]:
    """Per-feature `1 / max(|β|, eps)^η`. Used as the `weights=`
    argument to the final solver."""
    if not eta > 0.0:
        raise ValueError(f"eta must be > 0; got {eta}")
    if not eps_pilot > 0.0:
        raise ValueError(f"eps_pilot must be > 0; got {eps_pilot}")
    mag = np.maximum(np.abs(beta_pilot), eps_pilot)
    return 1.0 / np.power(mag, eta)


def _adaptive_weights_per_group(
    beta_pilot: NDArray[np.float64],
    groups: NDArray[np.int64],
    eta: float,
    eps_pilot: float,
) -> NDArray[np.float64]:
    """Per-group `w_g = 1 / max(‖β_pilot[g]‖_2, ε)^η`. Returns one weight
    per unique group label, in the same label order as the underlying
    Rust `Groups` (ascending integer labels starting at 0)."""
    if not eta > 0.0:
        raise ValueError(f"eta must be > 0; got {eta}")
    if not eps_pilot > 0.0:
        raise ValueError(f"eps_pilot must be > 0; got {eps_pilot}")
    groups_arr = np.asarray(groups, dtype=np.int64)
    if groups_arr.shape[0] != beta_pilot.shape[0]:
        raise ValueError(
            f"groups length {groups_arr.shape[0]} does not match β length {beta_pilot.shape[0]}"
        )
    n_groups = int(groups_arr.max()) + 1
    norms = np.zeros(n_groups, dtype=np.float64)
    for g in range(n_groups):
        mask = groups_arr == g
        norms[g] = float(np.linalg.norm(beta_pilot[mask]))
    mag = np.maximum(norms, eps_pilot)
    return 1.0 / np.power(mag, eta)


def _fit_pilot(
    x,
    y,
    n_pilot_lambdas: int,
    pilot_position,
    fit_intercept: bool,
    standardize: bool,
) -> NDArray[np.float64]:
    """Run a plain lasso path (MCP at γ = 1e9) on `(X, y)` and return the
    β at the resolved `pilot_position`. The path solver dispatches on
    sparse vs dense via the underlying estimator."""
    pilot = MCPPathRegressor(
        gamma=1e9,
        n_lambdas=n_pilot_lambdas,
        lambda_min_ratio=1e-3,
        fit_intercept=fit_intercept,
        standardize=standardize,
    ).fit(x, y)
    idx = _validate_pilot_position(pilot_position, len(pilot.lambdas_))
    return pilot.coefs_[idx].copy()


# =========================================================================
# Path estimators
# =========================================================================


class _AdaptivePathBase(BaseEstimator, RegressorMixin):
    """Common scaffold: pilot → weights → final path. Subclasses set
    `_final_cls` (the underlying skein path estimator class) and
    `_extra_kwargs()` (penalty-specific params like γ or a)."""

    coefs_: NDArray[np.float64]
    intercepts_: NDArray[np.float64]
    lambdas_: NDArray[np.float64]
    coef_pilot_: NDArray[np.float64]
    weights_: NDArray[np.float64]
    info_: dict[str, Any]
    n_features_in_: int

    _final_cls: Any  # subclass-defined

    def _extra_kwargs(self) -> dict[str, Any]:
        return {}

    def _make_final(self, weights: NDArray[np.float64]):
        kw: dict[str, Any] = dict(
            lambdas=self.lambdas,  # type: ignore[attr-defined]
            n_lambdas=self.n_lambdas,  # type: ignore[attr-defined]
            lambda_min_ratio=self.lambda_min_ratio,  # type: ignore[attr-defined]
            weights=weights,
            max_iter=self.max_iter,  # type: ignore[attr-defined]
            tol=self.tol,  # type: ignore[attr-defined]
            fit_intercept=self.fit_intercept,  # type: ignore[attr-defined]
            standardize=self.standardize,  # type: ignore[attr-defined]
            screening=self.screening,  # type: ignore[attr-defined]
            acceleration=self.acceleration,  # type: ignore[attr-defined]
        )
        kw.update(self._extra_kwargs())
        return self._final_cls(**kw)

    def fit(self, x, y) -> "_AdaptivePathBase":
        beta_pilot = _fit_pilot(
            x,
            y,
            self.n_pilot_lambdas,  # type: ignore[attr-defined]
            self.pilot_position,  # type: ignore[attr-defined]
            self.fit_intercept,  # type: ignore[attr-defined]
            self.standardize,  # type: ignore[attr-defined]
        )
        weights = _adaptive_weights(
            beta_pilot,
            self.eta,  # type: ignore[attr-defined]
            self.eps_pilot,  # type: ignore[attr-defined]
        )
        final = self._make_final(weights).fit(x, y)
        self.coefs_ = final.coefs_
        self.intercepts_ = final.intercepts_
        self.lambdas_ = final.lambdas_
        self.coef_pilot_ = beta_pilot
        self.weights_ = weights
        self.info_ = final.info_
        self.n_features_in_ = final.n_features_in_
        return self

    def predict(self, x) -> NDArray[np.float64]:
        if _is_sparse(x):
            return np.asarray(x @ self.coefs_.T) + self.intercepts_[None, :]
        x = np.ascontiguousarray(x, dtype=np.float64)
        return x @ self.coefs_.T + self.intercepts_[None, :]


[docs] class AdaptiveLassoPathRegressor(_AdaptivePathBase): """Adaptive lasso (Zou 2006) along a λ-path with warm starts. Pilot is a plain lasso fit (MCP at γ = 1e9); final is also lasso with the per-feature inverse-magnitude weights. With pilot magnitudes η-rescaled, the final solve produces the asymptotically unbiased "oracle" sparse solution under the right regularity conditions. Parameters ---------- eta : float, default 1.0 Adaptive-weight exponent. `w_j = 1 / max(|β_pilot[j]|, eps)^η`. eps_pilot : float, default 1e-6 Floor on `|β_pilot|` to keep weights finite. n_pilot_lambdas : int, default 10 Length of the pilot's auto λ-grid. pilot_position : {'mid', 'last'} or int, default 'mid' Which λ along the pilot path to read β from. ``'mid'`` is a good default — the path's middle is typically a reasonable bias/variance compromise. ``'last'`` is closest to OLS. lambdas : array-like or None, default None Forwarded to the final estimator (`MCPPathRegressor` at γ = 1e9). See its docstring for the rest of the standard kwargs (``n_lambdas``, ``lambda_min_ratio``, ``max_iter``, ``tol``, ``fit_intercept``, ``standardize``, ``screening``, ``acceleration``). Attributes ---------- coefs_ : (n_lambdas, n_features) intercepts_ : (n_lambdas,) lambdas_ : (n_lambdas,) coef_pilot_ : (n_features,) — the β read off the pilot path. weights_ : (n_features,) — the adaptive weights computed from the pilot. info_ : dict n_features_in_ : int """ _final_cls = MCPPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": 1e9}
[docs] class AdaptiveMCPPathRegressor(_AdaptivePathBase): """Adaptive MCP along a λ-path with warm starts. Pilot is plain lasso; final is MCP at the user's `gamma` with pilot-derived weights.""" _final_cls = MCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptiveSCADPathRegressor(_AdaptivePathBase): """Adaptive SCAD along a λ-path with warm starts. Pilot is plain lasso; final is SCAD at the user's `a` with pilot-derived weights.""" _final_cls = SCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
# ========================================================================= # Path-CV estimators (K-fold CV with pilot fit on full data) # ========================================================================= class _AdaptivePathCVBase(BaseEstimator, RegressorMixin): """K-fold CV for the adaptive estimators. Pilot weights are computed once on the full data (not per fold) — they're a data-derived hyperparameter rather than a model parameter, and the per-fold final fit uses these fixed weights. CV picks the λ minimizing mean test MSE.""" coef_: NDArray[np.float64] intercept_: float coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] cv_scores_: NDArray[np.float64] cv_mean_scores_: NDArray[np.float64] cv_std_scores_: NDArray[np.float64] lambdas_: NDArray[np.float64] lambda_best_: float info_: dict[str, Any] n_features_in_: int _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_final(self, weights: NDArray[np.float64], **overrides): kw: dict[str, Any] = dict( lambdas=self.lambdas, # type: ignore[attr-defined] n_lambdas=self.n_lambdas, # type: ignore[attr-defined] lambda_min_ratio=self.lambda_min_ratio, # type: ignore[attr-defined] weights=weights, max_iter=self.max_iter, # type: ignore[attr-defined] tol=self.tol, # type: ignore[attr-defined] fit_intercept=self.fit_intercept, # type: ignore[attr-defined] standardize=self.standardize, # type: ignore[attr-defined] screening=self.screening, # type: ignore[attr-defined] acceleration=self.acceleration, # type: ignore[attr-defined] ) kw.update(self._extra_kwargs()) kw.update(overrides) return self._final_cls(**kw) def fit(self, x, y) -> "_AdaptivePathCVBase": # Step 1: pilot weights from the full data. beta_pilot = _fit_pilot( x, y, self.n_pilot_lambdas, # type: ignore[attr-defined] self.pilot_position, # type: ignore[attr-defined] self.fit_intercept, # type: ignore[attr-defined] self.standardize, # type: ignore[attr-defined] ) weights = _adaptive_weights( beta_pilot, self.eta, # type: ignore[attr-defined] self.eps_pilot, # type: ignore[attr-defined] ) # Step 2: full-data final refit (provides λ-grid and final β). full = self._make_final(weights).fit(x, y) lambdas = full.lambdas_ # Step 3: K-fold CV on the final estimator with fixed pilot weights. y_arr = np.ascontiguousarray(y, dtype=np.float64) if _is_sparse(x): from scipy import sparse # type: ignore[import-untyped] x_for_indexing = x.tocsr() if not sparse.isspmatrix_csr(x) else x else: x_for_indexing = np.ascontiguousarray(x, dtype=np.float64) cv = self.cv # type: ignore[attr-defined] random_state = self.random_state # type: ignore[attr-defined] if isinstance(cv, int): splitter = KFold(n_splits=cv, shuffle=True, random_state=random_state) else: splitter = cv n_lambdas = lambdas.shape[0] n = y_arr.shape[0] split_input = ( np.zeros((n, 1)) if _is_sparse(x) else x_for_indexing ) scores = np.full((splitter.get_n_splits(split_input), n_lambdas), np.nan) for fold_idx, (train_idx, test_idx) in enumerate(splitter.split(split_input)): x_tr = x_for_indexing[train_idx] x_te = x_for_indexing[test_idx] y_tr = y_arr[train_idx] y_te = y_arr[test_idx] fold = self._make_final(weights, lambdas=lambdas).fit(x_tr, y_tr) for lam_idx in range(n_lambdas): pred = ( x_te @ fold.coefs_[lam_idx] + fold.intercepts_[lam_idx] ) if hasattr(pred, "toarray"): pred = pred.toarray() pred = np.asarray(pred).ravel() diff = y_te - pred scores[fold_idx, lam_idx] = float(np.mean(diff * diff)) self.cv_scores_ = scores self.cv_mean_scores_ = np.nanmean(scores, axis=0) self.cv_std_scores_ = np.nanstd(scores, axis=0) best = int(np.argmin(self.cv_mean_scores_)) self.lambdas_ = lambdas self.lambda_best_ = float(lambdas[best]) self.coef_ = full.coefs_[best] self.intercept_ = float(full.intercepts_[best]) self.coef_pilot_ = beta_pilot self.weights_ = weights self.info_ = full.info_ self.n_features_in_ = full.n_features_in_ return self def predict(self, x) -> NDArray[np.float64]: if _is_sparse(x): pred = x @ self.coef_ if hasattr(pred, "toarray"): pred = pred.toarray() return np.asarray(pred) + self.intercept_ x = np.ascontiguousarray(x, dtype=np.float64) return x @ self.coef_ + self.intercept_
[docs] class AdaptiveLassoPathCV(_AdaptivePathCVBase): """K-fold CV over an adaptive-lasso λ-path.""" _final_cls = MCPPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": 1e9}
[docs] class AdaptiveMCPPathCV(_AdaptivePathCVBase): """K-fold CV over an adaptive-MCP λ-path.""" _final_cls = MCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptiveSCADPathCV(_AdaptivePathCVBase): """K-fold CV over an adaptive-SCAD λ-path.""" _final_cls = SCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
# ========================================================================= # Adaptive group estimators (LS only; pilot is GroupLassoPathRegressor) # ========================================================================= def _fit_group_pilot( x, y, groups: NDArray[np.int64], n_pilot_lambdas: int, pilot_position, fit_intercept: bool, standardize: bool, ) -> NDArray[np.float64]: """Run a plain group lasso path on `(X, y)` and return β at the resolved `pilot_position`.""" pilot = GroupLassoPathRegressor( groups=groups, n_lambdas=n_pilot_lambdas, lambda_min_ratio=1e-3, fit_intercept=fit_intercept, standardize=standardize, ).fit(x, y) idx = _validate_pilot_position(pilot_position, len(pilot.lambdas_)) return pilot.coefs_[idx].copy() class _AdaptiveGroupPathBase(BaseEstimator, RegressorMixin): """Common scaffold for adaptive group-penalty path estimators. Subclasses set `_final_cls` and `_extra_kwargs()` for penalty params.""" coefs_: NDArray[np.float64] intercepts_: NDArray[np.float64] lambdas_: NDArray[np.float64] coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] info_: dict[str, Any] n_features_in_: int _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_final(self, weights: NDArray[np.float64]): kw: dict[str, Any] = dict( groups=self.groups, # type: ignore[attr-defined] lambdas=self.lambdas, # type: ignore[attr-defined] n_lambdas=self.n_lambdas, # type: ignore[attr-defined] lambda_min_ratio=self.lambda_min_ratio, # type: ignore[attr-defined] weights=weights, max_iter=self.max_iter, # type: ignore[attr-defined] tol=self.tol, # type: ignore[attr-defined] fit_intercept=self.fit_intercept, # type: ignore[attr-defined] standardize=self.standardize, # type: ignore[attr-defined] screening=self.screening, # type: ignore[attr-defined] acceleration=self.acceleration, # type: ignore[attr-defined] parallel=self.parallel, # type: ignore[attr-defined] ) kw.update(self._extra_kwargs()) return self._final_cls(**kw) def fit(self, x, y) -> "_AdaptiveGroupPathBase": beta_pilot = _fit_group_pilot( x, y, self.groups, # type: ignore[attr-defined] self.n_pilot_lambdas, # type: ignore[attr-defined] self.pilot_position, # type: ignore[attr-defined] self.fit_intercept, # type: ignore[attr-defined] self.standardize, # type: ignore[attr-defined] ) weights = _adaptive_weights_per_group( beta_pilot, self.groups, # type: ignore[attr-defined] self.eta, # type: ignore[attr-defined] self.eps_pilot, # type: ignore[attr-defined] ) final = self._make_final(weights).fit(x, y) self.coefs_ = final.coefs_ self.intercepts_ = final.intercepts_ self.lambdas_ = final.lambdas_ self.coef_pilot_ = beta_pilot self.weights_ = weights self.info_ = final.info_ self.n_features_in_ = final.n_features_in_ return self def predict(self, x) -> NDArray[np.float64]: if _is_sparse(x): return np.asarray(x @ self.coefs_.T) + self.intercepts_[None, :] x = np.ascontiguousarray(x, dtype=np.float64) return x @ self.coefs_.T + self.intercepts_[None, :]
[docs] class AdaptiveGroupLassoPathRegressor(_AdaptiveGroupPathBase): """Adaptive group lasso along a λ-path. Pilot is plain group lasso; final is also group lasso with per-group inverse-norm weights `w_g = 1 / max(‖β_pilot[g]‖_2, ε)^η`.""" _final_cls = GroupLassoPathRegressor def __init__( self, groups: NDArray[np.int64], *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, parallel: bool = False, ) -> None: self.groups = groups self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration self.parallel = parallel
[docs] class AdaptiveGroupMCPPathRegressor(_AdaptiveGroupPathBase): """Adaptive group MCP along a λ-path. Pilot is plain group lasso; final is group MCP at the user's `gamma` with per-group adaptive weights.""" _final_cls = GroupMCPPathRegressor def __init__( self, groups: NDArray[np.int64], gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, parallel: bool = False, ) -> None: self.groups = groups self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration self.parallel = parallel def _extra_kwargs(self) -> dict[str, Any]: return { "gamma": self.gamma, "max_outer": self.max_outer, "outer_tol": self.outer_tol, }
[docs] class AdaptiveGroupSCADPathRegressor(_AdaptiveGroupPathBase): """Adaptive group SCAD along a λ-path. Pilot is plain group lasso; final is group SCAD at the user's `a` with per-group adaptive weights `w_g = 1 / max(‖β_pilot[g]‖_2, ε)^η`.""" _final_cls = GroupSCADPathRegressor def __init__( self, groups: NDArray[np.int64], a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, parallel: bool = False, ) -> None: self.groups = groups self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration self.parallel = parallel def _extra_kwargs(self) -> dict[str, Any]: return { "a": self.a, "max_outer": self.max_outer, "outer_tol": self.outer_tol, }
class _AdaptiveGroupPathCVBase(BaseEstimator, RegressorMixin): """K-fold CV for adaptive group estimators. Pilot weights computed once on full data; per-fold final fit uses fixed weights.""" coef_: NDArray[np.float64] intercept_: float coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] cv_scores_: NDArray[np.float64] cv_mean_scores_: NDArray[np.float64] cv_std_scores_: NDArray[np.float64] lambdas_: NDArray[np.float64] lambda_best_: float info_: dict[str, Any] n_features_in_: int _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_final(self, weights: NDArray[np.float64], **overrides): kw: dict[str, Any] = dict( groups=self.groups, # type: ignore[attr-defined] lambdas=self.lambdas, # type: ignore[attr-defined] n_lambdas=self.n_lambdas, # type: ignore[attr-defined] lambda_min_ratio=self.lambda_min_ratio, # type: ignore[attr-defined] weights=weights, max_iter=self.max_iter, # type: ignore[attr-defined] tol=self.tol, # type: ignore[attr-defined] fit_intercept=self.fit_intercept, # type: ignore[attr-defined] standardize=self.standardize, # type: ignore[attr-defined] screening=self.screening, # type: ignore[attr-defined] acceleration=self.acceleration, # type: ignore[attr-defined] parallel=self.parallel, # type: ignore[attr-defined] ) kw.update(self._extra_kwargs()) kw.update(overrides) return self._final_cls(**kw) def fit(self, x, y) -> "_AdaptiveGroupPathCVBase": beta_pilot = _fit_group_pilot( x, y, self.groups, # type: ignore[attr-defined] self.n_pilot_lambdas, # type: ignore[attr-defined] self.pilot_position, # type: ignore[attr-defined] self.fit_intercept, # type: ignore[attr-defined] self.standardize, # type: ignore[attr-defined] ) weights = _adaptive_weights_per_group( beta_pilot, self.groups, # type: ignore[attr-defined] self.eta, # type: ignore[attr-defined] self.eps_pilot, # type: ignore[attr-defined] ) full = self._make_final(weights).fit(x, y) lambdas = full.lambdas_ y_arr = np.ascontiguousarray(y, dtype=np.float64) if _is_sparse(x): from scipy import sparse # type: ignore[import-untyped] x_for_indexing = x.tocsr() if not sparse.isspmatrix_csr(x) else x else: x_for_indexing = np.ascontiguousarray(x, dtype=np.float64) cv = self.cv # type: ignore[attr-defined] random_state = self.random_state # type: ignore[attr-defined] if isinstance(cv, int): splitter = KFold(n_splits=cv, shuffle=True, random_state=random_state) else: splitter = cv n_lambdas = lambdas.shape[0] n = y_arr.shape[0] split_input = np.zeros((n, 1)) if _is_sparse(x) else x_for_indexing scores = np.full((splitter.get_n_splits(split_input), n_lambdas), np.nan) for fold_idx, (train_idx, test_idx) in enumerate(splitter.split(split_input)): x_tr = x_for_indexing[train_idx] x_te = x_for_indexing[test_idx] y_tr = y_arr[train_idx] y_te = y_arr[test_idx] fold = self._make_final(weights, lambdas=lambdas).fit(x_tr, y_tr) for lam_idx in range(n_lambdas): pred = x_te @ fold.coefs_[lam_idx] + fold.intercepts_[lam_idx] if hasattr(pred, "toarray"): pred = pred.toarray() pred = np.asarray(pred).ravel() diff = y_te - pred scores[fold_idx, lam_idx] = float(np.mean(diff * diff)) self.cv_scores_ = scores self.cv_mean_scores_ = np.nanmean(scores, axis=0) self.cv_std_scores_ = np.nanstd(scores, axis=0) best = int(np.argmin(self.cv_mean_scores_)) self.lambdas_ = lambdas self.lambda_best_ = float(lambdas[best]) self.coef_ = full.coefs_[best] self.intercept_ = float(full.intercepts_[best]) self.coef_pilot_ = beta_pilot self.weights_ = weights self.info_ = full.info_ self.n_features_in_ = full.n_features_in_ return self def predict(self, x) -> NDArray[np.float64]: if _is_sparse(x): pred = x @ self.coef_ if hasattr(pred, "toarray"): pred = pred.toarray() return np.asarray(pred) + self.intercept_ x = np.ascontiguousarray(x, dtype=np.float64) return x @ self.coef_ + self.intercept_
[docs] class AdaptiveGroupLassoPathCV(_AdaptiveGroupPathCVBase): """K-fold CV over an adaptive-group-lasso λ-path.""" _final_cls = GroupLassoPathRegressor def __init__( self, groups: NDArray[np.int64], *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, parallel: bool = False, ) -> None: self.groups = groups self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration self.parallel = parallel
[docs] class AdaptiveGroupMCPPathCV(_AdaptiveGroupPathCVBase): """K-fold CV over an adaptive-group-MCP λ-path.""" _final_cls = GroupMCPPathRegressor def __init__( self, groups: NDArray[np.int64], gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, parallel: bool = False, ) -> None: self.groups = groups self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration self.parallel = parallel def _extra_kwargs(self) -> dict[str, Any]: return { "gamma": self.gamma, "max_outer": self.max_outer, "outer_tol": self.outer_tol, }
[docs] class AdaptiveGroupSCADPathCV(_AdaptiveGroupPathCVBase): """K-fold CV over an adaptive-group-SCAD λ-path.""" _final_cls = GroupSCADPathRegressor def __init__( self, groups: NDArray[np.int64], a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, screening: str = "strong", acceleration: int | None = 5, parallel: bool = False, ) -> None: self.groups = groups self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.screening = screening self.acceleration = acceleration self.parallel = parallel def _extra_kwargs(self) -> dict[str, Any]: return { "a": self.a, "max_outer": self.max_outer, "outer_tol": self.outer_tol, }
# ========================================================================= # Adaptive GLM estimators (Logistic, Poisson, Cox × Lasso, MCP, SCAD) # ========================================================================= # # Pilot is the corresponding GLM's lasso path (e.g., # `LogisticLassoPathRegressor` for binomial). Final is the user's # chosen GLM-penalty path with adaptive weights derived from the pilot. # # Path classes store the fitted final estimator as `_final_estimator_` # and delegate `predict_proba` / `decision_function` / `predict` to it # (so the GLM-specific predict semantics — binary class label for # logistic, μ = exp(η) for Poisson, η for Cox — are inherited). # # CV classes extend the existing per-family CV mixins # (`_LogisticPathCVMixin`, `_PoissonPathCVMixin`, `_CoxPathCVMixin`), # overriding `fit` to compute pilot weights once on the full data and # `_make_base_path` to inject those weights into every per-fold # refit. Cox uses `fit(x, time, event)` and StratifiedKFold by event. def _fit_pilot_logistic( x, y, n_pilot_lambdas: int, pilot_position, fit_intercept: bool, standardize: bool, ) -> NDArray[np.float64]: pilot = LogisticLassoPathRegressor( n_lambdas=n_pilot_lambdas, lambda_min_ratio=1e-3, fit_intercept=fit_intercept, standardize=standardize, ).fit(x, y) idx = _validate_pilot_position(pilot_position, len(pilot.lambdas_)) return pilot.coefs_[idx].copy() def _fit_pilot_poisson( x, y, n_pilot_lambdas: int, pilot_position, fit_intercept: bool, standardize: bool, ) -> NDArray[np.float64]: pilot = PoissonLassoPathRegressor( n_lambdas=n_pilot_lambdas, lambda_min_ratio=1e-3, fit_intercept=fit_intercept, standardize=standardize, ).fit(x, y) idx = _validate_pilot_position(pilot_position, len(pilot.lambdas_)) return pilot.coefs_[idx].copy() def _fit_pilot_cox( x, time, event, n_pilot_lambdas: int, pilot_position, standardize: bool, ) -> NDArray[np.float64]: pilot = CoxMCPPathRegressor( gamma=1e9, n_lambdas=n_pilot_lambdas, lambda_min_ratio=1e-3, standardize=standardize, ).fit(x, time, event) idx = _validate_pilot_position(pilot_position, len(pilot.lambdas_)) return pilot.coefs_[idx].copy() # ---- Logistic ---------------------------------------------------------- class _AdaptiveLogisticPathBase(BaseEstimator, ClassifierMixin): """Adaptive logistic path: pilot lasso → adaptive weights → final fit. Subclass sets `_final_cls` and `_extra_kwargs()`.""" coefs_: NDArray[np.float64] intercepts_: NDArray[np.float64] lambdas_: NDArray[np.float64] coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] info_: dict[str, Any] n_features_in_: int _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_final(self, weights: NDArray[np.float64]): kw: dict[str, Any] = dict( lambdas=self.lambdas, # type: ignore[attr-defined] n_lambdas=self.n_lambdas, # type: ignore[attr-defined] lambda_min_ratio=self.lambda_min_ratio, # type: ignore[attr-defined] weights=weights, max_iter=self.max_iter, # type: ignore[attr-defined] tol=self.tol, # type: ignore[attr-defined] fit_intercept=self.fit_intercept, # type: ignore[attr-defined] standardize=self.standardize, # type: ignore[attr-defined] acceleration=self.acceleration, # type: ignore[attr-defined] max_outer=self.max_outer, # type: ignore[attr-defined] outer_tol=self.outer_tol, # type: ignore[attr-defined] ) kw.update(self._extra_kwargs()) return self._final_cls(**kw) def fit(self, x, y) -> "_AdaptiveLogisticPathBase": beta_pilot = _fit_pilot_logistic( x, y, self.n_pilot_lambdas, # type: ignore[attr-defined] self.pilot_position, # type: ignore[attr-defined] self.fit_intercept, # type: ignore[attr-defined] self.standardize, # type: ignore[attr-defined] ) weights = _adaptive_weights( beta_pilot, self.eta, # type: ignore[attr-defined] self.eps_pilot, # type: ignore[attr-defined] ) final = self._make_final(weights).fit(x, y) self._final_estimator_ = final self.coefs_ = final.coefs_ self.intercepts_ = final.intercepts_ self.lambdas_ = final.lambdas_ self.coef_pilot_ = beta_pilot self.weights_ = weights self.info_ = final.info_ self.n_features_in_ = final.n_features_in_ return self def decision_function(self, x): return self._final_estimator_.decision_function(x) def predict_proba(self, x): return self._final_estimator_.predict_proba(x) def predict(self, x): return self._final_estimator_.predict(x) def _logistic_path_init(self, gamma_or_a, *, eta, eps_pilot, n_pilot_lambdas, pilot_position, lambdas, n_lambdas, lambda_min_ratio, max_iter, tol, max_outer, outer_tol, fit_intercept, standardize, acceleration, gamma_or_a_attr): setattr(self, gamma_or_a_attr, gamma_or_a) self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration
[docs] class AdaptiveLogisticLassoPathRegressor(_AdaptiveLogisticPathBase): """Adaptive logistic lasso (pilot lasso + adaptive lasso final).""" _final_cls = LogisticLassoPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration
[docs] class AdaptiveLogisticMCPPathRegressor(_AdaptiveLogisticPathBase): """Adaptive logistic MCP.""" _final_cls = LogisticMCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptiveLogisticSCADPathRegressor(_AdaptiveLogisticPathBase): """Adaptive logistic SCAD.""" _final_cls = LogisticSCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
class _AdaptiveLogisticPathCVBase(_LogisticPathCVMixin, BaseEstimator, ClassifierMixin): """Logistic adaptive CV: pilot weights once on full data, K-fold CV on the final fit with those fixed weights. Subclass sets `_final_cls` and `_extra_kwargs()`.""" coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_base_path(self, **overrides): kw: dict[str, Any] = dict( lambdas=self.lambdas, n_lambdas=self.n_lambdas, lambda_min_ratio=self.lambda_min_ratio, weights=self.weights_, max_iter=self.max_iter, tol=self.tol, max_outer=self.max_outer, outer_tol=self.outer_tol, fit_intercept=self.fit_intercept, standardize=self.standardize, acceleration=self.acceleration, ) kw.update(self._extra_kwargs()) kw.update(overrides) return self._final_cls(**kw) def fit(self, x, y): beta_pilot = _fit_pilot_logistic( x, y, self.n_pilot_lambdas, self.pilot_position, self.fit_intercept, self.standardize, ) self.coef_pilot_ = beta_pilot self.weights_ = _adaptive_weights(beta_pilot, self.eta, self.eps_pilot) return super().fit(x, y) def _adaptive_logistic_cv_init( self, gamma_or_a, gamma_or_a_attr, *, eta, eps_pilot, n_pilot_lambdas, pilot_position, cv, random_state, lambdas, n_lambdas, lambda_min_ratio, max_iter, tol, max_outer, outer_tol, fit_intercept, standardize, acceleration, ): setattr(self, gamma_or_a_attr, gamma_or_a) self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration
[docs] class AdaptiveLogisticLassoPathCV(_AdaptiveLogisticPathCVBase): """K-fold CV over an adaptive logistic-lasso path.""" _final_cls = LogisticLassoPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration
[docs] class AdaptiveLogisticMCPPathCV(_AdaptiveLogisticPathCVBase): """K-fold CV over an adaptive logistic-MCP path.""" _final_cls = LogisticMCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: _adaptive_logistic_cv_init( self, gamma, "gamma", eta=eta, eps_pilot=eps_pilot, n_pilot_lambdas=n_pilot_lambdas, pilot_position=pilot_position, cv=cv, random_state=random_state, lambdas=lambdas, n_lambdas=n_lambdas, lambda_min_ratio=lambda_min_ratio, max_iter=max_iter, tol=tol, max_outer=max_outer, outer_tol=outer_tol, fit_intercept=fit_intercept, standardize=standardize, acceleration=acceleration, ) def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptiveLogisticSCADPathCV(_AdaptiveLogisticPathCVBase): """K-fold CV over an adaptive logistic-SCAD path.""" _final_cls = LogisticSCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: _adaptive_logistic_cv_init( self, a, "a", eta=eta, eps_pilot=eps_pilot, n_pilot_lambdas=n_pilot_lambdas, pilot_position=pilot_position, cv=cv, random_state=random_state, lambdas=lambdas, n_lambdas=n_lambdas, lambda_min_ratio=lambda_min_ratio, max_iter=max_iter, tol=tol, max_outer=max_outer, outer_tol=outer_tol, fit_intercept=fit_intercept, standardize=standardize, acceleration=acceleration, ) def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
# ---- Poisson ----------------------------------------------------------- class _AdaptivePoissonPathBase(BaseEstimator, RegressorMixin): """Adaptive Poisson path: same pattern as logistic but uses `PoissonLassoPathRegressor` / `PoissonMCPPathRegressor` / `PoissonSCADPathRegressor` for the final.""" coefs_: NDArray[np.float64] intercepts_: NDArray[np.float64] lambdas_: NDArray[np.float64] coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] info_: dict[str, Any] n_features_in_: int _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_final(self, weights): kw: dict[str, Any] = dict( lambdas=self.lambdas, n_lambdas=self.n_lambdas, lambda_min_ratio=self.lambda_min_ratio, weights=weights, max_iter=self.max_iter, tol=self.tol, fit_intercept=self.fit_intercept, standardize=self.standardize, acceleration=self.acceleration, max_outer=self.max_outer, outer_tol=self.outer_tol, ) kw.update(self._extra_kwargs()) return self._final_cls(**kw) def fit(self, x, y) -> "_AdaptivePoissonPathBase": beta_pilot = _fit_pilot_poisson( x, y, self.n_pilot_lambdas, self.pilot_position, self.fit_intercept, self.standardize, ) weights = _adaptive_weights(beta_pilot, self.eta, self.eps_pilot) final = self._make_final(weights).fit(x, y) self._final_estimator_ = final self.coefs_ = final.coefs_ self.intercepts_ = final.intercepts_ self.lambdas_ = final.lambdas_ self.coef_pilot_ = beta_pilot self.weights_ = weights self.info_ = final.info_ self.n_features_in_ = final.n_features_in_ return self def decision_function(self, x): return self._final_estimator_.decision_function(x) def predict(self, x): return self._final_estimator_.predict(x)
[docs] class AdaptivePoissonLassoPathRegressor(_AdaptivePoissonPathBase): """Adaptive Poisson lasso.""" _final_cls = PoissonLassoPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration
[docs] class AdaptivePoissonMCPPathRegressor(_AdaptivePoissonPathBase): """Adaptive Poisson MCP.""" _final_cls = PoissonMCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptivePoissonSCADPathRegressor(_AdaptivePoissonPathBase): """Adaptive Poisson SCAD.""" _final_cls = PoissonSCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
class _AdaptivePoissonPathCVBase(_PoissonPathCVMixin, BaseEstimator, RegressorMixin): coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_base_path(self, **overrides): kw: dict[str, Any] = dict( lambdas=self.lambdas, n_lambdas=self.n_lambdas, lambda_min_ratio=self.lambda_min_ratio, weights=self.weights_, max_iter=self.max_iter, tol=self.tol, max_outer=self.max_outer, outer_tol=self.outer_tol, fit_intercept=self.fit_intercept, standardize=self.standardize, acceleration=self.acceleration, ) kw.update(self._extra_kwargs()) kw.update(overrides) return self._final_cls(**kw) def fit(self, x, y): beta_pilot = _fit_pilot_poisson( x, y, self.n_pilot_lambdas, self.pilot_position, self.fit_intercept, self.standardize, ) self.coef_pilot_ = beta_pilot self.weights_ = _adaptive_weights(beta_pilot, self.eta, self.eps_pilot) return super().fit(x, y)
[docs] class AdaptivePoissonLassoPathCV(_AdaptivePoissonPathCVBase): """K-fold CV over an adaptive Poisson-lasso path.""" _final_cls = PoissonLassoPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration
[docs] class AdaptivePoissonMCPPathCV(_AdaptivePoissonPathCVBase): """K-fold CV over an adaptive Poisson-MCP path.""" _final_cls = PoissonMCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptivePoissonSCADPathCV(_AdaptivePoissonPathCVBase): """K-fold CV over an adaptive Poisson-SCAD path.""" _final_cls = PoissonSCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, fit_intercept: bool = True, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.fit_intercept = fit_intercept self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
# ---- Cox (no intercept; fit(x, time, event)) --------------------------- class _AdaptiveCoxPathBase(BaseEstimator, RegressorMixin): """Adaptive Cox path. `fit(x, time, event)`. No intercept (baseline hazard absorbs). `predict(x) = decision_function(x) = Xβ` (Cox prognostic index) per the existing `CoxPathRegressor` convention.""" coefs_: NDArray[np.float64] lambdas_: NDArray[np.float64] coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] info_: dict[str, Any] n_features_in_: int _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_final(self, weights): kw: dict[str, Any] = dict( lambdas=self.lambdas, n_lambdas=self.n_lambdas, lambda_min_ratio=self.lambda_min_ratio, weights=weights, max_iter=self.max_iter, tol=self.tol, standardize=self.standardize, acceleration=self.acceleration, max_outer=self.max_outer, outer_tol=self.outer_tol, ) kw.update(self._extra_kwargs()) return self._final_cls(**kw) def fit(self, x, time, event) -> "_AdaptiveCoxPathBase": beta_pilot = _fit_pilot_cox( x, time, event, self.n_pilot_lambdas, self.pilot_position, self.standardize, ) weights = _adaptive_weights(beta_pilot, self.eta, self.eps_pilot) final = self._make_final(weights).fit(x, time, event) self._final_estimator_ = final self.coefs_ = final.coefs_ self.lambdas_ = final.lambdas_ self.coef_pilot_ = beta_pilot self.weights_ = weights self.info_ = final.info_ self.n_features_in_ = final.n_features_in_ return self def decision_function(self, x): return self._final_estimator_.decision_function(x) def predict(self, x): return self._final_estimator_.predict(x)
[docs] class AdaptiveCoxLassoPathRegressor(_AdaptiveCoxPathBase): """Adaptive Cox lasso.""" _final_cls = CoxMCPPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": 1e9}
[docs] class AdaptiveCoxMCPPathRegressor(_AdaptiveCoxPathBase): """Adaptive Cox MCP.""" _final_cls = CoxMCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptiveCoxSCADPathRegressor(_AdaptiveCoxPathBase): """Adaptive Cox SCAD.""" _final_cls = CoxSCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
class _AdaptiveCoxPathCVBase(_CoxPathCVMixin, BaseEstimator): """Cox adaptive CV: uses StratifiedKFold by event indicator (inherited from `_CoxPathCVMixin`) and Harrell c-index scoring. Pilot weights are computed once on full data.""" coef_pilot_: NDArray[np.float64] weights_: NDArray[np.float64] _final_cls: Any def _extra_kwargs(self) -> dict[str, Any]: return {} def _make_base_path(self, **overrides): kw: dict[str, Any] = dict( lambdas=self.lambdas, n_lambdas=self.n_lambdas, lambda_min_ratio=self.lambda_min_ratio, weights=self.weights_, max_iter=self.max_iter, tol=self.tol, max_outer=self.max_outer, outer_tol=self.outer_tol, standardize=self.standardize, acceleration=self.acceleration, ) kw.update(self._extra_kwargs()) kw.update(overrides) return self._final_cls(**kw) def fit(self, x, time, event): beta_pilot = _fit_pilot_cox( x, time, event, self.n_pilot_lambdas, self.pilot_position, self.standardize, ) self.coef_pilot_ = beta_pilot self.weights_ = _adaptive_weights(beta_pilot, self.eta, self.eps_pilot) return super().fit(x, time, event)
[docs] class AdaptiveCoxLassoPathCV(_AdaptiveCoxPathCVBase): """K-fold CV over an adaptive Cox-lasso path.""" _final_cls = CoxMCPPathRegressor def __init__( self, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": 1e9}
[docs] class AdaptiveCoxMCPPathCV(_AdaptiveCoxPathCVBase): """K-fold CV over an adaptive Cox-MCP path.""" _final_cls = CoxMCPPathRegressor def __init__( self, gamma: float = 3.0, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.gamma = gamma self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"gamma": self.gamma}
[docs] class AdaptiveCoxSCADPathCV(_AdaptiveCoxPathCVBase): """K-fold CV over an adaptive Cox-SCAD path.""" _final_cls = CoxSCADPathRegressor def __init__( self, a: float = 3.7, *, eta: float = 1.0, eps_pilot: float = 1e-6, n_pilot_lambdas: int = 10, pilot_position: PilotPosition | int = "mid", cv: Any = 5, random_state: int | None = None, lambdas: NDArray[np.float64] | None = None, n_lambdas: int = 100, lambda_min_ratio: float = 1e-3, max_iter: int = 100, tol: float = 1e-6, max_outer: int = 10, outer_tol: float = 1e-6, standardize: bool = False, acceleration: int | None = 5, ) -> None: self.a = a self.eta = eta self.eps_pilot = eps_pilot self.n_pilot_lambdas = n_pilot_lambdas self.pilot_position = pilot_position self.cv = cv self.random_state = random_state self.lambdas = lambdas self.n_lambdas = n_lambdas self.lambda_min_ratio = lambda_min_ratio self.max_iter = max_iter self.tol = tol self.max_outer = max_outer self.outer_tol = outer_tol self.standardize = standardize self.acceleration = acceleration def _extra_kwargs(self) -> dict[str, Any]: return {"a": self.a}
__all__ = [ "AdaptiveLassoPathRegressor", "AdaptiveMCPPathRegressor", "AdaptiveSCADPathRegressor", "AdaptiveLassoPathCV", "AdaptiveMCPPathCV", "AdaptiveSCADPathCV", "AdaptiveGroupLassoPathRegressor", "AdaptiveGroupMCPPathRegressor", "AdaptiveGroupSCADPathRegressor", "AdaptiveGroupLassoPathCV", "AdaptiveGroupMCPPathCV", "AdaptiveGroupSCADPathCV", "AdaptiveLogisticLassoPathRegressor", "AdaptiveLogisticMCPPathRegressor", "AdaptiveLogisticSCADPathRegressor", "AdaptiveLogisticLassoPathCV", "AdaptiveLogisticMCPPathCV", "AdaptiveLogisticSCADPathCV", "AdaptivePoissonLassoPathRegressor", "AdaptivePoissonMCPPathRegressor", "AdaptivePoissonSCADPathRegressor", "AdaptivePoissonLassoPathCV", "AdaptivePoissonMCPPathCV", "AdaptivePoissonSCADPathCV", "AdaptiveCoxLassoPathRegressor", "AdaptiveCoxMCPPathRegressor", "AdaptiveCoxSCADPathRegressor", "AdaptiveCoxLassoPathCV", "AdaptiveCoxMCPPathCV", "AdaptiveCoxSCADPathCV", ]