diff --git a/sklearn/decomposition/tests/test_pca.py b/sklearn/decomposition/tests/test_pca.py index 0b14ffecc82f9..2b97138c4dea3 100644 --- a/sklearn/decomposition/tests/test_pca.py +++ b/sklearn/decomposition/tests/test_pca.py @@ -15,6 +15,7 @@ from sklearn.utils._array_api import ( _atol_for_type, _convert_to_numpy, + _get_namespace_device_dtype_ids, yield_namespace_device_dtype_combinations, ) from sklearn.utils._array_api import device as array_device @@ -1006,7 +1007,9 @@ def check_array_api_get_precision(name, estimator, array_namespace, device, dtyp @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "check", @@ -1038,7 +1041,9 @@ def test_pca_array_api_compliance( @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "check", diff --git a/sklearn/linear_model/tests/test_ridge.py b/sklearn/linear_model/tests/test_ridge.py index 67225f0d340e0..043966afdc7d9 100644 --- a/sklearn/linear_model/tests/test_ridge.py +++ b/sklearn/linear_model/tests/test_ridge.py @@ -45,6 +45,7 @@ _NUMPY_NAMESPACE_NAMES, _atol_for_type, _convert_to_numpy, + _get_namespace_device_dtype_ids, yield_namespace_device_dtype_combinations, yield_namespaces, ) @@ -1256,7 +1257,9 @@ def check_array_api_attributes(name, estimator, array_namespace, device, dtype_n @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "check", diff --git a/sklearn/metrics/cluster/tests/test_supervised.py b/sklearn/metrics/cluster/tests/test_supervised.py index 1d04255633da2..417ae3ea4897f 100644 --- a/sklearn/metrics/cluster/tests/test_supervised.py +++ b/sklearn/metrics/cluster/tests/test_supervised.py @@ -23,7 +23,10 @@ ) from sklearn.metrics.cluster._supervised import _generalized_average, check_clusterings from sklearn.utils import assert_all_finite -from sklearn.utils._array_api import yield_namespace_device_dtype_combinations +from sklearn.utils._array_api import ( + _get_namespace_device_dtype_ids, + yield_namespace_device_dtype_combinations, +) from sklearn.utils._testing import _array_api_for_tests, assert_almost_equal score_funcs = [ @@ -262,7 +265,9 @@ def test_entropy(): @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_entropy_array_api(array_namespace, device, dtype_name): xp = _array_api_for_tests(array_namespace, device) diff --git a/sklearn/metrics/tests/test_common.py b/sklearn/metrics/tests/test_common.py index 8f412133813d6..6f9e11d4f4780 100644 --- a/sklearn/metrics/tests/test_common.py +++ b/sklearn/metrics/tests/test_common.py @@ -74,6 +74,7 @@ from sklearn.utils._array_api import ( _atol_for_type, _convert_to_numpy, + _get_namespace_device_dtype_ids, yield_namespace_device_dtype_combinations, ) from sklearn.utils._testing import ( @@ -2238,7 +2239,9 @@ def yield_metric_checker_combinations(metric_checkers=array_api_metric_checkers) @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize("metric, check_func", yield_metric_checker_combinations()) def test_array_api_compliance(metric, array_namespace, device, dtype_name, check_func): diff --git a/sklearn/model_selection/tests/test_search.py b/sklearn/model_selection/tests/test_search.py index 5d00a3d677330..e35a0dfb3a366 100644 --- a/sklearn/model_selection/tests/test_search.py +++ b/sklearn/model_selection/tests/test_search.py @@ -82,7 +82,10 @@ check_recorded_metadata, ) from sklearn.tree import DecisionTreeClassifier, DecisionTreeRegressor -from sklearn.utils._array_api import yield_namespace_device_dtype_combinations +from sklearn.utils._array_api import ( + _get_namespace_device_dtype_ids, + yield_namespace_device_dtype_combinations, +) from sklearn.utils._mocking import CheckingClassifier, MockDataFrame from sklearn.utils._testing import ( MinimalClassifier, @@ -2876,7 +2879,9 @@ def test_cv_results_multi_size_array(): @pytest.mark.parametrize( - "array_namespace, device, dtype", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize("SearchCV", [GridSearchCV, RandomizedSearchCV]) def test_array_api_search_cv_classifier(SearchCV, array_namespace, device, dtype): diff --git a/sklearn/model_selection/tests/test_split.py b/sklearn/model_selection/tests/test_split.py index c7af88ad2666b..2286c0ff2573e 100644 --- a/sklearn/model_selection/tests/test_split.py +++ b/sklearn/model_selection/tests/test_split.py @@ -43,6 +43,7 @@ from sklearn.tests.metadata_routing_common import assert_request_is_empty from sklearn.utils._array_api import ( _convert_to_numpy, + _get_namespace_device_dtype_ids, get_namespace, yield_namespace_device_dtype_combinations, ) @@ -1310,7 +1311,9 @@ def test_train_test_split_default_test_size(train_size, exp_train, exp_test): @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "shuffle,stratify", diff --git a/sklearn/preprocessing/tests/test_data.py b/sklearn/preprocessing/tests/test_data.py index 09fd4419ec5d2..ac303a1c93e96 100644 --- a/sklearn/preprocessing/tests/test_data.py +++ b/sklearn/preprocessing/tests/test_data.py @@ -38,6 +38,7 @@ from sklearn.svm import SVR from sklearn.utils import gen_batches, shuffle from sklearn.utils._array_api import ( + _get_namespace_device_dtype_ids, yield_namespace_device_dtype_combinations, ) from sklearn.utils._test_common.instance_generator import _get_check_estimator_ids @@ -689,7 +690,9 @@ def test_standard_check_array_of_inverse_transform(): @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "check", diff --git a/sklearn/preprocessing/tests/test_label.py b/sklearn/preprocessing/tests/test_label.py index da3079406b305..053b474e675bc 100644 --- a/sklearn/preprocessing/tests/test_label.py +++ b/sklearn/preprocessing/tests/test_label.py @@ -13,6 +13,7 @@ ) from sklearn.utils._array_api import ( _convert_to_numpy, + _get_namespace_device_dtype_ids, get_namespace, yield_namespace_device_dtype_combinations, ) @@ -707,7 +708,9 @@ def test_label_encoders_do_not_have_set_output(encoder): @pytest.mark.parametrize( - "array_namespace, device, dtype", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "y", diff --git a/sklearn/utils/_array_api.py b/sklearn/utils/_array_api.py index 0c915eb64f254..48c941f3c6e85 100644 --- a/sklearn/utils/_array_api.py +++ b/sklearn/utils/_array_api.py @@ -105,6 +105,23 @@ def yield_namespace_device_dtype_combinations(include_numpy_namespaces=True): yield array_namespace, None, None +def _get_namespace_device_dtype_ids(param): + """Get pytest parametrization IDs for `yield_namespace_device_dtype_combinations`""" + # Gives clearer IDs for array-api-strict devices, see #31042 for details + try: + import array_api_strict + except ImportError: + # `None` results in the default pytest representation + return None + else: + if param == array_api_strict.Device("CPU_DEVICE"): + return "CPU_DEVICE" + if param == array_api_strict.Device("device1"): + return "device1" + if param == array_api_strict.Device("device2"): + return "device2" + + def _check_array_api_dispatch(array_api_dispatch): """Check that array_api_compat is installed and NumPy version is compatible. diff --git a/sklearn/utils/tests/test_array_api.py b/sklearn/utils/tests/test_array_api.py index 4809a0ae5120a..164e3024a31e7 100644 --- a/sklearn/utils/tests/test_array_api.py +++ b/sklearn/utils/tests/test_array_api.py @@ -15,6 +15,7 @@ _count_nonzero, _estimator_with_converted_arrays, _fill_or_add_to_diagonal, + _get_namespace_device_dtype_ids, _is_numpy_namespace, _isin, _max_precision_float_dtype, @@ -113,7 +114,9 @@ def test_asarray_with_order(array_api): @pytest.mark.parametrize( - "array_namespace, device_, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device_, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "weights, axis, normalize, expected", @@ -169,6 +172,7 @@ def test_average( @pytest.mark.parametrize( "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations(include_numpy_namespaces=False), + ids=_get_namespace_device_dtype_ids, ) def test_average_raises_with_wrong_dtype(array_namespace, device, dtype_name): xp = _array_api_for_tests(array_namespace, device) @@ -194,6 +198,7 @@ def test_average_raises_with_wrong_dtype(array_namespace, device, dtype_name): @pytest.mark.parametrize( "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations(include_numpy_namespaces=True), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize( "axis, weights, error, error_msg", @@ -350,7 +355,9 @@ def test_nan_reductions(library, X, reduction, expected): @pytest.mark.parametrize( - "namespace, _device, _dtype", yield_namespace_device_dtype_combinations() + "namespace, _device, _dtype", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_ravel(namespace, _device, _dtype): xp = _array_api_for_tests(namespace, _device) @@ -437,7 +444,9 @@ def test_convert_estimator_to_array_api(): @pytest.mark.parametrize( - "namespace, _device, _dtype", yield_namespace_device_dtype_combinations() + "namespace, _device, _dtype", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_indexing_dtype(namespace, _device, _dtype): xp = _array_api_for_tests(namespace, _device) @@ -449,7 +458,9 @@ def test_indexing_dtype(namespace, _device, _dtype): @pytest.mark.parametrize( - "namespace, _device, _dtype", yield_namespace_device_dtype_combinations() + "namespace, _device, _dtype", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_max_precision_float_dtype(namespace, _device, _dtype): xp = _array_api_for_tests(namespace, _device) @@ -458,7 +469,9 @@ def test_max_precision_float_dtype(namespace, _device, _dtype): @pytest.mark.parametrize( - "array_namespace, device, _", yield_namespace_device_dtype_combinations() + "array_namespace, device, _", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize("invert", [True, False]) @pytest.mark.parametrize("assume_unique", [True, False]) @@ -522,7 +535,9 @@ def test_get_namespace_and_device(): @pytest.mark.parametrize( - "array_namespace, device_, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device_, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize("csr_container", CSR_CONTAINERS) @pytest.mark.parametrize("axis", [0, 1, None, -1, -2]) @@ -559,7 +574,9 @@ def test_count_nonzero( @pytest.mark.parametrize( - "array_namespace, device_, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device_, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) @pytest.mark.parametrize("wrap", [True, False]) def test_fill_or_add_to_diagonal(array_namespace, device_, dtype_name, wrap): diff --git a/sklearn/utils/tests/test_indexing.py b/sklearn/utils/tests/test_indexing.py index 87fb5c77bcfbf..e300ad6fdec87 100644 --- a/sklearn/utils/tests/test_indexing.py +++ b/sklearn/utils/tests/test_indexing.py @@ -9,7 +9,10 @@ import sklearn from sklearn.externals._packaging.version import parse as parse_version from sklearn.utils import _safe_indexing, resample, shuffle -from sklearn.utils._array_api import yield_namespace_device_dtype_combinations +from sklearn.utils._array_api import ( + _get_namespace_device_dtype_ids, + yield_namespace_device_dtype_combinations, +) from sklearn.utils._indexing import ( _determine_key_type, _get_column_indices, @@ -105,7 +108,9 @@ def test_determine_key_type_slice_error(): @skip_if_array_api_compat_not_configured @pytest.mark.parametrize( - "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations() + "array_namespace, device, dtype_name", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_determine_key_type_array_api(array_namespace, device, dtype_name): xp = _array_api_for_tests(array_namespace, device) diff --git a/sklearn/utils/tests/test_multiclass.py b/sklearn/utils/tests/test_multiclass.py index 199ffc2f751c6..e361a93e41b10 100644 --- a/sklearn/utils/tests/test_multiclass.py +++ b/sklearn/utils/tests/test_multiclass.py @@ -7,7 +7,10 @@ from sklearn import config_context, datasets from sklearn.model_selection import ShuffleSplit from sklearn.svm import SVC -from sklearn.utils._array_api import yield_namespace_device_dtype_combinations +from sklearn.utils._array_api import ( + _get_namespace_device_dtype_ids, + yield_namespace_device_dtype_combinations, +) from sklearn.utils._testing import ( _array_api_for_tests, _convert_container, @@ -382,6 +385,7 @@ def test_is_multilabel(): @pytest.mark.parametrize( "array_namespace, device, dtype_name", yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_is_multilabel_array_api_compliance(array_namespace, device, dtype_name): xp = _array_api_for_tests(array_namespace, device) diff --git a/sklearn/utils/tests/test_validation.py b/sklearn/utils/tests/test_validation.py index 5da866380c79e..ae12f13624055 100644 --- a/sklearn/utils/tests/test_validation.py +++ b/sklearn/utils/tests/test_validation.py @@ -34,7 +34,10 @@ check_X_y, deprecated, ) -from sklearn.utils._array_api import yield_namespace_device_dtype_combinations +from sklearn.utils._array_api import ( + _get_namespace_device_dtype_ids, + yield_namespace_device_dtype_combinations, +) from sklearn.utils._mocking import ( MockDataFrame, _MockEstimatorOnOffPrediction, @@ -1030,7 +1033,9 @@ def test_check_consistent_length(): @pytest.mark.parametrize( - "array_namespace, device, _", yield_namespace_device_dtype_combinations() + "array_namespace, device, _", + yield_namespace_device_dtype_combinations(), + ids=_get_namespace_device_dtype_ids, ) def test_check_consistent_length_array_api(array_namespace, device, _): """Test that check_consistent_length works with different array types."""