diff --git a/.github/workflows/cuda-ci.yml b/.github/workflows/cuda-ci.yml index 9124df6a57ad6..6f46bae60fe91 100644 --- a/.github/workflows/cuda-ci.yml +++ b/.github/workflows/cuda-ci.yml @@ -42,4 +42,4 @@ jobs: run: | source "${HOME}/conda/etc/profile.d/conda.sh" conda activate sklearn - pytest -k 'array_api' + SCIPY_ARRAY_API=1 pytest -k 'array_api' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3887be64be4a9..48a0efc327700 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -138,6 +138,7 @@ jobs: # Here we make sure, that they are still run on a regular basis. ${{ if eq(variables['Build.Reason'], 'Schedule') }}: SKLEARN_SKIP_NETWORK_TESTS: '0' + SCIPY_ARRAY_API: '1' # Check compilation with Ubuntu 22.04 LTS (Jammy Jellyfish) and scipy from conda-forge # By default the CI is sequential, where `Ubuntu_Jammy_Jellyfish` runs first and @@ -221,6 +222,7 @@ jobs: # makes sure that they are single threaded in each xdist subprocess. PYTEST_XDIST_VERSION: 'none' PIP_BUILD_ISOLATION: 'true' + SCIPY_ARRAY_API: '1' - template: build_tools/azure/posix-docker.yml parameters: @@ -259,6 +261,7 @@ jobs: DISTRIB: 'conda' LOCK_FILE: './build_tools/azure/pylatest_conda_forge_mkl_osx-64_conda.lock' SKLEARN_TESTS_GLOBAL_RANDOM_SEED: '5' # non-default seed + SCIPY_ARRAY_API: '1' pylatest_conda_mkl_no_openmp: DISTRIB: 'conda' LOCK_FILE: './build_tools/azure/pylatest_conda_mkl_no_openmp_osx-64_conda.lock' diff --git a/doc/modules/array_api.rst b/doc/modules/array_api.rst index ea1caef2e62aa..34ca3c3393bd7 100644 --- a/doc/modules/array_api.rst +++ b/doc/modules/array_api.rst @@ -119,6 +119,7 @@ Metrics - :func:`sklearn.metrics.mean_absolute_error` - :func:`sklearn.metrics.mean_absolute_percentage_error` - :func:`sklearn.metrics.mean_gamma_deviance` +- :func:`sklearn.metrics.mean_poisson_deviance` (requires `enabling array API support for SciPy `_) - :func:`sklearn.metrics.mean_squared_error` - :func:`sklearn.metrics.mean_tweedie_deviance` - :func:`sklearn.metrics.pairwise.additive_chi2_kernel` diff --git a/doc/whats_new/v1.6.rst b/doc/whats_new/v1.6.rst index 0810abc880201..75093d84059b9 100644 --- a/doc/whats_new/v1.6.rst +++ b/doc/whats_new/v1.6.rst @@ -39,6 +39,7 @@ See :ref:`array_api` for more details. and :pr:`29143` by :user:`Tialo ` and :user:`Loïc Estève `; - :func:`sklearn.metrics.mean_absolute_percentage_error` :pr:`29300` by :user:`Emily Chen `; - :func:`sklearn.metrics.mean_gamma_deviance` :pr:`29239` by :user:`Emily Chen `; +- :func:`sklearn.metrics.mean_poisson_deviance` :pr:`29227` by :user:`Emily Chen `; - :func:`sklearn.metrics.mean_squared_error` :pr:`29142` by :user:`Yaroslav Korobko `; - :func:`sklearn.metrics.mean_tweedie_deviance` :pr:`28106` by :user:`Thomas Li `; - :func:`sklearn.metrics.pairwise.additive_chi2_kernel` :pr:`29144` by :user:`Yaroslav Korobko `; diff --git a/sklearn/metrics/tests/test_common.py b/sklearn/metrics/tests/test_common.py index 2e48e8f4a230c..f65b4f6b65c7d 100644 --- a/sklearn/metrics/tests/test_common.py +++ b/sklearn/metrics/tests/test_common.py @@ -76,7 +76,7 @@ assert_array_less, ignore_warnings, ) -from sklearn.utils.fixes import COO_CONTAINERS +from sklearn.utils.fixes import COO_CONTAINERS, parse_version, sp_version from sklearn.utils.multiclass import type_of_target from sklearn.utils.validation import _num_samples, check_random_state @@ -1867,6 +1867,12 @@ def check_array_api_multilabel_classification_metric( def check_array_api_regression_metric(metric, array_namespace, device, dtype_name): + func_name = metric.func.__name__ if isinstance(metric, partial) else metric.__name__ + if func_name == "mean_poisson_deviance" and sp_version < parse_version("1.14.0"): + pytest.skip( + "mean_poisson_deviance's dependency `xlogy` is available as of scipy 1.14.0" + ) + y_true_np = np.array([2.0, 0.1, 1.0, 4.0], dtype=dtype_name) y_pred_np = np.array([0.5, 0.5, 2, 2], dtype=dtype_name) @@ -2012,6 +2018,7 @@ def check_array_api_metric_pairwise(metric, array_namespace, device, dtype_name) check_array_api_regression_metric, ], paired_cosine_distances: [check_array_api_metric_pairwise], + mean_poisson_deviance: [check_array_api_regression_metric], additive_chi2_kernel: [check_array_api_metric_pairwise], mean_gamma_deviance: [check_array_api_regression_metric], max_error: [check_array_api_regression_metric], diff --git a/sklearn/utils/_array_api.py b/sklearn/utils/_array_api.py index 231d2b3914a0c..a317059aa1415 100644 --- a/sklearn/utils/_array_api.py +++ b/sklearn/utils/_array_api.py @@ -5,6 +5,8 @@ import itertools import math +import os +import warnings from functools import wraps import numpy @@ -106,6 +108,17 @@ def _check_array_api_dispatch(array_api_dispatch): f"NumPy must be {min_numpy_version} or newer to dispatch array using" " the API specification" ) + if os.environ.get("SCIPY_ARRAY_API") != "1": + warnings.warn( + ( + "Some scikit-learn array API features might rely on enabling " + "SciPy's own support for array API to function properly. " + "Please set the SCIPY_ARRAY_API=1 environment variable " + "before importing sklearn or scipy. More details at: " + "https://docs.scipy.org/doc/scipy/dev/api-dev/array_api.html" + ), + UserWarning, + ) def _single_array_device(array): diff --git a/sklearn/utils/tests/test_array_api.py b/sklearn/utils/tests/test_array_api.py index 707304edacd11..5e3299781a531 100644 --- a/sklearn/utils/tests/test_array_api.py +++ b/sklearn/utils/tests/test_array_api.py @@ -1,3 +1,4 @@ +import os import re from functools import partial @@ -77,7 +78,7 @@ def test_get_namespace_ndarray_with_dispatch(): @skip_if_array_api_compat_not_configured -def test_get_namespace_array_api(): +def test_get_namespace_array_api(monkeypatch): """Test get_namespace for ArrayAPI arrays.""" xp = pytest.importorskip("array_api_strict") @@ -90,6 +91,18 @@ def test_get_namespace_array_api(): with pytest.raises(TypeError): xp_out, is_array_api_compliant = get_namespace(X_xp, X_np) + def mock_getenv(key): + if key == "SCIPY_ARRAY_API": + return "0" + + monkeypatch.setattr("os.environ.get", mock_getenv) + assert os.environ.get("SCIPY_ARRAY_API") != "1" + with pytest.warns( + UserWarning, + match="enabling SciPy's own support for array API to function properly. ", + ): + xp_out, is_array_api_compliant = get_namespace(X_xp) + class _AdjustableNameAPITestWrapper(_ArrayAPIWrapper): """API wrapper that has an adjustable name. Used for testing."""