diff --git a/CHANGELOG b/CHANGELOG index 679df31ae..7d9c0e581 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,8 @@ The rules for CHANGELOG file: 0.3.0 (XXXX/XX/XX) ------------------ +- Fixed moved function import from scipy and bump scipy dependency to 1.15.0 (#236) +- Fix rendering issues for `SparseKDE` and `QuickShift` (#236) - Updating ``FPS`` to allow a numpy array of ints as an initialize parameter (#145) - Supported Python versions are now ranging from 3.9 - 3.12. - Updating ``skmatter.datasets`` submodule to support sklearn 1.5.0 (#229) diff --git a/pyproject.toml b/pyproject.toml index 20399c980..4290b257a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,6 +39,7 @@ classifiers = [ ] dependencies = [ "scikit-learn>=1.1.0", + "scipy >= 1.15.0", # explicit here since need a newer version as scikit-learn ] dynamic = ["version"] diff --git a/src/skmatter/clustering/_quick_shift.py b/src/skmatter/clustering/_quick_shift.py index 92fc4eb6e..1e7b1c0d3 100644 --- a/src/skmatter/clustering/_quick_shift.py +++ b/src/skmatter/clustering/_quick_shift.py @@ -1,7 +1,6 @@ -from typing import Callable, Union +from typing import Callable, Optional import numpy as np -from numpy.typing import ArrayLike from sklearn.base import BaseEstimator from tqdm import tqdm @@ -34,19 +33,19 @@ class QuickShift(BaseEstimator): scale : float, default=1.0 Distance cutoff scaling factor used during the QS clustering. It will be squared since the squared distance is used in this class. - metric : Callable[[ArrayLike, ArrayLike, bool, dict], ArrayLike], \ - default= :func:`skmatter.metrics.pairwise_euclidean_distances()` + metric : Callable, default=None The metric to use. Your metric should be able to take at least three arguments in secquence: `X`, `Y`, and `squared=True`. Here, `X` and `Y` are two array-like of shape (n_samples, n_components). The return of the metric is an array-like of shape (n_samples, n_samples). If you want to use periodic boundary conditions, be sure to provide the cell length in the ``metric_params`` and - provide a metric that can take the cell argument. + provide a metric that can take the cell argument. If :obj:`None`, the + :func:`skmatter.metrics.periodic_pairwise_euclidean_distances()` is used. metric_params : dict, default=None Additional parameters to be passed to the use of metric. i.e. the dimension of a rectangular cell of side length :math:`a_i` - for :func:`skmatter.metrics.pairwise_euclidean_distances()` - `{'cell_length': [a_1, a_2, ..., a_n]}` + for :func:`skmatter.metrics.periodic_pairwise_euclidean_distances()` + ``{'cell_length': [a_1, a_2, ..., a_n]}`` Attributes ---------- @@ -97,13 +96,11 @@ class QuickShift(BaseEstimator): def __init__( self, - dist_cutoff_sq: Union[float, None] = None, - gabriel_shell: Union[int, None] = None, + dist_cutoff_sq: Optional[float] = None, + gabriel_shell: Optional[int] = None, scale: float = 1.0, - metric: Callable[ - [ArrayLike, ArrayLike, bool, dict], ArrayLike - ] = periodic_pairwise_euclidean_distances, - metric_params: Union[dict, None] = None, + metric: Optional[Callable] = None, + metric_params: Optional[dict] = None, ): if (dist_cutoff_sq is None) and (gabriel_shell is None): raise ValueError("Either dist_cutoff or gabriel_depth must be set.") @@ -115,6 +112,10 @@ def __init__( self.metric_params = ( metric_params if metric_params is not None else {"cell_length": None} ) + + if metric is None: + metric = periodic_pairwise_euclidean_distances + self.metric = lambda X, Y: metric(X, Y, squared=True, **self.metric_params) if isinstance(self.metric_params, dict): self.cell = self.metric_params["cell_length"] diff --git a/src/skmatter/neighbors/_sparsekde.py b/src/skmatter/neighbors/_sparsekde.py index b98c6a9e1..bbb7c1ea9 100644 --- a/src/skmatter/neighbors/_sparsekde.py +++ b/src/skmatter/neighbors/_sparsekde.py @@ -1,8 +1,7 @@ import warnings -from typing import Callable, Union +from typing import Callable, Optional, Union import numpy as np -from numpy.typing import ArrayLike from scipy.special import logsumexp as LSE from sklearn.base import BaseEstimator from sklearn.utils.validation import check_is_fitted, check_random_state @@ -33,19 +32,18 @@ class SparseKDE(BaseEstimator): weights: numpy.ndarray, default=None Weights of the descriptors. If None, all weights are set to `1/n_descriptors`. - metric : Callable[[ArrayLike, ArrayLike, bool, dict], ArrayLike], - default=:func:`skmatter.metrics.pairwise_euclidean_distances()` + metric : Callable, default=None The metric to use. Your metric should be able to take at least three arguments in secquence: `X`, `Y`, and `squared=True`. Here, `X` and `Y` are two array-like of shape (n_samples, n_components). The return of the metric is an array-like of - shape (n_samples, n_samples). If you want to use periodic boundary - conditions, be sure to provide the cell size in the metric_params and - provide a metric that can take the cell argument. + shape (n_samples, n_samples). If you want to use periodic boundary conditions, + be sure to provide the cell size in the metric_params and provide a metric that + can take the cell argument. If :obj:`None`, the + :func:`skmatter.metrics.periodic_pairwise_euclidean_distances()` is used. metric_params : dict, default=None - Additional parameters to be passed to the use of - metric. i.e. the cell dimension for - :func:`skmatter.metrics.pairwise_euclidean_distances()` - `{'cell_length': [side_length_1, ..., side_length_n]}` + Additional parameters to be passed to the use of metric. i.e. the cell + dimension for :func:`skmatter.metrics.periodic_pairwise_euclidean_distances()` + ``{'cell_length': [side_length_1, ..., side_length_n]}`` fspread : float, default=-1.0 The fractional "space" occupied by the voronoi cell of each grid. Use this when each cell is of a similar size. @@ -106,11 +104,9 @@ class SparseKDE(BaseEstimator): def __init__( self, descriptors: np.ndarray, - weights: Union[np.ndarray, None] = None, - metric: Callable[ - [ArrayLike, ArrayLike, bool, dict], ArrayLike - ] = periodic_pairwise_euclidean_distances, - metric_params: Union[dict, None] = None, + weights: Optional[np.ndarray] = None, + metric: Optional[Callable] = None, + metric_params: Optional[dict] = None, fspread: float = -1.0, fpoints: float = 0.15, kernel: str = "gaussian", @@ -119,6 +115,10 @@ def __init__( self.metric_params = ( metric_params if metric_params is not None else {"cell_length": None} ) + + if metric is None: + metric = periodic_pairwise_euclidean_distances + self.metric = lambda X, Y: metric(X, Y, squared=True, **self.metric_params) self.cell = metric_params["cell_length"] if metric_params is not None else None self._check_dimension(descriptors) diff --git a/src/skmatter/sample_selection/_base.py b/src/skmatter/sample_selection/_base.py index f5531d897..67d5f0472 100644 --- a/src/skmatter/sample_selection/_base.py +++ b/src/skmatter/sample_selection/_base.py @@ -4,7 +4,7 @@ import numpy as np from scipy.interpolate import LinearNDInterpolator, interp1d -from scipy.interpolate.interpnd import _ndim_coords_from_arrays +from scipy.interpolate._interpnd import _ndim_coords_from_arrays from scipy.spatial import ConvexHull from sklearn.utils.validation import check_array, check_is_fitted, check_X_y