Skip to content

Commit

Permalink
STY: fix more mypy errors
Browse files Browse the repository at this point in the history
  • Loading branch information
lbluque committed Sep 18, 2023
1 parent 1f2279c commit f023eb4
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 35 deletions.
4 changes: 2 additions & 2 deletions src/sparselm/_utils/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from numpy.typing import NDArray


def _check_groups(groups: NDArray, n_features: int) -> None:
def _check_groups(groups: NDArray | None, n_features: int) -> None:
"""Check that groups are 1D and of the correct length.
Args:
Expand All @@ -30,7 +30,7 @@ def _check_groups(groups: NDArray, n_features: int) -> None:
)


def _check_group_weights(group_weights: NDArray, n_groups: int) -> None:
def _check_group_weights(group_weights: NDArray | None, n_groups: int) -> None:
"""Check that group weights are 1D and of the correct length.
Args:
Expand Down
18 changes: 10 additions & 8 deletions src/sparselm/model/_adaptive_lasso.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def _get_weights_value(parameters: SimpleNamespace) -> NDArray[np.floating]:

def _check_convergence(
self, parameters: SimpleNamespace, previous_weights: NDArray
) -> bool:
) -> np.bool_ | bool:
"""Check if weights have converged to set tolerance."""
current_weights = parameters.adaptive_weights.value
return np.linalg.norm(current_weights - previous_weights) <= self.tol
Expand All @@ -198,13 +198,13 @@ def _iterative_update(
) -> None:
"""Update the adaptive weights."""
update = self._get_update_function()
parameters.adaptive_weights.value = self.alpha * update(beta, self.eps)
parameters.adaptive_weights.value = self.alpha * update(beta, self.eps) # type: ignore

def _solve(
self, X: NDArray, y: NDArray, solver_options: dict, *args, **kwargs
) -> NDArray[np.floating]:
"""Solve Lasso problem iteratively adaptive weights."""
previous_weights = self._get_weights_value(self.canonicals_.parameters)
previous_weights = self._get_weights_value(self.canonicals_.parameters) # type: ignore
for i in range(self.max_iter):
self.canonicals_.problem.solve(
solver=self.solver, warm_start=self.warm_start, **solver_options
Expand All @@ -218,13 +218,13 @@ def _solve(
self.n_iter_ = i + 1 # save number of iterations for sklearn
self._iterative_update(
self.canonicals_.beta.value,
self.canonicals_.parameters,
self.canonicals_.parameters, # type: ignore
self.canonicals_.auxiliaries,
)
# check convergence
if self._check_convergence(self.canonicals_.parameters, previous_weights):
if self._check_convergence(self.canonicals_.parameters, previous_weights): # type: ignore
break
previous_weights = self._get_weights_value(self.canonicals_.parameters)
previous_weights = self._get_weights_value(self.canonicals_.parameters) # type: ignore
return self.canonicals_.beta.value


Expand Down Expand Up @@ -446,7 +446,7 @@ class AdaptiveOverlapGroupLasso(OverlapGroupLasso, AdaptiveGroupLasso):

def __init__(
self,
group_list: list[list[int]] = None,
group_list: list[list[int]] | None = None,
alpha: float = 1.0,
group_weights: NDArray | None = None,
max_iter: int = 3,
Expand Down Expand Up @@ -709,7 +709,9 @@ def _iterative_update(
)
parameters.adaptive_group_weights.value = (
self.canonicals_.parameters.lambda2.value * parameters.group_weights
) * update(auxiliaries.group_norms.value, self.eps)
) * update(
auxiliaries.group_norms.value, self.eps
) # type: ignore


class AdaptiveRidgedGroupLasso(AdaptiveGroupLasso, RidgedGroupLasso):
Expand Down
10 changes: 5 additions & 5 deletions src/sparselm/model/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def _preprocess_data(
if sample_weight is not None:
sample_weight = _check_sample_weight(sample_weight, X, dtype=X.dtype)
# rescale sample_weight to sum to number of samples
sample_weight = sample_weight * (X.shape[0] / np.sum(sample_weight))
sample_weight = sample_weight * (X.shape[0] / np.sum(sample_weight)) # type: ignore

X, y, X_offset, y_offset, X_scale = _preprocess_data(
X,
Expand Down Expand Up @@ -452,7 +452,7 @@ def generate_problem(

beta = cp.Variable(X.shape[1])
parameters = self._generate_params(X, y)
auxiliaries = self._generate_auxiliaries(X, y, beta, parameters)
auxiliaries = self._generate_auxiliaries(X, y, beta, parameters) # type: ignore
objective = self._generate_objective(X, y, beta, parameters, auxiliaries)
constraints = self._generate_constraints(X, y, beta, parameters, auxiliaries)
problem = cp.Problem(cp.Minimize(objective), constraints)
Expand All @@ -466,7 +466,7 @@ def generate_problem(
user_constraints=[],
)

def add_constraints(self, constraints: list[cp.Constraint | cp.Expression]) -> None:
def add_constraints(self, constraints: list[cp.Constraint]) -> None:
"""Add a constraint to the problem.
.. warning::
Expand Down Expand Up @@ -542,7 +542,7 @@ def _generate_objective(
tikhonov_w = np.eye(X.shape[1])

c0 = 2 * X.shape[0] # keeps hyperparameter scale independent
objective = super()._generate_objective(X, y, beta, parameters, auxiliaries)
objective += c0 * parameters.eta * cp.sum_squares(tikhonov_w @ beta)
objective = super()._generate_objective(X, y, beta, parameters, auxiliaries) # type: ignore
objective += c0 * parameters.eta * cp.sum_squares(tikhonov_w @ beta) # type: ignore

return objective
29 changes: 15 additions & 14 deletions src/sparselm/model/_lasso.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
__author__ = "Luis Barroso-Luque, Fengyu Xie"

import warnings
from collections.abc import Sequence
from numbers import Real
from types import SimpleNamespace
from typing import Any
Expand Down Expand Up @@ -114,7 +115,7 @@ def _generate_objective(
auxiliaries: SimpleNamespace | None = None,
) -> cp.Expression:
# can also use cp.norm2(X @ beta - y)**2 not sure whats better
reg = self._generate_regularization(X, beta, parameters, auxiliaries)
reg = self._generate_regularization(X, beta, parameters, auxiliaries) # type: ignore
objective = 1 / (2 * X.shape[0]) * cp.sum_squares(X @ beta - y) + reg
return objective

Expand Down Expand Up @@ -222,15 +223,15 @@ def _validate_params(self, X: NDArray, y: NDArray) -> None:
def _set_param_values(self) -> None:
super()._set_param_values()
if self.group_weights is not None:
self.canonicals_.parameters.group_weights = self.group_weights
self.canonicals_.parameters.group_weights = self.group_weights # type: ignore

def _generate_params(self, X: NDArray, y: NDArray) -> SimpleNamespace | None:
parameters = super()._generate_params(X, y)
n_groups = X.shape[1] if self.groups is None else len(np.unique(self.groups))
group_weights = (
np.ones(n_groups) if self.group_weights is None else self.group_weights
)
parameters.group_weights = group_weights
parameters.group_weights = group_weights # type: ignore
return parameters

@staticmethod
Expand Down Expand Up @@ -268,7 +269,7 @@ def _generate_regularization(
parameters: SimpleNamespace,
auxiliaries: SimpleNamespace | None = None,
) -> cp.Expression:
return parameters.alpha * (parameters.group_weights @ auxiliaries.group_norms)
return parameters.alpha * (parameters.group_weights @ auxiliaries.group_norms) # type: ignore


# TODO this implementation is not efficient, reimplement, or simply deprecate.
Expand Down Expand Up @@ -397,7 +398,7 @@ def _generate_params(self, X: NDArray, y: NDArray) -> SimpleNamespace | None:
group_weights = (
np.ones(n_groups) if self.group_weights is None else self.group_weights
)
parameters.group_weights = group_weights
parameters.group_weights = group_weights # type: ignore
return parameters

def generate_problem(
Expand Down Expand Up @@ -439,7 +440,7 @@ def generate_problem(
group_list = self.group_list

group_ids = np.sort(np.unique([gid for grp in group_list for gid in grp]))
beta_indices = [
beta_inds_list = [
[i for i, grp in enumerate(group_list) if grp_id in grp]
for grp_id in group_ids
]
Expand All @@ -449,10 +450,10 @@ def generate_problem(
* [
i,
]
for i, g in enumerate(beta_indices)
for i, g in enumerate(beta_inds_list)
]
)
beta_indices = np.concatenate(beta_indices)
beta_indices = np.concatenate(beta_inds_list)

X_ext = X[:, beta_indices]
beta = cp.Variable(X_ext.shape[1])
Expand Down Expand Up @@ -488,7 +489,7 @@ def _solve(self, X, y, solver_options, *args, **kwargs) -> NDArray[np.floating]:
[
sum(
self.canonicals_.beta.value[
self.canonicals_.auxiliaries.extended_coef_indices == i
self.canonicals_.auxiliaries.extended_coef_indices == i # type: ignore
]
)
for i in range(X.shape[1])
Expand Down Expand Up @@ -604,14 +605,14 @@ def _validate_params(self, X, y):

def _set_param_values(self) -> None:
super()._set_param_values()
self.canonicals_.parameters.lambda1.value = self.l1_ratio * self.alpha
self.canonicals_.parameters.lambda2.value = (1 - self.l1_ratio) * self.alpha
self.canonicals_.parameters.lambda1.value = self.l1_ratio * self.alpha # type: ignore
self.canonicals_.parameters.lambda2.value = (1 - self.l1_ratio) * self.alpha # type: ignore

def _generate_params(self, X: NDArray, y: NDArray) -> SimpleNamespace | None:
"""Generate parameters."""
parameters = super()._generate_params(X, y)
# del parameters.alpha # hacky but we don't need this
parameters.l1_ratio = self.l1_ratio # save simply for infomation
# save for information purposes
parameters.l1_ratio = self.l1_ratio # type: ignore
parameters.lambda1 = cp.Parameter(nonneg=True, value=self.l1_ratio * self.alpha)
parameters.lambda2 = cp.Parameter(
nonneg=True, value=(1 - self.l1_ratio) * self.alpha
Expand Down Expand Up @@ -711,7 +712,7 @@ def __init__(
self,
groups: NDArray | None = None,
alpha: float = 1.0,
delta: NDArray = (1.0,),
delta: NDArray | Sequence = (1.0,),
group_weights: NDArray | None = None,
standardize: bool = False,
fit_intercept: bool = False,
Expand Down
2 changes: 1 addition & 1 deletion src/sparselm/model/_miqp/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ def _generate_hierarchy_constraints(
z0_index = {gid: i for i, gid in enumerate(group_ids)}
constraints = [
z0[z0_index[high_id]] <= z0[z0_index[sub_id]]
for high_id, sub_ids in zip(group_ids, self.hierarchy)
for high_id, sub_ids in zip(group_ids, self.hierarchy) # type: ignore
for sub_id in sub_ids
]
return constraints
2 changes: 1 addition & 1 deletion src/sparselm/model/_miqp/_best_subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def _generate_constraints(
) -> list[cp.Constraint]:
"""Generate the constraints for best subset selection."""
constraints = super()._generate_constraints(X, y, beta, parameters, auxiliaries)
constraints += [cp.sum(auxiliaries.z0) <= parameters.sparse_bound]
constraints += [cp.sum(auxiliaries.z0) <= parameters.sparse_bound] # type: ignore
return constraints


Expand Down
11 changes: 7 additions & 4 deletions src/sparselm/model/_miqp/_regularized_l0.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ def _generate_objective(
c0 = 2 * X.shape[0] # keeps hyperparameter scale independent
objective = super()._generate_objective(
X, y, beta, parameters, auxiliaries
) + c0 * parameters.alpha * cp.sum(auxiliaries.z0)
) + c0 * parameters.alpha * cp.sum(
auxiliaries.z0
) # type: ignore
return objective


Expand Down Expand Up @@ -372,7 +374,7 @@ def _generate_auxiliaries(
"""Generate the boolean slack variable."""
auxiliaries = super()._generate_auxiliaries(X, y, beta, parameters)
X.shape[1] if self.groups is None else len(np.unique(self.groups))
auxiliaries.z1 = cp.Variable(X.shape[1])
auxiliaries.z1 = cp.Variable(X.shape[1]) # type: ignore
return auxiliaries

def _generate_constraints(
Expand All @@ -386,7 +388,7 @@ def _generate_constraints(
"""Generate the constraints used to solve l1l0 regularization."""
constraints = super()._generate_constraints(X, y, beta, parameters, auxiliaries)
# L1 constraints (why not do an l1 norm in the objective instead?)
constraints += [-auxiliaries.z1 <= beta, beta <= auxiliaries.z1]
constraints += [-auxiliaries.z1 <= beta, beta <= auxiliaries.z1] # type: ignore
return constraints

def _generate_objective(
Expand All @@ -400,7 +402,8 @@ def _generate_objective(
"""Generate the objective function used in l1l0 regression model."""
c0 = 2 * X.shape[0] # keeps hyperparameter scale independent
objective = super()._generate_objective(X, y, beta, parameters, auxiliaries)
objective += c0 * parameters.eta * cp.sum(auxiliaries.z1)
# L1 term
objective += c0 * parameters.eta * cp.sum(auxiliaries.z1) # type: ignore
return objective


Expand Down

0 comments on commit f023eb4

Please sign in to comment.