Skip to content

Commit

Permalink
fix some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
PicoCentauri committed Feb 13, 2025
1 parent 7f80d78 commit feb10b0
Show file tree
Hide file tree
Showing 17 changed files with 105 additions and 79 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
python-version: "3.13"

- name: install tests dependencies
run: python -m pip install tox
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
python-version: "3.13"

- name: install tests dependencies
run: python -m pip install tox
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
python-version: "3.13"

- name: install tests dependencies
run: python -m pip install tox
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-22.04, macos-14, windows-2022]
python-version: ["3.9", "3.12"]
python-version: ["3.0", "3.13"]

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.coverage*
*.pyc
*.ipynb_checkpoints*
__pycache__
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
python: "3.12"
python: "3.13"

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
5 changes: 1 addition & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ authors = [
{name = "Michele Ceriotti"}
]
readme = "README.rst"
requires-python = ">=3.9"
requires-python = ">=3.10"
license = {text = "BSD-3-Clause"}
classifiers = [
"Development Status :: 4 - Beta",
Expand Down Expand Up @@ -75,9 +75,6 @@ include = [
[tool.coverage.xml]
output = 'tests/coverage.xml'

[tool.pytest.ini_options]
testpaths = "tests"

[tool.isort]
skip = "__init__.py"
profile = "black"
Expand Down
22 changes: 9 additions & 13 deletions src/skmatter/_selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,17 +561,15 @@ def score(self, X, y=None):
Parameters
----------
X : numpy.ndarray of shape [n_samples, n_features]
The input samples.
X : ignored
y : ignored
Returns
-------
score : numpy.ndarray of (n_to_select_from_)
:math:`\pi` importance for the given samples or features
"""
X, y = validate_data(self, X, y, reset=False)

validate_data(self, X, y, reset=False) # present for API consistency
return self.pi_

def _init_greedy_search(self, X, y, n_to_select):
Expand Down Expand Up @@ -746,8 +744,7 @@ def score(self, X, y=None):
score : numpy.ndarray of (n_to_select_from_)
:math:`\pi` importance for the given samples or features
"""
X, y = validate_data(self, X, y, reset=False)

validate_data(self, X, y, reset=False) # present for API consistency
return self.pi_

def _init_greedy_search(self, X, y, n_to_select):
Expand Down Expand Up @@ -941,8 +938,7 @@ def score(self, X, y=None):
-------
hausdorff : Hausdorff distances
"""
X, y = validate_data(self, X, y, reset=False)

validate_data(self, X, y, reset=False)
return self.hausdorff_

def get_distance(self):
Expand Down Expand Up @@ -1079,15 +1075,16 @@ def __init__(
)

def fit(self, X, y=None, warm_start=False):

if self.mixing == 1.0:
raise ValueError(
"Mixing = 1.0 corresponds to traditional FPS."
"Please use the FPS class."
"Mixing = 1.0 corresponds to traditional FPS. Please use the FPS class."
)

return super().fit(X, y)

# docstring is inherited and set from the base class
fit.__doc__ = GreedySelector.fit.__doc__

def score(self, X, y=None):
"""Returns the Hausdorff distances of all samples to previous selections.
Expand All @@ -1104,8 +1101,7 @@ def score(self, X, y=None):
-------
hausdorff : Hausdorff distances
"""
X, y = validate_data(self, X, y, reset=False)

validate_data(self, X, y, reset=False)
return self.hausdorff_

def get_distance(self):
Expand Down
2 changes: 1 addition & 1 deletion src/skmatter/decomposition/_kernel_pcovr.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ def score(self, X, y):
"""
check_is_fitted(self, ["pkt_", "X_fit_"])

X, y = validate_data(self, X, y, reset=False)
X = validate_data(self, X, reset=False)

K_NN = self._get_kernel(self.X_fit_, self.X_fit_)
K_VN = self._get_kernel(X, self.X_fit_)
Expand Down
4 changes: 2 additions & 2 deletions src/skmatter/decomposition/_pcovr.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from sklearn.decomposition._pca import _infer_dimension
from sklearn.linear_model import LinearRegression, Ridge, RidgeCV
from sklearn.linear_model._base import LinearModel
from sklearn.utils import check_random_state
from sklearn.utils import check_array, check_random_state
from sklearn.utils._arpack import _init_arpack_v0
from sklearn.utils.extmath import randomized_svd, stable_cumsum, svd_flip
from sklearn.utils.validation import check_is_fitted, validate_data
Expand Down Expand Up @@ -585,7 +585,7 @@ def predict(self, X=None, T=None):
X = validate_data(self, X, reset=False)
return X @ self.pxy_
else:
T = validate_data(self, T, reset=False)
T = check_array(T)
return T @ self.pty_

def transform(self, X=None):
Expand Down
109 changes: 69 additions & 40 deletions src/skmatter/utils/_pcovr_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,30 @@


def check_lr_fit(regressor, X, y):
r"""
"""
Checks that a (linear) regressor is fitted, and if not,
fits it with the provided data
:param regressor: sklearn-style regressor
:type regressor: object
:param X: feature matrix with which to fit the regressor
if it is not already fitted
:type X: array
:param y: target values with which to fit the regressor
if it is not already fitted
:type y: array
fits it with the provided data.
Parameters
----------
regressor : object
sklearn-style regressor
X : array-like
Feature matrix with which to fit the regressor if it is not already fitted
y : array-like
Target values with which to fit the regressor if it is not already fitted
Returns
-------
fitted_regressor : object
The fitted regressor. If input regressor was already fitted and compatible with
the data, returns a deep copy. Otherwise returns a newly fitted regressor.
Raises
------
ValueError
If the fitted regressor's coefficients dimensions are incompatible with the
target space.
"""
try:
check_is_fitted(regressor)
Expand All @@ -32,18 +44,18 @@ def check_lr_fit(regressor, X, y):
# Check compatibility with y
if fitted_regressor.coef_.ndim != y.ndim:
raise ValueError(
"The regressor coefficients have a dimension incompatible "
"with the supplied target space. "
"The coefficients have dimension %d and the targets "
"have dimension %d" % (fitted_regressor.coef_.ndim, y.ndim)
"The regressor coefficients have a dimension incompatible with the "
"supplied target space. The coefficients have dimension "
f"{fitted_regressor.coef_.ndim} and the targets have dimension "
f"{y.ndim}"
)
elif y.ndim == 2:
if fitted_regressor.coef_.shape[0] != y.shape[1]:
raise ValueError(
"The regressor coefficients have a shape incompatible "
"with the supplied target space. "
"The coefficients have shape %r and the targets "
"have shape %r" % (fitted_regressor.coef_.shape, y.shape)
"The regressor coefficients have a shape incompatible with the "
"supplied target space. The coefficients have shape "
f"{fitted_regressor.coef_.shape} and the targets have shape "
f"{y.shape}"
)

except NotFittedError:
Expand All @@ -54,20 +66,37 @@ def check_lr_fit(regressor, X, y):


def check_krr_fit(regressor, K, X, y):
r"""
"""
Checks that a (kernel ridge) regressor is fitted, and if not,
fits it with the provided data
:param regressor: sklearn-style regressor
:type regressor: object
:param K: kernel matrix with which to fit the regressor
if it is not already fitted
:type K: array
:param X: feature matrix with which to check the regressor
:type X: array
:param y: target values with which to fit the regressor
if it is not already fitted
:type y: array
fits it with the provided data.
Parameters
----------
regressor : object
sklearn-style regressor
K : array-like
Kernel matrix with which to fit the regressor if it is not already fitted
X : array-like
Feature matrix with which to check the regressor
y : array-like
Target values with which to fit the regressor if it is not already fitted
Returns
-------
fitted_regressor : object
The fitted regressor. If input regressor was already fitted and compatible with
the data, returns a deep copy. Otherwise returns a newly fitted regressor.
Raises
------
ValueError
If the fitted regressor's coefficients dimensions are incompatible with the
target space.
Notes
-----
For unfitted regressors, sets the kernel to "precomputed" before fitting with the
provided kernel matrix K to avoid recomputation.
"""
try:
check_is_fitted(regressor)
Expand All @@ -79,18 +108,18 @@ def check_krr_fit(regressor, K, X, y):
# Check compatibility with y
if fitted_regressor.dual_coef_.ndim != y.ndim:
raise ValueError(
"The regressor coefficients have a dimension incompatible "
"with the supplied target space. "
"The coefficients have dimension %d and the targets "
"have dimension %d" % (fitted_regressor.dual_coef_.ndim, y.ndim)
"The regressor coefficients have a dimension incompatible with the "
"supplied target space. The coefficients have dimension "
f"{fitted_regressor.dual_coef_.ndim} and the targets have dimension "
f"{y.ndim}"
)
elif y.ndim == 2:
if fitted_regressor.dual_coef_.shape[1] != y.shape[1]:
raise ValueError(
"The regressor coefficients have a shape incompatible "
"with the supplied target space. "
"The coefficients have shape %r and the targets "
"have shape %r" % (fitted_regressor.dual_coef_.shape, y.shape)
"The regressor coefficients have a shape incompatible with the "
"supplied target space. The coefficients have shape "
f"{fitted_regressor.dual_coef_.shape} and the targets have shape "
f"{y.shape}"
)

except NotFittedError:
Expand Down
Binary file not shown.
5 changes: 3 additions & 2 deletions tests/test_feature_pcov_fps.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ def test_restart(self):

def test_no_mixing_1(self):
"""Check that the model throws an error when mixing = 1.0."""
selector = PCovFPS(n_to_select=1, mixing=1.0)
with self.assertRaises(ValueError) as cm:
_ = PCovFPS(n_to_select=1, mixing=1.0)
selector.fit(self.X, y=self.y)
self.assertEqual(
str(cm.exception),
"Mixing = 1.0 corresponds to traditional FPS." "Please use the FPS class.",
"Mixing = 1.0 corresponds to traditional FPS. Please use the FPS class.",
)


Expand Down
8 changes: 4 additions & 4 deletions tests/test_greedy_selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def test_bad_transform(self):
_ = selector.transform(self.X[:, :3])
self.assertEqual(
str(cm.exception),
"X has a different shape than during fitting. Reshape your data.",
"X has 3 features, but GreedyTester is expecting 10 features as input.",
)

def test_no_nfeatures(self):
Expand Down Expand Up @@ -124,8 +124,8 @@ def test_size_input(self):
selector_feature.fit(X)
self.assertEqual(
str(cm.exception),
f"Found array with 1 feature(s) (shape={X.shape})"
" while a minimum of 2 is required.",
f"Found array with 1 feature(s) (shape={X.shape}) while a minimum of 2 is "
"required by GreedyTester.",
)

X = X.reshape(1, -1)
Expand All @@ -135,7 +135,7 @@ def test_size_input(self):
self.assertEqual(
str(cm.exception),
f"Found array with 1 sample(s) (shape={X.shape}) while a minimum of 2 is "
"required.",
"required by GreedyTester.",
)


Expand Down
4 changes: 3 additions & 1 deletion tests/test_kernel_pcovr.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ def test_centerer(self):
self.assertTrue(hasattr(kpcovr, "centerer_"))
_ = kpcovr.predict(self.X)
_ = kpcovr.transform(self.X)

print(self.Y.shape)
_ = kpcovr.score(self.X, self.Y)

def test_prefit_regressor(self):
Expand Down Expand Up @@ -255,7 +257,7 @@ def test_incompatible_coef_shape(self):

# Dimension mismatch
with self.assertRaises(ValueError) as cm:
kpcovr.fit(self.X, self.Y[:, 0])
kpcovr.fit(self.X, np.zeros(self.Y.shape + (2,)))
self.assertTrue(
str(cm.exception),
"The regressor coefficients have a dimension incompatible "
Expand Down
9 changes: 4 additions & 5 deletions tests/test_pcovr.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,13 +491,12 @@ def test_incompatible_coef_shape(self):

# Dimension mismatch
with self.assertRaises(ValueError) as cm:
pcovr.fit(self.X, self.Y.squeeze())
pcovr.fit(self.X, np.zeros((self.Y.shape[0], 2)))
self.assertEqual(
str(cm.exception),
"The regressor coefficients have a dimension incompatible "
"with the supplied target space. "
"The coefficients have dimension %d and the targets "
"have dimension %d" % (regressor.coef_.ndim, self.Y.squeeze().ndim),
"The regressor coefficients have a dimension incompatible with the "
"supplied target space. The coefficients have dimension 1 and the targets "
"have dimension 2",
)

# Shape mismatch (number of targets)
Expand Down
Loading

0 comments on commit feb10b0

Please sign in to comment.