diff --git a/doc/whats_new/v1.1.rst b/doc/whats_new/v1.1.rst index 98108f175b8fe..20801abbcdcbc 100644 --- a/doc/whats_new/v1.1.rst +++ b/doc/whats_new/v1.1.rst @@ -15,6 +15,10 @@ Changelog - |Fix| A default HTML representation is shown for meta-estimators with invalid parameters. :pr:`24015` by `Thomas Fan`_. +- |Fix| Add support for F-contiguous arrays for estimators and functions whose back-end + have been changed in 1.1. + :pr:`23990` by :user:`Julien Jerphanion `. + :mod:`sklearn.base` ...................... diff --git a/sklearn/metrics/_pairwise_distances_reduction/_dispatcher.py b/sklearn/metrics/_pairwise_distances_reduction/_dispatcher.py index a79fde694a9ed..1cf670ed35dec 100644 --- a/sklearn/metrics/_pairwise_distances_reduction/_dispatcher.py +++ b/sklearn/metrics/_pairwise_distances_reduction/_dispatcher.py @@ -80,11 +80,18 @@ def is_usable_for(cls, X, Y, metric) -> bool: True if the PairwiseDistancesReduction can be used, else False. """ dtypes_validity = X.dtype == Y.dtype == np.float64 + c_contiguity = ( + hasattr(X, "flags") + and X.flags.c_contiguous + and hasattr(Y, "flags") + and Y.flags.c_contiguous + ) return ( get_config().get("enable_cython_pairwise_dist", True) and not issparse(X) and not issparse(Y) and dtypes_validity + and c_contiguity and metric in cls.valid_metrics() ) diff --git a/sklearn/metrics/tests/test_pairwise.py b/sklearn/metrics/tests/test_pairwise.py index f14c558d5a3c1..31964e2d182dd 100644 --- a/sklearn/metrics/tests/test_pairwise.py +++ b/sklearn/metrics/tests/test_pairwise.py @@ -534,6 +534,14 @@ def test_pairwise_distances_argmin_min(dtype): assert_array_equal(argmin_0, argmin_1) + # F-contiguous arrays must be supported and must return identical results. + argmin_C_contiguous = pairwise_distances_argmin(X, Y) + argmin_F_contiguous = pairwise_distances_argmin( + np.asfortranarray(X), np.asfortranarray(Y) + ) + + assert_array_equal(argmin_C_contiguous, argmin_F_contiguous) + def _reduce_func(dist, start): return dist[:, :100] diff --git a/sklearn/metrics/tests/test_pairwise_distances_reduction.py b/sklearn/metrics/tests/test_pairwise_distances_reduction.py index 57438b211918f..b8b7d584aee56 100644 --- a/sklearn/metrics/tests/test_pairwise_distances_reduction.py +++ b/sklearn/metrics/tests/test_pairwise_distances_reduction.py @@ -527,6 +527,9 @@ def test_pairwise_distances_reduction_is_usable_for(): assert not PairwiseDistancesReduction.is_usable_for(csr_matrix(X), Y, metric) assert not PairwiseDistancesReduction.is_usable_for(X, csr_matrix(Y), metric) + # F-ordered arrays are not supported + assert not PairwiseDistancesReduction.is_usable_for(np.asfortranarray(X), Y, metric) + def test_argkmin_factory_method_wrong_usages(): rng = np.random.RandomState(1) diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index 185e9e1f61c79..1030f46607d33 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -18,6 +18,24 @@ import pytest import numpy as np +from sklearn.cluster import ( + AffinityPropagation, + Birch, + MeanShift, + OPTICS, + SpectralClustering, +) +from sklearn.datasets import make_blobs +from sklearn.manifold import Isomap, TSNE, LocallyLinearEmbedding +from sklearn.neighbors import ( + LocalOutlierFactor, + KNeighborsClassifier, + KNeighborsRegressor, + RadiusNeighborsClassifier, + RadiusNeighborsRegressor, +) +from sklearn.semi_supervised import LabelPropagation, LabelSpreading + from sklearn.utils import all_estimators from sklearn.utils._testing import ignore_warnings from sklearn.exceptions import ConvergenceWarning @@ -518,3 +536,44 @@ def test_check_param_validation(estimator): ) _set_checking_parameters(estimator) check_param_validation(name, estimator) + + +# TODO: remove this filter in 1.2 +@pytest.mark.filterwarnings("ignore::FutureWarning:sklearn") +@pytest.mark.parametrize( + "Estimator", + [ + AffinityPropagation, + Birch, + MeanShift, + KNeighborsClassifier, + KNeighborsRegressor, + RadiusNeighborsClassifier, + RadiusNeighborsRegressor, + LabelPropagation, + LabelSpreading, + OPTICS, + SpectralClustering, + LocalOutlierFactor, + LocallyLinearEmbedding, + Isomap, + TSNE, + ], +) +def test_f_contiguous_array_estimator(Estimator): + # Non-regression test for: + # https://github.com/scikit-learn/scikit-learn/issues/23988 + # https://github.com/scikit-learn/scikit-learn/issues/24013 + + X, _ = make_blobs(n_samples=80, n_features=4, random_state=0) + X = np.asfortranarray(X) + y = np.round(X[:, 0]) + + est = Estimator() + est.fit(X, y) + + if hasattr(est, "transform"): + est.transform(X) + + if hasattr(est, "predict"): + est.predict(X)