diff --git a/.binder/runtime.txt b/.binder/runtime.txt index 8fdd90711cf30..d2aca3a7e1014 100644 --- a/.binder/runtime.txt +++ b/.binder/runtime.txt @@ -1 +1 @@ -python-3.9 +python-3.12 diff --git a/.github/workflows/cuda-ci.yml b/.github/workflows/cuda-ci.yml index 028ff06903e8a..a8e82b4488229 100644 --- a/.github/workflows/cuda-ci.yml +++ b/.github/workflows/cuda-ci.yml @@ -18,10 +18,10 @@ jobs: - uses: actions/checkout@v4 - name: Build wheels - uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a + uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 env: CIBW_BUILD: cp313-manylinux_x86_64 - CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 CIBW_BUILD_VERBOSITY: 1 CIBW_ARCHS: x86_64 diff --git a/.github/workflows/emscripten.yml b/.github/workflows/emscripten.yml index 47e54f6125638..dbd2439e9b32d 100644 --- a/.github/workflows/emscripten.yml +++ b/.github/workflows/emscripten.yml @@ -67,12 +67,14 @@ jobs: with: persist-credentials: false - - uses: pypa/cibuildwheel@faf86a6ed7efa889faf6996aa23820831055001a + - uses: pypa/cibuildwheel@5f22145df44122af0f5a201f93cf0207171beca7 env: CIBW_PLATFORM: pyodide SKLEARN_SKIP_OPENMP_TEST: "true" SKLEARN_SKIP_NETWORK_TESTS: 1 - CIBW_TEST_REQUIRES: "pytest pandas" + # Temporary work-around to avoid joblib 1.5.0 until there is a joblib + # release with https://github.com/joblib/joblib/pull/1721 + CIBW_TEST_REQUIRES: "pytest pandas joblib!=1.5.0" # -s pytest argument is needed to avoid an issue in pytest output capturing with Pyodide CIBW_TEST_COMMAND: "python -m pytest -svra --pyargs sklearn --durations 20 --showlocals" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index f8075e779c56b..0000000000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,103 +0,0 @@ -# This linter job on GH actions is used to trigger the commenter bot -# in bot-lint-comment.yml file. It stores the output of the linter to be used -# by the commenter bot. -name: linter - -on: - - pull_request_target - -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - - # setting any permission will set everything else to none for GITHUB_TOKEN - permissions: - pull-requests: none - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - - name: Install dependencies - run: | - curl https://raw.githubusercontent.com/${{ github.repository }}/main/build_tools/shared.sh --retry 5 -o ./build_tools/shared.sh - source build_tools/shared.sh - # Include pytest compatibility with mypy - pip install pytest $(get_dep ruff min) $(get_dep mypy min) cython-lint - # we save the versions of the linters to be used in the error message later. - python -c "from importlib.metadata import version; print(f\"ruff={version('ruff')}\")" >> /tmp/versions.txt - python -c "from importlib.metadata import version; print(f\"mypy={version('mypy')}\")" >> /tmp/versions.txt - python -c "from importlib.metadata import version; print(f\"cython-lint={version('cython-lint')}\")" >> /tmp/versions.txt - - - name: Run linting - id: lint-script - # We download the linting script from main, since this workflow is run - # from main itself. - run: | - curl https://raw.githubusercontent.com/${{ github.repository }}/main/build_tools/linting.sh --retry 5 -o ./build_tools/linting.sh - set +e - ./build_tools/linting.sh &> /tmp/linting_output.txt - cat /tmp/linting_output.txt - - - name: Upload Artifact - if: always() - uses: actions/upload-artifact@v4 - with: - name: lint-log - path: | - /tmp/linting_output.txt - /tmp/versions.txt - retention-days: 1 - - comment: - needs: lint - if: ${{ !cancelled() }} - runs-on: ubuntu-latest - - # We need these permissions to be able to post / update comments - permissions: - pull-requests: write - issues: write - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - - name: Install dependencies - run: python -m pip install requests - - - name: Download artifact - id: download-artifact - uses: actions/download-artifact@v4 - with: - name: lint-log - - - name: Print log - run: cat linting_output.txt - - - name: Process Comments - id: process-comments - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} - BRANCH_SHA: ${{ github.event.pull_request.head.sha }} - RUN_ID: ${{ github.run_id }} - LOG_FILE: linting_output.txt - VERSIONS_FILE: versions.txt - run: python ./build_tools/get_comment.py diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 33e8897c147f7..25fc711cdc38c 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -75,7 +75,10 @@ jobs: - os: windows-latest python: 313t platform_id: win_amd64 - free_threaded_support: True + cibw_enable: cpython-freethreading + - os: windows-latest + python: 314 + platform_id: win_amd64 # Linux 64 bit manylinux2014 - os: ubuntu-latest @@ -98,9 +101,13 @@ jobs: python: 313t platform_id: manylinux_x86_64 manylinux_image: manylinux2014 - free_threaded_support: True + cibw_enable: cpython-freethreading + - os: ubuntu-latest + python: 314 + platform_id: manylinux_x86_64 + manylinux_image: manylinux2014 - # # Linux 64 bit manylinux2014 + # Linux 64 bit manylinux2014 - os: ubuntu-24.04-arm python: 310 platform_id: manylinux_aarch64 @@ -117,6 +124,15 @@ jobs: python: 313 platform_id: manylinux_aarch64 manylinux_image: manylinux2014 + - os: ubuntu-24.04-arm + python: 313t + platform_id: manylinux_aarch64 + manylinux_image: manylinux2014 + cibw_enable: cpython-freethreading + - os: ubuntu-24.04-arm + python: 314 + platform_id: manylinux_aarch64 + manylinux_image: manylinux2014 # MacOS x86_64 - os: macos-13 @@ -134,7 +150,10 @@ jobs: - os: macos-13 python: 313t platform_id: macosx_x86_64 - free_threaded_support: True + cibw_enable: cpython-freethreading + - os: macos-13 + python: 314 + platform_id: macosx_x86_64 # MacOS arm64 - os: macos-14 @@ -152,7 +171,10 @@ jobs: - os: macos-14 python: 313t platform_id: macosx_arm64 - free_threaded_support: True + cibw_enable: cpython-freethreading + - os: macos-14 + python: 314 + platform_id: macosx_arm64 steps: - name: Checkout scikit-learn @@ -165,11 +187,12 @@ jobs: - uses: conda-incubator/setup-miniconda@v3 if: ${{ startsWith(matrix.platform_id, 'macosx') }} + with: + miniforge-version: latest - name: Build and test wheels env: - CIBW_PRERELEASE_PYTHONS: ${{ matrix.prerelease_pythons }} - CIBW_FREE_THREADED_SUPPORT: ${{ matrix.free_threaded_support }} + CIBW_ENABLE: ${{ matrix.cibw_enable }} CIBW_ENVIRONMENT: SKLEARN_SKIP_NETWORK_TESTS=1 CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform_id }} CIBW_ARCHS: all @@ -183,7 +206,8 @@ jobs: CIBW_BEFORE_BUILD: bash {project}/build_tools/wheels/cibw_before_build.sh {project} CIBW_BEFORE_TEST_WINDOWS: bash build_tools/github/build_minimal_windows_image.sh ${{ matrix.python }} CIBW_ENVIRONMENT_PASS_LINUX: RUNNER_OS - CIBW_TEST_REQUIRES: pytest pandas + # TODO Put back pandas when there is a pandas release with Python 3.14 wheels + CIBW_TEST_REQUIRES: ${{ contains(matrix.python, '314') && 'pytest' || 'pytest pandas' }} # On Windows, we use a custom Docker image and CIBW_TEST_REQUIRES_WINDOWS # does not make sense because it would install dependencies in the host # rather than inside the Docker image diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 48871d2a4abed..d02000a24581a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,9 +7,9 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.7 + rev: v0.12.2 hooks: - - id: ruff + - id: ruff-check args: ["--fix", "--output-format=full"] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy diff --git a/README.rst b/README.rst index 4f4741a090dee..5885bce67baa7 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ .. |Codecov| image:: https://codecov.io/gh/scikit-learn/scikit-learn/branch/main/graph/badge.svg?token=Pk8G9gg3y9 :target: https://codecov.io/gh/scikit-learn/scikit-learn -.. |Nightly wheels| image:: https://github.com/scikit-learn/scikit-learn/workflows/Wheel%20builder/badge.svg?event=schedule +.. |Nightly wheels| image:: https://github.com/scikit-learn/scikit-learn/actions/workflows/wheels.yml/badge.svg?event=schedule :target: https://github.com/scikit-learn/scikit-learn/actions?query=workflow%3A%22Wheel+builder%22+event%3Aschedule .. |Ruff| image:: https://img.shields.io/badge/code%20style-ruff-000000.svg @@ -176,22 +176,36 @@ Documentation Communication ~~~~~~~~~~~~~ -- Mailing list: https://mail.python.org/mailman/listinfo/scikit-learn -- Logos & Branding: https://github.com/scikit-learn/scikit-learn/tree/main/doc/logos -- Blog: https://blog.scikit-learn.org -- Calendar: https://blog.scikit-learn.org/calendar/ -- Stack Overflow: https://stackoverflow.com/questions/tagged/scikit-learn -- GitHub Discussions: https://github.com/scikit-learn/scikit-learn/discussions -- Website: https://scikit-learn.org -- LinkedIn: https://www.linkedin.com/company/scikit-learn -- Bluesky: https://bsky.app/profile/scikit-learn.org -- Mastodon: https://mastodon.social/@sklearn@fosstodon.org -- YouTube: https://www.youtube.com/channel/UCJosFjYm0ZYVUARxuOZqnnw/playlists -- Facebook: https://www.facebook.com/scikitlearnofficial/ -- Instagram: https://www.instagram.com/scikitlearnofficial/ -- TikTok: https://www.tiktok.com/@scikit.learn -- Discord: https://discord.gg/h9qyrK8Jc8 +Main Channels +^^^^^^^^^^^^^ +- **Website**: https://scikit-learn.org +- **Blog**: https://blog.scikit-learn.org +- **Mailing list**: https://mail.python.org/mailman/listinfo/scikit-learn + +Developer & Support +^^^^^^^^^^^^^^^^^^^^^^ + +- **GitHub Discussions**: https://github.com/scikit-learn/scikit-learn/discussions +- **Stack Overflow**: https://stackoverflow.com/questions/tagged/scikit-learn +- **Discord**: https://discord.gg/h9qyrK8Jc8 + +Social Media Platforms +^^^^^^^^^^^^^^^^^^^^^^ + +- **LinkedIn**: https://www.linkedin.com/company/scikit-learn +- **YouTube**: https://www.youtube.com/channel/UCJosFjYm0ZYVUARxuOZqnnw/playlists +- **Facebook**: https://www.facebook.com/scikitlearnofficial/ +- **Instagram**: https://www.instagram.com/scikitlearnofficial/ +- **TikTok**: https://www.tiktok.com/@scikit.learn +- **Bluesky**: https://bsky.app/profile/scikit-learn.org +- **Mastodon**: https://mastodon.social/@sklearn@fosstodon.org + +Resources +^^^^^^^^^ + +- **Calendar**: https://blog.scikit-learn.org/calendar/ +- **Logos & Branding**: https://github.com/scikit-learn/scikit-learn/tree/main/doc/logos Citation ~~~~~~~~ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 804214f97808a..4d3248f2d0729 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -84,7 +84,6 @@ jobs: ) matrix: pylatest_free_threaded: - PYTHON_GIL: '0' DISTRIB: 'conda-free-threaded' LOCK_FILE: './build_tools/azure/pylatest_free_threaded_linux-64_conda.lock' COVERAGE: 'false' @@ -235,9 +234,9 @@ jobs: 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: + pylatest_conda_forge_mkl_no_openmp: DISTRIB: 'conda' - LOCK_FILE: './build_tools/azure/pylatest_conda_mkl_no_openmp_osx-64_conda.lock' + LOCK_FILE: './build_tools/azure/pylatest_conda_forge_mkl_no_openmp_osx-64_conda.lock' SKLEARN_TEST_NO_OPENMP: 'true' SKLEARN_SKIP_OPENMP_TEST: 'true' SKLEARN_TESTS_GLOBAL_RANDOM_SEED: '6' # non-default seed @@ -254,9 +253,9 @@ jobs: not(contains(dependencies['git_commit']['outputs']['commit.message'], '[ci skip]')) ) matrix: - pymin_conda_forge_mkl: + pymin_conda_forge_openblas: DISTRIB: 'conda' - LOCK_FILE: ./build_tools/azure/pymin_conda_forge_mkl_win-64_conda.lock + LOCK_FILE: ./build_tools/azure/pymin_conda_forge_openblas_win-64_conda.lock SKLEARN_WARNINGS_AS_ERRORS: '1' # The Azure Windows runner is typically much slower than other CI # runners due to the lack of compiler cache. Running the tests with diff --git a/build_tools/azure/debian_32bit_lock.txt b/build_tools/azure/debian_32bit_lock.txt index 051a8b8ef7e48..c9526638fdfbc 100644 --- a/build_tools/azure/debian_32bit_lock.txt +++ b/build_tools/azure/debian_32bit_lock.txt @@ -4,17 +4,17 @@ # # pip-compile --output-file=build_tools/azure/debian_32bit_lock.txt build_tools/azure/debian_32bit_requirements.txt # -coverage[toml]==7.8.0 +coverage[toml]==7.9.2 # via pytest-cov -cython==3.0.12 +cython==3.1.2 # via -r build_tools/azure/debian_32bit_requirements.txt iniconfig==2.1.0 # via pytest -joblib==1.5.0 +joblib==1.5.1 # via -r build_tools/azure/debian_32bit_requirements.txt -meson==1.8.0 +meson==1.8.2 # via meson-python -meson-python==0.17.1 +meson-python==0.18.0 # via -r build_tools/azure/debian_32bit_requirements.txt ninja==1.11.1.4 # via -r build_tools/azure/debian_32bit_requirements.txt @@ -23,15 +23,19 @@ packaging==25.0 # meson-python # pyproject-metadata # pytest -pluggy==1.5.0 +pluggy==1.6.0 + # via + # pytest + # pytest-cov +pygments==2.19.2 # via pytest pyproject-metadata==0.9.1 # via meson-python -pytest==8.3.5 +pytest==8.4.1 # via # -r build_tools/azure/debian_32bit_requirements.txt # pytest-cov -pytest-cov==6.1.1 +pytest-cov==6.2.1 # via -r build_tools/azure/debian_32bit_requirements.txt threadpoolctl==3.6.0 # via -r build_tools/azure/debian_32bit_requirements.txt diff --git a/build_tools/azure/install.sh b/build_tools/azure/install.sh index c009e2972036e..9ae67f8db5e29 100755 --- a/build_tools/azure/install.sh +++ b/build_tools/azure/install.sh @@ -67,17 +67,7 @@ python_environment_install_and_activate() { fi # Install additional packages on top of the lock-file in specific cases - if [[ "$DISTRIB" == "conda-free-threaded" ]]; then - # TODO: we install scipy with pip. When there is a conda-forge package, - # we can update build_tools/update_environments_and_lock_files.py and - # remove the line below - pip install scipy --only-binary :all: - # TODO: we install cython 3.1 alpha from pip. When there is a conda-forge package, - # we can update build_tools/update_environments_and_lock_files.py and - # remove the line below - pip install --pre cython --only-binary :all: - - elif [[ "$DISTRIB" == "conda-pip-scipy-dev" ]]; then + if [[ "$DISTRIB" == "conda-pip-scipy-dev" ]]; then echo "Installing development dependency wheels" dev_anaconda_url=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple dev_packages="numpy scipy pandas Cython" diff --git a/build_tools/azure/install_setup_conda.sh b/build_tools/azure/install_setup_conda.sh index d09a02cda5a9f..e57d7dbe155be 100755 --- a/build_tools/azure/install_setup_conda.sh +++ b/build_tools/azure/install_setup_conda.sh @@ -3,22 +3,34 @@ set -e set -x -if [[ -z "${CONDA}" ]]; then - # In some runners (macOS-13 and macOS-14 in October 2024) conda is not - # installed so we install it ourselves - MINIFORGE_URL="https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh" - wget ${MINIFORGE_URL} -O miniforge.sh - bash miniforge.sh -b -u -p $HOME/miniforge3 - CONDA="$HOME/miniforge3" +PLATFORM=$(uname) +if [[ "$PLATFORM" =~ MINGW|MSYS ]]; then + PLATFORM=Windows +fi +if [[ "$PLATFORM" == "Windows" ]]; then + EXTENSION="exe" +else + EXTENSION="sh" +fi +INSTALLER="miniforge.$EXTENSION" +MINIFORGE_URL="https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$PLATFORM-$(uname -m).$EXTENSION" +curl -L ${MINIFORGE_URL} -o "$INSTALLER" + +MINIFORGE_DIR="$HOME/miniforge3" +if [[ "$PLATFORM" == "Windows" ]]; then + WIN_MINIFORGE_DIR=$(cygpath -w "$MINIFORGE_DIR") + cmd "/C $INSTALLER /InstallationType=JustMe /RegisterPython=0 /S /D=$WIN_MINIFORGE_DIR" else - # In most runners (in October 2024) conda is installed, - # but in a system folder and we want it user writable - sudo chown -R $USER $CONDA + bash "$INSTALLER" -b -u -p $MINIFORGE_DIR fi # Add conda to the PATH so that it can be used in further Azure CI steps. # Need set +x for ##vso Azure magic otherwise it may add a quote in the PATH. # For more details, see https://github.com/microsoft/azure-pipelines-tasks/issues/10331 set +x -echo "##vso[task.prependpath]$CONDA/bin" +if [[ "$PLATFORM" == "Windows" ]]; then + echo "##vso[task.prependpath]$MINIFORGE_DIR/Scripts" +else + echo "##vso[task.prependpath]$MINIFORGE_DIR/bin" +fi set -x diff --git a/build_tools/azure/pylatest_conda_forge_mkl_linux-64_conda.lock b/build_tools/azure/pylatest_conda_forge_mkl_linux-64_conda.lock index 9b452e7ecba3d..89ac9d486b0c9 100644 --- a/build_tools/azure/pylatest_conda_forge_mkl_linux-64_conda.lock +++ b/build_tools/azure/pylatest_conda_forge_mkl_linux-64_conda.lock @@ -1,188 +1,147 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 15de7a0d1a0d046ada825ffa5ad3547c790bf903bd5d9b03e7c0e9a6a62c680d +# input_hash: f524d159a11a0a80ead3448f16255169f24edde269f6b81e8e28453bc4f7fc53 @EXPLICIT https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.20.0-ha770c72_0.conda#96806e6c31dc89253daff2134aeb58f3 +https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_1.conda#9e298d76f543deb06eb0f3413675e13a https://conda.anaconda.org/conda-forge/linux-64/mkl-include-2024.2.2-ha957f24_16.conda#42b0d14354b5910a9f41e29289914f6b https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h3f2d84a_0.conda#d76872d096d063e226482c99337209dc -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda#e84b44e6300f1703cb25d29120c5b1d8 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8 https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-20.1.4-h024ca30_0.conda#4fc395cda27912a7d904b86b5dbf3a4d +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-20.1.8-h4922eb0_0.conda#dda42855e1d9a0b59e071e28a820d0f5 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-3_kmp_llvm.conda#ee5c2118262e30b972bc0b4db8ef0ba5 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda#c151d5eb730e9b7480e6d48c0fc44048 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda#7df50d44d4a14d6c31a2c54f2cd92157 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda#76df83c2a9035c54df5d04ff81bcc02d -https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.2-hb9d3cd8_0.conda#bd52f376d1d34d7823a7bf0773be86e8 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.4-hb03c661_0.conda#ae5621814cb99642c9308977fe90ed0d https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda#f7f0d6cc2dc986d42ac2689ec88192be -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda#41b599ed2b02abcfdd84302bff174b23 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda#27fe770decaf469a53f3e3a6d593067f -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_3.conda#cb98af5db26e3f482bebb80ce9d947d3 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda#e796ff8ddc598affdf7c173d6145f087 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 -https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.10.0-h4c51ac1_0.conda#aeccfff2806ae38430638ffbb4be9610 -https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda#771ee65e13bc599b0b62af5359d80169 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda#63f790534398730f59e1b899c3644d4a +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda#70e3400cbbfa03e96dcde7fc13e38c7b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.10.0-h202a827_0.conda#0f98f3e95272d118f7931b6bef69bfe5 +https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb9d3cd8_0.conda#1349c022c92c5efd3fd705a79a5804d8 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda#f6ebe2cb3f82ba6c057dde5d9debe4f7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda#8035c64cb77ed555e3f150b7b3972480 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.0-hada3f3f_0.conda#05a965f6def53dbcb5217945eb0b3689 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.1-hc2d532b_4.conda#4cc4dcd582b2f087d62c70b2d6daa59f -https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.3-hc2d532b_4.conda#15a1f6fb713b4cd3fee74588b996a846 -https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.7-hc2d532b_0.conda#398521f53e58db246658e7cff56d669f +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.2-he7b75e1_1.conda#c04d1312e7feec369308d656c18e7f3e +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.1-h92c474e_6.conda#3490e744cb8b9d5a3b9785839d618a17 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h92c474e_1.conda#4ab554b102065910f098f88b40163835 +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.7-h92c474e_2.conda#248831703050fe9a5b2680a7589fdba9 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda#d6845ae4dea52a2f90178bf1829a21f8 https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-h5888daf_0.conda#951ff8d9e5536896408e89d63230b8d5 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 -https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250127.1-cxx17_hbbce691_0.conda#00290e549c5c8a32cc271020acc9ec6b -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda#9566f0bd264fbd463002e759b8a82401 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda#06f70867945ea6a84d35836af780f1de +https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda#83b160d4da3e1e847bf044997621ed63 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_3.conda#1c6eecffad553bde44c5238770cfb7da +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_3.conda#3facafe58f3858eb95527c7d3a3fc578 +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.conda#4c0ab57463117fbb8df85268415082f5 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 -https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-h4bc722e_0.conda#aeb98fdeb2e8f25d43ef71fbacbeec80 -https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hd590300_0.conda#48f4330bfcd959c3cfb704d424903c82 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.47-h943b412_0.conda#55199e2ae2c3651f6f9b2a447b47bdc9 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h943b412_0.conda#51de14db340a848869e69c632b43cca7 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.2.0-h266115a_0.conda#db22a0962c953e81a2a679ecb1fc6027 -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.0-h29eaf8c_0.conda#d2f1c87d4416d1e7344cf92b1aaee1c4 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.2-h29eaf8c_0.conda#39b4228a867772d610c02e06f939a5b8 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 -https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.17-hba75a32_0.conda#dbb899164b5451c34969e67a35ca17a9 +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.22-h96f233e_0.conda#2f6fc0cf7cd248a32a52d7c8609d93a9 https://conda.anaconda.org/conda-forge/linux-64/sleef-3.8-h1b44611_0.conda#aec4dba5d4c2924730088753f6fa164b https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda#3b3e64af585eadfb52bb90b553db5edf -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.23.1-h3e06ad9_1.conda#a37843723437ba75f42c9270ffe800b1 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda#0f2ca7906bf166247d1d760c3422cb8a https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.18.1-h1a9f769_2.conda#19221489bff45371c13b983848f79a24 -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda#c63b5e52939e795ba8d26e35d767a843 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.21.1-h1d8da38_0.conda#f5b0c1cd7bf6433fb88698af45f5ad5f +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_3.conda#58178ef8ba927229fba6d84abf62c108 https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda#ff862eebdfeb2fd048ae9dc92510baca https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda#c94a5994ef49749880a8139cf9afcbe1 -https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda#f87c7b7c2cb45f323ffbce941c78ab7c https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 -https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.124-hb9d3cd8_0.conda#8bc89311041d7fcb510238cf0848ccae https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda#3c255be50a506c50765a93a6644f32fe -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_2.conda#4056c857af1a99ee50589a941059ec55 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda#19e57602824042dfd0446292ef90488b -https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.29.3-h501fc15_1.conda#edb86556cf4a0c133e7932a1597ff236 -https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hba17884_3.conda#545e93a513c10603327c76c15485e946 -https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda#dcb95c0a98ba9ff737f7ae482aef7833 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_4.conda#6c1028898cf3a2032d9af46689e1b81a -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.2.0-he0572af_0.conda#93340b072c393d23c4700a1d40565dca -https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda#31614c73d7b103ef76faa4d83d261d34 -https://conda.anaconda.org/conda-forge/linux-64/python-3.13.3-hf636f53_101_cp313.conda#10622e12d649154af0bd76bcf33a7c5c +https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h9ef548d_1.conda#b92e2a26764fcadb4304add7e698ccf2 +https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.07.17-h7b12aa8_0.conda#88931c828194a8f3cc2ef122b8b3a40c +https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.22.0-h093b73b_0.conda#9286aa66758de99bcbe92a42ff8a07fd +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hf01ce69_5.conda#e79a094918988bb1807462cd42c83962 +https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.45-hc749103_0.conda#b90bece58b4c2bf25969b70f3be42d25 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-hb711507_2.conda#8637c3e5821654d0edf97e2b0404b443 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.4-hc5e5e9e_7.conda#eb339cb6cd7c881b3f0e7910e99c261b -https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.0-h6884c39_0.conda#76a0f88aeb377e0eee84d48ac65ca747 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.3-py313hd8ed1ab_101.conda#904a822cbd380adafb9070debf8579a8 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda#dce22f70b4e5a407ce88f2be046f4ceb -https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.12-py313h5dec8f5_0.conda#24a42a0c1cc33743e33572d63d489b54 -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e -https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 -https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda#4547b39256e296bb758166893e909a7c -https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.3.2-pyhd8ed1ab_0.conda#9c40692c3d24c7aaf335f673ac09d308 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py313h33d0bda_0.conda#9862d13a5e466273d5a4738cffcb8d6c +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.5-h84d2157_2.conda#2ccd570f5678ff2f5e44ac4f0b8f1839 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.3-hbe0f4a8_1.conda#53917af94e9515f32a34831cbb4142e6 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_3.conda#5d08a0ac29e6a5a984817584775d4131 +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.13.0-h332b0f4_0.conda#cbdc92ac0d93fe3c796e36ad65c7905c +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.14.1-h332b0f4_0.conda#45f6713cb00f124af300342512219182 https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.1-h2ff4ddf_0.conda#0305434da649d4fb48a425e588b79ea6 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.2-h3618099_0.conda#072ab14a02164b7c0c089055368ff776 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h4bc477f_1.conda#ad1f1f8238834cd3c88ceeaee8da444a -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py313h8060acc_1.conda#21b62c55924f01b6eef6827167b46acb -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda#14dbe05b929e329dbaa6f2d0aa19466d https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda#2eeb50cab6652538eee8fc0bc3340c81 -https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda#3585aa87c43ab15b167b574cd73b057b -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda#fd40bf7f7f4bc4b647dc8512053d9873 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 -https://conda.anaconda.org/conda-forge/linux-64/orc-2.1.1-h17f744e_1.conda#cfe9bc267c22b6d53438eff187649d43 -https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 -https://conda.anaconda.org/conda-forge/noarch/pybind11-global-2.13.6-pyh415d2e4_2.conda#120541563e520d12d8e39abd7de9092c -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 -https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 -https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 -https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h9925aae_3.conda#6f445fb139c356f903746b2b91bbe786 -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.8.2-pyhff2d567_0.conda#9bddfdbf4e061821a1a443f93223be61 -https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 -https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py313h536fd9c_0.conda#5f5cbdd527d2e74e270d8b6255ba714f -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda#83fc6ae00127671e301c9f44254c31b8 +https://conda.anaconda.org/conda-forge/linux-64/orc-2.1.3-h61e0c1e_0.conda#451e93e0c51efff54f9e91d61187a572 +https://conda.anaconda.org/conda-forge/linux-64/re2-2025.07.17-h5a314c3_0.conda#3182185490eb814b1487d0f22a5b285c https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.44-hb9d3cd8_0.conda#7c91bfc90672888259675ad2ad28af9c +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.45-hb9d3cd8_0.conda#397a013c2dc5145a70737871aaa87e98 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e -https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.0-h9a6e2ae_4.conda#a948110dbbde6491c62815643a96d589 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.12.3-hef6a231_4.conda#fd1d89d79c8287e6bcb2a529292f537a -https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda#0a8838771cc2e985cd295e01ae83baf1 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.0-hcc895bc_17.conda#8b2218f442cfdc52a9fbab864d4f1527 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.13.3-h9c20097_1.conda#75f6584c4b9d006bac0d39fc2b4f0017 +https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.15.0-h5cfcd09_0.conda#72b359efa4d9c56c0d6f083034be353d https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.8.0-py313h8060acc_0.conda#375064d30e709bf7c1d4580e70aaea61 -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.57.0-py313h8060acc_0.conda#76b3a3367ac578a7cc43f4b7814e7e87 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 -https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.71.0-h8e591d7_1.conda#c3cfd72cbb14113abee7bbd86f44ad69 -https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.2-default_h0d58e46_1001.conda#804ca9e91bcaea0824a341d55b1684f2 -https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda#96c33bbd084ef2b2463503fb7f1482ae -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.9.1-h65c71a3_0.conda#6e45090fe0eec179ecc8041a3a3fc9f8 +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h1e535eb_0.conda#8075d8550f773a17288c7ec2cf2f2d56 +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.2-default_h3d81e11_1002.conda#56aacccb6356b6b6134a79cdf5688506 +https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.8-hecd9e04_0.conda#59a7b967b6ef5d63029b1712f8dcf661 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.10.0-h65c71a3_0.conda#fedf6bfe5d21d21d2b1785ec00a8889a https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda#aa14b9a5196a6d8dd364164b7ce56acf -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda#ca2de8bbdc871bce41dbf59e51324165 +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda#a83f6a2fdc079e643237887a37460668 -https://conda.anaconda.org/conda-forge/noarch/pybind11-2.13.6-pyh1ec8472_2.conda#8088a5e7b2888c780738c3130f2a969d -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.3-h4df99d1_101.conda#82c2641f2f0f513f7d2d1b847a2588e3 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.13.2-h0e9735f_0.conda#568ed1300869dca0ba09fb750cda5dbb +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda#89e07d92cf50743886f41638d58c4328 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b @@ -190,59 +149,100 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa -https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f -https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.16-h7dfd680_1.conda#d8870015dbf8a8bb44832f4c330bf044 -https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda#73f73f60854f325a55f1d31459f2ab73 -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda#13de36be8de3ae3f05ba127631599213 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.8.5-h317ce67_1.conda#d8d6c9ab770c661c75aee7b654fa9ddd +https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.11.0-hb5324b0_1.conda#3e3be716b250ca912f5d6351f684820c +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.10.0-h40e822a_1.conda#2c8b8c4d1c5b1b41e153a8bacdb58b88 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 +https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.5-py313hd8ed1ab_102.conda#0401f31e3c9e48cebf215472aa3e7104 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.2-py313h5dec8f5_2.conda#790ba9e115dfa69fde25212a51fe3d30 +https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 +https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda#4547b39256e296bb758166893e909a7c https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee +https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda#a31ce802cd0ebfce298f342c02757019 https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py313h11186cd_0.conda#54d020e0eaacf1e99bfb2410b9aa2e5e -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda#96f8d5b2e94c9ba4fef19f1adf068a15 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda#2d933632c8004be47deb2be61bf013be -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.36.0-hc4361e1_1.conda#ae36e6296a8dd8e8a9a8375965bf6398 -https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.20.0-hd1b1c89_0.conda#e1185384cc23e3bbf85486987835df94 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda#37fba334855ef3b51549308e61ed7a3d -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-64/optree-0.15.0-py313h33d0bda_0.conda#151f92ff0806c7c700419c8b8cf7cb4b -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py313h8db990d_0.conda#1e86810c6c3fb6d6aebdba26564eb2e8 -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.1.1-pyhd8ed1ab_0.conda#1e35d8f975bc0e984a19819aa91c440a -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py313h33d0bda_1.conda#6d8d806d9db877ace75ca67aa572bf84 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.8-default_hddf928d_0.conda#b939740734ad5a8e8f6c942374dee68d +https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.8-default_ha444ac7_0.conda#783f9cdcb0255ed00e3f1be22e16de40 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-hdb79228_0.conda#a2e30ccd49f753fd30de0d30b1569789 +https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-hb9b0907_1.conda#1c0320794855f457dea27d35c4c71e23 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.5-h27ae623_0.conda#6458be24f09e1b034902ab44fe9de908 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py313h8060acc_1.conda#21b62c55924f01b6eef6827167b46acb +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda#3585aa87c43ab15b167b574cd73b057b +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/noarch/networkx-3.5-pyhe01879c_0.conda#16bff3d37a4f99e3aa089c36c2b8d650 +https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py313h8db990d_0.conda#114a74a6e184101112fdffd3a1cb5b8f +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/pybind11-global-3.0.0-pyhf748d72_1.conda#5da3d3a7c804a3cd719448b81dd3dcb5 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 +https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 +https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.13.0-hceb3a55_1.conda#ba7726b8df7b9d34ea80e82b097a4893 +https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 +https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py313h536fd9c_0.conda#e9434a5155db25c38ade26f71a2f5a48 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.32.4-h0cee55f_2.conda#bc519b9909ef60e85ef2d59cd9542a0f -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda#7eb66060455c7a47d9dcdbfa9f46579b +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.33.1-h4f272d1_0.conda#a36ea5a9fb65327da93e2871ba2bc2f5 +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-hf182047_2.conda#5af3dea5eec5d96f1d12277700752f65 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.36.0-h0121fbd_1.conda#a0f7588c1f0a26d550e7bae4fb49427a +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.9.2-py313h8060acc_0.conda#5efd7abeadb3e88a6a219066682942de +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py313h3dea7bd_0.conda#9ab0ef93a0904a39910d1835588e25cd +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_0.conda#bd21962ff8a9d1ce4720d42a35a4af40 https://conda.anaconda.org/conda-forge/linux-64/mkl-2024.2.2-ha957f24_16.conda#1459379c79dda834673426504d52b319 +https://conda.anaconda.org/conda-forge/noarch/pybind11-3.0.0-pyh9380348_1.conda#309c97c5918389f181cc7d9c29e2a6e5 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 +https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.5-h4df99d1_102.conda#2eabcede0db21acee23c181db58b4128 https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_105.conda#8c09fac3785696e1c477156192d64b91 -https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.510-h5b777a2_6.conda#2fd0b0d4cc7fc86024b2965feedd628a -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda#7c1980f89dd41b097549782121a73490 -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.1.0-h3beb420_0.conda#95e3bb97f9cdc251c0c68640e9c10ed3 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_hfdb39a5_mkl.conda#bdf4a57254e8248222cb631db4393ff1 +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.1-h4440ef1_0.conda#75be1a943e0a7f99fcf118309092c635 +https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.606-h31ade35_1.conda#e33b3d2a2d44ba0fb35373d2343b71dd +https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-h141ff2a_2.conda#fe30a6595fc3e6a92757ac162997a365 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda#0e6e192d4b3d95708ad192d957cf3163 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-32_hfdb39a5_mkl.conda#eceb19ae9105bc4d0e8d5a321d66c426 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 https://conda.anaconda.org/conda-forge/linux-64/mkl-devel-2024.2.2-ha770c72_16.conda#140891ea14285fc634353b31e9e40a95 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-20.0.0-h27f8bab_0_cpu.conda#6dacb4d072204ce0fd13835759418872 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_h372d94f_mkl.conda#2a06a6c16b45bd3d10002927ca204b67 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_hc41d3b0_mkl.conda#10d012ddd7cc1c7ff9093d4974a34e53 -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.0-h6441bc3_1.conda#4029a8dcb1d97ea241dbe5abfda1fad6 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-20.0.0-hcb10f89_0_cpu.conda#025bf09c4f59e6f5d9a6a4b82dd5894f -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-31_hbc6e62b_mkl.conda#562026e418363dc346ad5a9e18cce73c -https://conda.anaconda.org/conda-forge/linux-64/libparquet-20.0.0-h081d1f1_0_cpu.conda#4ad62607dd9f9902e0bd3d91c5bbce58 -https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.7.0-cpu_mkl_hf6ddc5a_100.conda#6bdda0b10852c6d03b030bab7ec251f0 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h17eae1a_0.conda#6ceeff9ed72e54e4a2f9a1c88f47bdde +https://conda.anaconda.org/conda-forge/linux-64/optree-0.16.0-py313h33d0bda_0.conda#5c211bb056e1a3263a163ba21e3fbf73 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-20.0.0-h296ad67_16_cpu.conda#d63e640c67f7fd50520b5d8daaa909b9 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-32_h372d94f_mkl.conda#68b55daaf083682f58d9b7f5d52aeb37 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-32_hc41d3b0_mkl.conda#6dc827963c12f90c79f5b2be4eaea072 +https://conda.anaconda.org/conda-forge/linux-64/polars-default-1.31.0-py39hf521cc8_1.conda#85f9f61975ba5a8f3d40b477aef457cb +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda#ce978e1b9ed8b8d49164e90a5cdc94cd +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.1-h0384650_1.conda#3610aa92d2de36047886f30e99342f21 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-20.0.0-h635bf11_16_cpu.conda#fdb9abcab20298807658642299db2825 +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-32_hbc6e62b_mkl.conda#1524bf380c8b6a65a856a335feb4984e +https://conda.anaconda.org/conda-forge/linux-64/libparquet-20.0.0-h790f06f_16_cpu.conda#9d17c551ed3d37793a52a2680b443f99 +https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.7.1-cpu_mkl_hf38bc2d_103.conda#cc613cc921fe87d8ecda7a7c8fafc097 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.1-py313hf6604e3_1.conda#392b48cb8239fee6d03c6c38a74b0cf4 +https://conda.anaconda.org/conda-forge/linux-64/polars-1.31.0-default_h70f2ef1_1.conda#0217d9e4176cf33942996a7ee3afac0e https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-20.0.0-py313he5f92c8_0_cpu.conda#2afdef63d9fbc2cd0e52f8e8f3472404 -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.0-py313h5f61773_0.conda#f51f25ec8fcbf777f8b186bb5deeed40 -https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.3.1-pyhd8ed1ab_0.conda#11107d0aeb8c590a34fee0894909816b -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-31_hcf00494_mkl.conda#368c93bde87a67d24a74de15bf4c49fd +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.1-py313h7dabd7a_0.conda#42a24d0f4fe3a2e8307de3838e162452 +https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.4-pyhe01879c_1.conda#61d4f8b95dac300a1b7f665bcc79653a +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-32_hcf00494_mkl.conda#92820d2178317944b3f17760b03d73a9 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py313h33d0bda_0.conda#5dc81fffe102f63045225007a33d6199 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-20.0.0-hcb10f89_0_cpu.conda#ebdbd9d4522b4106246866054f7520bf -https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py313ha87cce1_3.conda#6248b529e537b1d4cb5ab3ef7f537795 -https://conda.anaconda.org/conda-forge/linux-64/polars-1.27.1-py39h2a4a510_3.conda#fba08963eaa1f954480045d033d1221e -https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.7.0-cpu_mkl_py313_hea9ba1b_100.conda#3c2ce6a304aa827f1e3cc21f7df9190d -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py313h86fcf2b_0.conda#ca68acd9febc86448eeed68d0c6c8643 -https://conda.anaconda.org/conda-forge/noarch/scipy-doctest-1.7.1-pyh29332c3_0.conda#d3b3b7b88385648eff6ae39694692f27 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.131-mkl.conda#9bb865b7e01104255ca54e61a58ded15 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-20.0.0-h1bed206_0_cpu.conda#1763dd016d6eee48e2bb29382f8d1562 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.1-py313h129903b_0.conda#4e23b3fabf434b418e0d9c6975a6453f +https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-20.0.0-h635bf11_16_cpu.conda#63108d2fb9e4d0762dd2b76ab78ff53d +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py313h08cd8bf_0.conda#0b23bc9b44d838b88f3ec8ab780113f1 +https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.7.1-cpu_mkl_py313_h58dab0e_103.conda#14fd59c6195a9d61987cf42e138b1a92 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.0-py313h86fcf2b_0.conda#8c60fe574a5abab59cd365d32e279872 +https://conda.anaconda.org/conda-forge/noarch/scipy-doctest-1.8.0-pyhe01879c_0.conda#5bc3f4bc1e027aa4ba6fdad1a84b5d3c +https://conda.anaconda.org/conda-forge/linux-64/blas-2.132-mkl.conda#b8b0988c5e1abbb5f05c7f086f76b6bd +https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-20.0.0-h3f74fd7_16_cpu.conda#2d27fd608cf6b7b8052c1185ba2637ce +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.3-py313h129903b_0.conda#4f8816d006b1c155ec416bcf7ff6cee2 https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py313hf0ab243_1.conda#4c769bf3858f424cb2ecf952175ec600 -https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.7.0-cpu_mkl_hc60beec_100.conda#20b3051f55ad823a27818dfa46a41c8f -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.1-py313h78bf25f_0.conda#d0c80dea550ca97fc0710b2ecef919ba +https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.7.1-cpu_mkl_hc60beec_103.conda#5832b21e4193b05a096a8db177b14031 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.3-py313h78bf25f_0.conda#cc9324e614a297fdf23439d887d3513d https://conda.anaconda.org/conda-forge/linux-64/pyarrow-20.0.0-py313h78bf25f_0.conda#6b8d388845ce750fe2ad8436669182f3 diff --git a/build_tools/azure/pylatest_conda_mkl_no_openmp_environment.yml b/build_tools/azure/pylatest_conda_forge_mkl_no_openmp_environment.yml similarity index 78% rename from build_tools/azure/pylatest_conda_mkl_no_openmp_environment.yml rename to build_tools/azure/pylatest_conda_forge_mkl_no_openmp_environment.yml index 0c2eec344c26b..8d8fe676698e6 100644 --- a/build_tools/azure/pylatest_conda_mkl_no_openmp_environment.yml +++ b/build_tools/azure/pylatest_conda_forge_mkl_no_openmp_environment.yml @@ -2,13 +2,15 @@ # following script to centralize the configuration for CI builds: # build_tools/update_environments_and_lock_files.py channels: - - defaults + - conda-forge dependencies: - python - numpy - blas[build=mkl] - - scipy<1.12 + - scipy + - cython - joblib + - threadpoolctl - matplotlib - pandas - pyamg @@ -17,12 +19,7 @@ dependencies: - pillow - pip - ninja + - meson-python - pytest-cov - coverage - ccache - - pip - - pip: - - cython - - threadpoolctl - - meson-python - - meson diff --git a/build_tools/azure/pylatest_conda_forge_mkl_no_openmp_osx-64_conda.lock b/build_tools/azure/pylatest_conda_forge_mkl_no_openmp_osx-64_conda.lock new file mode 100644 index 0000000000000..8a85a1f980b7b --- /dev/null +++ b/build_tools/azure/pylatest_conda_forge_mkl_no_openmp_osx-64_conda.lock @@ -0,0 +1,102 @@ +# Generated by conda-lock. +# platform: osx-64 +# input_hash: 12e3e511a3041fa8d542ec769028e21d8276a3aacad33a6e0125494942ec565e +@EXPLICIT +https://conda.anaconda.org/conda-forge/osx-64/mkl-include-2023.2.0-h6bab518_50500.conda#835abb8ded5e26f23ea6996259c7972e +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a +https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda#7ed4301d437b59045be7e051a0308211 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b +https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda#d68d48a3060eb5abdc1cdc8e2a3a5966 +https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.1.0-h6e16a3a_3.conda#ec21ca03bcc08f89b7e88627ae787eaf +https://conda.anaconda.org/conda-forge/osx-64/libcxx-20.1.8-h3d58e20_1.conda#d2db320b940047515f7a27f870984fe7 +https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.24-hcc1b750_0.conda#f0a46c359722a3e84deb05cd4072d153 +https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.1-h21dd04a_0.conda#9fdeae0b7edda62e989557d645769515 +https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.6-h281671d_1.conda#4ca9ea59839a9ca8df84170fab4ceb41 +https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.18-h4b5e92a_1.conda#6283140d7b2b55b6b095af939b71b13f +https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.0-h6e16a3a_0.conda#87537967e6de2f885a9fcebd42b7cb10 +https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda#8468beea04b9065b9807fc8b9cdc5894 +https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda#18b81186a6adb43f000ad19ed7b70381 +https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda#7bb6608cf1f83578587297a158a6630b +https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda#003a54a4e32b02f7355b50a837e699da +https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-20.1.8-hf4e0ed4_0.conda#ab3b31ebe0afdf903fa5ac7f13357e39 +https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda#ced34dd9929f491ca6dab6a2927aff25 +https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda#8bcf980d2c6b17094961198284b8e862 +https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h6e16a3a_0.conda#4cf40e60b444d56512a64f39d12c20bd +https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h00291cd_0.conda#9f438e1b6f4e73fd9e6d78bfe7c36743 +https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hcca01a6_1.conda#21f765ced1a0ef4070df53cb425e1967 +https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.1.0-h6e16a3a_3.conda#71d03e5e44801782faff90c455b3e69a +https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.1.0-h6e16a3a_3.conda#94c0090989db51216f40558958a3dd40 +https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-14.2.0-h51e75f0_103.conda#6183f7e9cd1e7ba20118ff0ca20a05e5 +https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.50-h3c4a55f_0.conda#0b750895b4a3cbd06e685f86c24c205d +https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.50.3-h39a8b3b_0.conda#41e1a78df514ac69dd9d22a804d51310 +https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda#bbeca862892e2898bdb45792a61c4afc +https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.13.8-h93c44a6_0.conda#e42a93a31cbc6826620144343d42f472 +https://conda.anaconda.org/conda-forge/osx-64/ninja-1.13.1-h0ba0a54_0.conda#71576ca895305a20c73304fcb581ae1a +https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.1-hc426f3f_0.conda#f1ac2dbc36ce2017bd8f471960b1261d +https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda#dd1ea9ff27c93db7c01a7b7656bd4ad4 +https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda#342570f8e02f2f022147a7f841475784 +https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_2.conda#9864891a6946c2fe037c02fca7392ab4 +https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h8210216_2.conda#cd60a4a5a8d6a476b30d8aa4bb49251a +https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.1.0-h6e16a3a_3.conda#a240d09be7c84cb1d33535ebd36fe422 +https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.13.3-h40dfd5c_1.conda#c76e6f421a0e95c282142f820835e186 +https://conda.anaconda.org/conda-forge/osx-64/libgfortran-5.0.0-14_2_0_h51e75f0_103.conda#090b3c9ae1282c8f9b394ac9e4773b10 +https://conda.anaconda.org/conda-forge/osx-64/libhwloc-2.11.2-default_h8c32e24_1002.conda#a9f64b764e16b830465ae64364394f36 +https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h1167cee_5.conda#fc84af14a09e779f1d37ab1d16d5c4e2 +https://conda.anaconda.org/conda-forge/osx-64/python-3.13.5-hc3a4c56_102_cp313.conda#afa9492a7d31f6f7189ca8f08aceadac +https://conda.anaconda.org/conda-forge/osx-64/brotli-1.1.0-h6e16a3a_3.conda#44903b29bc866576c42d5c0a25e76569 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 +https://conda.anaconda.org/conda-forge/osx-64/cython-3.1.2-py313h9efc8c2_2.conda#c37814cffeee2c9184595d522b381b95 +https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.8-py313ha0b1807_1.conda#32cf8c99c5559e08f336d79436fbe873 +https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.17-h72f5680_0.conda#bf210d0c63f2afb9e414a858b79f0eaa +https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.13.3-h694c41f_1.conda#07c8d3fbbe907f32014b121834b36dd5 +https://conda.anaconda.org/conda-forge/osx-64/libhiredis-1.0.2-h2beb688_0.tar.bz2#524282b2c46c9dedf051b3bc2ae05494 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.3-h7fd6d84_0.conda#025c711177fc3309228ca1a32374458d +https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 +https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 +https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 +https://conda.anaconda.org/conda-forge/osx-64/tbb-2021.13.0-hb890de9_1.conda#284892942cdddfded53d090050b639a5 +https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 +https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 +https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.1-py313h63b0ddb_0.conda#7554d07cbe64f41c73a403e99bccf3c6 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f +https://conda.anaconda.org/conda-forge/osx-64/ccache-4.11.3-h33566b8_0.conda#b65cad834bd6c1f660c101cca09430bf +https://conda.anaconda.org/conda-forge/osx-64/coverage-7.9.2-py313h717bdf5_0.conda#855af2d2eb136ec60e572d8403775500 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.59.0-py313h4db2fa4_0.conda#1dab5b45690c319aba7d846f9267349c +https://conda.anaconda.org/conda-forge/osx-64/freetype-2.13.3-h694c41f_1.conda#126dba1baf5030cb6f34533718924577 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c +https://conda.anaconda.org/conda-forge/osx-64/mkl-2023.2.0-h54c2260_50500.conda#0a342ccdc79e4fcd359245ac51941e7b +https://conda.anaconda.org/conda-forge/osx-64/pillow-11.3.0-py313h0c4f865_0.conda#4cedae60046caf240dda5b29ba2f60a7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 +https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-20_osx64_mkl.conda#160fdc97a51d66d51dc782fb67d35205 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/osx-64/mkl-devel-2023.2.0-h694c41f_50500.conda#1b4d0235ef253a1e19459351badf4f9f +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-20_osx64_mkl.conda#51089a4865eb4aec2bc5c7468bd07f9f +https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-20_osx64_mkl.conda#58f08e12ad487fac4a08f90ff0b87aec +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda#ce978e1b9ed8b8d49164e90a5cdc94cd +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/osx-64/liblapacke-3.9.0-20_osx64_mkl.conda#124ae8e384268a8da66f1d64114a1eda +https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.1-py313hdb1a8e5_1.conda#fcf306b390eb68fbee1943d9979e51aa +https://conda.anaconda.org/conda-forge/osx-64/blas-devel-3.9.0-20_osx64_mkl.conda#cc3260179093918b801e373c6e888e02 +https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.2-py313ha0b1807_0.conda#2c2d1f840df1c512b34e0537ef928169 +https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.1-py313h366a99e_0.conda#3f95c70574b670f1f8e4f28d66aca339 +https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.0-py313h7e69c36_0.conda#ffba48a156734dfa47fabea9b59b7fa1 +https://conda.anaconda.org/conda-forge/osx-64/blas-2.120-mkl.conda#b041a7677a412f3d925d8208936cb1e2 +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.3-py313he981572_0.conda#91c22969c0974f2f23470d517774d457 +https://conda.anaconda.org/conda-forge/osx-64/pyamg-5.2.1-py313h0322a6a_1.conda#4bda5182eeaef3d2017a2ec625802e1a +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.3-py313habf4b1d_0.conda#c1043254f405998ece984e5f66a10943 diff --git a/build_tools/azure/pylatest_conda_forge_mkl_osx-64_conda.lock b/build_tools/azure/pylatest_conda_forge_mkl_osx-64_conda.lock index 4def307b28f84..2ec6034ebf11f 100644 --- a/build_tools/azure/pylatest_conda_forge_mkl_osx-64_conda.lock +++ b/build_tools/azure/pylatest_conda_forge_mkl_osx-64_conda.lock @@ -1,27 +1,27 @@ # Generated by conda-lock. # platform: osx-64 -# input_hash: b4e9eb0fbe1a7a6d067e4f4b43ca9e632309794c2a76d5c254ce023cb2fa2d99 +# input_hash: cee22335ff0a429180f2d8eeb31943f2646e3e653f1197f57ba6e39fc9659b05 @EXPLICIT https://conda.anaconda.org/conda-forge/noarch/libgfortran-devel_osx-64-13.3.0-h297be85_105.conda#c4967f8e797d0ffef3c5650fcdc2cdb5 https://conda.anaconda.org/conda-forge/osx-64/mkl-include-2023.2.0-h6bab518_50500.conda#835abb8ded5e26f23ea6996259c7972e -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda#e84b44e6300f1703cb25d29120c5b1d8 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8 https://conda.anaconda.org/conda-forge/osx-64/tbb-2021.10.0-h1c7c39f_2.conda#73434bcf87082942e938352afae9b0fa https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda#7ed4301d437b59045be7e051a0308211 -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda#d68d48a3060eb5abdc1cdc8e2a3a5966 -https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.1.0-h00291cd_2.conda#58f2c4bdd56c46cc7451596e4ae68e0b -https://conda.anaconda.org/conda-forge/osx-64/libcxx-20.1.4-hf95d169_0.conda#9a38a63cfe950dd3e1b3adfcba731d3a -https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.23-hcc1b750_0.conda#5d3507f22dda24f7d9a79325ad313e44 -https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.0-h240833e_0.conda#026d0a1056ba2a3dbbea6d4b08188676 +https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.1.0-h6e16a3a_3.conda#ec21ca03bcc08f89b7e88627ae787eaf +https://conda.anaconda.org/conda-forge/osx-64/libcxx-20.1.8-h3d58e20_1.conda#d2db320b940047515f7a27f870984fe7 +https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.24-hcc1b750_0.conda#f0a46c359722a3e84deb05cd4072d153 +https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.1-h21dd04a_0.conda#9fdeae0b7edda62e989557d645769515 https://conda.anaconda.org/conda-forge/osx-64/libffi-3.4.6-h281671d_1.conda#4ca9ea59839a9ca8df84170fab4ceb41 https://conda.anaconda.org/conda-forge/osx-64/libiconv-1.18-h4b5e92a_1.conda#6283140d7b2b55b6b095af939b71b13f https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.0-h6e16a3a_0.conda#87537967e6de2f885a9fcebd42b7cb10 -https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_0.conda#8e1197f652c67e87a9ece738d82cef4f -https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hfdf4475_0.conda#ed625b2e59dff82859c23dd24774156b -https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.5.0-h6cf52b4_0.conda#5e0cefc99a231ac46ba21e27ae44689f +https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda#8468beea04b9065b9807fc8b9cdc5894 +https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda#18b81186a6adb43f000ad19ed7b70381 +https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda#7bb6608cf1f83578587297a158a6630b https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda#003a54a4e32b02f7355b50a837e699da -https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-20.1.4-ha54dae1_0.conda#985619d7704847d30346abb6feeb8351 +https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-20.1.8-hf4e0ed4_0.conda#ab3b31ebe0afdf903fa5ac7f13357e39 https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda#ced34dd9929f491ca6dab6a2927aff25 https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda#8bcf980d2c6b17094961198284b8e862 https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h6e16a3a_0.conda#4cf40e60b444d56512a64f39d12c20bd @@ -29,104 +29,106 @@ https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h00291cd_0.con https://conda.anaconda.org/conda-forge/osx-64/gmp-6.3.0-hf036a51_2.conda#427101d13f19c4974552a4e5b072eef1 https://conda.anaconda.org/conda-forge/osx-64/isl-0.26-imath32_h2e86a7b_101.conda#d06222822a9144918333346f145b68c6 https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hcca01a6_1.conda#21f765ced1a0ef4070df53cb425e1967 -https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.1.0-h00291cd_2.conda#34709a1f5df44e054c4a12ab536c5459 -https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.1.0-h00291cd_2.conda#691f0dcb36f1ae67f5c489f20ae987ea +https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.1.0-h6e16a3a_3.conda#71d03e5e44801782faff90c455b3e69a +https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.1.0-h6e16a3a_3.conda#94c0090989db51216f40558958a3dd40 https://conda.anaconda.org/conda-forge/osx-64/libcxx-devel-18.1.8-h7c275be_8.conda#a9513c41f070a9e2d5c370ba5d6c0c00 -https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-14.2.0-h58528f3_105.conda#94560312ff3c78225bed62ab59854c31 -https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.47-h3c4a55f_0.conda#8461ab86d2cdb76d6e971aab225be73f -https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.49.1-hdb6dae5_2.conda#1819e770584a7e83a81541d8253cbabe +https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-14.2.0-h51e75f0_103.conda#6183f7e9cd1e7ba20118ff0ca20a05e5 +https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.50-h3c4a55f_0.conda#0b750895b4a3cbd06e685f86c24c205d +https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.50.3-h39a8b3b_0.conda#41e1a78df514ac69dd9d22a804d51310 https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda#bbeca862892e2898bdb45792a61c4afc -https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.14.2-h8c082e5_0.conda#4adac80accf99fa253f0620444ad01fb +https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.14.5-hf180ddd_0.conda#b6a0c7420f0650a3268a3cf2e9c542fa https://conda.anaconda.org/conda-forge/osx-64/mkl-2023.2.0-h54c2260_50500.conda#0a342ccdc79e4fcd359245ac51941e7b -https://conda.anaconda.org/conda-forge/osx-64/ninja-1.12.1-hd6aca1a_1.conda#1cf196736676270fa876001901e4e1db -https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.0-hc426f3f_1.conda#919faa07b9647beb99a0e7404596a465 +https://conda.anaconda.org/conda-forge/osx-64/ninja-1.13.1-h0ba0a54_0.conda#71576ca895305a20c73304fcb581ae1a +https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.1-hc426f3f_0.conda#f1ac2dbc36ce2017bd8f471960b1261d https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda#dd1ea9ff27c93db7c01a7b7656bd4ad4 https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda#342570f8e02f2f022147a7f841475784 https://conda.anaconda.org/conda-forge/osx-64/tapi-1300.6.5-h390ca13_0.conda#c6ee25eb54accb3f1c8fc39203acfaf1 -https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h1abcd95_1.conda#bf830ba5afc507c6232d4ef0fb1a882d +https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_2.conda#9864891a6946c2fe037c02fca7392ab4 https://conda.anaconda.org/conda-forge/osx-64/zlib-1.3.1-hd23fc13_2.conda#c989e0295dcbdc08106fe5d9e935f0b9 https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h8210216_2.conda#cd60a4a5a8d6a476b30d8aa4bb49251a -https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.1.0-h00291cd_2.conda#049933ecbf552479a12c7917f0a4ce59 +https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.1.0-h6e16a3a_3.conda#a240d09be7c84cb1d33535ebd36fe422 https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-20_osx64_mkl.conda#160fdc97a51d66d51dc782fb67d35205 https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.13.3-h40dfd5c_1.conda#c76e6f421a0e95c282142f820835e186 -https://conda.anaconda.org/conda-forge/osx-64/libgfortran-14.2.0-hef36b68_105.conda#6b27baf030f5d6603713c7e72d3f6b9a +https://conda.anaconda.org/conda-forge/osx-64/libgfortran-5.0.0-14_2_0_h51e75f0_103.conda#090b3c9ae1282c8f9b394ac9e4773b10 https://conda.anaconda.org/conda-forge/osx-64/libllvm18-18.1.8-default_h3571c67_5.conda#01dd8559b569ad39b64fef0a61ded1e9 -https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-hb77a491_4.conda#b36d793dd65b28e3aeaa3a77abe71678 +https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h1167cee_5.conda#fc84af14a09e779f1d37ab1d16d5c4e2 https://conda.anaconda.org/conda-forge/osx-64/mkl-devel-2023.2.0-h694c41f_50500.conda#1b4d0235ef253a1e19459351badf4f9f https://conda.anaconda.org/conda-forge/osx-64/mpfr-4.2.1-haed47dc_3.conda#d511e58aaaabfc23136880d9956fa7a6 -https://conda.anaconda.org/conda-forge/osx-64/python-3.13.3-h534c281_101_cp313.conda#ebcc7c42561d8d8b01477020b63218c0 +https://conda.anaconda.org/conda-forge/osx-64/python-3.13.5-hc3a4c56_102_cp313.conda#afa9492a7d31f6f7189ca8f08aceadac https://conda.anaconda.org/conda-forge/osx-64/sigtool-0.1.3-h88f4db0_0.tar.bz2#fbfb84b9de9a6939cb165c02c69b1865 -https://conda.anaconda.org/conda-forge/osx-64/brotli-1.1.0-h00291cd_2.conda#2db0c38a7f2321c5bdaf32b181e832c7 +https://conda.anaconda.org/conda-forge/osx-64/brotli-1.1.0-h6e16a3a_3.conda#44903b29bc866576c42d5c0a25e76569 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/osx-64/cython-3.0.12-py313h9efc8c2_0.conda#ddace7cae5c3073c031ad08ef01881da -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e +https://conda.anaconda.org/conda-forge/osx-64/cython-3.1.2-py313h9efc8c2_2.conda#c37814cffeee2c9184595d522b381b95 https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.7-py313h0c4e38b_0.conda#c37fceab459e104e77bb5456e219fc37 +https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.8-py313ha0b1807_1.conda#32cf8c99c5559e08f336d79436fbe873 https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.17-h72f5680_0.conda#bf210d0c63f2afb9e414a858b79f0eaa -https://conda.anaconda.org/conda-forge/osx-64/ld64_osx-64-951.9-h33512f0_6.conda#6cd120f5c9dae65b858e1fad2b7959a0 +https://conda.anaconda.org/conda-forge/osx-64/ld64_osx-64-954.16-h28b3ac7_0.conda#e198e41dada835a065079e4c70905974 https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-20_osx64_mkl.conda#51089a4865eb4aec2bc5c7468bd07f9f -https://conda.anaconda.org/conda-forge/osx-64/libclang-cpp18.1-18.1.8-default_h3571c67_9.conda#ef1a444913775b76f3391431967090a9 +https://conda.anaconda.org/conda-forge/osx-64/libclang-cpp18.1-18.1.8-default_h3571c67_10.conda#bf6753267e6f848f369c5bc2373dddd6 https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.13.3-h694c41f_1.conda#07c8d3fbbe907f32014b121834b36dd5 https://conda.anaconda.org/conda-forge/osx-64/libhiredis-1.0.2-h2beb688_0.tar.bz2#524282b2c46c9dedf051b3bc2ae05494 https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-20_osx64_mkl.conda#58f08e12ad487fac4a08f90ff0b87aec https://conda.anaconda.org/conda-forge/osx-64/llvm-tools-18-18.1.8-default_h3571c67_5.conda#4391981e855468ced32ca1940b3d7613 -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d https://conda.anaconda.org/conda-forge/osx-64/mpc-1.3.1-h9d8efa1_1.conda#0520855aaae268ea413d6bc913f1384c -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.3-h7fd6d84_0.conda#025c711177fc3309228ca1a32374458d https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/osx-64/tornado-6.4.2-py313h63b0ddb_0.conda#74a3a14f82dc65fa19f4fd4e2eb8da93 +https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.1-py313h63b0ddb_0.conda#7554d07cbe64f41c73a403e99bccf3c6 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f https://conda.anaconda.org/conda-forge/osx-64/ccache-4.11.3-h33566b8_0.conda#b65cad834bd6c1f660c101cca09430bf -https://conda.anaconda.org/conda-forge/osx-64/clang-18-18.1.8-default_h3571c67_9.conda#e29d8d2866f15f3b167938cc0e775b2f -https://conda.anaconda.org/conda-forge/osx-64/coverage-7.8.0-py313h717bdf5_0.conda#1215b56c8d9915318d1714cbd004035f -https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.57.0-py313h717bdf5_0.conda#190b8625dd6c38afe4f10e3be50122e4 +https://conda.anaconda.org/conda-forge/osx-64/clang-18-18.1.8-default_h3571c67_10.conda#62e1cd0882dad47d6a6878ad037f7b9d +https://conda.anaconda.org/conda-forge/osx-64/coverage-7.9.2-py313h717bdf5_0.conda#855af2d2eb136ec60e572d8403775500 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.59.0-py313h4db2fa4_0.conda#1dab5b45690c319aba7d846f9267349c https://conda.anaconda.org/conda-forge/osx-64/freetype-2.13.3-h694c41f_1.conda#126dba1baf5030cb6f34533718924577 https://conda.anaconda.org/conda-forge/osx-64/gfortran_impl_osx-64-13.3.0-hbf5bf67_105.conda#f56a107c8d1253346d01785ecece7977 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/osx-64/ld64-951.9-h4e51db5_6.conda#45bf526d53b1bc95bc0b932a91a41576 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c +https://conda.anaconda.org/conda-forge/osx-64/ld64-954.16-h4e51db5_0.conda#98b4c4a0eb19523f11219ea5cc21c17b https://conda.anaconda.org/conda-forge/osx-64/liblapacke-3.9.0-20_osx64_mkl.conda#124ae8e384268a8da66f1d64114a1eda https://conda.anaconda.org/conda-forge/osx-64/llvm-tools-18.1.8-default_h3571c67_5.conda#cc07ff74d2547da1f1452c42b67bafd6 -https://conda.anaconda.org/conda-forge/osx-64/numpy-2.2.5-py313hc518a0f_0.conda#eba644ccc203cfde2fa1f450f528c70d +https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.1-py313hdb1a8e5_1.conda#fcf306b390eb68fbee1943d9979e51aa +https://conda.anaconda.org/conda-forge/osx-64/pillow-11.3.0-py313h0c4f865_0.conda#4cedae60046caf240dda5b29ba2f60a7 https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/osx-64/blas-devel-3.9.0-20_osx64_mkl.conda#cc3260179093918b801e373c6e888e02 -https://conda.anaconda.org/conda-forge/osx-64/cctools_osx-64-1010.6-hd19c6af_6.conda#4694e9e497454a8ce5b9fb61e50d9c5d -https://conda.anaconda.org/conda-forge/osx-64/clang-18.1.8-default_h576c50e_9.conda#266e7e8fa2190df09e6f236571c91511 +https://conda.anaconda.org/conda-forge/osx-64/cctools_osx-64-1021.4-h508880d_0.conda#4813f891c9cf3901d3c9c091000c6569 +https://conda.anaconda.org/conda-forge/osx-64/clang-18.1.8-default_h576c50e_10.conda#350a10c62423982b0c80a043b9921c00 https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.2-py313ha0b1807_0.conda#2c2d1f840df1c512b34e0537ef928169 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/osx-64/pandas-2.2.3-py313h2e7108f_3.conda#5c37fc7549913fc4895d7d2e097091ed -https://conda.anaconda.org/conda-forge/osx-64/pillow-11.1.0-py313h0c4f865_0.conda#11b4dd7a814202f2a0b655420f1c1c3a -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.1.1-pyhd8ed1ab_0.conda#1e35d8f975bc0e984a19819aa91c440a -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd -https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py313h7e69c36_0.conda#53c23f87aedf2d139d54c88894c8a07f +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.1-py313h366a99e_0.conda#3f95c70574b670f1f8e4f28d66aca339 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.0-py313h7e69c36_0.conda#ffba48a156734dfa47fabea9b59b7fa1 https://conda.anaconda.org/conda-forge/osx-64/blas-2.120-mkl.conda#b041a7677a412f3d925d8208936cb1e2 -https://conda.anaconda.org/conda-forge/osx-64/cctools-1010.6-ha66f10e_6.conda#a126dcde2752751ac781b67238f7fac4 -https://conda.anaconda.org/conda-forge/osx-64/clangxx-18.1.8-default_heb2e8d1_9.conda#4ba6bd39da787a7306eba77555e86dd3 -https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.1-py313he981572_0.conda#45a80d45944fbc43f081d719b23bf366 +https://conda.anaconda.org/conda-forge/osx-64/cctools-1021.4-ha66f10e_0.conda#37619e89a65bb3688c67d82fd8645afc +https://conda.anaconda.org/conda-forge/osx-64/clangxx-18.1.8-default_heb2e8d1_10.conda#c39251c90faf5ba495d9f9ef88d7563e +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.3-py313he981572_0.conda#91c22969c0974f2f23470d517774d457 https://conda.anaconda.org/conda-forge/osx-64/pyamg-5.2.1-py313h0322a6a_1.conda#4bda5182eeaef3d2017a2ec625802e1a +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda#ce978e1b9ed8b8d49164e90a5cdc94cd +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 https://conda.anaconda.org/conda-forge/noarch/compiler-rt_osx-64-18.1.8-hf2b8a54_1.conda#76f906e6bdc58976c5593f650290ae20 -https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.1-py313habf4b1d_0.conda#81ea3344e4fc2066a38199a64738ca6b +https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.3-py313habf4b1d_0.conda#c1043254f405998ece984e5f66a10943 https://conda.anaconda.org/conda-forge/osx-64/compiler-rt-18.1.8-h1020d70_1.conda#bc1714a1e73be18e411cff30dc1fe011 -https://conda.anaconda.org/conda-forge/osx-64/clang_impl_osx-64-18.1.8-h6a44ed1_24.conda#5224d53acc2604a86d790f664d7fcbc4 -https://conda.anaconda.org/conda-forge/osx-64/clang_osx-64-18.1.8-h7e5c614_24.conda#24e1a9c1296772ec45bfcd6a0d855fa5 -https://conda.anaconda.org/conda-forge/osx-64/c-compiler-1.9.0-h09a7c41_0.conda#ab45badcb5d035d3bddfdbdd96e00967 -https://conda.anaconda.org/conda-forge/osx-64/clangxx_impl_osx-64-18.1.8-h4b7810f_24.conda#9d27517a71e7268679f1c47e7f34e47b +https://conda.anaconda.org/conda-forge/osx-64/clang_impl_osx-64-18.1.8-h6a44ed1_25.conda#bfc995f8ab9e8c22ebf365844da3383d +https://conda.anaconda.org/conda-forge/osx-64/clang_osx-64-18.1.8-h7e5c614_25.conda#1fea06d9ced6b87fe63384443bc2efaf +https://conda.anaconda.org/conda-forge/osx-64/c-compiler-1.10.0-h09a7c41_0.conda#7b7c12e4774b83c18612c78073d12adc +https://conda.anaconda.org/conda-forge/osx-64/clangxx_impl_osx-64-18.1.8-h4b7810f_25.conda#c03c94381d9ffbec45c98b800e7d3e86 https://conda.anaconda.org/conda-forge/osx-64/gfortran_osx-64-13.3.0-h3223c34_1.conda#a6eeb1519091ac3239b88ee3914d6cb6 -https://conda.anaconda.org/conda-forge/osx-64/clangxx_osx-64-18.1.8-h7e5c614_24.conda#c1e7c7d5c04d0ea456aa48ddb8a9dc2b +https://conda.anaconda.org/conda-forge/osx-64/clangxx_osx-64-18.1.8-h7e5c614_25.conda#2e5c84e93a3519d77a0d8d9b3ea664fd https://conda.anaconda.org/conda-forge/osx-64/gfortran-13.3.0-hcc3c99d_1.conda#e1177b9b139c6cf43250427819f2f07b -https://conda.anaconda.org/conda-forge/osx-64/cxx-compiler-1.9.0-h20888b2_0.conda#cd17d9bf9780b0db4ed31fb9958b167f -https://conda.anaconda.org/conda-forge/osx-64/fortran-compiler-1.9.0-h02557f8_0.conda#2cf645572d7ae534926093b6e9f3bdff -https://conda.anaconda.org/conda-forge/osx-64/compilers-1.9.0-h694c41f_0.conda#b84884262dcd1c2f56a9e1961fdd3326 +https://conda.anaconda.org/conda-forge/osx-64/cxx-compiler-1.10.0-h20888b2_0.conda#b3a935ade707c54ebbea5f8a7c6f4549 +https://conda.anaconda.org/conda-forge/osx-64/fortran-compiler-1.10.0-h02557f8_0.conda#aa3288408631f87b70295594cd4daba8 +https://conda.anaconda.org/conda-forge/osx-64/compilers-1.10.0-h694c41f_0.conda#d43a090863429d66e0986c84de7a7906 diff --git a/build_tools/azure/pylatest_conda_mkl_no_openmp_osx-64_conda.lock b/build_tools/azure/pylatest_conda_mkl_no_openmp_osx-64_conda.lock deleted file mode 100644 index ed4af051f10c6..0000000000000 --- a/build_tools/azure/pylatest_conda_mkl_no_openmp_osx-64_conda.lock +++ /dev/null @@ -1,82 +0,0 @@ -# Generated by conda-lock. -# platform: osx-64 -# input_hash: 037fecf9454db91c21c8a57ee632e7221447f0bcfd9a5850dfcd6d727a30b086 -@EXPLICIT -https://repo.anaconda.com/pkgs/main/osx-64/blas-1.0-mkl.conda#cb2c87e85ac8e0ceae776d26d4214c8a -https://repo.anaconda.com/pkgs/main/osx-64/bzip2-1.0.8-h6c40b1e_6.conda#96224786021d0765ce05818fa3c59bdb -https://repo.anaconda.com/pkgs/main/osx-64/ca-certificates-2025.2.25-hecd8cb5_0.conda#12ab77db61795036e15a5b14929ad4a1 -https://repo.anaconda.com/pkgs/main/osx-64/jpeg-9e-h46256e1_3.conda#b1d9769eac428e11f5f922531a1da2e0 -https://repo.anaconda.com/pkgs/main/osx-64/libcxx-14.0.6-h9765a3e_0.conda#387757bb354ae9042370452cd0fb5627 -https://repo.anaconda.com/pkgs/main/osx-64/libdeflate-1.22-h46256e1_0.conda#7612fb79e5e76fcd16655c7d026f4a66 -https://repo.anaconda.com/pkgs/main/osx-64/libffi-3.4.4-hecd8cb5_1.conda#eb7f09ada4d95f1a26f483f1009d9286 -https://repo.anaconda.com/pkgs/main/osx-64/libwebp-base-1.3.2-h46256e1_1.conda#399c11b50e6e7a6969aca9a84ea416b7 -https://repo.anaconda.com/pkgs/main/osx-64/llvm-openmp-14.0.6-h0dcd299_0.conda#b5804d32b87dc61ca94561ade33d5f2d -https://repo.anaconda.com/pkgs/main/osx-64/ncurses-6.4-hcec6c5f_0.conda#0214d1ee980e217fabc695f1e40662aa -https://repo.anaconda.com/pkgs/main/noarch/tzdata-2025b-h04d1e81_0.conda#1d027393db3427ab22a02aa44a56f143 -https://repo.anaconda.com/pkgs/main/osx-64/xz-5.6.4-h46256e1_1.conda#ce989a528575ad332a650bb7c7f7e5d5 -https://repo.anaconda.com/pkgs/main/osx-64/zlib-1.2.13-h4b97444_1.conda#38e35f7c817fac0973034bfce6706ec2 -https://repo.anaconda.com/pkgs/main/osx-64/ccache-3.7.9-hf120daa_0.conda#a01515a32e721c51d631283f991bc8ea -https://repo.anaconda.com/pkgs/main/osx-64/expat-2.7.1-h6d0c2b6_0.conda#6cdc93776b7551083854e7f106a62720 -https://repo.anaconda.com/pkgs/main/osx-64/intel-openmp-2023.1.0-ha357a0b_43548.conda#ba8a89ffe593eb88e4c01334753c40c3 -https://repo.anaconda.com/pkgs/main/osx-64/lerc-4.0.0-h6d0c2b6_0.conda#824f87854c58df1525557c8639ce7f93 -https://repo.anaconda.com/pkgs/main/osx-64/libgfortran5-11.3.0-h9dfd629_28.conda#1fa1a27ee100b1918c3021dbfa3895a3 -https://repo.anaconda.com/pkgs/main/osx-64/libpng-1.6.39-h6c40b1e_0.conda#a3c824835f53ad27aeb86d2b55e47804 -https://repo.anaconda.com/pkgs/main/osx-64/lz4-c-1.9.4-hcec6c5f_1.conda#aee0efbb45220e1985533dbff48551f8 -https://repo.anaconda.com/pkgs/main/osx-64/ninja-base-1.12.1-h1962661_0.conda#9c0a94a811e88f182519d9309cf5f634 -https://repo.anaconda.com/pkgs/main/osx-64/openssl-3.0.16-h184c1cd_0.conda#8e3c130ef85c3260d535153b4d0fd63a -https://repo.anaconda.com/pkgs/main/osx-64/readline-8.2-hca72f7f_0.conda#971667436260e523f6f7355fdfa238bf -https://repo.anaconda.com/pkgs/main/osx-64/tbb-2021.8.0-ha357a0b_0.conda#fb48530a3eea681c11dafb95b3387c0f -https://repo.anaconda.com/pkgs/main/osx-64/tk-8.6.14-h4d00af3_0.conda#a2c03940c2ae54614301ec82e6a98d75 -https://repo.anaconda.com/pkgs/main/osx-64/freetype-2.13.3-h02243ff_0.conda#acf5e48106235eb200eecb79119c7ffc -https://repo.anaconda.com/pkgs/main/osx-64/libgfortran-5.0.0-11_3_0_hecd8cb5_28.conda#2eb13b680803f1064e53873ae0aaafb3 -https://repo.anaconda.com/pkgs/main/osx-64/mkl-2023.1.0-h8e150cf_43560.conda#85d0f3431dd5c6ae44f8725fdd3d3e59 -https://repo.anaconda.com/pkgs/main/osx-64/sqlite-3.45.3-h6c40b1e_0.conda#2edf909b937b3aad48322c9cb2e8f1a0 -https://repo.anaconda.com/pkgs/main/osx-64/zstd-1.5.6-h138b38a_0.conda#f4d15d7d0054d39e6a24fe8d7d1e37c5 -https://repo.anaconda.com/pkgs/main/osx-64/libtiff-4.7.0-h2dfa3ea_0.conda#82a118ce0139e2bf6f7a99c4cfbd4749 -https://repo.anaconda.com/pkgs/main/osx-64/python-3.12.9-hcd54a6c_0.conda#1bf9af06f3e476df1f72e8674a9224df -https://repo.anaconda.com/pkgs/main/osx-64/brotli-python-1.0.9-py312h6d0c2b6_9.conda#425936421fe402074163ac3ffe33a060 -https://repo.anaconda.com/pkgs/main/osx-64/coverage-7.6.9-py312h46256e1_0.conda#f8c1547bbf522a600ee795901240a7b0 -https://repo.anaconda.com/pkgs/main/noarch/cycler-0.11.0-pyhd3eb1b0_0.conda#f5e365d2cdb66d547eb8c3ab93843aab -https://repo.anaconda.com/pkgs/main/noarch/execnet-2.1.1-pyhd3eb1b0_0.conda#b3cb797432ee4657d5907b91a5dc65ad -https://repo.anaconda.com/pkgs/main/noarch/iniconfig-1.1.1-pyhd3eb1b0_0.tar.bz2#e40edff2c5708f342cef43c7f280c507 -https://repo.anaconda.com/pkgs/main/osx-64/joblib-1.4.2-py312hecd8cb5_0.conda#8ab03dfa447b4e0bfa0bd3d25930f3b6 -https://repo.anaconda.com/pkgs/main/osx-64/kiwisolver-1.4.8-py312h6d0c2b6_0.conda#060d4498fcc967a640829cb7e55c95f2 -https://repo.anaconda.com/pkgs/main/osx-64/lcms2-2.16-h31d93a5_1.conda#42450b66e91caf9ab0672a599e2a7bd0 -https://repo.anaconda.com/pkgs/main/osx-64/mkl-service-2.4.0-py312h46256e1_2.conda#04297cb766cabf38613ed6eb4eec85c3 -https://repo.anaconda.com/pkgs/main/osx-64/ninja-1.12.1-hecd8cb5_0.conda#ee3b660616ef0fbcbd0096a67c11c94b -https://repo.anaconda.com/pkgs/main/osx-64/openjpeg-2.5.2-h2d09ccc_1.conda#0f2e221843154b436b5982c695df627b -https://repo.anaconda.com/pkgs/main/osx-64/packaging-24.2-py312hecd8cb5_0.conda#76512e47c9c37443444ef0624769f620 -https://repo.anaconda.com/pkgs/main/osx-64/pluggy-1.5.0-py312hecd8cb5_0.conda#ca381e438f1dbd7986ac0fa0da70c9d8 -https://repo.anaconda.com/pkgs/main/osx-64/pyparsing-3.2.0-py312hecd8cb5_0.conda#e4086daaaed13f68cc8d5b9da7db73cc -https://repo.anaconda.com/pkgs/main/noarch/python-tzdata-2025.2-pyhd3eb1b0_0.conda#5ac858f05dbf9d3cdb04d53516901247 -https://repo.anaconda.com/pkgs/main/osx-64/pytz-2024.1-py312hecd8cb5_0.conda#2b28ec0e0d07f5c0c701f75200b1e8b6 -https://repo.anaconda.com/pkgs/main/osx-64/setuptools-78.1.1-py312hecd8cb5_0.conda#76b66b96a1564cb76011408c1eb8df3e -https://repo.anaconda.com/pkgs/main/osx-64/six-1.17.0-py312hecd8cb5_0.conda#aadd782bc06426887ae0835eedd98ceb -https://repo.anaconda.com/pkgs/main/noarch/toml-0.10.2-pyhd3eb1b0_0.conda#cda05f5f6d8509529d1a2743288d197a -https://repo.anaconda.com/pkgs/main/osx-64/tornado-6.4.2-py312h46256e1_0.conda#6b41d7d8a2bf93ae3fc512202b14a9ec -https://repo.anaconda.com/pkgs/main/osx-64/unicodedata2-15.1.0-py312h46256e1_1.conda#4a7fd1dec7277c8ab71aa11aa08df86b -https://repo.anaconda.com/pkgs/main/osx-64/wheel-0.45.1-py312hecd8cb5_0.conda#fafb8687668467d8624d2ddd0909bce9 -https://repo.anaconda.com/pkgs/main/osx-64/fonttools-4.55.3-py312h46256e1_0.conda#f7680dd6b8b1c2f8aab17cf6630c6deb -https://repo.anaconda.com/pkgs/main/osx-64/numpy-base-1.26.4-py312h6f81483_0.conda#87f73efbf26ab2e2ea7c32481a71bd47 -https://repo.anaconda.com/pkgs/main/osx-64/pillow-11.1.0-py312h935ef2f_1.conda#c2f7a3f027cc93a3626d50b765b75dc5 -https://repo.anaconda.com/pkgs/main/noarch/pip-25.1-pyhc872135_2.conda#2778327d2a700153fefe0e69438b18e1 -https://repo.anaconda.com/pkgs/main/osx-64/pytest-8.3.4-py312hecd8cb5_0.conda#b15ee02022967632dfa1672669228bee -https://repo.anaconda.com/pkgs/main/osx-64/python-dateutil-2.9.0post0-py312hecd8cb5_2.conda#1047dde28f78127dd9f6121e882926dd -https://repo.anaconda.com/pkgs/main/osx-64/pytest-cov-6.0.0-py312hecd8cb5_0.conda#db697e319a4d1145363246a51eef0352 -https://repo.anaconda.com/pkgs/main/osx-64/pytest-xdist-3.6.1-py312hecd8cb5_0.conda#38df9520774ee82bf143218f1271f936 -https://repo.anaconda.com/pkgs/main/osx-64/bottleneck-1.4.2-py312ha2b695f_0.conda#7efb63b6a5b33829a3b2c7a3efcf53ce -https://repo.anaconda.com/pkgs/main/osx-64/contourpy-1.3.1-py312h1962661_0.conda#41499d3a415721b0514f0cccb8288cb1 -https://repo.anaconda.com/pkgs/main/osx-64/matplotlib-3.10.0-py312hecd8cb5_0.conda#2977e81a7775be7963daf49df981b6e0 -https://repo.anaconda.com/pkgs/main/osx-64/matplotlib-base-3.10.0-py312h919b35b_0.conda#afc11bf311f5921ca4674ebac9592cf8 -https://repo.anaconda.com/pkgs/main/osx-64/mkl_fft-1.3.8-py312h6c40b1e_0.conda#d59d01b940493f2b6a84aac922fd0c76 -https://repo.anaconda.com/pkgs/main/osx-64/mkl_random-1.2.4-py312ha357a0b_0.conda#c1ea9c8eee79a5af3399f3c31be0e9c6 -https://repo.anaconda.com/pkgs/main/osx-64/numpy-1.26.4-py312hac873b0_0.conda#3150bac1e382156f82a153229e1ebd06 -https://repo.anaconda.com/pkgs/main/osx-64/numexpr-2.8.7-py312hac873b0_0.conda#6303ba071636ef57fddf69eb6f440ec1 -https://repo.anaconda.com/pkgs/main/osx-64/scipy-1.11.4-py312h81688c2_0.conda#7d57b4c21a9261f97fa511e0940c5d93 -https://repo.anaconda.com/pkgs/main/osx-64/pandas-2.2.3-py312h6d0c2b6_0.conda#84ce5b8ec4a986d13a5df17811f556a2 -https://repo.anaconda.com/pkgs/main/osx-64/pyamg-5.2.1-py312h1962661_0.conda#58881950d4ce74c9302b56961f97a43c -# pip cython @ https://files.pythonhosted.org/packages/e6/6c/3be501a6520a93449b1e7e6f63e598ec56f3b5d1bc7ad14167c72a22ddf7/Cython-3.0.12-cp312-cp312-macosx_10_9_x86_64.whl#sha256=fe030d4a00afb2844f5f70896b7f2a1a0d7da09bf3aa3d884cbe5f73fff5d310 -# pip meson @ https://files.pythonhosted.org/packages/df/d7/f1c8acf0e597d4d07532f519780ee6e11ba285a9b092f18706b4c9118331/meson-1.8.0-py3-none-any.whl#sha256=472b7b25da286447333d32872b82d1c6f1a34024fb8ee017d7308056c25fec1f -# pip threadpoolctl @ https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl#sha256=43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb -# pip pyproject-metadata @ https://files.pythonhosted.org/packages/7e/b1/8e63033b259e0a4e40dd1ec4a9fee17718016845048b43a36ec67d62e6fe/pyproject_metadata-0.9.1-py3-none-any.whl#sha256=ee5efde548c3ed9b75a354fc319d5afd25e9585fa918a34f62f904cc731973ad -# pip meson-python @ https://files.pythonhosted.org/packages/7d/ec/40c0ddd29ef4daa6689a2b9c5ced47d5b58fa54ae149b19e9a97f4979c8c/meson_python-0.17.1-py3-none-any.whl#sha256=30a75c52578ef14aff8392677b09c39346e0a24d2b2c6204b8ed30583c11269c diff --git a/build_tools/azure/pylatest_free_threaded_environment.yml b/build_tools/azure/pylatest_free_threaded_environment.yml index b947f31beb14a..8980bfce4adaf 100644 --- a/build_tools/azure/pylatest_free_threaded_environment.yml +++ b/build_tools/azure/pylatest_free_threaded_environment.yml @@ -6,6 +6,8 @@ channels: dependencies: - python-freethreading - numpy + - scipy + - cython - joblib - threadpoolctl - pytest diff --git a/build_tools/azure/pylatest_free_threaded_linux-64_conda.lock b/build_tools/azure/pylatest_free_threaded_linux-64_conda.lock index 39b5e6021d170..b707c17e48507 100644 --- a/build_tools/azure/pylatest_free_threaded_linux-64_conda.lock +++ b/build_tools/azure/pylatest_free_threaded_linux-64_conda.lock @@ -1,58 +1,63 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: a4b2a317ef7733b7244b987f8b6b61126b9e647153cd112ba9565ae8eb5558e8 +# input_hash: b76364b5635e8c36a0fc0777955b5664a336ba94ac96f3ade7aad842ab7e15c5 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 -https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.13-5_cp313t.conda#ea4c21b96e8280414d9e243da0ec3201 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313t.conda#e1dd2408e4ff08393fbc3502fbe4316d https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda#06d02030237f4d5b3d9a7e7d348fe3c6 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_3.conda#3cd1a7238a0dd3d0860fdefc496cc854 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 -https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-h4bc722e_0.conda#aeb98fdeb2e8f25d43ef71fbacbeec80 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_2.conda#4056c857af1a99ee50589a941059ec55 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda#0a4d0252248ef9a0f88f2ba8b8a08e12 -https://conda.anaconda.org/conda-forge/linux-64/python-3.13.3-h4724d56_1_cp313t.conda#8193603fe48ace3d8801cfb246f44491 +https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_0.conda#323dc8f259224d13078aaf7ce96c3efe +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-32_h59b9bed_openblas.conda#2af9f3d5c2e39f417ce040f5a35c40c6 +https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-32_he106b2a_openblas.conda#3d3f9355e52f269cd8bc2c440d8a5263 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-32_h7ac8fdf_openblas.conda#6c3f04ccb6c578138e9f9899da0bd714 +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-h71033d7_2_cp313t.conda#0ccb0928bc1d7519a0889a9a5ae5b656 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.3-py313hd8ed1ab_1.conda#6ba9ba47b91b7758cb963d0f0eaf3422 -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e +https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.5-py313hd8ed1ab_2.conda#064c2671d943161ff2682bfabe92d84f +https://conda.anaconda.org/conda-forge/noarch/cython-3.1.2-pyh2c78169_102.conda#e250288041263e65630a5802c72fa76b https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h59b9bed_openblas.conda#728dbebd0f7a20337218beacffd37916 -https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.1-py313hfc84e54_1.conda#45e968119c8e7ba861d164fce43105b6 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda#abb32c727da370c481a1c206f5159ce9 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda#452b98eafe050ecff932f0ec832dd03f +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-freethreading-3.13.3-h92d6c8b_1.conda#4fa25290aec662a01642ba4b3c0ff5c1 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h103f029_0.conda#7dcbd568d6f8a4ffba5ace28915f1230 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd +https://conda.anaconda.org/conda-forge/noarch/python-freethreading-3.13.5-h92d6c8b_2.conda#32180e39991faf3fd42b4d74ef01daa0 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.0-py313h7f7b39c_0.conda#efa6724dab9395e1307c65a589d35459 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 diff --git a/build_tools/azure/pylatest_pip_openblas_pandas_environment.yml b/build_tools/azure/pylatest_pip_openblas_pandas_environment.yml index 6c3da4bb863b4..64baefb3e816d 100644 --- a/build_tools/azure/pylatest_pip_openblas_pandas_environment.yml +++ b/build_tools/azure/pylatest_pip_openblas_pandas_environment.yml @@ -2,7 +2,7 @@ # following script to centralize the configuration for CI builds: # build_tools/update_environments_and_lock_files.py channels: - - defaults + - conda-forge dependencies: - python - ccache @@ -24,7 +24,7 @@ dependencies: - pytest-cov - coverage - sphinx - - numpydoc + - numpydoc<1.9.0 - lightgbm - scikit-image - array-api-strict diff --git a/build_tools/azure/pylatest_pip_openblas_pandas_linux-64_conda.lock b/build_tools/azure/pylatest_pip_openblas_pandas_linux-64_conda.lock index edffbc7d70f46..4c67570d47a60 100644 --- a/build_tools/azure/pylatest_pip_openblas_pandas_linux-64_conda.lock +++ b/build_tools/azure/pylatest_pip_openblas_pandas_linux-64_conda.lock @@ -1,62 +1,68 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 830b1d953ebfc9e46b73f639e733ee09b5171952cf987981d569b1d5abd16292 +# input_hash: 0668d85ecef342f1056dfe3d1fd8d677c967d4037f6f95fff49c097fec0cd624 @EXPLICIT -https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda#c3473ff8bdb3d124ed5ff11ec380d6f9 -https://repo.anaconda.com/pkgs/main/linux-64/ca-certificates-2025.2.25-h06a4308_0.conda#495015d24da8ad929e3ae2d18571016d -https://repo.anaconda.com/pkgs/main/linux-64/ld_impl_linux-64-2.40-h12ee557_0.conda#ee672b5f635340734f58d618b7bca024 -https://repo.anaconda.com/pkgs/main/linux-64/python_abi-3.13-0_cp313.conda#d4009c49dd2b54ffded7f1365b5f6505 -https://repo.anaconda.com/pkgs/main/noarch/tzdata-2025b-h04d1e81_0.conda#1d027393db3427ab22a02aa44a56f143 -https://repo.anaconda.com/pkgs/main/linux-64/libgomp-11.2.0-h1234567_1.conda#b372c0eea9b60732fdae4b817a63c8cd -https://repo.anaconda.com/pkgs/main/linux-64/libstdcxx-ng-11.2.0-h1234567_1.conda#57623d10a70e09e1d048c2b2b6f4e2dd -https://repo.anaconda.com/pkgs/main/linux-64/_openmp_mutex-5.1-1_gnu.conda#71d281e9c2192cb3fa425655a8defb85 -https://repo.anaconda.com/pkgs/main/linux-64/libgcc-ng-11.2.0-h1234567_1.conda#a87728dabf3151fb9cfa990bd2eb0464 -https://repo.anaconda.com/pkgs/main/linux-64/bzip2-1.0.8-h5eee18b_6.conda#f21a3ff51c1b271977f53ce956a69297 -https://repo.anaconda.com/pkgs/main/linux-64/expat-2.7.1-h6a678d5_0.conda#269942a9f3f943e2e5d8a2516a861f7c -https://repo.anaconda.com/pkgs/main/linux-64/libffi-3.4.4-h6a678d5_1.conda#70646cc713f0c43926cfdcfe9b695fe0 -https://repo.anaconda.com/pkgs/main/linux-64/libmpdec-4.0.0-h5eee18b_0.conda#feb10f42b1a7b523acbf85461be41a3e -https://repo.anaconda.com/pkgs/main/linux-64/libuuid-1.41.5-h5eee18b_0.conda#4a6a2354414c9080327274aa514e5299 -https://repo.anaconda.com/pkgs/main/linux-64/ncurses-6.4-h6a678d5_0.conda#5558eec6e2191741a92f832ea826251c -https://repo.anaconda.com/pkgs/main/linux-64/openssl-3.0.16-h5eee18b_0.conda#5875526739afa058cfa84da1fa7a2ef4 -https://repo.anaconda.com/pkgs/main/linux-64/xz-5.6.4-h5eee18b_1.conda#3581505fa450962d631bd82b8616350e -https://repo.anaconda.com/pkgs/main/linux-64/zlib-1.2.13-h5eee18b_1.conda#92e42d8310108b0a440fb2e60b2b2a25 -https://repo.anaconda.com/pkgs/main/linux-64/ccache-3.7.9-hfe4627d_0.conda#bef6fc681c273bb7bd0c67d1a591365e -https://repo.anaconda.com/pkgs/main/linux-64/readline-8.2-h5eee18b_0.conda#be42180685cce6e6b0329201d9f48efb -https://repo.anaconda.com/pkgs/main/linux-64/tk-8.6.14-h39e8969_0.conda#78dbc5e3c69143ebc037fc5d5b22e597 -https://repo.anaconda.com/pkgs/main/linux-64/sqlite-3.45.3-h5eee18b_0.conda#acf93d6aceb74d6110e20b44cc45939e -https://repo.anaconda.com/pkgs/main/linux-64/python-3.13.2-hf623796_100_cp313.conda#bf836f30ac4c16fd3d71c1aaa25da08c -https://repo.anaconda.com/pkgs/main/linux-64/setuptools-78.1.1-py313h06a4308_0.conda#8f8e1c1e3af9d2d371aaa0ee8316ae7c -https://repo.anaconda.com/pkgs/main/linux-64/wheel-0.45.1-py313h06a4308_0.conda#29057e876eedce0e37c2388c138a19f9 -https://repo.anaconda.com/pkgs/main/noarch/pip-25.1-pyhc872135_2.conda#2778327d2a700153fefe0e69438b18e1 +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_3.conda#3cd1a7238a0dd3d0860fdefc496cc854 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b +https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 +https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 +https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda#89e07d92cf50743886f41638d58c4328 +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 # pip alabaster @ https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl#sha256=fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b # pip babel @ https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl#sha256=4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2 -# pip certifi @ https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl#sha256=30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3 +# pip certifi @ https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl#sha256=6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2 # pip charset-normalizer @ https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c -# pip coverage @ https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008 +# pip coverage @ https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6 # pip cycler @ https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl#sha256=85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30 -# pip cython @ https://files.pythonhosted.org/packages/a8/30/7f48207ea13dab46604db0dd388e807d53513ba6ad1c34462892072f8f8c/Cython-3.0.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=879ae9023958d63c0675015369384642d0afb9c9d1f3473df9186c42f7a9d265 +# pip cython @ https://files.pythonhosted.org/packages/b3/9b/20a8a12d1454416141479380f7722f2ad298d2b41d0d7833fc409894715d/cython-3.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=80d0ce057672ca50728153757d022842d5dcec536b50c79615a22dda2a874ea0 # pip docutils @ https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl#sha256=dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 # pip execnet @ https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl#sha256=26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc -# pip fonttools @ https://files.pythonhosted.org/packages/f8/ad/c25116352f456c0d1287545a7aa24e98987b6d99c5b0456c4bd14321f20f/fonttools-4.57.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=4dea5893b58d4637ffa925536462ba626f8a1b9ffbe2f5c272cdf2c6ebadb817 +# pip fonttools @ https://files.pythonhosted.org/packages/75/b4/b96bb66f6f8cc4669de44a158099b249c8159231d254ab6b092909388be5/fonttools-4.59.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl#sha256=efd7e6660674e234e29937bc1481dceb7e0336bfae75b856b4fb272b5093c5d4 # pip idna @ https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl#sha256=946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # pip imagesize @ https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl#sha256=0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b # pip iniconfig @ https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl#sha256=9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760 -# pip joblib @ https://files.pythonhosted.org/packages/da/d3/13ee227a148af1c693654932b8b0b02ed64af5e1f7406d56b088b57574cd/joblib-1.5.0-py3-none-any.whl#sha256=206144b320246485b712fc8cc51f017de58225fa8b414a1fe1764a7231aca491 +# pip joblib @ https://files.pythonhosted.org/packages/7d/4f/1195bbac8e0c2acc5f740661631d8d750dc38d4a32b23ee5df3cde6f4e0d/joblib-1.5.1-py3-none-any.whl#sha256=4719a31f054c7d766948dcd83e9613686b27114f190f717cec7eaa2084f8a74a # pip kiwisolver @ https://files.pythonhosted.org/packages/8f/e9/6a7d025d8da8c4931522922cd706105aa32b3291d1add8c5427cdcd66e63/kiwisolver-1.4.8-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=a5ce1e481a74b44dd5e92ff03ea0cb371ae7a0268318e202be06c8f04f4f1246 # pip markupsafe @ https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 -# pip meson @ https://files.pythonhosted.org/packages/df/d7/f1c8acf0e597d4d07532f519780ee6e11ba285a9b092f18706b4c9118331/meson-1.8.0-py3-none-any.whl#sha256=472b7b25da286447333d32872b82d1c6f1a34024fb8ee017d7308056c25fec1f -# pip networkx @ https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl#sha256=df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f +# pip meson @ https://files.pythonhosted.org/packages/8e/6e/b9dfeac98dd508f88bcaff134ee0bf5e602caf3ccb5a12b5dd9466206df1/meson-1.8.2-py3-none-any.whl#sha256=274b49dbe26e00c9a591442dd30f4ae9da8ce11ce53d0f4682cd10a45d50f6fd +# pip networkx @ https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl#sha256=0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec # pip ninja @ https://files.pythonhosted.org/packages/eb/7a/455d2877fe6cf99886849c7f9755d897df32eaf3a0fba47b56e615f880f7/ninja-1.11.1.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl#sha256=096487995473320de7f65d622c3f1d16c3ad174797602218ca8c967f51ec38a0 -# pip numpy @ https://files.pythonhosted.org/packages/aa/fc/ebfd32c3e124e6a1043e19c0ab0769818aa69050ce5589b63d05ff185526/numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d +# pip numpy @ https://files.pythonhosted.org/packages/50/30/af1b277b443f2fb08acf1c55ce9d68ee540043f158630d62cef012750f9f/numpy-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl#sha256=5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1 # pip packaging @ https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl#sha256=29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 -# pip pillow @ https://files.pythonhosted.org/packages/13/eb/2552ecebc0b887f539111c2cd241f538b8ff5891b8903dfe672e997529be/pillow-11.2.1-cp313-cp313-manylinux_2_28_x86_64.whl#sha256=ad275964d52e2243430472fc5d2c2334b4fc3ff9c16cb0a19254e25efa03a155 -# pip pluggy @ https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl#sha256=44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 -# pip pygments @ https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl#sha256=9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c +# pip pillow @ https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl#sha256=13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8 +# pip pluggy @ https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl#sha256=e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 +# pip pygments @ https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl#sha256=86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b # pip pyparsing @ https://files.pythonhosted.org/packages/05/e7/df2285f3d08fee213f2d041540fa4fc9ca6c2d44cf36d3a035bf2a8d2bcc/pyparsing-3.2.3-py3-none-any.whl#sha256=a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf # pip pytz @ https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl#sha256=5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00 # pip roman-numerals-py @ https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl#sha256=9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c # pip six @ https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl#sha256=4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 -# pip snowballstemmer @ https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl#sha256=c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a +# pip snowballstemmer @ https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl#sha256=6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064 # pip sphinxcontrib-applehelp @ https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl#sha256=4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5 # pip sphinxcontrib-devhelp @ https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl#sha256=aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2 # pip sphinxcontrib-htmlhelp @ https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl#sha256=166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8 @@ -66,26 +72,26 @@ https://repo.anaconda.com/pkgs/main/noarch/pip-25.1-pyhc872135_2.conda#2778327d2 # pip tabulate @ https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl#sha256=024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f # pip threadpoolctl @ https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl#sha256=43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb # pip tzdata @ https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl#sha256=1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8 -# pip urllib3 @ https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl#sha256=4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813 -# pip array-api-strict @ https://files.pythonhosted.org/packages/fe/c7/a97e26083985b49a7a54006364348cf1c26e5523850b8522a39b02b19715/array_api_strict-2.3.1-py3-none-any.whl#sha256=0ca6988be1c82d2f05b6cd44bc7e14cb390555d1455deb50f431d6d0cf468ded +# pip urllib3 @ https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl#sha256=e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc +# pip array-api-strict @ https://files.pythonhosted.org/packages/e5/33/cede42b7b866db4b77432889314fc652ecc5cb6988f831ef08881a767089/array_api_strict-2.4-py3-none-any.whl#sha256=1cb20acd008f171ad8cce49589cc59897d8a242d1acf8ce6a61c3d57b61ecd14 # pip contourpy @ https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841 # pip imageio @ https://files.pythonhosted.org/packages/cb/bd/b394387b598ed84d8d0fa90611a90bee0adc2021820ad5729f7ced74a8e2/imageio-2.37.0-py3-none-any.whl#sha256=11efa15b87bc7871b61590326b2d635439acc321cf7f8ce996f812543ce10eed # pip jinja2 @ https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl#sha256=85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # pip lazy-loader @ https://files.pythonhosted.org/packages/83/60/d497a310bde3f01cb805196ac61b7ad6dc5dcf8dce66634dc34364b20b4f/lazy_loader-0.4-py3-none-any.whl#sha256=342aa8e14d543a154047afb4ba8ef17f5563baad3fc610d7b15b213b0f119efc # pip pyproject-metadata @ https://files.pythonhosted.org/packages/7e/b1/8e63033b259e0a4e40dd1ec4a9fee17718016845048b43a36ec67d62e6fe/pyproject_metadata-0.9.1-py3-none-any.whl#sha256=ee5efde548c3ed9b75a354fc319d5afd25e9585fa918a34f62f904cc731973ad -# pip pytest @ https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl#sha256=c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820 +# pip pytest @ https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl#sha256=539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7 # pip python-dateutil @ https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl#sha256=a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 -# pip requests @ https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl#sha256=70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 -# pip scipy @ https://files.pythonhosted.org/packages/03/5a/fc34bf1aa14dc7c0e701691fa8685f3faec80e57d816615e3625f28feb43/scipy-1.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=fb530e4794fc8ea76a4a21ccb67dea33e5e0e60f07fc38a49e821e1eae3b71a0 -# pip tifffile @ https://files.pythonhosted.org/packages/6e/be/10d23cfd4078fbec6aba768a357eff9e70c0b6d2a07398425985c524ad2a/tifffile-2025.3.30-py3-none-any.whl#sha256=0ed6eee7b66771db2d1bfc42262a51b01887505d35539daef118f4ff8c0f629c +# pip requests @ https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl#sha256=27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c +# pip scipy @ https://files.pythonhosted.org/packages/11/6b/3443abcd0707d52e48eb315e33cc669a95e29fc102229919646f5a501171/scipy-1.16.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl#sha256=1d8747f7736accd39289943f7fe53a8333be7f15a82eea08e4afe47d79568c32 +# pip tifffile @ https://files.pythonhosted.org/packages/3a/d8/1ba8f32bfc9cb69e37edeca93738e883f478fbe84ae401f72c0d8d507841/tifffile-2025.6.11-py3-none-any.whl#sha256=32effb78b10b3a283eb92d4ebf844ae7e93e151458b0412f38518b4e6d2d7542 # pip lightgbm @ https://files.pythonhosted.org/packages/42/86/dabda8fbcb1b00bcfb0003c3776e8ade1aa7b413dff0a2c08f457dace22f/lightgbm-4.6.0-py3-none-manylinux_2_28_x86_64.whl#sha256=cb19b5afea55b5b61cbb2131095f50538bd608a00655f23ad5d25ae3e3bf1c8d -# pip matplotlib @ https://files.pythonhosted.org/packages/51/d0/2bc4368abf766203e548dc7ab57cf7e9c621f1a3c72b516cc7715347b179/matplotlib-3.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=7e496c01441be4c7d5f96d4e40f7fca06e20dcb40e44c8daa2e740e1757ad9e6 -# pip meson-python @ https://files.pythonhosted.org/packages/7d/ec/40c0ddd29ef4daa6689a2b9c5ced47d5b58fa54ae149b19e9a97f4979c8c/meson_python-0.17.1-py3-none-any.whl#sha256=30a75c52578ef14aff8392677b09c39346e0a24d2b2c6204b8ed30583c11269c -# pip pandas @ https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24 +# pip matplotlib @ https://files.pythonhosted.org/packages/f5/64/41c4367bcaecbc03ef0d2a3ecee58a7065d0a36ae1aa817fe573a2da66d4/matplotlib-3.10.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=a80fcccbef63302c0efd78042ea3c2436104c5b1a4d3ae20f864593696364ac7 +# pip meson-python @ https://files.pythonhosted.org/packages/28/58/66db620a8a7ccb32633de9f403fe49f1b63c68ca94e5c340ec5cceeb9821/meson_python-0.18.0-py3-none-any.whl#sha256=3b0fe051551cc238f5febb873247c0949cd60ded556efa130aa57021804868e2 +# pip pandas @ https://files.pythonhosted.org/packages/e9/e2/20a317688435470872885e7fc8f95109ae9683dec7c50be29b56911515a5/pandas-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=2ba6aff74075311fc88504b1db890187a3cd0f887a5b10f5525f8e2ef55bfdb9 # pip pyamg @ https://files.pythonhosted.org/packages/cd/a7/0df731cbfb09e73979a1a032fc7bc5be0eba617d798b998a0f887afe8ade/pyamg-5.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=6999b351ab969c79faacb81faa74c0fa9682feeff3954979212872a3ee40c298 -# pip pytest-cov @ https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl#sha256=bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde -# pip pytest-xdist @ https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl#sha256=9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7 +# pip pytest-cov @ https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl#sha256=f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5 +# pip pytest-xdist @ https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl#sha256=202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88 # pip scikit-image @ https://files.pythonhosted.org/packages/cd/9b/c3da56a145f52cd61a68b8465d6a29d9503bc45bc993bb45e84371c97d94/scikit_image-0.25.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=b8abd3c805ce6944b941cfed0406d88faeb19bab3ed3d4b50187af55cf24d147 -# pip scipy-doctest @ https://files.pythonhosted.org/packages/76/eb/668949f884d5fe8a0d231dcba42c02e7b84626b35ca9072d6283c3aae773/scipy_doctest-1.7.1-py3-none-any.whl#sha256=dece106ec5ac8c595cc6372480d724e68c684450124dd0ddeb6be487ad62b365 -# pip sphinx @ https://files.pythonhosted.org/packages/2f/72/9a437a9dc5393c0eabba447bdb6233a7b02bb23e84975f17ad9a9ca86677/sphinx-8.3.0-py3-none-any.whl#sha256=bd8fcf35ab2c4240b01c74a411c948350a3aebd6aa175579363754ed380d350a +# pip scipy-doctest @ https://files.pythonhosted.org/packages/c9/13/cd25d1875f3804b73fd4a4ae00e2c76e274e1e0608d79148cac251b644b1/scipy_doctest-1.8.0-py3-none-any.whl#sha256=5863208368c35486e143ce3283ab2f517a0d6b0c63d0d5f19f38a823fc82016f +# pip sphinx @ https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl#sha256=4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3 # pip numpydoc @ https://files.pythonhosted.org/packages/6c/45/56d99ba9366476cd8548527667f01869279cedb9e66b28eb4dfb27701679/numpydoc-1.8.0-py3-none-any.whl#sha256=72024c7fd5e17375dec3608a27c03303e8ad00c81292667955c6fea7a3ccf541 diff --git a/build_tools/azure/pylatest_pip_scipy_dev_environment.yml b/build_tools/azure/pylatest_pip_scipy_dev_environment.yml index 01709b79e3720..a4bf229b5f0fa 100644 --- a/build_tools/azure/pylatest_pip_scipy_dev_environment.yml +++ b/build_tools/azure/pylatest_pip_scipy_dev_environment.yml @@ -2,7 +2,7 @@ # following script to centralize the configuration for CI builds: # build_tools/update_environments_and_lock_files.py channels: - - defaults + - conda-forge dependencies: - python - ccache @@ -18,5 +18,5 @@ dependencies: - coverage - pooch - sphinx - - numpydoc + - numpydoc<1.9.0 - python-dateutil diff --git a/build_tools/azure/pylatest_pip_scipy_dev_linux-64_conda.lock b/build_tools/azure/pylatest_pip_scipy_dev_linux-64_conda.lock index 068aee47c99a3..8667dd977f242 100644 --- a/build_tools/azure/pylatest_pip_scipy_dev_linux-64_conda.lock +++ b/build_tools/azure/pylatest_pip_scipy_dev_linux-64_conda.lock @@ -1,53 +1,59 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 45bccf0e77c6967a2f49b8c304ef02337f7bd84c59e63221f8c0cb0e75dfe269 +# input_hash: 66c01323547a35e8550a7303dac1f0cb19e0af6173e62d689006d7ca8f1cd385 @EXPLICIT -https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda#c3473ff8bdb3d124ed5ff11ec380d6f9 -https://repo.anaconda.com/pkgs/main/linux-64/ca-certificates-2025.2.25-h06a4308_0.conda#495015d24da8ad929e3ae2d18571016d -https://repo.anaconda.com/pkgs/main/linux-64/ld_impl_linux-64-2.40-h12ee557_0.conda#ee672b5f635340734f58d618b7bca024 -https://repo.anaconda.com/pkgs/main/linux-64/python_abi-3.13-0_cp313.conda#d4009c49dd2b54ffded7f1365b5f6505 -https://repo.anaconda.com/pkgs/main/noarch/tzdata-2025b-h04d1e81_0.conda#1d027393db3427ab22a02aa44a56f143 -https://repo.anaconda.com/pkgs/main/linux-64/libgomp-11.2.0-h1234567_1.conda#b372c0eea9b60732fdae4b817a63c8cd -https://repo.anaconda.com/pkgs/main/linux-64/libstdcxx-ng-11.2.0-h1234567_1.conda#57623d10a70e09e1d048c2b2b6f4e2dd -https://repo.anaconda.com/pkgs/main/linux-64/_openmp_mutex-5.1-1_gnu.conda#71d281e9c2192cb3fa425655a8defb85 -https://repo.anaconda.com/pkgs/main/linux-64/libgcc-ng-11.2.0-h1234567_1.conda#a87728dabf3151fb9cfa990bd2eb0464 -https://repo.anaconda.com/pkgs/main/linux-64/bzip2-1.0.8-h5eee18b_6.conda#f21a3ff51c1b271977f53ce956a69297 -https://repo.anaconda.com/pkgs/main/linux-64/expat-2.7.1-h6a678d5_0.conda#269942a9f3f943e2e5d8a2516a861f7c -https://repo.anaconda.com/pkgs/main/linux-64/libffi-3.4.4-h6a678d5_1.conda#70646cc713f0c43926cfdcfe9b695fe0 -https://repo.anaconda.com/pkgs/main/linux-64/libmpdec-4.0.0-h5eee18b_0.conda#feb10f42b1a7b523acbf85461be41a3e -https://repo.anaconda.com/pkgs/main/linux-64/libuuid-1.41.5-h5eee18b_0.conda#4a6a2354414c9080327274aa514e5299 -https://repo.anaconda.com/pkgs/main/linux-64/ncurses-6.4-h6a678d5_0.conda#5558eec6e2191741a92f832ea826251c -https://repo.anaconda.com/pkgs/main/linux-64/openssl-3.0.16-h5eee18b_0.conda#5875526739afa058cfa84da1fa7a2ef4 -https://repo.anaconda.com/pkgs/main/linux-64/xz-5.6.4-h5eee18b_1.conda#3581505fa450962d631bd82b8616350e -https://repo.anaconda.com/pkgs/main/linux-64/zlib-1.2.13-h5eee18b_1.conda#92e42d8310108b0a440fb2e60b2b2a25 -https://repo.anaconda.com/pkgs/main/linux-64/ccache-3.7.9-hfe4627d_0.conda#bef6fc681c273bb7bd0c67d1a591365e -https://repo.anaconda.com/pkgs/main/linux-64/readline-8.2-h5eee18b_0.conda#be42180685cce6e6b0329201d9f48efb -https://repo.anaconda.com/pkgs/main/linux-64/tk-8.6.14-h39e8969_0.conda#78dbc5e3c69143ebc037fc5d5b22e597 -https://repo.anaconda.com/pkgs/main/linux-64/sqlite-3.45.3-h5eee18b_0.conda#acf93d6aceb74d6110e20b44cc45939e -https://repo.anaconda.com/pkgs/main/linux-64/python-3.13.2-hf623796_100_cp313.conda#bf836f30ac4c16fd3d71c1aaa25da08c -https://repo.anaconda.com/pkgs/main/linux-64/setuptools-78.1.1-py313h06a4308_0.conda#8f8e1c1e3af9d2d371aaa0ee8316ae7c -https://repo.anaconda.com/pkgs/main/linux-64/wheel-0.45.1-py313h06a4308_0.conda#29057e876eedce0e37c2388c138a19f9 -https://repo.anaconda.com/pkgs/main/noarch/pip-25.1-pyhc872135_2.conda#2778327d2a700153fefe0e69438b18e1 +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda#e84b44e6300f1703cb25d29120c5b1d8 +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_3.conda#3cd1a7238a0dd3d0860fdefc496cc854 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d +https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b +https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 +https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 +https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.2-hee844dc_2.conda#be96b9fdd7b579159df77ece9bb80e48 +https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda#89e07d92cf50743886f41638d58c4328 +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 # pip alabaster @ https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl#sha256=fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b # pip babel @ https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl#sha256=4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2 -# pip certifi @ https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl#sha256=30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3 +# pip certifi @ https://files.pythonhosted.org/packages/4f/52/34c6cf5bb9285074dc3531c437b3919e825d976fde097a7a73f79e726d03/certifi-2025.7.14-py3-none-any.whl#sha256=6b31f564a415d79ee77df69d757bb49a5bb53bd9f756cbbe24394ffd6fc1f4b2 # pip charset-normalizer @ https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c -# pip coverage @ https://files.pythonhosted.org/packages/cb/74/2f8cc196643b15bc096d60e073691dadb3dca48418f08bc78dd6e899383e/coverage-7.8.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008 +# pip coverage @ https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6 # pip docutils @ https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl#sha256=dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 # pip execnet @ https://files.pythonhosted.org/packages/43/09/2aea36ff60d16dd8879bdb2f5b3ee0ba8d08cbbdcdfe870e695ce3784385/execnet-2.1.1-py3-none-any.whl#sha256=26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc # pip idna @ https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl#sha256=946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3 # pip imagesize @ https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl#sha256=0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b # pip iniconfig @ https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl#sha256=9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760 # pip markupsafe @ https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396 -# pip meson @ https://files.pythonhosted.org/packages/df/d7/f1c8acf0e597d4d07532f519780ee6e11ba285a9b092f18706b4c9118331/meson-1.8.0-py3-none-any.whl#sha256=472b7b25da286447333d32872b82d1c6f1a34024fb8ee017d7308056c25fec1f +# pip meson @ https://files.pythonhosted.org/packages/8e/6e/b9dfeac98dd508f88bcaff134ee0bf5e602caf3ccb5a12b5dd9466206df1/meson-1.8.2-py3-none-any.whl#sha256=274b49dbe26e00c9a591442dd30f4ae9da8ce11ce53d0f4682cd10a45d50f6fd # pip ninja @ https://files.pythonhosted.org/packages/eb/7a/455d2877fe6cf99886849c7f9755d897df32eaf3a0fba47b56e615f880f7/ninja-1.11.1.4-py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl#sha256=096487995473320de7f65d622c3f1d16c3ad174797602218ca8c967f51ec38a0 # pip packaging @ https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl#sha256=29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484 -# pip platformdirs @ https://files.pythonhosted.org/packages/6d/45/59578566b3275b8fd9157885918fcd0c4d74162928a5310926887b856a51/platformdirs-4.3.7-py3-none-any.whl#sha256=a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94 -# pip pluggy @ https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl#sha256=44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669 -# pip pygments @ https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl#sha256=9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c +# pip platformdirs @ https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl#sha256=ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4 +# pip pluggy @ https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl#sha256=e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 +# pip pygments @ https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl#sha256=86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b # pip roman-numerals-py @ https://files.pythonhosted.org/packages/53/97/d2cbbaa10c9b826af0e10fdf836e1bf344d9f0abb873ebc34d1f49642d3f/roman_numerals_py-3.1.0-py3-none-any.whl#sha256=9da2ad2fb670bcf24e81070ceb3be72f6c11c440d73bd579fbeca1e9f330954c # pip six @ https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl#sha256=4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274 -# pip snowballstemmer @ https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl#sha256=c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a +# pip snowballstemmer @ https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl#sha256=6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064 # pip sphinxcontrib-applehelp @ https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl#sha256=4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5 # pip sphinxcontrib-devhelp @ https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl#sha256=aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2 # pip sphinxcontrib-htmlhelp @ https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl#sha256=166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8 @@ -56,15 +62,15 @@ https://repo.anaconda.com/pkgs/main/noarch/pip-25.1-pyhc872135_2.conda#2778327d2 # pip sphinxcontrib-serializinghtml @ https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl#sha256=6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 # pip tabulate @ https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl#sha256=024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f # pip threadpoolctl @ https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl#sha256=43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb -# pip urllib3 @ https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl#sha256=4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813 +# pip urllib3 @ https://files.pythonhosted.org/packages/a7/c2/fe1e52489ae3122415c51f387e221dd0773709bad6c6cdaa599e8a2c5185/urllib3-2.5.0-py3-none-any.whl#sha256=e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc # pip jinja2 @ https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl#sha256=85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67 # pip pyproject-metadata @ https://files.pythonhosted.org/packages/7e/b1/8e63033b259e0a4e40dd1ec4a9fee17718016845048b43a36ec67d62e6fe/pyproject_metadata-0.9.1-py3-none-any.whl#sha256=ee5efde548c3ed9b75a354fc319d5afd25e9585fa918a34f62f904cc731973ad -# pip pytest @ https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl#sha256=c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820 +# pip pytest @ https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl#sha256=539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7 # pip python-dateutil @ https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl#sha256=a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427 -# pip requests @ https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl#sha256=70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6 -# pip meson-python @ https://files.pythonhosted.org/packages/7d/ec/40c0ddd29ef4daa6689a2b9c5ced47d5b58fa54ae149b19e9a97f4979c8c/meson_python-0.17.1-py3-none-any.whl#sha256=30a75c52578ef14aff8392677b09c39346e0a24d2b2c6204b8ed30583c11269c +# pip requests @ https://files.pythonhosted.org/packages/7c/e4/56027c4a6b4ae70ca9de302488c5ca95ad4a39e190093d6c1a8ace08341b/requests-2.32.4-py3-none-any.whl#sha256=27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c +# pip meson-python @ https://files.pythonhosted.org/packages/28/58/66db620a8a7ccb32633de9f403fe49f1b63c68ca94e5c340ec5cceeb9821/meson_python-0.18.0-py3-none-any.whl#sha256=3b0fe051551cc238f5febb873247c0949cd60ded556efa130aa57021804868e2 # pip pooch @ https://files.pythonhosted.org/packages/a8/87/77cc11c7a9ea9fd05503def69e3d18605852cd0d4b0d3b8f15bbeb3ef1d1/pooch-1.8.2-py3-none-any.whl#sha256=3529a57096f7198778a5ceefd5ac3ef0e4d06a6ddaf9fc2d609b806f25302c47 -# pip pytest-cov @ https://files.pythonhosted.org/packages/28/d0/def53b4a790cfb21483016430ed828f64830dd981ebe1089971cd10cab25/pytest_cov-6.1.1-py3-none-any.whl#sha256=bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde -# pip pytest-xdist @ https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl#sha256=9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7 -# pip sphinx @ https://files.pythonhosted.org/packages/2f/72/9a437a9dc5393c0eabba447bdb6233a7b02bb23e84975f17ad9a9ca86677/sphinx-8.3.0-py3-none-any.whl#sha256=bd8fcf35ab2c4240b01c74a411c948350a3aebd6aa175579363754ed380d350a +# pip pytest-cov @ https://files.pythonhosted.org/packages/bc/16/4ea354101abb1287856baa4af2732be351c7bee728065aed451b678153fd/pytest_cov-6.2.1-py3-none-any.whl#sha256=f5bc4c23f42f1cdd23c70b1dab1bbaef4fc505ba950d53e0081d0730dd7e86d5 +# pip pytest-xdist @ https://files.pythonhosted.org/packages/ca/31/d4e37e9e550c2b92a9cbc2e4d0b7420a27224968580b5a447f420847c975/pytest_xdist-3.8.0-py3-none-any.whl#sha256=202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88 +# pip sphinx @ https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl#sha256=4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3 # pip numpydoc @ https://files.pythonhosted.org/packages/6c/45/56d99ba9366476cd8548527667f01869279cedb9e66b28eb4dfb27701679/numpydoc-1.8.0-py3-none-any.whl#sha256=72024c7fd5e17375dec3608a27c03303e8ad00c81292667955c6fea7a3ccf541 diff --git a/build_tools/azure/pymin_conda_forge_mkl_win-64_conda.lock b/build_tools/azure/pymin_conda_forge_mkl_win-64_conda.lock deleted file mode 100644 index 051a5041f1138..0000000000000 --- a/build_tools/azure/pymin_conda_forge_mkl_win-64_conda.lock +++ /dev/null @@ -1,117 +0,0 @@ -# Generated by conda-lock. -# platform: win-64 -# input_hash: b3869076628274fd49d96cadc2692c963f26cbed79ec7498ecbfd50011a55e67 -@EXPLICIT -https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb -https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/win-64/intel-openmp-2024.2.1-h57928b3_1083.conda#2d89243bfb53652c182a7c73182cce4f -https://conda.anaconda.org/conda-forge/win-64/mkl-include-2024.2.2-h66d3029_15.conda#e2f516189b44b6e042199d13e7015361 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-7_cp310.conda#44e871cba2b162368476a84b8d040b6c -https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda#6797b005cd0f439c4c5c9ac565783700 -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-h4c7d964_0.conda#23c7fd5062b48d8294fc7f61bf157fba -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_9.conda#08bfa5da6e242025304b206d152479ef -https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.42.34438-hfd919c2_26.conda#91651a36d31aa20c7ba36299fb7068f4 -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/win-64/libgomp-14.2.0-h1383e82_2.conda#dd6b1ab49e28bcb6154cd131acec985b -https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_26.conda#d3f0381e38093bde620a8d85f266ae55 -https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda#37e16618af5c4851a3f3d66dd0e11141 -https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda#276e7ffe9ffe39688abc665ef0f45596 -https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda#e9a1402439c18a4e3c7a52e4246e9e1c -https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.13-h63175ca_1003.conda#3194499ee7d1a67404a87d0eefdd92c6 -https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda#8579b6bb8d18be7c0b27fb08adeeeb40 -https://conda.anaconda.org/conda-forge/win-64/lerc-4.0.0-h6470a55_1.conda#c1b81da6d29a14b542da14a36c9fbf3f -https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-h2466b09_2.conda#f7dc9a8f21d74eab46456df301da2972 -https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.23-h76ddb4d_0.conda#34f03138e46543944d4d7f8538048842 -https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.0-he0c23c2_0.conda#b6f5352fdb525662f4169a0431d2dd7a -https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda#85d8fa5e55ed8f93f874b3b23ed54ec6 -https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-h135ad9c_1.conda#21fc5dba2cbcd8e5e26ff976a312122c -https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.0-h2466b09_0.conda#7c51d27540389de84852daa1cdb9c63c -https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_0.conda#8d5cb0016b645d6688e2ff57c5d51302 -https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.49.1-h67fdade_2.conda#b58b66d4ad1aaf1c2543cbbd6afb1a59 -https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.5.0-h3b0e114_0.conda#33f7313967072c6e6d8f865f5493c7ae -https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda#41fbfac52c601159df6c01f875de31b9 -https://conda.anaconda.org/conda-forge/win-64/ninja-1.12.1-hc790b64_1.conda#3974c522f3248d4a93e6940c463d2de7 -https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.0-ha4e3fda_1.conda#72c07e46b6766bb057018a9a74861b89 -https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.0-had0cd8c_0.conda#01617534ef71b5385ebba940a6d6150d -https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11 -https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h5226925_1.conda#fc048363eb8f03cd1737600a5d08aafe -https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda#31aec030344e962fbd7dbbbbd68e60a9 -https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-h2466b09_2.conda#9bae75ce723fa34e98e239d21d752a7e -https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-h2466b09_2.conda#85741a24d97954a991e55e34bc55990b -https://conda.anaconda.org/conda-forge/win-64/libgcc-14.2.0-h1383e82_2.conda#4a74c1461a0ba47a3346c04bdccbe2ad -https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda#2cf0cf76cc15d360dfa2f17fd6cf9772 -https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.47-h7a4582a_0.conda#ad620e92b82d2948bc019e029c574ebb -https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.7-h442d1da_1.conda#c14ff7f05e57489df9244917d2b55763 -https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h99c9b8b_2.conda#a912b2c4ff0f03101c751aa79a331831 -https://conda.anaconda.org/conda-forge/win-64/python-3.10.17-h8c5b53a_0_cpython.conda#0c59918f056ab2e9c7bb45970d32b2ea -https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda#21f56217d6125fb30c3c3f10c786d751 -https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-h2466b09_2.conda#d22534a9be5771fc58eb7564947f669d -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/win-64/cython-3.0.12-py310h6bd2d47_0.conda#8b4e32766e91dfad20bdfd9747e66d54 -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e -https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.7-py310hc19bc0b_0.conda#50d96539497fc7493cbe469fbb6b8b6e -https://conda.anaconda.org/conda-forge/win-64/libclang13-20.1.4-default_h6e92b77_0.conda#80c3ee2ffb5f35f2b6c4b10d636b04fb -https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.13.3-h0b5ce68_1.conda#a84b7d1a13060a9372bea961a8131dbc -https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.1-h7025463_0.conda#6cbaea9075a4f007eb7d0a90bb9a2a09 -https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.11.2-default_ha69328c_1001.conda#b87a0ac5ab6495d8225db5dc72dd21cd -https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.0-h797046b_4.conda#7d938ca70c64c5516767b4eae0a56172 -https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.39-h3df6e99_0.conda#279ee338c9b34871d578cb3c7aa68f70 -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 -https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda#3c8f2573569bb816483e5cf57efbbe29 -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 -https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 -https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/win-64/tornado-6.4.2-py310ha8f682b_0.conda#e6819d3a0cae0f1b1838875f858421d1 -https://conda.anaconda.org/conda-forge/win-64/unicodedata2-16.0.0-py310ha8f682b_0.conda#b28aead44c6e19a1fbba7752aa242b34 -https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 -https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-h0e40799_0.conda#2ffbfae4548098297c033228256eb96e -https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-h0e40799_0.conda#8393c0f7e7870b4eb45553326f81f0ff -https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-h2466b09_2.conda#378f1c9421775dfe644731cb121c8979 -https://conda.anaconda.org/conda-forge/win-64/coverage-7.8.0-py310h38315fa_0.conda#30a825dae940c63c55bca8df4f806f3e -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/win-64/lcms2-2.17-hbcf6048_0.conda#3538827f77b82a837fa681a4579e37a1 -https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.13.3-h57928b3_1.conda#410ba2c8e7bdb278dfbb5d40220e39d2 -https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda#a69bbf778a462da324489976c84cfc8c -https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h4d64b90_0.conda#fc050366dd0b8313eb797ed1ffef3a29 -https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/win-64/tbb-2021.13.0-h62715c5_1.conda#9190dd0a23d925f7602f9628b3aed511 -https://conda.anaconda.org/conda-forge/win-64/fonttools-4.57.0-py310h38315fa_0.conda#1f25f742c39582715cc058f5fe451975 -https://conda.anaconda.org/conda-forge/win-64/freetype-2.13.3-h57928b3_1.conda#633504fe3f96031192e40e3e6c18ef06 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h66d3029_15.conda#302dff2807f2927b3e9e0d19d60121de -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.1.1-pyhd8ed1ab_0.conda#1e35d8f975bc0e984a19819aa91c440a -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd -https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.15.0-h765892d_1.conda#9bb0026a2131b09404c59c4290c697cd -https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-31_h641d27c_mkl.conda#d05563c577fe2f37693a554b3f271e8f -https://conda.anaconda.org/conda-forge/win-64/mkl-devel-2024.2.2-h57928b3_15.conda#a85f53093da069c7c657f090e388f3ef -https://conda.anaconda.org/conda-forge/win-64/pillow-11.1.0-py310h9595edc_0.conda#67a38507ac20bd85226fe6dd7ed87462 -https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda#20e32ced54300292aff690a69c5e7b97 -https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-31_h5e41251_mkl.conda#43c100b94ad2607382b0cf0f3a6b0bf3 -https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-31_h1aa476e_mkl.conda#40b47ee720a185289760960fc6185750 -https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.1.0-h8796e6f_0.conda#dcc4a63f231cc52197c558f5e07e0a69 -https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-31_h845c4fa_mkl.conda#003a2041cb07a7cf698f48dd26301273 -https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.5-py310h4987827_0.conda#19e9c5868faa8046020ce870a9a9d0fc -https://conda.anaconda.org/conda-forge/win-64/blas-devel-3.9.0-31_hfb1a452_mkl.conda#0deeb3d9d6f0e56393c55ef382899010 -https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.2-py310hc19bc0b_0.conda#039416813b5290e7d100a05bb4326110 -https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.0-h83cda92_1.conda#412f970fc305449b6ee626fe9c6638a8 -https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py310h15c175c_0.conda#81798168111d1021e3d815217c444418 -https://conda.anaconda.org/conda-forge/win-64/blas-2.131-mkl.conda#1842bfaa4e349875c47bde1d9871bda6 -https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.1-py310h37e0a56_0.conda#1b78c5c0741473537e39e425ff30ea80 -https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.0-py310hc1b6536_0.conda#e90c8d8a817b5d63b7785d7d18c99ae0 -https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.1-py310h5588dad_0.conda#246bfc9ca36dccad2d78a020ab8d2aab diff --git a/build_tools/azure/pymin_conda_forge_mkl_environment.yml b/build_tools/azure/pymin_conda_forge_openblas_environment.yml similarity index 94% rename from build_tools/azure/pymin_conda_forge_mkl_environment.yml rename to build_tools/azure/pymin_conda_forge_openblas_environment.yml index fe6ce91950e4a..7fce5776e930a 100644 --- a/build_tools/azure/pymin_conda_forge_mkl_environment.yml +++ b/build_tools/azure/pymin_conda_forge_openblas_environment.yml @@ -6,7 +6,7 @@ channels: dependencies: - python=3.10 - numpy - - blas[build=mkl] + - blas[build=openblas] - scipy - cython - joblib diff --git a/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_environment.yml b/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_environment.yml index a179c55fed993..1e7c36708ee30 100644 --- a/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_environment.yml +++ b/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_environment.yml @@ -19,8 +19,9 @@ dependencies: - pillow - pip - ninja - - meson-python=0.16.0 # min + - meson-python=0.17.1 # min - pytest-cov - coverage - ccache - polars=0.20.30 # min + - pyarrow=12.0.0 # min diff --git a/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_linux-64_conda.lock b/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_linux-64_conda.lock index 5d0b23f9e2f41..ba452f84f7b02 100644 --- a/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_linux-64_conda.lock +++ b/build_tools/azure/pymin_conda_forge_openblas_min_dependencies_linux-64_conda.lock @@ -1,190 +1,231 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: fbba4fe2a9e1ebfa6e5d79269f12618306ade6ba86f95bb43c9719cd8dbe0e38 +# input_hash: 0f062944edccd8efd48c86d9c76c5f9ea5bde5a64b16e6076bca3d84b06da831 @EXPLICIT https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-7_cp310.conda#44e871cba2b162368476a84b8d040b6c +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-20.1.4-h024ca30_0.conda#4fc395cda27912a7d904b86b5dbf3a4d +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-20.1.8-h4922eb0_0.conda#dda42855e1d9a0b59e071e28a820d0f5 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-3_kmp_llvm.conda#ee5c2118262e30b972bc0b4db8ef0ba5 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda#c151d5eb730e9b7480e6d48c0fc44048 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 +https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda#7df50d44d4a14d6c31a2c54f2cd92157 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda#76df83c2a9035c54df5d04ff81bcc02d -https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.24.1-h5888daf_0.conda#d54305672f0361c2f3886750e7165b5f -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda#41b599ed2b02abcfdd84302bff174b23 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda#27fe770decaf469a53f3e3a6d593067f -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda#f7f0d6cc2dc986d42ac2689ec88192be +https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h5888daf_0.conda#4836fff66ad6089f356e29063f52b790 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.24.1-h5888daf_0.conda#2ee6d71b72f75d50581f2f68e965efdb -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h5888daf_0.conda#8d2f4f3884f01aad1e197c3db4ef305f +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda#e796ff8ddc598affdf7c173d6145f087 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 +https://conda.anaconda.org/conda-forge/linux-64/libnuma-2.0.18-hb9d3cd8_3.conda#20ab6b90150325f1af7ca96bffafde63 https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda#68e52064ed3897463c0e958ab5c8f91b https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda#b64523fb87ac6f87f0790f324ad43046 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda#63f790534398730f59e1b899c3644d4a +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda#70e3400cbbfa03e96dcde7fc13e38c7b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-hf23e847_1.conda#b1aa0faa95017bca11369bd080487ec4 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda#f6ebe2cb3f82ba6c057dde5d9debe4f7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda#8035c64cb77ed555e3f150b7b3972480 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxshmfence-1.3.3-hb9d3cd8_0.conda#9a809ce9f65460195777f2f2116bae02 https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.8.23-hd590300_0.conda#cc4f06f7eedb1523f3b83fd0fb3942ff https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda#d6845ae4dea52a2f90178bf1829a21f8 +https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-h5888daf_0.conda#951ff8d9e5536896408e89d63230b8d5 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 -https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.24.1-h8e693c7_0.conda#57566a81dd1e5aa3d98ac7582e8bfe03 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda#9566f0bd264fbd463002e759b8a82401 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda#06f70867945ea6a84d35836af780f1de +https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h8e693c7_0.conda#96ae2046abdf1bb9c65e3338725c06ac +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_9.conda#61641e239f96eae2b8492dc7e755828c +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.conda#4c0ab57463117fbb8df85268415082f5 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b +https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d -https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.24.1-h5888daf_0.conda#8f04c7aae6a46503bc36d1ed5abc8c7c -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 +https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h5888daf_0.conda#f467fbfc552a50dbae2def93692bcc67 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.55-h3f2d84a_0.conda#2bd47db5807daade8500ed7ca4c512a4 -https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 -https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hd590300_0.conda#48f4330bfcd959c3cfb704d424903c82 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.47-h943b412_0.conda#55199e2ae2c3651f6f9b2a447b47bdc9 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/liblzma-devel-5.8.1-hb9d3cd8_2.conda#f61edadbb301530bd65a32646bd81552 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h943b412_0.conda#51de14db340a848869e69c632b43cca7 +https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc -https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393 https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda#c7f302fd11eeb0987a6a5e1f3aed6a21 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda#94116b69829e90b72d566e64421e1bff -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 -https://conda.anaconda.org/conda-forge/linux-64/nspr-4.36-h5888daf_0.conda#de9cd5bca9e4918527b9b72b6e2e1409 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.0-h29eaf8c_0.conda#d2f1c87d4416d1e7344cf92b1aaee1c4 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.37-h29cc59b_0.conda#d73ccc379297a67ed921bd55b38a6c6a +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.2-h29eaf8c_0.conda#39b4228a867772d610c02e06f939a5b8 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.3.46-h06160fa_0.conda#413d96a0b655c8f8aacc36473a2dbb04 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 +https://conda.anaconda.org/conda-forge/linux-64/xz-gpl-tools-5.8.1-hbcc6ac9_2.conda#bf627c16aa26231720af037a2709ab09 +https://conda.anaconda.org/conda-forge/linux-64/xz-tools-5.8.1-hb9d3cd8_2.conda#1bad2995c8f1c8075c6c331bf96e46fb +https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda#c63b5e52939e795ba8d26e35d767a843 -https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda#f87c7b7c2cb45f323ffbce941c78ab7c +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.6.0-h93469e0_0.conda#580a52a05f5be28ce00764149017c6d4 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.17-h862ab75_1.conda#0013fcee7acb3cfc801c5929824feb3c +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.11-h862ab75_1.conda#6fbc9bd49434eb36d3a59c5020f4af95 +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.16-h862ab75_1.conda#f883d61afbc95c50f7b3f62546da4235 +https://conda.anaconda.org/conda-forge/linux-64/glog-0.6.0-h6f12383_0.tar.bz2#b31f3565cb84435407594e548a2fb7b2 https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 -https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.24.1-h8e693c7_0.conda#8f66ed2e34507b7ae44afa31c3e4ec79 -https://conda.anaconda.org/conda-forge/linux-64/libcap-2.75-h39aace5_0.conda#c44c16d6976d2aebbd65894d7741e67e -https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.124-hb9d3cd8_0.conda#8bc89311041d7fcb510238cf0848ccae +https://conda.anaconda.org/conda-forge/linux-64/libabseil-20230125.3-cxx17_h59595ed_0.conda#d1db1b8be7c3a8983dcbbbfe4f0765de +https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h8e693c7_0.conda#6c07a6cd50acc5fceb5bd33e8e30dac8 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_9.conda#081aa22f4581c08e4372b0b6c2f8478e +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_9.conda#1f0a03af852a9659ed2bf08f2f1704fd +https://conda.anaconda.org/conda-forge/linux-64/libcap-2.71-h39aace5_0.conda#dd19e4e3043f6948bd7454b946ee0983 +https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda#3c255be50a506c50765a93a6644f32fe -https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-lib-1.11.0-hb9d3cd8_2.conda#e55712ff40a054134d51b89afca57dbc -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_2.conda#4056c857af1a99ee50589a941059ec55 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_4.conda#6c1028898cf3a2032d9af46689e1b81a +https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-lib-1.11.1-hb9d3cd8_0.conda#8504a291085c9fb809b66cabd5834307 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda#19e57602824042dfd0446292ef90488b +https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-3.21.12-hfc55251_2.conda#e3a7d4ba09b8dc939b98fef55f539220 +https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.18.1-h8fd135c_2.conda#bbf65f7688512872f063810623b755dc +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hf01ce69_5.conda#e79a094918988bb1807462cd42c83962 https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda#9802ae6d20982f42c0f5d69008988763 -https://conda.anaconda.org/conda-forge/linux-64/nss-3.110-h159eef7_0.conda#945659af183e87429c8aa7e0be3cc91d +https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.45-hc749103_0.conda#b90bece58b4c2bf25969b70f3be42d25 -https://conda.anaconda.org/conda-forge/linux-64/python-3.10.17-hd6af730_0_cpython.conda#7bb89638dae9ce1b8e051d0b721e83c2 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-hb711507_2.conda#8637c3e5821654d0edf97e2b0404b443 +https://conda.anaconda.org/conda-forge/linux-64/rdma-core-28.9-h59595ed_1.conda#aeffb7c06b5f65e55e6c637408dc4100 +https://conda.anaconda.org/conda-forge/linux-64/re2-2023.03.02-h8c504da_0.conda#206f8fa808748f6e90599c3368a1114e +https://conda.anaconda.org/conda-forge/linux-64/snappy-1.1.10-hdb0a2a9_1.conda#78b8b85bdf1f42b8a2b3cb577d8742d1 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda#c207fa5ac7ea99b149344385a9c0880d -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda#dce22f70b4e5a407ce88f2be046f4ceb -https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.10-py310hc6cd4ac_0.conda#bd1d71ee240be36f1d85c86177d6964f -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e -https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 -https://conda.anaconda.org/conda-forge/linux-64/gettext-0.24.1-h5888daf_0.conda#c63e7590d4d6f4c85721040ed8b12888 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py310h3788b33_0.conda#4186d9b4d004b0fe0de6aa62496fb48a +https://conda.anaconda.org/conda-forge/linux-64/xz-5.8.1-hbcc6ac9_2.conda#68eae977d7d1196d32b636a026dc015d +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.13.27-h3870b5a_0.conda#b868db6b48436bdbda71aa8576f4a44d +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_9.conda#d47dee1856d9cb955b8076eeff304a5b +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h5888daf_0.conda#df1ca81a8be317854cb06c22582b731c https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.14.1-h332b0f4_0.conda#45f6713cb00f124af300342512219182 https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.1-h3618099_1.conda#714c97d4ff495ab69d1fdfcadbcae985 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.2-h3618099_0.conda#072ab14a02164b7c0c089055368ff776 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.54.3-hb20ce57_0.conda#7af7c59ab24db007dfd82e0a3a343f66 https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.25-pthreads_h413a1c8_0.conda#d172b34a443b95f86089e8229ddc9a17 -https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.4-h4e0b6ca_1.conda#04bcf3055e51f8dde6fab9672fb9fca0 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h4bc477f_1.conda#ad1f1f8238834cd3c88ceeaee8da444a -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.9-h2774228_0.conda#7b283ff97a87409a884bc11283855c17 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda#14dbe05b929e329dbaa6f2d0aa19466d https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 -https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 -https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_3.conda#fd5062942bfa1b0bd5e0d2a4397b099e -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 -https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 -https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.1.0-pyh8a188c0_0.tar.bz2#a2995ee828f65687ac5b1e71a2ab1e0c -https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 -https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py310ha75aee5_0.conda#166d59aab40b9c607b4cc21c03924e9d -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda#83fc6ae00127671e301c9f44254c31b8 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py310ha75aee5_0.conda#1d7a4b9202cdd10d56ecdd7f6c347190 -https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 +https://conda.anaconda.org/conda-forge/linux-64/orc-1.8.4-h2f23424_0.conda#4bb92585a250e67d49b46c073d29f9dd +https://conda.anaconda.org/conda-forge/linux-64/ucx-1.14.1-h64cca9d_5.conda#39aa3b356d10d7e5add0c540945a0944 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.44-hb9d3cd8_0.conda#7c91bfc90672888259675ad2ad28af9c +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.45-hb9d3cd8_0.conda#397a013c2dc5145a70737871aaa87e98 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.3.1-h1e03375_0.conda#3082be841420d6288bc1268a9be45b75 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.7.10-h9ab9c9b_2.conda#cf49873da2e59f876a2ad4794b05801b +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_9.conda#4601544b4982ba1861fa9b9c607b2c06 https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.8.0-py310h89163eb_0.conda#9f7865c17117d16f804b687b498e35fa -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.57.0-py310h89163eb_0.conda#34378af82141b3c1725dcdf898b28fc6 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.84.1-h4833e2c_1.conda#418de18c9b79a3d8583d90d27e0937c2 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2#7583652522d71ad78ba536bba06940eb +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.84.2-h4833e2c_0.conda#f2ec1facec64147850b7674633978050 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-20_linux64_openblas.conda#2b7bb4f7562c8cf334fc2e20c2d28abc https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda#96c33bbd084ef2b2463503fb7f1482ae -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.9.1-h65c71a3_0.conda#6e45090fe0eec179ecc8041a3a3fc9f8 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.12.0-hac9eb74_1.conda#0dee716254497604762957076ac76540 +https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.8-hecd9e04_0.conda#59a7b967b6ef5d63029b1712f8dcf661 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.10.0-h65c71a3_0.conda#fedf6bfe5d21d21d2b1785ec00a8889a +https://conda.anaconda.org/conda-forge/linux-64/nss-3.114-hc3c8bcf_0.conda#7d5713b9f8346d094ac046277db1c12b https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.25-pthreads_h7a3da1a_0.conda#87661673941b5e702275fdf0fc095ad0 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda#ca2de8bbdc871bce41dbf59e51324165 -https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/linux-64/sip-6.8.6-py310hf71b8c6_2.conda#a50d1007fecaff3f98b19034a8e0b2e7 +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/python-3.10.18-hd6af730_0_cpython.conda#4ea0c77cdcb0b81813a0436b162d7316 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.0-h435f46f_0.conda#c7726f96aab024855ede05e0ca6e94a0 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.8.13-hd4f18eb_5.conda#860fb8c0efec64a4a678eb2ea066ff65 +https://conda.anaconda.org/conda-forge/noarch/certifi-2025.7.14-pyhd8ed1ab_0.conda#4c07624f3faefd0bb6659fb7396cfa76 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.10-py310hc6cd4ac_0.conda#bd1d71ee240be36f1d85c86177d6964f +https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee -https://conda.anaconda.org/conda-forge/linux-64/glib-2.84.1-h6287aef_1.conda#35012688d30e1b52bff2ba5d1f342a50 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py310h3788b33_1.conda#b70dd76da5231e6073fd44c42a1d78c5 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-20_linux64_openblas.conda#36d486d72ab64ffea932329a1d3729a3 -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda#96f8d5b2e94c9ba4fef19f1adf068a15 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda#2d933632c8004be47deb2be61bf013be +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.8-default_hddf928d_0.conda#b939740734ad5a8e8f6c942374dee68d +https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.8-default_ha444ac7_0.conda#783f9cdcb0255ed00e3f1be22e16de40 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-20_linux64_openblas.conda#6fabc51f5e647d09cc010c40061557e0 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda#37fba334855ef3b51549308e61ed7a3d +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.5-h27ae623_0.conda#6458be24f09e1b034902ab44fe9de908 https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda#ef1910918dd895516a769ed36b5b3a4e -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py310h7e6dc6c_0.conda#14d300b9e1504748e70cc6499a7b4d25 -https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.13.0-py310hf71b8c6_1.conda#0c8cbfbe70f4c8a47b040a14615e6f1f -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.1.1-pyhd8ed1ab_0.conda#1e35d8f975bc0e984a19819aa91c440a -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h7e6dc6c_0.conda#e609995f031bc848be8ea159865e8afc +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_3.conda#fd5062942bfa1b0bd5e0d2a4397b099e +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 +https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 +https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.1.0-pyh8a188c0_0.tar.bz2#a2995ee828f65687ac5b1e71a2ab1e0c +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 +https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py310ha75aee5_0.conda#6f3da1072c0c4d2a1beb1e84615f7c9c +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py310ha75aee5_0.conda#1d7a4b9202cdd10d56ecdd7f6c347190 +https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.3.12-he2a37c1_2.conda#44876aca9aa47da1e5e2d3f9906169ba https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 -https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.24.11-hc37bda9_0.conda#056d86cacf2b48c79c6a562a2486eb8c +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.9.2-py310h89163eb_0.conda#f02d32dc5b0547e137f871a33e032842 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py310h3406613_0.conda#dc2e5602e20bbffb18314a70094b3c4a +https://conda.anaconda.org/conda-forge/linux-64/glib-2.84.2-h6287aef_0.conda#704648df3a01d4d24bc2c0466b718d63 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2#7583652522d71ad78ba536bba06940eb https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-20_linux64_openblas.conda#05c5862c7dc25e65ba6c471d96429dae https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.0-py310h454958d_1.tar.bz2#607c66f0cce2986515a8fe9e136b2b57 -https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hac146a9_1.conda#66b1fa9608d8836e25f9919159adc9c6 +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c +https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hb77b528_0.conda#07f45f1be1c25345faddb8db0de8039b +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 +https://conda.anaconda.org/conda-forge/linux-64/sip-6.10.0-py310hf71b8c6_0.conda#2d7e4445be227e8210140b75725689ad +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.20.2-h2a5cb19_18.conda#7313674073496cec938f73b71163bc31 https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-20_linux64_openblas.conda#9932a1d4e9ecf2d35fb19475446e361e -https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.24.11-h651a532_0.conda#d8d8894f8ced2c9be76dc9ad1ae531ce -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.1.0-h3beb420_0.conda#95e3bb97f9cdc251c0c68640e9c10ed3 +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.24.11-hc37bda9_0.conda#056d86cacf2b48c79c6a562a2486eb8c +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda#0e6e192d4b3d95708ad192d957cf3163 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.0-py310h23f4a51_0.tar.bz2#9911225650b298776c8e8c083b5cacf1 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.0-py310hb5077e9_0.tar.bz2#43e920bc9856daa7d8d18fcbfb244c4e https://conda.anaconda.org/conda-forge/linux-64/polars-0.20.30-py310h031f9ce_0.conda#0743f5db9f978b6df92d412935ff8371 +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.17.0-py310hf71b8c6_1.conda#696c7414297907d7647a5176031c8c69 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.8.0-py310hea5193d_1.tar.bz2#664d80ddeb51241629b3ada5ea926e4d +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.10.57-h7b9373a_16.conda#54db1af780a69493a2e0675113a027f9 https://conda.anaconda.org/conda-forge/linux-64/blas-2.120-openblas.conda#c8f6916a81a340650078171b1d852574 +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.24.11-h651a532_0.conda#d8d8894f8ced2c9be76dc9ad1ae531ce https://conda.anaconda.org/conda-forge/linux-64/pyamg-4.2.1-py310h7c3ba0c_0.tar.bz2#89f5a48e1f23b5cf3163a6094903d181 -https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.15-h993ce98_3.conda#aa49f5308f39277477d47cd6687eb8f3 -https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.10-py310hb3b5edb_1.conda#c370972fc4557cb54d265c9c1f71bd20 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda#ce978e1b9ed8b8d49164e90a5cdc94cd +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-12.0.0-hc410076_9_cpu.conda#3dcb50139596ef80908e2dd9a931d84c +https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.15-hea1682b_4.conda#c054d7f22cc719e12c72d454b2328d6c +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-12.0.0-py310h0576679_9_cpu.conda#b2d6ee1cff5acc5509633f8eac7108f7 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.11-py310hf392a12_1.conda#e07b23661b711fb46d25b14206e0db47 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.0-py310hff52083_0.tar.bz2#1b2f3b135d5d9c594b5e0e6150c03b7b diff --git a/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_environment.yml b/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_environment.yml index 267c149fd1c35..30466d12a3f20 100644 --- a/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_environment.yml +++ b/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_environment.yml @@ -20,5 +20,5 @@ dependencies: - ninja - meson-python - sphinx - - numpydoc + - numpydoc<1.9.0 - ccache diff --git a/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_linux-64_conda.lock b/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_linux-64_conda.lock index 009d15a7d3713..1f477d63167ab 100644 --- a/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_linux-64_conda.lock +++ b/build_tools/azure/pymin_conda_forge_openblas_ubuntu_2204_linux-64_conda.lock @@ -1,112 +1,113 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: ec41f4a9538671e542d266b999ea055a685df8323c3c879f7d01fb2c259197cb +# input_hash: 4abfb998e26e3beaa198409ac1ebc1278024921c4b3c6fc8de5c93be1b6193ba @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-7_cp310.conda#44e871cba2b162368476a84b8d040b6c +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda#06d02030237f4d5b3d9a7e7d348fe3c6 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_3.conda#3cd1a7238a0dd3d0860fdefc496cc854 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda#27fe770decaf469a53f3e3a6d593067f -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda#63f790534398730f59e1b899c3644d4a +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda#f6ebe2cb3f82ba6c057dde5d9debe4f7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda#8035c64cb77ed555e3f150b7b3972480 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 -https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.47-h943b412_0.conda#55199e2ae2c3651f6f9b2a447b47bdc9 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h943b412_0.conda#51de14db340a848869e69c632b43cca7 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 +https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda#3c255be50a506c50765a93a6644f32fe -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_2.conda#4056c857af1a99ee50589a941059ec55 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda#0a4d0252248ef9a0f88f2ba8b8a08e12 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_4.conda#6c1028898cf3a2032d9af46689e1b81a -https://conda.anaconda.org/conda-forge/linux-64/python-3.10.17-hd6af730_0_cpython.conda#7bb89638dae9ce1b8e051d0b721e83c2 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_0.conda#323dc8f259224d13078aaf7ce96c3efe +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hf01ce69_5.conda#e79a094918988bb1807462cd42c83962 +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-32_h59b9bed_openblas.conda#2af9f3d5c2e39f417ce040f5a35c40c6 +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 +https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_0.conda#15fa8c1f683e68ff08ef0ea106012add +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 +https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-32_he106b2a_openblas.conda#3d3f9355e52f269cd8bc2c440d8a5263 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-32_h7ac8fdf_openblas.conda#6c3f04ccb6c578138e9f9899da0bd714 +https://conda.anaconda.org/conda-forge/linux-64/python-3.10.18-hd6af730_0_cpython.conda#4ea0c77cdcb0b81813a0436b162d7316 https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda#1fd9696649f65fd6611fcdb4ffec738a -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_2.conda#bf502c169c71e3c6ac0d6175addfacc2 -https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda#c207fa5ac7ea99b149344385a9c0880d +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_3.conda#63d24a5dd21c738d706f91569dbd1892 +https://conda.anaconda.org/conda-forge/noarch/certifi-2025.7.14-pyhd8ed1ab_0.conda#4c07624f3faefd0bb6659fb7396cfa76 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.2-pyhd8ed1ab_0.conda#40fe4284b8b5835a9073a645139f35af https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.12-py310had8cdd9_0.conda#b630fe36f0b621d23e74872dc4fd2bd7 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.2-py310had8cdd9_2.conda#be416b1d5ffef48c394cbbb04bc864ae https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda#24c1ca34138ee57de72a943237cde4cc -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h59b9bed_openblas.conda#728dbebd0f7a20337218beacffd37916 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 -https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-32_he2f377e_openblas.conda#54e7f7896d0dbf56665bcb0078bfa9d2 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py310h89163eb_1.conda#8ce3f0332fd6de0d737e2911d329523f -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 -https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.29-pthreads_h6ec200e_0.conda#7e4d48870b3258bea920d51b7f495a81 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.6-py310hefbff90_0.conda#b0cea2c364bf65cd19e023040eeab05d https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h7e6dc6c_0.conda#e609995f031bc848be8ea159865e8afc +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef -https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda#232fb4577b6687b2d503ef8e254270c9 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e +https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda#755cf22df8693aa0d1aec1c123fa5863 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda#fa839b5ff59e192f411ccc7dae6588bb https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhd8ed1ab_2.conda#959484a66b4b76befcddc4fa97c95567 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4 -https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-32_h1ea3ea9_openblas.conda#34cb4b6753b38a62ae25f3a73efd16b0 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py310h8deb56e_0.conda#1fc24a3196ad5ede2a68148be61894f4 -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda#b4754fb1bdcb70c8fd54f918301582c6 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda#abb32c727da370c481a1c206f5159ce9 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda#452b98eafe050ecff932f0ec832dd03f +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-31_he2f377e_openblas.conda#7e5fff7d0db69be3a266f7e79a3bb0e2 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py310hefbff90_0.conda#5526bc875ec897f0d335e38da832b6ee -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py310h7e6dc6c_0.conda#14d300b9e1504748e70cc6499a7b4d25 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310ha75aee5_2.conda#f9254b5b0193982416b91edcb4b2676f -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-31_h1ea3ea9_openblas.conda#ba652ee0576396d4765e567f043c57f9 -https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py310h5eaa309_3.conda#07697a584fab513ce895c4511f7a2403 +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda#8c29cd33b64b2eb78597fa28b5595c8d -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.4.0-pyhd8ed1ab_0.conda#c1e349028e0052c4eea844e94f773065 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.131-openblas.conda#38b2ec894c69bb4be0e66d2ef7fc60bf +https://conda.anaconda.org/conda-forge/linux-64/blas-2.132-openblas.conda#9c4a27ab2463f9b1d9019e0a798a5b81 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py310h0158d43_0.conda#94eb2db0b8f769a1e554843e3586504d https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py310ha2bacc8_1.conda#817d32861729e14f474249f1036291c4 -https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda#a9b9368f3701a417eac9edbcae7cb737 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310ha75aee5_2.conda#f9254b5b0193982416b91edcb4b2676f +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.4-pyhd8ed1ab_0.conda#f6082eae112814f1447b56a5e1f6ed05 https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.8.0-pyhd8ed1ab_1.conda#5af206d64d18d6c8dfb3122b4d9e643b https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda#16e3f039c0aa6446513e94ab18a8784b https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda#910f28a05c178feba832f842155cbfff diff --git a/build_tools/azure/pymin_conda_forge_openblas_win-64_conda.lock b/build_tools/azure/pymin_conda_forge_openblas_win-64_conda.lock new file mode 100644 index 0000000000000..80d8fe2ffbdda --- /dev/null +++ b/build_tools/azure/pymin_conda_forge_openblas_win-64_conda.lock @@ -0,0 +1,115 @@ +# Generated by conda-lock. +# platform: win-64 +# input_hash: 4ff41dadb8a7a77d0b784bfc6b32126b8e1a41c8b9a87375b48c18c9aee4ea2a +@EXPLICIT +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c +https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a +https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda#6797b005cd0f439c4c5c9ac565783700 +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-h4c7d964_0.conda#40334594f5916bc4c0a0313d64bfe046 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 +https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_9.conda#08bfa5da6e242025304b206d152479ef +https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_28.conda#c5dbb7fee79868438261a74498fb6082 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab +https://conda.anaconda.org/conda-forge/win-64/libgomp-15.1.0-h1383e82_3.conda#94545e52b3d21a7ab89961f7bda3da0d +https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h41ae7f8_28.conda#db018bf64624649a6cac827533c7971e +https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda#37e16618af5c4851a3f3d66dd0e11141 +https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h2466b09_7.conda#276e7ffe9ffe39688abc665ef0f45596 +https://conda.anaconda.org/conda-forge/win-64/double-conversion-3.3.1-he0c23c2_0.conda#e9a1402439c18a4e3c7a52e4246e9e1c +https://conda.anaconda.org/conda-forge/win-64/graphite2-1.3.14-he0c23c2_0.conda#692bc31c646f7e221af07ccc924e1ae4 +https://conda.anaconda.org/conda-forge/win-64/icu-75.1-he0c23c2_0.conda#8579b6bb8d18be7c0b27fb08adeeeb40 +https://conda.anaconda.org/conda-forge/win-64/lerc-4.0.0-h6470a55_1.conda#c1b81da6d29a14b542da14a36c9fbf3f +https://conda.anaconda.org/conda-forge/win-64/libbrotlicommon-1.1.0-h2466b09_3.conda#cf20c8b8b48ab5252ec64b9c66bfe0a4 +https://conda.anaconda.org/conda-forge/win-64/libdeflate-1.24-h76ddb4d_0.conda#08d988e266c6ae77e03d164b83786dc4 +https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda#3608ffde260281fa641e70d6e34b1b96 +https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.6-h537db12_1.conda#85d8fa5e55ed8f93f874b3b23ed54ec6 +https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-h135ad9c_1.conda#21fc5dba2cbcd8e5e26ff976a312122c +https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.1.0-h2466b09_0.conda#7c51d27540389de84852daa1cdb9c63c +https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda#c15148b2e18da456f5108ccb5e411446 +https://conda.anaconda.org/conda-forge/win-64/libopenblas-0.3.30-pthreads_ha4fe6b2_0.conda#c09864590782cb17fee135db4796bdcb +https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.50.3-hf5d6505_0.conda#c93ed8c395dc41956fe29c5470dea103 +https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.6.0-h4d5522a_0.conda#f9bbae5e2537e3b06e0f7310ba76c893 +https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda#41fbfac52c601159df6c01f875de31b9 +https://conda.anaconda.org/conda-forge/win-64/ninja-1.13.1-h477610d_0.conda#b8a603d4b32e113e3551b257b677de67 +https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.1-h725018a_0.conda#d124fc2fd7070177b5e2450627f8fc1a +https://conda.anaconda.org/conda-forge/win-64/pixman-0.46.2-had0cd8c_0.conda#2566a45fb15e2f540eff14261f1242af +https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854fbdff64b572b5c0b470f334d34c11 +https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda#ebd0e761de9aa879a51d22cc721bd095 +https://conda.anaconda.org/conda-forge/win-64/krb5-1.21.3-hdf4eb48_0.conda#31aec030344e962fbd7dbbbbd68e60a9 +https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-32_h11dc60a_openblas.conda#0696abde82f7b82d4f74e963ebdd430c +https://conda.anaconda.org/conda-forge/win-64/libbrotlidec-1.1.0-h2466b09_3.conda#a342933dbc6d814541234c7c81cb5205 +https://conda.anaconda.org/conda-forge/win-64/libbrotlienc-1.1.0-h2466b09_3.conda#7ef0af55d70cbd9de324bb88b7f9d81e +https://conda.anaconda.org/conda-forge/win-64/libgcc-15.1.0-h1383e82_3.conda#d8314be93c803e2e2b430f6389d6ce6a +https://conda.anaconda.org/conda-forge/win-64/libintl-0.22.5-h5728263_3.conda#2cf0cf76cc15d360dfa2f17fd6cf9772 +https://conda.anaconda.org/conda-forge/win-64/libpng-1.6.50-h95bef1e_0.conda#2e63db2e13cd6a5e2c08f771253fb8a0 +https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.8-h442d1da_0.conda#833c2dbc1a5020007b520b044c713ed3 +https://conda.anaconda.org/conda-forge/win-64/openblas-0.3.30-pthreads_h4a7f399_0.conda#2773d23da17eb31ed3a0911334a08805 +https://conda.anaconda.org/conda-forge/win-64/pcre2-10.45-h99c9b8b_0.conda#f4c483274001678e129f5cbaf3a8d765 +https://conda.anaconda.org/conda-forge/win-64/python-3.10.18-h8c5b53a_0_cpython.conda#f1775dab55c8a073ebd024bfb2f689c1 +https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-hbeecb71_2.conda#21f56217d6125fb30c3c3f10c786d751 +https://conda.anaconda.org/conda-forge/win-64/brotli-bin-1.1.0-h2466b09_3.conda#c7c345559c1ac25eede6dccb7b931202 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 +https://conda.anaconda.org/conda-forge/win-64/cython-3.1.2-py310h6bd2d47_2.conda#4cc20be3a890b2e640504478b2aa7d56 +https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.8-py310he9f1925_1.conda#e2755283837d9bd45838564cf54872c8 +https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-32_h9bd4c3b_openblas.conda#69e8e83a9ed37d070b0c5ed4996648a8 +https://conda.anaconda.org/conda-forge/win-64/libclang13-20.1.8-default_hadf22e1_0.conda#cf1a9a4c7395c5d6cc0dcf8f7c40acb3 +https://conda.anaconda.org/conda-forge/win-64/libfreetype6-2.13.3-h0b5ce68_1.conda#a84b7d1a13060a9372bea961a8131dbc +https://conda.anaconda.org/conda-forge/win-64/libglib-2.84.2-hbc94333_0.conda#fee05801cc5db97bec20a5e78fb3905b +https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-32_h2526c6b_openblas.conda#13c3da761e89eec8a40bf8c877dd7a71 +https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.0-h05922d8_5.conda#75370aba951b47ec3b5bfe689f1bcf7f +https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.39-h3df6e99_0.conda#279ee338c9b34871d578cb3c7aa68f70 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/win-64/pthread-stubs-0.4-h0e40799_1002.conda#3c8f2573569bb816483e5cf57efbbe29 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 +https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 +https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 +https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.1-py310ha8f682b_0.conda#4c8f599990e386f3a0aba3f3bd8608da +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f +https://conda.anaconda.org/conda-forge/win-64/unicodedata2-16.0.0-py310ha8f682b_0.conda#b28aead44c6e19a1fbba7752aa242b34 +https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 +https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.12-h0e40799_0.conda#2ffbfae4548098297c033228256eb96e +https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-h0e40799_0.conda#8393c0f7e7870b4eb45553326f81f0ff +https://conda.anaconda.org/conda-forge/win-64/brotli-1.1.0-h2466b09_3.conda#c2a23d8a8986c72148c63bdf855ac99a +https://conda.anaconda.org/conda-forge/win-64/coverage-7.9.2-py310hdb0e946_0.conda#99a4cbaef874f64995c896860445a659 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c +https://conda.anaconda.org/conda-forge/win-64/lcms2-2.17-hbcf6048_0.conda#3538827f77b82a837fa681a4579e37a1 +https://conda.anaconda.org/conda-forge/win-64/libfreetype-2.13.3-h57928b3_1.conda#410ba2c8e7bdb278dfbb5d40220e39d2 +https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-32_h1d0e49f_openblas.conda#cca697e07375fde34cced92d66e8bdf2 +https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda#a69bbf778a462da324489976c84cfc8c +https://conda.anaconda.org/conda-forge/win-64/numpy-2.2.6-py310h4987827_0.conda#d2596785ac2cf5bab04e2ee9e5d04041 +https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.3-h4d64b90_0.conda#fc050366dd0b8313eb797ed1ffef3a29 +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 +https://conda.anaconda.org/conda-forge/win-64/blas-devel-3.9.0-32_hc0f8095_openblas.conda#c07c54d62ee5a9886933051e10ad4b1e +https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.2-py310hc19bc0b_0.conda#039416813b5290e7d100a05bb4326110 +https://conda.anaconda.org/conda-forge/win-64/fonttools-4.59.0-py310hdb0e946_0.conda#eae900c4fcb37e4a3f9fe9417c669f11 +https://conda.anaconda.org/conda-forge/win-64/freetype-2.13.3-h57928b3_1.conda#633504fe3f96031192e40e3e6c18ef06 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/win-64/pillow-11.3.0-py310h6d647b9_0.conda#246b33a0eb812754b529065262aeb1c5 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py310h15c175c_0.conda#81798168111d1021e3d815217c444418 +https://conda.anaconda.org/conda-forge/win-64/blas-2.132-openblas.conda#b59780f3fbd2bf992d3702e59d8d1653 +https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.15.0-h765892d_1.conda#9bb0026a2131b09404c59c4290c697cd +https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.10.3-py310h37e0a56_0.conda#de9ddae6f97b78860c256de480ea1a84 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda#ce978e1b9ed8b8d49164e90a5cdc94cd +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.4-h5782bbf_0.conda#20e32ced54300292aff690a69c5e7b97 +https://conda.anaconda.org/conda-forge/win-64/harfbuzz-11.2.1-h8796e6f_0.conda#bccea58fbf7910ce868b084f27ffe8bd +https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.9.1-h02ddd7d_1.conda#fc796cf6c16db38d44c2efefbe6afcea +https://conda.anaconda.org/conda-forge/win-64/pyside6-6.9.1-py310h2d19612_0.conda#01b830c0fd6ca7ab03c85a008a6f4a2d +https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.10.3-py310h5588dad_0.conda#103adee33db124a0263d0b4551e232e3 diff --git a/build_tools/azure/test_docs.sh b/build_tools/azure/test_docs.sh index f3f824d5806b0..f41072bf23a8b 100755 --- a/build_tools/azure/test_docs.sh +++ b/build_tools/azure/test_docs.sh @@ -14,8 +14,6 @@ if [[ "$scipy_doctest_installed" == "True" ]]; then # conda with putting conda in the PATH and source activate, rather than # source /etc/profile.d/conda.sh + conda activate. cd $TEST_DIR - # with scipy-doctest, --doctest-modules only runs doctests (in contrary to - # vanilla pytest where it runs doctests on top of normal tests) - python -m pytest --doctest-modules --pyargs sklearn + python -m pytest --doctest-modules --doctest-only-doctests=true --pyargs sklearn python -m pytest --doctest-modules $doc_rst_files fi diff --git a/build_tools/azure/test_script.sh b/build_tools/azure/test_script.sh index d8152bd7c3ae2..eb4414283be2b 100755 --- a/build_tools/azure/test_script.sh +++ b/build_tools/azure/test_script.sh @@ -75,6 +75,14 @@ else echo "Could not inspect CPU architecture." fi +if [[ "$DISTRIB" == "conda-free-threaded" ]]; then + # Make sure that GIL is disabled even when importing extensions that have + # not declared free-threaded compatibility. This can be removed when numpy, + # scipy and scikit-learn extensions all have declared free-threaded + # compatibility. + export PYTHON_GIL=0 +fi + TEST_CMD="$TEST_CMD --pyargs sklearn" set -x diff --git a/build_tools/azure/ubuntu_atlas_lock.txt b/build_tools/azure/ubuntu_atlas_lock.txt index ea978eeabcb51..12f0cadf784e6 100644 --- a/build_tools/azure/ubuntu_atlas_lock.txt +++ b/build_tools/azure/ubuntu_atlas_lock.txt @@ -6,7 +6,7 @@ # cython==3.0.10 # via -r build_tools/azure/ubuntu_atlas_requirements.txt -exceptiongroup==1.2.2 +exceptiongroup==1.3.0 # via pytest execnet==2.1.1 # via pytest-xdist @@ -14,9 +14,9 @@ iniconfig==2.1.0 # via pytest joblib==1.2.0 # via -r build_tools/azure/ubuntu_atlas_requirements.txt -meson==1.8.0 +meson==1.8.2 # via meson-python -meson-python==0.17.1 +meson-python==0.18.0 # via -r build_tools/azure/ubuntu_atlas_requirements.txt ninja==1.11.1.4 # via -r build_tools/azure/ubuntu_atlas_requirements.txt @@ -25,15 +25,17 @@ packaging==25.0 # meson-python # pyproject-metadata # pytest -pluggy==1.5.0 +pluggy==1.6.0 + # via pytest +pygments==2.19.2 # via pytest pyproject-metadata==0.9.1 # via meson-python -pytest==8.3.5 +pytest==8.4.1 # via # -r build_tools/azure/ubuntu_atlas_requirements.txt # pytest-xdist -pytest-xdist==3.6.1 +pytest-xdist==3.8.0 # via -r build_tools/azure/ubuntu_atlas_requirements.txt threadpoolctl==3.1.0 # via -r build_tools/azure/ubuntu_atlas_requirements.txt @@ -41,3 +43,5 @@ tomli==2.2.1 # via # meson-python # pytest +typing-extensions==4.14.1 + # via exceptiongroup diff --git a/build_tools/azure/windows.yml b/build_tools/azure/windows.yml index b3fcf130f9350..b49273d40a16d 100644 --- a/build_tools/azure/windows.yml +++ b/build_tools/azure/windows.yml @@ -27,8 +27,24 @@ jobs: - bash: python build_tools/azure/get_selected_tests.py displayName: Check selected tests for all random seeds condition: eq(variables['Build.Reason'], 'PullRequest') - - bash: echo "##vso[task.prependpath]$CONDA/Scripts" - displayName: Add conda to PATH + - task: PowerShell@2 + displayName: 'Get CPU Information' + inputs: + targetType: 'inline' + script: | + Write-Host "=== CPU Information ===" + $cpu = Get-WmiObject -Class Win32_Processor + Write-Host "CPU Model: $($cpu.Name)" + Write-Host "Architecture: $($cpu.Architecture)" + Write-Host "Physical Cores: $($cpu.NumberOfCores)" + Write-Host "Logical Processors: $($cpu.NumberOfLogicalProcessors)" + Write-Host "Max Clock Speed: $($cpu.MaxClockSpeed) MHz" + Write-Host "Current Clock Speed: $($cpu.CurrentClockSpeed) MHz" + Write-Host "L2 Cache Size: $($cpu.L2CacheSize) KB" + Write-Host "L3 Cache Size: $($cpu.L3CacheSize) KB" + Write-Host "===========================" + - bash: build_tools/azure/install_setup_conda.sh + displayName: Install conda if necessary and set it up condition: startsWith(variables['DISTRIB'], 'conda') - task: UsePythonVersion@0 inputs: diff --git a/build_tools/circle/doc_environment.yml b/build_tools/circle/doc_environment.yml index bc36e178de058..dcf3f0b0db699 100644 --- a/build_tools/circle/doc_environment.yml +++ b/build_tools/circle/doc_environment.yml @@ -27,7 +27,7 @@ dependencies: - sphinx - sphinx-gallery - sphinx-copybutton - - numpydoc + - numpydoc<1.9.0 - sphinx-prompt - plotly - polars @@ -37,8 +37,8 @@ dependencies: - sphinx-design - pydata-sphinx-theme - towncrier + - jupyterlite-sphinx + - jupyterlite-pyodide-kernel - pip - pip: - - jupyterlite-sphinx - - jupyterlite-pyodide-kernel - sphinxcontrib-sass diff --git a/build_tools/circle/doc_linux-64_conda.lock b/build_tools/circle/doc_linux-64_conda.lock index c489e4f01a9f7..479abad123e05 100644 --- a/build_tools/circle/doc_linux-64_conda.lock +++ b/build_tools/circle/doc_linux-64_conda.lock @@ -1,263 +1,326 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 208134f3b8c140a6fe6fffe85293a731d77b7bf6cdcf0b12f7a44fdcf6e665d2 +# input_hash: 207a7209ba4771c5fc039939c36a47d93b9e5478fbdf6fe01c4ac5837581d49a @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda#ad8527bf134a90e1c9ed35fa0b64318c -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-7_cp310.conda#44e871cba2b162368476a84b8d040b6c +https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda#ff007ab0f0fdc53d245972bba8a6d40c +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-13.3.0-hc03c837_102.conda#4c1d6961a6a54f602ae510d9bf31fa60 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda#06d02030237f4d5b3d9a7e7d348fe3c6 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_3.conda#3cd1a7238a0dd3d0860fdefc496cc854 https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-13.3.0-hc03c837_102.conda#aa38de2738c5f4a72a880e3d31ffe8b4 -https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h0157908_18.conda#460eba7851277ec1fd80a1a24080787a +https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda#1bad93f0aa428d618875ef3a588a889e https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d -https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_4.conda#ef67db625ad0d2dce398837102f875ed +https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.44-h4bf12b8_1.conda#e45cfedc8ca5630e02c106ea36d2c5c6 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda#c151d5eb730e9b7480e6d48c0fc44048 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda#7df50d44d4a14d6c31a2c54f2cd92157 -https://conda.anaconda.org/conda-forge/linux-64/binutils-2.43-h4852527_4.conda#29782348a527eda3ecfc673109d28e93 -https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.43-h4852527_4.conda#c87e146f5b685672d4aa6b527c6d3b5e -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 +https://conda.anaconda.org/conda-forge/linux-64/binutils-2.44-h4852527_1.conda#0fab3ce18775aba71131028a04c20dfe +https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.44-h4852527_1.conda#38e0be090e3af56e44a9cac46101f6cd +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda#76df83c2a9035c54df5d04ff81bcc02d -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda#41b599ed2b02abcfdd84302bff174b23 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda#27fe770decaf469a53f3e3a6d593067f -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_3.conda#cb98af5db26e3f482bebb80ce9d947d3 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda#e796ff8ddc598affdf7c173d6145f087 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda#63f790534398730f59e1b899c3644d4a +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda#70e3400cbbfa03e96dcde7fc13e38c7b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e +https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda#2c42649888aac645608191ffdc80d13a https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda#f6ebe2cb3f82ba6c057dde5d9debe4f7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda#8035c64cb77ed555e3f150b7b3972480 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda#418c6ca5929a611cbd69204907a83995 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda#d6845ae4dea52a2f90178bf1829a21f8 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-h5888daf_0.conda#951ff8d9e5536896408e89d63230b8d5 https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda#5aeabe88534ea4169d4c49998f293d6c https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda#9566f0bd264fbd463002e759b8a82401 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda#06f70867945ea6a84d35836af780f1de +https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda#01ba04e414e47f95c03d6ddd81fd37be +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_3.conda#1c6eecffad553bde44c5238770cfb7da +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_3.conda#3facafe58f3858eb95527c7d3a3fc578 +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.conda#4c0ab57463117fbb8df85268415082f5 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.2.0-hf40a0c7_0.conda#2f433d593a66044c3f163cb25f0a09de -https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 -https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hd590300_0.conda#48f4330bfcd959c3cfb704d424903c82 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.47-h943b412_0.conda#55199e2ae2c3651f6f9b2a447b47bdc9 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h943b412_0.conda#51de14db340a848869e69c632b43cca7 https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-13.3.0-he8ea267_2.conda#2b6cdf7bb95d3d10ef4e38ce0bc95dba -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda#a587892d3c13b6621a6091be690dbca2 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.2.0-h266115a_0.conda#db22a0962c953e81a2a679ecb1fc6027 -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.0-h29eaf8c_0.conda#d2f1c87d4416d1e7344cf92b1aaee1c4 -https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.6.6-he8a937b_2.conda#77d9955b4abddb811cb8ab1aa7d743e4 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.2-h29eaf8c_0.conda#39b4228a867772d610c02e06f939a5b8 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda#3b3e64af585eadfb52bb90b553db5edf https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.0.2-h5888daf_0.conda#0096882bd623e6cc09e8bf920fc8fb47 -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.23.1-h3e06ad9_1.conda#a37843723437ba75f42c9270ffe800b1 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda#0f2ca7906bf166247d1d760c3422cb8a +https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h5888daf_2.conda#e0409515c467b87176b070bff5d9442e https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.2.4-h7955e40_0.conda#c8a816dbf59eb8ba6346a8f10014b302 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda#346722a0be40f6edc53f12640d301338 https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda#c63b5e52939e795ba8d26e35d767a843 -https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.15.2-h3122c55_1.conda#2bc8d76acd818d7e79229f5157d5c156 +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_3.conda#58178ef8ba927229fba6d84abf62c108 +https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.19.1-h4cfbee9_0.conda#041ee44c15d1efdc84740510796425df https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.2-h59595ed_0.conda#4336bd67920dd504cd8c6761d6a99645 https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-13.3.0-h1e990d8_2.conda#f46cf0acdcb6019397d37df1e407ab91 -https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda#f87c7b7c2cb45f323ffbce941c78ab7c https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 -https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.3-h59595ed_0.conda#5e97e271911b8b2001a8b71860c32faa -https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.124-hb9d3cd8_0.conda#8bc89311041d7fcb510238cf0848ccae https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda#3c255be50a506c50765a93a6644f32fe -https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-h7b0646d_1.conda#959fc2b6c0df7883e070b3fe525219a5 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda#0a4d0252248ef9a0f88f2ba8b8a08e12 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_4.conda#6c1028898cf3a2032d9af46689e1b81a +https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-h7b0646d_2.conda#7b7baf93533744be2c0228bfa7149e2d +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_0.conda#323dc8f259224d13078aaf7ce96c3efe +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hf01ce69_5.conda#e79a094918988bb1807462cd42c83962 https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2#c66fe2d123249af7651ebde8984c51c2 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.2.0-he0572af_0.conda#93340b072c393d23c4700a1d40565dca -https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda#31614c73d7b103ef76faa4d83d261d34 -https://conda.anaconda.org/conda-forge/linux-64/python-3.10.17-hd6af730_0_cpython.conda#7bb89638dae9ce1b8e051d0b721e83c2 +https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.45-hc749103_0.conda#b90bece58b4c2bf25969b70f3be42d25 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-hb711507_2.conda#8637c3e5821654d0edf97e2b0404b443 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_3.conda#5d08a0ac29e6a5a984817584775d4131 +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/gcc-13.3.0-h9576a4e_2.conda#d92e51bf4b6bdbfe45e5884fb0755afe +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-h6f18a23_11.conda#639ef869618e311eee4888fcb40747e2 +https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-13.3.0-h84c1745_2.conda#4e21ed177b76537067736f20f54fee0a +https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hae580e1_2.conda#b55f02540605c322a47719029f8404cc +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 +https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h766b0b6_0.conda#f17f2d0e5c9ad6b958547fd67b155771 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-32_h59b9bed_openblas.conda#2af9f3d5c2e39f417ce040f5a35c40c6 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.2-h3618099_0.conda#072ab14a02164b7c0c089055368ff776 +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda#14dbe05b929e329dbaa6f2d0aa19466d +https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_0.conda#15fa8c1f683e68ff08ef0ea106012add +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.45-hb9d3cd8_0.conda#397a013c2dc5145a70737871aaa87e98 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h3b0a872_7.conda#3947a35e916fcc6b9825449affbf4214 +https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-h9c3ff4c_0.tar.bz2#c1ac6229d0bfd14f8354ff9ad2a26cad +https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.10.0-h2b85faf_0.conda#9256b7e5e900a1b98aedc8d6ffe91bec +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 +https://conda.anaconda.org/conda-forge/linux-64/gfortran-13.3.0-h9576a4e_2.conda#19e6d3c9cde10a0a9a170a684082588e +https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-h1917dac_11.conda#85b2fa3c287710011199f5da1bac5b43 +https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_2.conda#07e8df00b7cd3084ad3ef598ce32a71c +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-hb14504d_11.conda#2ca7575e4f2da39c5ee260e022ab1a6f +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-32_he106b2a_openblas.conda#3d3f9355e52f269cd8bc2c440d8a5263 +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-32_h7ac8fdf_openblas.conda#6c3f04ccb6c578138e9f9899da0bd714 +https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.8-hecd9e04_0.conda#59a7b967b6ef5d63029b1712f8dcf661 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.10.0-h65c71a3_0.conda#fedf6bfe5d21d21d2b1785ec00a8889a +https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/python-3.10.18-hd6af730_0_cpython.conda#4ea0c77cdcb0b81813a0436b162d7316 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda#1fd9696649f65fd6611fcdb4ffec738a -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_2.conda#bf502c169c71e3c6ac0d6175addfacc2 -https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda#c207fa5ac7ea99b149344385a9c0880d +https://conda.anaconda.org/conda-forge/noarch/attrs-25.3.0-pyh71513ae_0.conda#a10d11958cadc13fdb43df75f8b1903f +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_3.conda#63d24a5dd21c738d706f91569dbd1892 +https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2#576d629e47797577ab0f1b351297ef4a +https://conda.anaconda.org/conda-forge/noarch/certifi-2025.7.14-pyhd8ed1ab_0.conda#4c07624f3faefd0bb6659fb7396cfa76 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.2-pyhd8ed1ab_0.conda#40fe4284b8b5835a9073a645139f35af -https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda#f22f4d4970e09d68a10b922cbb0408d3 +https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda#94b550b8d3a614dbd326af798c7dfb40 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.1-pyhd8ed1ab_0.conda#364ba6c9fb03886ac979b482f39ebb92 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.17-py310hd8ed1ab_0.conda#e2b81369f0473107784f8b7da8e6a8e9 +https://conda.anaconda.org/conda-forge/noarch/cpython-3.10.18-py310hd8ed1ab_0.conda#7004cb3fa62ad44d1cb70f3b080dfc8f +https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.10.0-h1a2810e_0.conda#3cd322edac3d40904ff07355a8be8086 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda#dce22f70b4e5a407ce88f2be046f4ceb -https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.12-py310had8cdd9_0.conda#b630fe36f0b621d23e74872dc4fd2bd7 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.2-py310had8cdd9_2.conda#be416b1d5ffef48c394cbbb04bc864ae +https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2#961b3a227b437d82ad7054484cfa71b2 https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda#24c1ca34138ee57de72a943237cde4cc -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 -https://conda.anaconda.org/conda-forge/linux-64/gcc-13.3.0-h9576a4e_2.conda#d92e51bf4b6bdbfe45e5884fb0755afe -https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-hc28eda2_10.conda#d151142bbafe5e68ec7fc065c5e6f80c -https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-13.3.0-h84c1745_2.conda#4e21ed177b76537067736f20f54fee0a -https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hae580e1_2.conda#b55f02540605c322a47719029f8404cc +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee +https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.10.0-h36df796_0.conda#e2d49a61c0ebc4ee2c7779d940f2f3e7 https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py310h3788b33_0.conda#4186d9b4d004b0fe0de6aa62496fb48a -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.2.1-hbb36593_2.conda#971387a27e61235b97cacb440a37e991 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h59b9bed_openblas.conda#728dbebd0f7a20337218beacffd37916 -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.1-h2ff4ddf_0.conda#0305434da649d4fb48a425e588b79ea6 -https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h4bc477f_1.conda#ad1f1f8238834cd3c88ceeaee8da444a +https://conda.anaconda.org/conda-forge/noarch/json5-0.12.0-pyhd8ed1ab_0.conda#56275442557b3b45752c10980abfe2db +https://conda.anaconda.org/conda-forge/linux-64/jsonpointer-3.0.0-py310hff52083_1.conda#ce614a01b0aee1b29cee13d606bcb5d5 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py310h3788b33_1.conda#b70dd76da5231e6073fd44c42a1d78c5 +https://conda.anaconda.org/conda-forge/noarch/lark-1.2.2-pyhd8ed1ab_1.conda#3a8063b25e603999188ed4bbf3485404 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.8-default_hddf928d_0.conda#b939740734ad5a8e8f6c942374dee68d +https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.8-default_ha444ac7_0.conda#783f9cdcb0255ed00e3f1be22e16de40 +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-32_he2f377e_openblas.conda#54e7f7896d0dbf56665bcb0078bfa9d2 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.5-h27ae623_0.conda#6458be24f09e1b034902ab44fe9de908 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py310h89163eb_1.conda#8ce3f0332fd6de0d737e2911d329523f -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/noarch/narwhals-1.37.0-pyh29332c3_0.conda#f9ae420fa431efd502a5d5c4c1f08263 +https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda#592132998493b3ff25fd7479396e8351 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/noarch/narwhals-1.47.1-pyhe01879c_0.conda#8ebf6f2d5dca14126e63882bbf25a992 https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda#fd40bf7f7f4bc4b647dc8512053d9873 -https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.29-pthreads_h6ec200e_0.conda#7e4d48870b3258bea920d51b7f495a81 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.6-py310hefbff90_0.conda#b0cea2c364bf65cd19e023040eeab05d https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.7-pyh29332c3_0.conda#e57da6fe54bb3a5556cf36d199ff07d8 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 +https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2#457c2c8c08e54905d6954e79cb5b5db9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h7e6dc6c_0.conda#e609995f031bc848be8ea159865e8afc +https://conda.anaconda.org/conda-forge/noarch/pkginfo-1.12.1.2-pyhd8ed1ab_0.conda#dc702b2fae7ebe770aff3c83adb16b63 +https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.8-pyhe01879c_0.conda#424844562f5d337077b445ec6b1398a7 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.22.1-pyhd8ed1ab_0.conda#c64b77ccab10b822722904d889fa83b5 https://conda.anaconda.org/conda-forge/linux-64/psutil-7.0.0-py310ha75aee5_0.conda#da7d592394ff9084a23f62a1186451a2 +https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda#7d9daffbb8d8e0af0f769dbbcd173a54 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef -https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda#232fb4577b6687b2d503ef8e254270c9 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac +https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.1-pyhd8ed1ab_0.conda#38e34d2d1d9dca4fb2b9a0a04f604e2c +https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda#a61bf9ec79426938ff785eb69dbb1960 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py310h89163eb_2.conda#fd343408e64cf1e273ab7c710da374db +https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.0.0-py310h71f11fc_0.conda#de862cdd8a959ac9a751fd8a5f7dc82d +https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2#912a71cc01012ee38e6b90ddd561e36f +https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.26.0-py310hbcd0ec0_0.conda#e59b1ae4bfd0e42664fa3336bff5b4f0 +https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda#938c8de6b9de091997145b3bf25cdbf9 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e -https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561 +https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_1.conda#bf7a226e58dfb8346c70df36065d86c9 +https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda#755cf22df8693aa0d1aec1c123fa5863 +https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.7-pyhd8ed1ab_0.conda#fb32097c717486aa34b38a9db57eb49e https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda#fa839b5ff59e192f411ccc7dae6588bb https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhd8ed1ab_2.conda#959484a66b4b76befcddc4fa97c95567 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py310ha75aee5_0.conda#166d59aab40b9c607b4cc21c03924e9d -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda#83fc6ae00127671e301c9f44254c31b8 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py310ha75aee5_0.conda#6f3da1072c0c4d2a1beb1e84615f7c9c +https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda#019a7385be9af33791c989871317e1ed +https://conda.anaconda.org/conda-forge/noarch/types-python-dateutil-2.9.0.20250708-pyhd8ed1ab_0.conda#b6d4c200582ead6427f49a189e2c6d65 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f +https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda#f6d7aa696c67756a650e91e15e88223c https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py310ha75aee5_0.conda#1d7a4b9202cdd10d56ecdd7f6c347190 +https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda#e7cb0f5745e4c5035a460248334af7eb +https://conda.anaconda.org/conda-forge/noarch/webcolors-24.11.1-pyhd8ed1ab_0.conda#b49f7b291e15494aafb0a7d74806f337 +https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda#2841eb5bfc75ce15e9a0054b98dcd64d +https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.8.0-pyhd8ed1ab_1.conda#84f8f77f0a9c6ef401ee96611745da8f https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.44-hb9d3cd8_0.conda#7c91bfc90672888259675ad2ad28af9c -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e -https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda#0c3cc595284c5e8f0f9900a9b228a332 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f +https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda#74ac5069774cdbc53910ec4d631a3999 https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4 -https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-h9c3ff4c_0.tar.bz2#c1ac6229d0bfd14f8354ff9ad2a26cad -https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.9.0-h2b85faf_0.conda#3cb814f83f1f71ac1985013697f80cc1 +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-32_h1ea3ea9_openblas.conda#34cb4b6753b38a62ae25f3a73efd16b0 +https://conda.anaconda.org/conda-forge/noarch/bleach-6.2.0-pyh29332c3_4.conda#f0b4c8e370446ef89797608d60a564b3 +https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2#9b347a7ec10940d3f7941ff6c460b551 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py310h8deb56e_0.conda#1fc24a3196ad5ede2a68148be61894f4 -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.57.0-py310h89163eb_0.conda#34378af82141b3c1725dcdf898b28fc6 -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 -https://conda.anaconda.org/conda-forge/linux-64/gfortran-13.3.0-h9576a4e_2.conda#19e6d3c9cde10a0a9a170a684082588e -https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-hb919d3a_10.conda#7ce070e3329cd10bf79dbed562a21bd4 -https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_2.conda#07e8df00b7cd3084ad3ef598ce32a71c -https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_10.conda#9a8ebde471cec5cc9c48f8682f434f92 +https://conda.anaconda.org/conda-forge/linux-64/compilers-1.10.0-ha770c72_0.conda#993ae32cac4879279af74ba12aa0979c +https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py310h3788b33_0.conda#b6420d29123c7c823de168f49ccdfe6a +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py310h3406613_0.conda#dc2e5602e20bbffb18314a70094b3c4a https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda#b4754fb1bdcb70c8fd54f918301582c6 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda#f4b39bf00c69f56ac01e020ebfac066c +https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2025.3.30-py310h4eb8eaf_2.conda#a9c921699d37e862f9bf8dcf9d343838 +https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda#b5577bc2212219566578fd5af9993af6 +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8 https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda#c85c76dc67d75619a92f51dfbce06992 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda#abb32c727da370c481a1c206f5159ce9 -https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda#452b98eafe050ecff932f0ec832dd03f -https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda#96c33bbd084ef2b2463503fb7f1482ae -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.9.1-h65c71a3_0.conda#6e45090fe0eec179ecc8041a3a3fc9f8 -https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c +https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.8.1-pyh31011fe_0.conda#b7d89d860ebcda28a5303526cdee68ab +https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda#fd312693df06da3578383232528c468d +https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-3.0.0-pyhd8ed1ab_1.conda#fee3164ac23dfca50cfcc8b85ddefb81 https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhd8ed1ab_1.conda#71abbefb6f3b95e1668cd5e0af3affb9 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda#ca2de8bbdc871bce41dbf59e51324165 +https://conda.anaconda.org/conda-forge/noarch/mistune-3.1.3-pyh29332c3_0.conda#7ec6576e328bc128f4982cd646eeba85 +https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda#e51f1e4089cad105b6cac64bd8166587 +https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda#ee23fabfd0a8c6b8d6f3729b47b2859d https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c -https://conda.anaconda.org/conda-forge/noarch/plotly-6.0.1-pyhd8ed1ab_0.conda#37ce02c899ff42ac5c554257b1a5906e +https://conda.anaconda.org/conda-forge/noarch/plotly-6.2.0-pyhd8ed1ab_0.conda#8a9590843af49b36f37ac3dbcf5fc3d9 https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.17-hd8ed1ab_0.conda#c856adbd93a57004e21cd26564f4f724 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.13.2-h0e9735f_0.conda#568ed1300869dca0ba09fb750cda5dbb -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 +https://conda.anaconda.org/conda-forge/noarch/python-gil-3.10.18-hd8ed1ab_0.conda#a40e3a920f2c46f94e027bd599b88b17 +https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.8.0-py310hf462985_0.conda#4c441eff2be2e65bd67765c5642051c5 +https://conda.anaconda.org/conda-forge/noarch/referencing-0.36.2-pyh29332c3_0.conda#9140f1c09dd5489549c6a33931b943c7 +https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda#36de09a8d3e5d5e6f4ee63af49e59706 +https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda#7234f99325263a5af6d4cd195035e8f2 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda#8c29cd33b64b2eb78597fa28b5595c8d +https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda#efba281bbdae5f6b0a1d53c6d4a97c93 +https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda#f1acf5fdefa8300de697982bcb1761c9 +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.1-h4440ef1_0.conda#75be1a943e0a7f99fcf118309092c635 https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f +https://conda.anaconda.org/conda-forge/noarch/anyio-4.9.0-pyh29332c3_0.conda#9749a2c77a7c40d432ea0927662d7e52 +https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-21.2.0-py310ha75aee5_5.conda#a2da54f3a705d518c95a5b6de8ad8af6 +https://conda.anaconda.org/conda-forge/noarch/arrow-1.3.0-pyhd8ed1ab_1.conda#46b53236fdd990271b03c3978d4218a9 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.13.4-pyha770c72_0.conda#9f07c4fc992adb2d6c30da7fab3959a7 -https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.9.0-h1a2810e_0.conda#1ce8b218d359d9ed0ab481f2a3f3c512 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee -https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.9.0-h36df796_0.conda#cc0cf942201f9d3b0e9654ea02e12486 +https://conda.anaconda.org/conda-forge/linux-64/blas-2.132-openblas.conda#9c4a27ab2463f9b1d9019e0a798a5b81 +https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.2.0-h82add2a_4.conda#a30e9406c873940383555af4c873220d +https://conda.anaconda.org/conda-forge/noarch/doit-0.36.0-pyhd8ed1ab_1.conda#18d4243b3d30352f9dea8e522f6ff4d1 +https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda#d3549fd50d450b6d9e7dddff25dd2110 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda#0e6e192d4b3d95708ad192d957cf3163 https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.5.2-pyhd8ed1ab_0.conda#e376ea42e9ae40f3278b0f79c9bf9826 +https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.4.1-pyh29332c3_0.conda#41ff526b1083fde51fbdc93f29282e0e +https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.6.3-pyhd8ed1ab_1.conda#4ebae00eae9705b0c3d6d1018a81d047 +https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda#2d983ff1b82a1ccb6f2e9d8784bdd6bd https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_2.conda#d10d9393680734a8febc4b362a4c94f2 -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda#96f8d5b2e94c9ba4fef19f1adf068a15 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda#2d933632c8004be47deb2be61bf013be -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-31_he2f377e_openblas.conda#7e5fff7d0db69be3a266f7e79a3bb0e2 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda#37fba334855ef3b51549308e61ed7a3d -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py310hefbff90_0.conda#5526bc875ec897f0d335e38da832b6ee -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py310h7e6dc6c_0.conda#14d300b9e1504748e70cc6499a7b4d25 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310ha75aee5_2.conda#f9254b5b0193982416b91edcb4b2676f -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-31_h1ea3ea9_openblas.conda#ba652ee0576396d4765e567f043c57f9 -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 -https://conda.anaconda.org/conda-forge/linux-64/compilers-1.9.0-ha770c72_0.conda#5859096e397aba423340d0bbbb11ec64 -https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py310h3788b33_0.conda#b6420d29123c7c823de168f49ccdfe6a -https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2024.12.30-py310h78a9a29_0.conda#e0c50079904122427bcf52e1afcd1cdb -https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda#b5577bc2212219566578fd5af9993af6 -https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_2.conda#bb0230917e2473c77d615104dbe8a49d -https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py310h5eaa309_3.conda#07697a584fab513ce895c4511f7a2403 -https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda#ee23fabfd0a8c6b8d6f3729b47b2859d -https://conda.anaconda.org/conda-forge/linux-64/polars-1.27.1-py39h2a4a510_3.conda#fba08963eaa1f954480045d033d1221e -https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.8.0-py310hf462985_0.conda#4c441eff2be2e65bd67765c5642051c5 -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda#8c29cd33b64b2eb78597fa28b5595c8d -https://conda.anaconda.org/conda-forge/noarch/towncrier-24.8.0-pyhd8ed1ab_1.conda#820b6a1ddf590fba253f8204f7200d82 -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.4.0-pyhd8ed1ab_0.conda#c1e349028e0052c4eea844e94f773065 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.131-openblas.conda#38b2ec894c69bb4be0e66d2ef7fc60bf -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.1.0-h3beb420_0.conda#95e3bb97f9cdc251c0c68640e9c10ed3 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.1-py310h68603db_0.conda#29cf3f5959afb841eda926541f26b0fb +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.3-py310h68603db_0.conda#50084ca38bf28440e2762966bac143fc +https://conda.anaconda.org/conda-forge/noarch/mdit-py-plugins-0.4.2-pyhd8ed1ab_1.conda#af2060041d4f3250a7eb6ab3ec0e549b +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py310h0158d43_0.conda#94eb2db0b8f769a1e554843e3586504d https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py310ha2bacc8_1.conda#817d32861729e14f474249f1036291c4 -https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda#a9b9368f3701a417eac9edbcae7cb737 -https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.4-py310hf462985_0.conda#636d3c500d8a851e377360e88ec95372 -https://conda.anaconda.org/conda-forge/noarch/tifffile-2025.3.30-pyhd8ed1ab_0.conda#14f46147fae19bb867f82a787c7059e9 -https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_1.conda#b3e783e8e8ed7577cf0b6dee37d1fbac -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.0-h6441bc3_1.conda#4029a8dcb1d97ea241dbe5abfda1fad6 -https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.25.2-py310h5eaa309_0.conda#4cc3a231679ecb3c0ba20ebf3c27d12e +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/noarch/tifffile-2025.5.10-pyhd8ed1ab_0.conda#1fdb801f28bf4987294c49aaa314bf5e +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310ha75aee5_2.conda#f9254b5b0193982416b91edcb4b2676f +https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda#8ac12aff0860280ee0cff7fa2cf63f3b +https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda#0b0154421989637d424ccf0f104be51a +https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.0-pyhe01879c_0.conda#c6e3fd94e058dba67d917f38a11b50ab +https://conda.anaconda.org/conda-forge/noarch/jupyterlite-core-0.6.3-pyhe01879c_0.conda#36ebdbf67840763b491045b5a36a2b78 +https://conda.anaconda.org/conda-forge/linux-64/polars-default-1.31.0-py39hf521cc8_1.conda#85f9f61975ba5a8f3d40b477aef457cb +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.1-h0384650_1.conda#3610aa92d2de36047886f30e99342f21 +https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.25.2-py310h5eaa309_1.conda#ed21ab72d049ecdb60f829f04b4dca1c https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda#fd96da444e81f9e6fcaac38590f3dd42 -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.0-py310hfd10a26_0.conda#1610ccfe262ee519716bb69bd4395572 +https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.5-py310haaf2d95_0.conda#92b4b51b83f2cfded298f1b8c7a99e32 +https://conda.anaconda.org/conda-forge/noarch/towncrier-24.8.0-pyhd8ed1ab_1.conda#820b6a1ddf590fba253f8204f7200d82 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a +https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.25.0-he01879c_0.conda#f4c7afaf838ab5bb1c4e73eb3095fb26 +https://conda.anaconda.org/conda-forge/noarch/jupyterlite-pyodide-kernel-0.6.1-pyhe01879c_0.conda#b55913693e8934299585267ce95af06e +https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda#bbe1963f1e47f594070ffe87cdf612ea +https://conda.anaconda.org/conda-forge/linux-64/polars-1.31.0-default_h70f2ef1_1.conda#0217d9e4176cf33942996a7ee3afac0e +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.1-py310h21765ff_0.conda#a64f8b57dd1b84d5d4f02f565a3cb630 +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.4-pyhd8ed1ab_0.conda#f6082eae112814f1447b56a5e1f6ed05 https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda#62afb877ca2c2b4b6f9ecb37320085b6 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.1-py310hff52083_0.conda#45c1ad6a0351492b56d1b2bb5442cdfa +https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.0-pyh29332c3_0.conda#f56000b36f09ab7533877e695e4e8cb0 +https://conda.anaconda.org/conda-forge/noarch/jupytext-1.17.2-pyh80e38bb_0.conda#6d0652a97ef103de0c77b9c610d0c20d +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.3-py310hff52083_0.conda#4162a00ddf1d805557aff34ddf113f46 +https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.2-pyhd8ed1ab_0.conda#6bb0d77277061742744176ab555b723c +https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_1.conda#b3e783e8e8ed7577cf0b6dee37d1fbac +https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyh29332c3_0.conda#d24beda1d30748afcc87c429454ece1b +https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.16.0-pyhe01879c_0.conda#f062e04d7cd585c937acbf194dceec36 +https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.27.3-pyhd8ed1ab_1.conda#9dc4b2b0f41f0de41d27f3293e319357 +https://conda.anaconda.org/conda-forge/noarch/jupyterlite-sphinx-0.20.2-pyhd8ed1ab_0.conda#6e12bee196f27964a79759d99c071df9 https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.8.0-pyhd8ed1ab_1.conda#5af206d64d18d6c8dfb3122b4d9e643b https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.16.1-pyhd8ed1ab_0.conda#837aaf71ddf3b27acae0e7e9015eebc6 https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda#bf22cb9c439572760316ce0748af3713 @@ -271,62 +334,6 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda#00534ebcc0375929b45c3039b5ba7636 https://conda.anaconda.org/conda-forge/noarch/sphinx-8.1.3-pyhd8ed1ab_1.conda#1a3281a0dc355c02b5506d87db2d78ac https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda#3bc61f7161d28137797e038263c04c54 -https://conda.anaconda.org/conda-forge/noarch/sphinxext-opengraph-0.9.1-pyhd8ed1ab_1.conda#79f5d05ad914baf152fb7f75073fe36d -# pip attrs @ https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl#sha256=427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3 -# pip cloudpickle @ https://files.pythonhosted.org/packages/7e/e8/64c37fadfc2816a7701fa8a6ed8d87327c7d54eacfbfb6edab14a2f2be75/cloudpickle-3.1.1-py3-none-any.whl#sha256=c8c5a44295039331ee9dad40ba100a9c7297b6f988e50e87ccdf3765a668350e -# pip defusedxml @ https://files.pythonhosted.org/packages/07/6c/aa3f2f849e01cb6a001cd8554a88d4c77c5c1a31c95bdf1cf9301e6d9ef4/defusedxml-0.7.1-py2.py3-none-any.whl#sha256=a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61 -# pip fastjsonschema @ https://files.pythonhosted.org/packages/90/2b/0817a2b257fe88725c25589d89aec060581aabf668707a8d03b2e9e0cb2a/fastjsonschema-2.21.1-py3-none-any.whl#sha256=c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667 -# pip fqdn @ https://files.pythonhosted.org/packages/cf/58/8acf1b3e91c58313ce5cb67df61001fc9dcd21be4fadb76c1a2d540e09ed/fqdn-1.5.1-py3-none-any.whl#sha256=3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014 -# pip json5 @ https://files.pythonhosted.org/packages/41/9f/3500910d5a98549e3098807493851eeef2b89cdd3032227558a104dfe926/json5-0.12.0-py3-none-any.whl#sha256=6d37aa6c08b0609f16e1ec5ff94697e2cbbfbad5ac112afa05794da9ab7810db -# pip jsonpointer @ https://files.pythonhosted.org/packages/71/92/5e77f98553e9e75130c78900d000368476aed74276eb8ae8796f65f00918/jsonpointer-3.0.0-py2.py3-none-any.whl#sha256=13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942 -# pip jupyterlab-pygments @ https://files.pythonhosted.org/packages/b1/dd/ead9d8ea85bf202d90cc513b533f9c363121c7792674f78e0d8a854b63b4/jupyterlab_pygments-0.3.0-py3-none-any.whl#sha256=841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780 +https://conda.anaconda.org/conda-forge/noarch/sphinxext-opengraph-0.10.0-pyhd8ed1ab_0.conda#c9446c05bf81e5b613bdafa3bc15becf # pip libsass @ https://files.pythonhosted.org/packages/fd/5a/eb5b62641df0459a3291fc206cf5bd669c0feed7814dded8edef4ade8512/libsass-0.23.0-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl#sha256=4a218406d605f325d234e4678bd57126a66a88841cb95bee2caeafdc6f138306 -# pip mdurl @ https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl#sha256=84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 -# pip overrides @ https://files.pythonhosted.org/packages/2c/ab/fc8290c6a4c722e5514d80f62b2dc4c4df1a68a41d1364e625c35990fcf3/overrides-7.7.0-py3-none-any.whl#sha256=c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49 -# pip pandocfilters @ https://files.pythonhosted.org/packages/ef/af/4fbc8cab944db5d21b7e2a5b8e9211a03a79852b1157e2c102fcc61ac440/pandocfilters-1.5.1-py2.py3-none-any.whl#sha256=93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc -# pip pkginfo @ https://files.pythonhosted.org/packages/fa/3d/f4f2ba829efb54b6cd2d91349c7463316a9cc55a43fc980447416c88540f/pkginfo-1.12.1.2-py3-none-any.whl#sha256=c783ac885519cab2c34927ccfa6bf64b5a704d7c69afaea583dd9b7afe969343 -# pip prometheus-client @ https://files.pythonhosted.org/packages/ff/c2/ab7d37426c179ceb9aeb109a85cda8948bb269b7561a0be870cc656eefe4/prometheus_client-0.21.1-py3-none-any.whl#sha256=594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301 -# pip ptyprocess @ https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl#sha256=4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35 -# pip python-json-logger @ https://files.pythonhosted.org/packages/08/20/0f2523b9e50a8052bc6a8b732dfc8568abbdc42010aef03a2d750bdab3b2/python_json_logger-3.3.0-py3-none-any.whl#sha256=dd980fae8cffb24c13caf6e158d3d61c0d6d22342f932cb6e9deedab3d35eec7 -# pip pyyaml @ https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed -# pip rfc3986-validator @ https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl#sha256=2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9 -# pip rpds-py @ https://files.pythonhosted.org/packages/a7/a7/6d04d438f53d8bb2356bb000bea9cf5c96a9315e405b577117e344cc7404/rpds_py-0.24.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=1b221c2457d92a1fb3c97bee9095c874144d196f47c038462ae6e4a14436f7bc -# pip send2trash @ https://files.pythonhosted.org/packages/40/b0/4562db6223154aa4e22f939003cb92514c79f3d4dccca3444253fd17f902/Send2Trash-1.8.3-py3-none-any.whl#sha256=0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9 -# pip sniffio @ https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl#sha256=2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 -# pip traitlets @ https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl#sha256=b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f -# pip types-python-dateutil @ https://files.pythonhosted.org/packages/0f/b3/ca41df24db5eb99b00d97f89d7674a90cb6b3134c52fb8121b6d8d30f15c/types_python_dateutil-2.9.0.20241206-py3-none-any.whl#sha256=e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53 -# pip uri-template @ https://files.pythonhosted.org/packages/e7/00/3fca040d7cf8a32776d3d81a00c8ee7457e00f80c649f1e4a863c8321ae9/uri_template-1.3.0-py3-none-any.whl#sha256=a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363 -# pip webcolors @ https://files.pythonhosted.org/packages/60/e8/c0e05e4684d13459f93d312077a9a2efbe04d59c393bc2b8802248c908d4/webcolors-24.11.1-py3-none-any.whl#sha256=515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9 -# pip webencodings @ https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl#sha256=a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 -# pip websocket-client @ https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl#sha256=17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526 -# pip anyio @ https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl#sha256=9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c -# pip argon2-cffi-bindings @ https://files.pythonhosted.org/packages/ec/f7/378254e6dd7ae6f31fe40c8649eea7d4832a42243acaf0f1fff9083b2bed/argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae -# pip arrow @ https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl#sha256=c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80 -# pip doit @ https://files.pythonhosted.org/packages/44/83/a2960d2c975836daa629a73995134fd86520c101412578c57da3d2aa71ee/doit-0.36.0-py3-none-any.whl#sha256=ebc285f6666871b5300091c26eafdff3de968a6bd60ea35dd1e3fc6f2e32479a -# pip jupyter-core @ https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl#sha256=4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409 -# pip markdown-it-py @ https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl#sha256=355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1 -# pip mistune @ https://files.pythonhosted.org/packages/01/4d/23c4e4f09da849e127e9f123241946c23c1e30f45a88366879e064211815/mistune-3.1.3-py3-none-any.whl#sha256=1a32314113cff28aa6432e99e522677c8587fd83e3d51c29b82a52409c842bd9 -# pip pyzmq @ https://files.pythonhosted.org/packages/c1/3e/2de5928cdadc2105e7c8f890cc5f404136b41ce5b6eae5902167f1d5641c/pyzmq-26.4.0-cp310-cp310-manylinux_2_28_x86_64.whl#sha256=7dacb06a9c83b007cc01e8e5277f94c95c453c5851aac5e83efe93e72226353f -# pip referencing @ https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl#sha256=e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0 -# pip rfc3339-validator @ https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl#sha256=24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa # pip sphinxcontrib-sass @ https://files.pythonhosted.org/packages/3f/ec/194f2dbe55b3fe0941b43286c21abb49064d9d023abfb99305c79ad77cad/sphinxcontrib_sass-0.3.5-py2.py3-none-any.whl#sha256=850c83a36ed2d2059562504ccf496ca626c9c0bb89ec642a2d9c42105704bef6 -# pip terminado @ https://files.pythonhosted.org/packages/6a/9e/2064975477fdc887e47ad42157e214526dcad8f317a948dee17e1659a62f/terminado-0.18.1-py3-none-any.whl#sha256=a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0 -# pip tinycss2 @ https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl#sha256=3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289 -# pip argon2-cffi @ https://files.pythonhosted.org/packages/a4/6a/e8a041599e78b6b3752da48000b14c8d1e8a04ded09c88c714ba047f34f5/argon2_cffi-23.1.0-py3-none-any.whl#sha256=c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea -# pip bleach @ https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl#sha256=117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e -# pip isoduration @ https://files.pythonhosted.org/packages/7b/55/e5326141505c5d5e34c5e0935d2908a74e4561eca44108fbfb9c13d2911a/isoduration-20.11.0-py3-none-any.whl#sha256=b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042 -# pip jsonschema-specifications @ https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl#sha256=4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af -# pip jupyter-client @ https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl#sha256=e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f -# pip jupyter-server-terminals @ https://files.pythonhosted.org/packages/07/2d/2b32cdbe8d2a602f697a649798554e4f072115438e92249624e532e8aca6/jupyter_server_terminals-0.5.3-py3-none-any.whl#sha256=41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa -# pip jupyterlite-core @ https://files.pythonhosted.org/packages/46/15/1d9160819d1e6e018d15de0e98b9297d0a09cfcfdc73add6e24ee3b2b83c/jupyterlite_core-0.5.1-py3-none-any.whl#sha256=76381619a632f06bf67fb47e5464af762ad8836df5ffe3d7e7ee0e316c1407ee -# pip mdit-py-plugins @ https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl#sha256=0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636 -# pip jsonschema @ https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl#sha256=fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566 -# pip jupyterlite-pyodide-kernel @ https://files.pythonhosted.org/packages/1b/b5/959a03ca011d1031abac03c18af9e767c18d6a9beb443eb106dda609748c/jupyterlite_pyodide_kernel-0.5.2-py3-none-any.whl#sha256=63ba6ce28d32f2cd19f636c40c153e171369a24189e11e2235457bd7000c5907 -# pip jupyter-events @ https://files.pythonhosted.org/packages/e2/48/577993f1f99c552f18a0428731a755e06171f9902fa118c379eb7c04ea22/jupyter_events-0.12.0-py3-none-any.whl#sha256=6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb -# pip nbformat @ https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl#sha256=3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b -# pip jupytext @ https://files.pythonhosted.org/packages/12/b7/e7e3d34c8095c19228874b1babedfb5d901374e40d51ae66f2a90203be53/jupytext-1.17.1-py3-none-any.whl#sha256=99145b1e1fa96520c21ba157de7d354ffa4904724dcebdcd70b8413688a312de -# pip nbclient @ https://files.pythonhosted.org/packages/34/6d/e7fa07f03a4a7b221d94b4d586edb754a9b0dc3c9e2c93353e9fa4e0d117/nbclient-0.10.2-py3-none-any.whl#sha256=4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d -# pip nbconvert @ https://files.pythonhosted.org/packages/cc/9a/cd673b2f773a12c992f41309ef81b99da1690426bd2f96957a7ade0d3ed7/nbconvert-7.16.6-py3-none-any.whl#sha256=1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b -# pip jupyter-server @ https://files.pythonhosted.org/packages/e2/a2/89eeaf0bb954a123a909859fa507fa86f96eb61b62dc30667b60dbd5fdaf/jupyter_server-2.15.0-py3-none-any.whl#sha256=872d989becf83517012ee669f09604aa4a28097c0bd90b2f424310156c2cdae3 -# pip jupyterlab-server @ https://files.pythonhosted.org/packages/54/09/2032e7d15c544a0e3cd831c51d77a8ca57f7555b2e1b2922142eddb02a84/jupyterlab_server-2.27.3-py3-none-any.whl#sha256=e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4 -# pip jupyterlite-sphinx @ https://files.pythonhosted.org/packages/a9/f2/b64ad053b8b6fed95c46e8df85ee3349a1cca47e006eb6a65671c9a1c6e5/jupyterlite_sphinx-0.20.0-py3-none-any.whl#sha256=de2cb966f389d70cc269f501af24f0cbb1f47d521a89ee79ac83f0ad302214fc diff --git a/build_tools/circle/doc_min_dependencies_environment.yml b/build_tools/circle/doc_min_dependencies_environment.yml index 1a93231019fbb..2e16632152d1f 100644 --- a/build_tools/circle/doc_min_dependencies_environment.yml +++ b/build_tools/circle/doc_min_dependencies_environment.yml @@ -32,11 +32,11 @@ dependencies: - plotly=5.14.0 # min - polars=0.20.30 # min - pooch=1.6.0 # min + - sphinxext-opengraph=0.9.1 # min - sphinx-remove-toctrees=1.0.0.post1 # min - sphinx-design=0.6.0 # min - pydata-sphinx-theme=0.15.3 # min - towncrier=24.8.0 # min - pip - pip: - - sphinxext-opengraph==0.9.1 # min - sphinxcontrib-sass==0.3.4 # min diff --git a/build_tools/circle/doc_min_dependencies_linux-64_conda.lock b/build_tools/circle/doc_min_dependencies_linux-64_conda.lock index 4e9d8501dc411..7e93aa7f2b938 100644 --- a/build_tools/circle/doc_min_dependencies_linux-64_conda.lock +++ b/build_tools/circle/doc_min_dependencies_linux-64_conda.lock @@ -1,51 +1,55 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: 1ff580fa5b39efc9a616b69d09ea9208049b15bb1bd5e42883b7295d717cc6ba +# input_hash: e32b19b18fba3e64af830b6f9b7d9e826f7c625fc3ed7a3a5d16edad94228ad6 @EXPLICIT https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda#ad8527bf134a90e1c9ed35fa0b64318c -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-7_cp310.conda#44e871cba2b162368476a84b8d040b6c +https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda#ff007ab0f0fdc53d245972bba8a6d40c +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-13.3.0-hc03c837_102.conda#4c1d6961a6a54f602ae510d9bf31fa60 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h767d61c_2.conda#06d02030237f4d5b3d9a7e7d348fe3c6 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.1.0-h767d61c_3.conda#3cd1a7238a0dd3d0860fdefc496cc854 https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-13.3.0-hc03c837_102.conda#aa38de2738c5f4a72a880e3d31ffe8b4 -https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h0157908_18.conda#460eba7851277ec1fd80a1a24080787a +https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda#1bad93f0aa428d618875ef3a588a889e https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d -https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_4.conda#ef67db625ad0d2dce398837102f875ed +https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.44-h4bf12b8_1.conda#e45cfedc8ca5630e02c106ea36d2c5c6 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda#c151d5eb730e9b7480e6d48c0fc44048 -https://conda.anaconda.org/conda-forge/linux-64/binutils-2.43-h4852527_4.conda#29782348a527eda3ecfc673109d28e93 -https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.43-h4852527_4.conda#c87e146f5b685672d4aa6b527c6d3b5e -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 +https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda#7df50d44d4a14d6c31a2c54f2cd92157 +https://conda.anaconda.org/conda-forge/linux-64/binutils-2.44-h4852527_1.conda#0fab3ce18775aba71131028a04c20dfe +https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.44-h4852527_1.conda#38e0be090e3af56e44a9cac46101f6cd +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda#76df83c2a9035c54df5d04ff81bcc02d -https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.24.1-h5888daf_0.conda#d54305672f0361c2f3886750e7165b5f -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda#41b599ed2b02abcfdd84302bff174b23 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda#27fe770decaf469a53f3e3a6d593067f -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/gettext-tools-0.25.1-h5888daf_0.conda#4836fff66ad6089f356e29063f52b790 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_3.conda#cb98af5db26e3f482bebb80ce9d947d3 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.24.1-h5888daf_0.conda#2ee6d71b72f75d50581f2f68e965efdb -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.25.1-h5888daf_0.conda#8d2f4f3884f01aad1e197c3db4ef305f +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda#e796ff8ddc598affdf7c173d6145f087 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda#d864d34357c3b65a4b731f78c0801dc4 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-hd0c01bc_1.conda#68e52064ed3897463c0e958ab5c8f91b https://conda.anaconda.org/conda-forge/linux-64/libopus-1.5.2-hd0c01bc_0.conda#b64523fb87ac6f87f0790f324ad43046 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda#63f790534398730f59e1b899c3644d4a +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda#70e3400cbbfa03e96dcde7fc13e38c7b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e +https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.7.1-h8fae777_3.conda#2c42649888aac645608191ffdc80d13a https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda#f6ebe2cb3f82ba6c057dde5d9debe4f7 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb9d3cd8_0.conda#8035c64cb77ed555e3f150b7b3972480 @@ -54,230 +58,225 @@ https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9 https://conda.anaconda.org/conda-forge/linux-64/blis-0.9.0-h4ab18f5_2.conda#6f77ba1352b69c4a6f8a6d20def30e4e https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda#418c6ca5929a611cbd69204907a83995 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda#d6845ae4dea52a2f90178bf1829a21f8 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda#3bf7b9fd5a7136126e0234db4b87c8b6 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-h5888daf_0.conda#951ff8d9e5536896408e89d63230b8d5 https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda#5aeabe88534ea4169d4c49998f293d6c https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 -https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.24.1-h8e693c7_0.conda#57566a81dd1e5aa3d98ac7582e8bfe03 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda#9566f0bd264fbd463002e759b8a82401 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda#06f70867945ea6a84d35836af780f1de +https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda#01ba04e414e47f95c03d6ddd81fd37be +https://conda.anaconda.org/conda-forge/linux-64/libasprintf-0.25.1-h8e693c7_0.conda#96ae2046abdf1bb9c65e3338725c06ac +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_3.conda#1c6eecffad553bde44c5238770cfb7da +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_3.conda#3facafe58f3858eb95527c7d3a3fc578 +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.conda#4c0ab57463117fbb8df85268415082f5 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d -https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.24.1-h5888daf_0.conda#8f04c7aae6a46503bc36d1ed5abc8c7c -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 +https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.25.1-h5888daf_0.conda#f467fbfc552a50dbae2def93692bcc67 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.55-h3f2d84a_0.conda#2bd47db5807daade8500ed7ca4c512a4 https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.2.0-hf40a0c7_0.conda#2f433d593a66044c3f163cb25f0a09de -https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 -https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hd590300_0.conda#48f4330bfcd959c3cfb704d424903c82 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.47-h943b412_0.conda#55199e2ae2c3651f6f9b2a447b47bdc9 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h943b412_0.conda#51de14db340a848869e69c632b43cca7 https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-13.3.0-he8ea267_2.conda#2b6cdf7bb95d3d10ef4e38ce0bc95dba -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393 https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.9-hc50e24c_0.conda#c7f302fd11eeb0987a6a5e1f3aed6a21 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_6.conda#94116b69829e90b72d566e64421e1bff -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 -https://conda.anaconda.org/conda-forge/linux-64/nspr-4.36-h5888daf_0.conda#de9cd5bca9e4918527b9b72b6e2e1409 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.0-h29eaf8c_0.conda#d2f1c87d4416d1e7344cf92b1aaee1c4 -https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.6.6-he8a937b_2.conda#77d9955b4abddb811cb8ab1aa7d743e4 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.37-h29cc59b_0.conda#d73ccc379297a67ed921bd55b38a6c6a +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.2-h29eaf8c_0.conda#39b4228a867772d610c02e06f939a5b8 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda#3b3e64af585eadfb52bb90b553db5edf https://conda.anaconda.org/conda-forge/linux-64/svt-av1-3.0.2-h5888daf_0.conda#0096882bd623e6cc09e8bf920fc8fb47 -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h5888daf_2.conda#e0409515c467b87176b070bff5d9442e https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.2.4-h7955e40_0.conda#c8a816dbf59eb8ba6346a8f10014b302 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda#346722a0be40f6edc53f12640d301338 https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda#2c2fae981fd2afd00812c92ac47d023d -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda#c63b5e52939e795ba8d26e35d767a843 -https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.15.2-h3122c55_1.conda#2bc8d76acd818d7e79229f5157d5c156 +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_3.conda#58178ef8ba927229fba6d84abf62c108 +https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.19.1-h4cfbee9_0.conda#041ee44c15d1efdc84740510796425df https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.2-h59595ed_0.conda#4336bd67920dd504cd8c6761d6a99645 https://conda.anaconda.org/conda-forge/linux-64/gcc_impl_linux-64-13.3.0-h1e990d8_2.conda#f46cf0acdcb6019397d37df1e407ab91 -https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda#f87c7b7c2cb45f323ffbce941c78ab7c https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 -https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.3-h59595ed_0.conda#5e97e271911b8b2001a8b71860c32faa -https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.24.1-h8e693c7_0.conda#8f66ed2e34507b7ae44afa31c3e4ec79 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h66dfbfd_blis.conda#612d513ce8103e41dbcb4d941a325027 +https://conda.anaconda.org/conda-forge/linux-64/libasprintf-devel-0.25.1-h8e693c7_0.conda#6c07a6cd50acc5fceb5bd33e8e30dac8 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-32_h66dfbfd_blis.conda#dca8fde8cc52d44049339be5ee888dda https://conda.anaconda.org/conda-forge/linux-64/libcap-2.75-h39aace5_0.conda#c44c16d6976d2aebbd65894d7741e67e -https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.124-hb9d3cd8_0.conda#8bc89311041d7fcb510238cf0848ccae https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda#3c255be50a506c50765a93a6644f32fe -https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-lib-1.11.0-hb9d3cd8_2.conda#e55712ff40a054134d51b89afca57dbc -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_2.conda#4056c857af1a99ee50589a941059ec55 -https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-h7b0646d_1.conda#959fc2b6c0df7883e070b3fe525219a5 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_4.conda#6c1028898cf3a2032d9af46689e1b81a +https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-lib-1.11.1-hb9d3cd8_0.conda#8504a291085c9fb809b66cabd5834307 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 +https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.1-h7b0646d_2.conda#7b7baf93533744be2c0228bfa7149e2d +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hf01ce69_5.conda#e79a094918988bb1807462cd42c83962 https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0 https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2#c66fe2d123249af7651ebde8984c51c2 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_6.conda#9802ae6d20982f42c0f5d69008988763 -https://conda.anaconda.org/conda-forge/linux-64/nss-3.110-h159eef7_0.conda#945659af183e87429c8aa7e0be3cc91d https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.45-hc749103_0.conda#b90bece58b4c2bf25969b70f3be42d25 -https://conda.anaconda.org/conda-forge/linux-64/python-3.10.17-hd6af730_0_cpython.conda#7bb89638dae9ce1b8e051d0b721e83c2 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-hb711507_2.conda#8637c3e5821654d0edf97e2b0404b443 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda#1c74ff8c35dcadf952a16f752ca5aa49 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_3.conda#5d08a0ac29e6a5a984817584775d4131 +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 +https://conda.anaconda.org/conda-forge/linux-64/gcc-13.3.0-h9576a4e_2.conda#d92e51bf4b6bdbfe45e5884fb0755afe +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-h6f18a23_11.conda#639ef869618e311eee4888fcb40747e2 +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.25.1-h5888daf_0.conda#df1ca81a8be317854cb06c22582b731c +https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-13.3.0-h84c1745_2.conda#4e21ed177b76537067736f20f54fee0a +https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hae580e1_2.conda#b55f02540605c322a47719029f8404cc +https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 +https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.3.0-h766b0b6_0.conda#f17f2d0e5c9ad6b958547fd67b155771 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-32_hba4ea11_blis.conda#34de11c815d0c739a80e8cc359da90fc +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.2-h3618099_0.conda#072ab14a02164b7c0c089055368ff776 +https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-12_hd37a5e2_netlib.conda#4b181b55915cefcd35c8398c9274e629 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.7-h4e0b6ca_0.conda#1e12c8aa74fa4c3166a9bdc135bc4abf +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda#14dbe05b929e329dbaa6f2d0aa19466d +https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.45-hb9d3cd8_0.conda#397a013c2dc5145a70737871aaa87e98 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e +https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-h9c3ff4c_0.tar.bz2#c1ac6229d0bfd14f8354ff9ad2a26cad +https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.10.0-h2b85faf_0.conda#9256b7e5e900a1b98aedc8d6ffe91bec +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 +https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 +https://conda.anaconda.org/conda-forge/linux-64/gfortran-13.3.0-h9576a4e_2.conda#19e6d3c9cde10a0a9a170a684082588e +https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-h1917dac_11.conda#85b2fa3c287710011199f5da1bac5b43 +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.84.2-h4833e2c_0.conda#f2ec1facec64147850b7674633978050 +https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_2.conda#07e8df00b7cd3084ad3ef598ce32a71c +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-hb14504d_11.conda#2ca7575e4f2da39c5ee260e022ab1a6f +https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869 +https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-12_hce4cc19_netlib.conda#bdcf65db13abdddba7af29592f93600b +https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.8-hecd9e04_0.conda#59a7b967b6ef5d63029b1712f8dcf661 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.10.0-h65c71a3_0.conda#fedf6bfe5d21d21d2b1785ec00a8889a +https://conda.anaconda.org/conda-forge/linux-64/nss-3.114-hc3c8bcf_0.conda#7d5713b9f8346d094ac046277db1c12b +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 +https://conda.anaconda.org/conda-forge/linux-64/python-3.10.18-hd6af730_0_cpython.conda#4ea0c77cdcb0b81813a0436b162d7316 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.16-pyhd8ed1ab_0.conda#def531a3ac77b7fb8c21d17bb5d0badb https://conda.anaconda.org/conda-forge/noarch/appdirs-1.4.4-pyhd8ed1ab_1.conda#f4e90937bbfc3a4a92539545a37bb448 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_2.conda#bf502c169c71e3c6ac0d6175addfacc2 -https://conda.anaconda.org/conda-forge/noarch/certifi-2025.1.31-pyhd8ed1ab_0.conda#c207fa5ac7ea99b149344385a9c0880d +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-32_hdec4247_blis.conda#a1a7e1ecfcf8a6d251af652b108fc825 +https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py310hf71b8c6_3.conda#63d24a5dd21c738d706f91569dbd1892 +https://conda.anaconda.org/conda-forge/noarch/certifi-2025.7.14-pyhd8ed1ab_0.conda#4c07624f3faefd0bb6659fb7396cfa76 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.2-pyhd8ed1ab_0.conda#40fe4284b8b5835a9073a645139f35af -https://conda.anaconda.org/conda-forge/noarch/click-8.1.8-pyh707e725_0.conda#f22f4d4970e09d68a10b922cbb0408d3 +https://conda.anaconda.org/conda-forge/noarch/click-8.2.1-pyh707e725_0.conda#94b550b8d3a614dbd326af798c7dfb40 https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.1-pyhd8ed1ab_0.conda#364ba6c9fb03886ac979b482f39ebb92 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 +https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.10.0-h1a2810e_0.conda#3cd322edac3d40904ff07355a8be8086 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda#dce22f70b4e5a407ce88f2be046f4ceb https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.10-py310hc6cd4ac_0.conda#bd1d71ee240be36f1d85c86177d6964f https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda#24c1ca34138ee57de72a943237cde4cc -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.3.2-pyhd8ed1ab_0.conda#9c40692c3d24c7aaf335f673ac09d308 -https://conda.anaconda.org/conda-forge/linux-64/gcc-13.3.0-h9576a4e_2.conda#d92e51bf4b6bdbfe45e5884fb0755afe -https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-hc28eda2_10.conda#d151142bbafe5e68ec7fc065c5e6f80c -https://conda.anaconda.org/conda-forge/linux-64/gettext-0.24.1-h5888daf_0.conda#c63e7590d4d6f4c85721040ed8b12888 -https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-13.3.0-h84c1745_2.conda#4e21ed177b76537067736f20f54fee0a -https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hae580e1_2.conda#b55f02540605c322a47719029f8404cc +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee +https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.10.0-h36df796_0.conda#e2d49a61c0ebc4ee2c7779d940f2f3e7 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda#a31ce802cd0ebfce298f342c02757019 https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda#0a802cb9888dd14eeefc611f05c40b6e https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda#8e6923fc12f1fe8f8c4e5c9f343256ac https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_1.conda#39a4f67be3286c86d696df570b1201b7 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py310h3788b33_0.conda#4186d9b4d004b0fe0de6aa62496fb48a -https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.2.1-hbb36593_2.conda#971387a27e61235b97cacb440a37e991 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_hba4ea11_blis.conda#1ea7ae3db0fea0c5222388d841583c51 -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 -https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.1-h3618099_1.conda#714c97d4ff495ab69d1fdfcadbcae985 -https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-12_hd37a5e2_netlib.conda#4b181b55915cefcd35c8398c9274e629 -https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-257.4-h4e0b6ca_1.conda#04bcf3055e51f8dde6fab9672fb9fca0 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h4bc477f_1.conda#ad1f1f8238834cd3c88ceeaee8da444a +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py310h3788b33_1.conda#b70dd76da5231e6073fd44c42a1d78c5 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.8-default_hddf928d_0.conda#b939740734ad5a8e8f6c942374dee68d +https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.8-default_ha444ac7_0.conda#783f9cdcb0255ed00e3f1be22e16de40 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.5-h27ae623_0.conda#6458be24f09e1b034902ab44fe9de908 +https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda#ef1910918dd895516a769ed36b5b3a4e https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py310h89163eb_1.conda#8ce3f0332fd6de0d737e2911d329523f -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 https://conda.anaconda.org/conda-forge/noarch/networkx-3.2-pyhd8ed1ab_0.conda#cec8cc498664cc00a070676aa89e69a7 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.0-py310h454958d_1.tar.bz2#607c66f0cce2986515a8fe9e136b2b57 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py310h7e6dc6c_0.conda#e609995f031bc848be8ea159865e8afc +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_3.conda#fd5062942bfa1b0bd5e0d2a4397b099e https://conda.anaconda.org/conda-forge/linux-64/psutil-7.0.0-py310ha75aee5_0.conda#da7d592394ff9084a23f62a1186451a2 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda#12c566707c80111f9799308d9e265aef -https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.1-pyhd8ed1ab_0.conda#232fb4577b6687b2d503ef8e254270c9 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda#461219d1a5bd61342293efa2c0c90eac https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py310h89163eb_2.conda#fd343408e64cf1e273ab7c710da374db -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e -https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.5-pyhd8ed1ab_1.conda#3f144b2c34f8cb5a9abd9ed23a39c561 +https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda#755cf22df8693aa0d1aec1c123fa5863 +https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.7-pyhd8ed1ab_0.conda#fb32097c717486aa34b38a9db57eb49e https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda#fa839b5ff59e192f411ccc7dae6588bb https://conda.anaconda.org/conda-forge/noarch/tenacity-9.1.2-pyhd8ed1ab_0.conda#5d99943f2ae3cc69e1ada12ce9d4d701 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 https://conda.anaconda.org/conda-forge/noarch/toolz-1.0.0-pyhd8ed1ab_1.conda#40d0ed782a8aaa16ef248e68c06c168d -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py310ha75aee5_0.conda#166d59aab40b9c607b4cc21c03924e9d -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda#83fc6ae00127671e301c9f44254c31b8 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py310ha75aee5_0.conda#6f3da1072c0c4d2a1beb1e84615f7c9c +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-16.0.0-py310ha75aee5_0.conda#1d7a4b9202cdd10d56ecdd7f6c347190 https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.44-hb9d3cd8_0.conda#7c91bfc90672888259675ad2ad28af9c -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e -https://conda.anaconda.org/conda-forge/noarch/zipp-3.21.0-pyhd8ed1ab_1.conda#0c3cc595284c5e8f0f9900a9b228a332 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhd8ed1ab_0.conda#df5e78d904988eb55042c0c97446079f https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda#74ac5069774cdbc53910ec4d631a3999 https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda#0a01c169f0ab0f91b26e77a3301fbfe4 -https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-h9c3ff4c_0.tar.bz2#c1ac6229d0bfd14f8354ff9ad2a26cad -https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.9.0-h2b85faf_0.conda#3cb814f83f1f71ac1985013697f80cc1 +https://conda.anaconda.org/conda-forge/linux-64/blas-2.132-blis.conda#065bbe23b3290f63b78ab644a29fbf8f +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py310h8deb56e_0.conda#1fc24a3196ad5ede2a68148be61894f4 +https://conda.anaconda.org/conda-forge/linux-64/compilers-1.10.0-ha770c72_0.conda#993ae32cac4879279af74ba12aa0979c https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.0.1-py310ha75aee5_0.conda#d0be1adaa04a03aed745f3d02afb59ce -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.57.0-py310h89163eb_0.conda#34378af82141b3c1725dcdf898b28fc6 -https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 -https://conda.anaconda.org/conda-forge/linux-64/gfortran-13.3.0-h9576a4e_2.conda#19e6d3c9cde10a0a9a170a684082588e -https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-hb919d3a_10.conda#7ce070e3329cd10bf79dbed562a21bd4 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.84.1-h4833e2c_1.conda#418de18c9b79a3d8583d90d27e0937c2 -https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_2.conda#07e8df00b7cd3084ad3ef598ce32a71c -https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_10.conda#9a8ebde471cec5cc9c48f8682f434f92 +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py310h3406613_0.conda#dc2e5602e20bbffb18314a70094b3c4a +https://conda.anaconda.org/conda-forge/linux-64/glib-2.84.2-h6287aef_0.conda#704648df3a01d4d24bc2c0466b718d63 https://conda.anaconda.org/conda-forge/noarch/h2-4.2.0-pyhd8ed1ab_0.conda#b4754fb1bdcb70c8fd54f918301582c6 -https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.6.1-pyha770c72_0.conda#f4b39bf00c69f56ac01e020ebfac066c +https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2025.3.30-py310h4eb8eaf_2.conda#a9c921699d37e862f9bf8dcf9d343838 +https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda#b5577bc2212219566578fd5af9993af6 +https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda#63ccfdc3a3ce25b027b8767eb722fca8 https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda#c85c76dc67d75619a92f51dfbce06992 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869 -https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-12_hce4cc19_netlib.conda#bdcf65db13abdddba7af29592f93600b -https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda#96c33bbd084ef2b2463503fb7f1482ae -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.9.1-h65c71a3_0.conda#6e45090fe0eec179ecc8041a3a3fc9f8 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhd8ed1ab_1.conda#71abbefb6f3b95e1668cd5e0af3affb9 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.0-py310h454958d_1.tar.bz2#607c66f0cce2986515a8fe9e136b2b57 -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda#ca2de8bbdc871bce41dbf59e51324165 https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 +https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda#ee23fabfd0a8c6b8d6f3729b47b2859d https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c https://conda.anaconda.org/conda-forge/noarch/plotly-5.14.0-pyhd8ed1ab_0.conda#6a7bcc42ef58dd6cf3da9333ea102433 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/linux-64/sip-6.8.6-py310hf71b8c6_2.conda#a50d1007fecaff3f98b19034a8e0b2e7 -https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.13.2-h0e9735f_0.conda#568ed1300869dca0ba09fb750cda5dbb -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda#b5fcc7172d22516e1f965490e65e33a4 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa -https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.13.4-pyha770c72_0.conda#9f07c4fc992adb2d6c30da7fab3959a7 -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-31_hdec4247_blis.conda#1675e95a742c910204645f7b6d7e56dc -https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.9.0-h1a2810e_0.conda#1ce8b218d359d9ed0ab481f2a3f3c512 -https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.4.1-pyhd8ed1ab_0.conda#0735ecef025a6c2d6eb61aae4785fc3f -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee -https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.9.0-h36df796_0.conda#cc0cf942201f9d3b0e9654ea02e12486 -https://conda.anaconda.org/conda-forge/linux-64/glib-2.84.1-h6287aef_1.conda#35012688d30e1b52bff2ba5d1f342a50 -https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2024.12.30-py310h78a9a29_0.conda#e0c50079904122427bcf52e1afcd1cdb -https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.5.2-pyhd8ed1ab_0.conda#e376ea42e9ae40f3278b0f79c9bf9826 -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda#96f8d5b2e94c9ba4fef19f1adf068a15 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda#2d933632c8004be47deb2be61bf013be -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda#37fba334855ef3b51549308e61ed7a3d -https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.conda#ef1910918dd895516a769ed36b5b3a4e -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.0-py310hb5077e9_0.tar.bz2#43e920bc9856daa7d8d18fcbfb244c4e -https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.1-pyhd8ed1ab_1.conda#ee23fabfd0a8c6b8d6f3729b47b2859d -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py310h7e6dc6c_0.conda#14d300b9e1504748e70cc6499a7b4d25 https://conda.anaconda.org/conda-forge/linux-64/polars-0.20.30-py310h031f9ce_0.conda#0743f5db9f978b6df92d412935ff8371 -https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.13.0-py310hf71b8c6_1.conda#0c8cbfbe70f4c8a47b040a14615e6f1f -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd +https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hac146a9_1.conda#66b1fa9608d8836e25f9919159adc9c6 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.6.0-py310h261611a_0.conda#04a405ee0bccb4de8d1ed0c87704f5f6 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.8.0-py310hea5193d_1.tar.bz2#664d80ddeb51241629b3ada5ea926e4d -https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310ha75aee5_2.conda#f9254b5b0193982416b91edcb4b2676f -https://conda.anaconda.org/conda-forge/linux-64/blas-2.131-blis.conda#87829e6b9fe49a926280e100959b7d2b -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 -https://conda.anaconda.org/conda-forge/linux-64/compilers-1.9.0-ha770c72_0.conda#5859096e397aba423340d0bbbb11ec64 +https://conda.anaconda.org/conda-forge/linux-64/sip-6.10.0-py310hf71b8c6_0.conda#2d7e4445be227e8210140b75725689ad +https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.14.1-h4440ef1_0.conda#75be1a943e0a7f99fcf118309092c635 +https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.13.4-pyha770c72_0.conda#9f07c4fc992adb2d6c30da7fab3959a7 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2025.7.0-pyhe01879c_1.conda#3293644021329a96c606c3d95e180991 https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.24.11-hc37bda9_0.conda#056d86cacf2b48c79c6a562a2486eb8c -https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda#b5577bc2212219566578fd5af9993af6 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda#0e6e192d4b3d95708ad192d957cf3163 +https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.5.2-pyhd8ed1ab_0.conda#e376ea42e9ae40f3278b0f79c9bf9826 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.0-py310h23f4a51_0.tar.bz2#9911225650b298776c8e8c083b5cacf1 -https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hac146a9_1.conda#66b1fa9608d8836e25f9919159adc9c6 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.0-py310hb5077e9_0.tar.bz2#43e920bc9856daa7d8d18fcbfb244c4e https://conda.anaconda.org/conda-forge/linux-64/pyamg-4.2.1-py310h7c3ba0c_0.tar.bz2#89f5a48e1f23b5cf3163a6094903d181 -https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.2-py310h261611a_0.conda#4b8508bab02b2aa2cef12eab4883f4a1 -https://conda.anaconda.org/conda-forge/noarch/tifffile-2025.3.30-pyhd8ed1ab_0.conda#14f46147fae19bb867f82a787c7059e9 -https://conda.anaconda.org/conda-forge/noarch/towncrier-24.8.0-pyhd8ed1ab_1.conda#820b6a1ddf590fba253f8204f7200d82 -https://conda.anaconda.org/conda-forge/noarch/urllib3-2.4.0-pyhd8ed1ab_0.conda#c1e349028e0052c4eea844e94f773065 +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.17.0-py310hf71b8c6_1.conda#696c7414297907d7647a5176031c8c69 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 +https://conda.anaconda.org/conda-forge/noarch/tifffile-2025.5.10-pyhd8ed1ab_0.conda#1fdb801f28bf4987294c49aaa314bf5e +https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py310ha75aee5_2.conda#f9254b5b0193982416b91edcb4b2676f https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.24.11-h651a532_0.conda#d8d8894f8ced2c9be76dc9ad1ae531ce -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.1.0-h3beb420_0.conda#95e3bb97f9cdc251c0c68640e9c10ed3 -https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_1.conda#a9b9368f3701a417eac9edbcae7cb737 +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.19.0-py310hb5077e9_0.tar.bz2#aa24b3a4aa979641ac3144405209cd89 https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda#fd96da444e81f9e6fcaac38590f3dd42 -https://conda.anaconda.org/conda-forge/noarch/pooch-1.6.0-pyhd8ed1ab_0.tar.bz2#6429e1d1091c51f626b5dcfdd38bf429 -https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.15-h993ce98_3.conda#aa49f5308f39277477d47cd6687eb8f3 +https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.2-py310h261611a_0.conda#4b8508bab02b2aa2cef12eab4883f4a1 +https://conda.anaconda.org/conda-forge/noarch/towncrier-24.8.0-pyhd8ed1ab_1.conda#820b6a1ddf590fba253f8204f7200d82 +https://conda.anaconda.org/conda-forge/noarch/urllib3-2.5.0-pyhd8ed1ab_0.conda#436c165519e140cb08d246a4472a9d6a +https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.15-hea1682b_4.conda#c054d7f22cc719e12c72d454b2328d6c +https://conda.anaconda.org/conda-forge/noarch/requests-2.32.4-pyhd8ed1ab_0.conda#f6082eae112814f1447b56a5e1f6ed05 https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda#62afb877ca2c2b4b6f9ecb37320085b6 -https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.10-py310hb3b5edb_1.conda#c370972fc4557cb54d265c9c1f71bd20 +https://conda.anaconda.org/conda-forge/noarch/pooch-1.6.0-pyhd8ed1ab_0.tar.bz2#6429e1d1091c51f626b5dcfdd38bf429 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.11-py310hf392a12_1.conda#e07b23661b711fb46d25b14206e0db47 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.0-py310hff52083_0.tar.bz2#1b2f3b135d5d9c594b5e0e6150c03b7b https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.2-pyhd8ed1ab_0.tar.bz2#025ad7ca2c7f65007ab6b6f5d93a56eb https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.15.3-pyhd8ed1ab_0.conda#55e445f4fcb07f2471fb0e1102d36488 @@ -292,6 +291,6 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda#00534ebcc0375929b45c3039b5ba7636 https://conda.anaconda.org/conda-forge/noarch/sphinx-7.3.7-pyhd8ed1ab_0.conda#7b1465205e28d75d2c0e1a868ee00a67 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda#3bc61f7161d28137797e038263c04c54 +https://conda.anaconda.org/conda-forge/noarch/sphinxext-opengraph-0.9.1-pyhd8ed1ab_1.conda#79f5d05ad914baf152fb7f75073fe36d # pip libsass @ https://files.pythonhosted.org/packages/fd/5a/eb5b62641df0459a3291fc206cf5bd669c0feed7814dded8edef4ade8512/libsass-0.23.0-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl#sha256=4a218406d605f325d234e4678bd57126a66a88841cb95bee2caeafdc6f138306 # pip sphinxcontrib-sass @ https://files.pythonhosted.org/packages/2e/87/7c2eb08e3ca1d6baae32c0a5e005330fe1cec93a36aa085e714c3b3a3c7d/sphinxcontrib_sass-0.3.4-py2.py3-none-any.whl#sha256=a0c79a44ae8b8935c02dc340ebe40c9e002c839331201c899dc93708970c355a -# pip sphinxext-opengraph @ https://files.pythonhosted.org/packages/92/0a/970b80b4fa1feeb6deb6f2e22d4cb14e388b27b315a1afdb9db930ff91a4/sphinxext_opengraph-0.9.1-py3-none-any.whl#sha256=b3b230cc6a5b5189139df937f0d9c7b23c7c204493b22646273687969dcb760e diff --git a/build_tools/github/build_minimal_windows_image.sh b/build_tools/github/build_minimal_windows_image.sh index 8cc9af937dfd9..80f739a328d93 100755 --- a/build_tools/github/build_minimal_windows_image.sh +++ b/build_tools/github/build_minimal_windows_image.sh @@ -20,8 +20,9 @@ if [[ $FREE_THREADED_BUILD == "False" ]]; then # Dot the Python version for identifying the base Docker image PYTHON_DOCKER_IMAGE_PART=$(echo ${PYTHON_VERSION:0:1}.${PYTHON_VERSION:1:2}) - if [[ "$CIBW_PRERELEASE_PYTHONS" =~ [tT]rue ]]; then - PYTHON_DOCKER_IMAGE_PART="${PYTHON_DOCKER_IMAGE_PART}-rc" + # TODO Remove this when Python 3.14 is released and there is a Docker image + if [[ "$PYTHON_DOCKER_IMAGE_PART" == "3.14" ]]; then + PYTHON_DOCKER_IMAGE_PART="3.14-rc" fi # We could have all of the following logic in a Dockerfile but it's a lot diff --git a/build_tools/github/pylatest_conda_forge_cuda_array-api_linux-64_conda.lock b/build_tools/github/pylatest_conda_forge_cuda_array-api_linux-64_conda.lock index 8a707637fbc9b..912384b19cef8 100644 --- a/build_tools/github/pylatest_conda_forge_cuda_array-api_linux-64_conda.lock +++ b/build_tools/github/pylatest_conda_forge_cuda_array-api_linux-64_conda.lock @@ -1,48 +1,50 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: e141e0789f4a2b4be527fb91bb83f873bd14718407fa58b8790d2198f61bc6f5 +# input_hash: 0c167b26e12c284b769bf4d76bd3e604db266ed21c8f9e11e4bb737419ccdc93 @EXPLICIT https://conda.anaconda.org/conda-forge/noarch/cuda-version-11.8-h70ddcb2_3.conda#670f0e1593b8c1d84f57ad5fe5256799 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda#ad8527bf134a90e1c9ed35fa0b64318c +https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-4.18.0-he073ed8_8.conda#ff007ab0f0fdc53d245972bba8a6d40c https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-headers-1.18.0-ha770c72_1.conda#4fb055f57404920a43b147031471e03b https://conda.anaconda.org/conda-forge/linux-64/nlohmann_json-3.12.0-h3f2d84a_0.conda#d76872d096d063e226482c99337209dc -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-7_cp313.conda#e84b44e6300f1703cb25d29120c5b1d8 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda#94305520c52a4aa3f6c2b1ff6008d9f8 https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_4.conda#01f8d123c96816249efd255a31ad7712 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1423503_1.conda#0be7c6e070c19105f966d3758448d018 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda#434ca7e50e40f4918ab701e3facd59a0 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-20.1.4-h024ca30_0.conda#4fc395cda27912a7d904b86b5dbf3a4d -https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h0157908_18.conda#460eba7851277ec1fd80a1a24080787a +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-20.1.8-h4922eb0_0.conda#dda42855e1d9a0b59e071e28a820d0f5 +https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.28-h4ee821c_8.conda#1bad93f0aa428d618875ef3a588a889e https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-3_kmp_llvm.conda#ee5c2118262e30b972bc0b4db8ef0ba5 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_2.conda#c151d5eb730e9b7480e6d48c0fc44048 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda#7df50d44d4a14d6c31a2c54f2cd92157 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h767d61c_2.conda#ef504d1acbd74b7cc6849ef8af47dd03 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.1.0-h767d61c_3.conda#9e60c55e725c20d23125a5f0dd69af5d https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.14-hb9d3cd8_0.conda#76df83c2a9035c54df5d04ff81bcc02d https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.0-hb9d3cd8_0.conda#f65c946f28f0518f41ced702f44c52b7 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.5-hb9d3cd8_0.conda#f7f0d6cc2dc986d42ac2689ec88192be -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_2.conda#41b599ed2b02abcfdd84302bff174b23 -https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.23-h86f0d12_0.conda#27fe770decaf469a53f3e3a6d593067f -https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.0-h5888daf_0.conda#db0bfbe7dd197b68ad5f30333bae6ce0 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.1.0-hb9d3cd8_3.conda#cb98af5db26e3f482bebb80ce9d947d3 +https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.24-h86f0d12_0.conda#64f0c503da58ec25ebd359e4d990afa8 +https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda#4211416ecba1866fab0c6470986c22d6 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.6-h2dba641_1.conda#ede4673863426c0883c0063d853bbd85 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_2.conda#a2222a6ada71fb478682efe483ce0f92 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hf1ad2bd_2.conda#556a4fdfac7287d349b8f09aba899693 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.1.0-h69a702a_3.conda#e66f2b8ad787e7beb0f846e4bd7e8493 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.1.0-hcea5267_3.conda#530566b68c3b8ce7eec4cd047eae19fe https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h4ce23a2_1.conda#e796ff8ddc598affdf7c173d6145f087 https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.0-hb9d3cd8_0.conda#9fa334557db9f63da6c9285fd2a48638 -https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_0.conda#0e87378639676987af32fee53ba32258 +https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda#1a580f7796c7bf6393fddb8bbbde58dc +https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda#c7e925f37e3b40d893459e625f6a53f1 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda#7c7927b404672409d9917d49bff5f2d6 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-h8f9b012_2.conda#a78c856b6dc6bf4ea8daeb9beaaa3fb0 -https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.10.0-h4c51ac1_0.conda#aeccfff2806ae38430638ffbb4be9610 -https://conda.anaconda.org/conda-forge/linux-64/libuv-1.50.0-hb9d3cd8_0.conda#771ee65e13bc599b0b62af5359d80169 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.5.0-h851e524_0.conda#63f790534398730f59e1b899c3644d4a +https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda#70e3400cbbfa03e96dcde7fc13e38c7b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.1.0-h8f9b012_3.conda#6d11a5edae89fe413c0569f16d308f5a +https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.10.0-h202a827_0.conda#0f98f3e95272d118f7931b6bef69bfe5 +https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb9d3cd8_0.conda#1349c022c92c5efd3fd705a79a5804d8 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda#aea31d2e5b1091feca96fcfe945c3cf9 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda#47e340acb35de30501a76c7c799c41d7 -https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.0-h7b32b05_1.conda#de356753cfdbffcde5bb1e86e3aa6cd0 +https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.1-h7b32b05_0.conda#c87df2ab1448ba69169652ab9547082d https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda#fb901ff28063514abb6046c9ec2c4a45 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb9d3cd8_0.conda#f6ebe2cb3f82ba6c057dde5d9debe4f7 @@ -53,61 +55,55 @@ https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.3-h3870646_2. https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.3-h3870646_2.conda#303d9e83e0518f1dcb66e90054635ca6 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h4bc722e_7.conda#62ee74e96c5ebb0af99386de58cf9553 https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.3.1-h5888daf_0.conda#bfd56492d8346d669010eccafe0ba058 -https://conda.anaconda.org/conda-forge/linux-64/expat-2.7.0-h5888daf_0.conda#d6845ae4dea52a2f90178bf1829a21f8 https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 +https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-h5888daf_0.conda#951ff8d9e5536896408e89d63230b8d5 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda#9344155d33912347b37f0ae6c410a835 https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240722.0-cxx17_hbbce691_4.conda#488f260ccda0afaf08acb286db439c2f -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_2.conda#9566f0bd264fbd463002e759b8a82401 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.conda#06f70867945ea6a84d35836af780f1de +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.1.0-hb9d3cd8_3.conda#1c6eecffad553bde44c5238770cfb7da +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_3.conda#3facafe58f3858eb95527c7d3a3fc578 +https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.125-hb9d3cd8_0.conda#4c0ab57463117fbb8df85268415082f5 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda#c277e0a4d549b03ac1e9d6cbbe3d017b https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda#172bf1cd1ff8629f2b1179945ed45055 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_2.conda#fb54c4ea68b460c278d26eea89cfbcc3 -https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-h4bc722e_0.conda#aeb98fdeb2e8f25d43ef71fbacbeec80 -https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hd590300_0.conda#48f4330bfcd959c3cfb704d424903c82 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.47-h943b412_0.conda#55199e2ae2c3651f6f9b2a447b47bdc9 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.49.1-hee588c1_2.conda#962d6ac93c30b1dfc54c9cccafd1003e +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.1.0-h69a702a_3.conda#bfbca721fd33188ef923dfe9ba172f29 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.50-h943b412_0.conda#51de14db340a848869e69c632b43cca7 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda#eecce068c7e4eddeb169591baac20ac4 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_2.conda#c75da67f045c2627f59e6fcb5f4e3a9b +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.1.0-h4852527_3.conda#57541755b5a51691955012b8e197c06c https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda#92ed62436b625154323d40d5f2f11dd7 +https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda#5aa797f8787fe7a17d1b0821485b5adc https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda#9de5350a85c4a20c685259b889aa6393 -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.2.0-h266115a_0.conda#db22a0962c953e81a2a679ecb1fc6027 -https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-hff21bea_1.conda#2322531904f27501ee19847b87ba7c64 -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.0-h29eaf8c_0.conda#d2f1c87d4416d1e7344cf92b1aaee1c4 +https://conda.anaconda.org/conda-forge/linux-64/ninja-1.13.1-h171cf75_0.conda#6567fa1d9ca189076d9443a0b125541c +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.2-h29eaf8c_0.conda#39b4228a867772d610c02e06f939a5b8 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda#283b96675859b20a825f8fa30f311446 https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.14-h6c98b2b_0.conda#efab4ad81ba5731b2fefa0ab4359e884 https://conda.anaconda.org/conda-forge/linux-64/sleef-3.8-h1b44611_0.conda#aec4dba5d4c2924730088753f6fa164b https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-h8bd8927_1.conda#3b3e64af585eadfb52bb90b553db5edf -https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h4845f30_101.conda#d453b98d9c83e71da0741bb0ff4d76bc -https://conda.anaconda.org/conda-forge/linux-64/wayland-1.23.1-h3e06ad9_1.conda#a37843723437ba75f42c9270ffe800b1 +https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda#a0116df4f4ed05c303811a837d5b39d8 +https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-h3e06ad9_0.conda#0f2ca7906bf166247d1d760c3422cb8a https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda#6432cb5d4ac0046c3ac0a8a0f95842f9 https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.17.0-h3dad3f2_6.conda#3a127d28266cdc0da93384d1f59fe8df -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_2.conda#c63b5e52939e795ba8d26e35d767a843 +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.1.0-hb9d3cd8_3.conda#58178ef8ba927229fba6d84abf62c108 https://conda.anaconda.org/conda-forge/linux-64/cudatoolkit-11.8.0-h4ba93d1_13.conda#eb43f5f1f16e2fad2eba22219c3e499b https://conda.anaconda.org/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda#ff862eebdfeb2fd048ae9dc92510baca https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda#c94a5994ef49749880a8139cf9afcbe1 -https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda#f87c7b7c2cb45f323ffbce941c78ab7c https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b189310083baabfb622af68fd9d3ae3 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2#c965a5aa0d5c1c37ffc62dff36e28400 -https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.124-hb9d3cd8_0.conda#8bc89311041d7fcb510238cf0848ccae https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.13.3-h48d6fc4_1.conda#3c255be50a506c50765a93a6644f32fe -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_2.conda#4056c857af1a99ee50589a941059ec55 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.1.0-h69a702a_3.conda#6e5d0574e57a38c36e674e9a18eee2b4 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda#19e57602824042dfd0446292ef90488b -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.29-pthreads_h94d23a6_0.conda#0a4d0252248ef9a0f88f2ba8b8a08e12 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_0.conda#323dc8f259224d13078aaf7ce96c3efe https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.3-h6128344_1.conda#d8703f1ffe5a06356f06467f1d0b9464 https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_2.conda#b2fede24428726dd867611664fb372e8 https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda#dcb95c0a98ba9ff737f7ae482aef7833 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hd9ff511_4.conda#6c1028898cf3a2032d9af46689e1b81a -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.2.0-he0572af_0.conda#93340b072c393d23c4700a1d40565dca -https://conda.anaconda.org/conda-forge/linux-64/nccl-2.26.5.1-h03a54cd_0.conda#47dc81d35df91d38609df9c93d608b2b -https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hc749103_2.conda#31614c73d7b103ef76faa4d83d261d34 -https://conda.anaconda.org/conda-forge/linux-64/python-3.13.3-hf636f53_101_cp313.conda#10622e12d649154af0bd76bcf33a7c5c +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-hf01ce69_5.conda#e79a094918988bb1807462cd42c83962 +https://conda.anaconda.org/conda-forge/linux-64/nccl-2.27.3.1-h03a54cd_0.conda#616e835be8126fab0bf4cec1f40cc4ea +https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.45-hc749103_0.conda#b90bece58b4c2bf25969b70f3be42d25 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 -https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-hb711507_2.conda#8637c3e5821654d0edf97e2b0404b443 +https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda#fdc27cb255a7a2cc73b7919a968b48f0 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda#0e0cbe0564d03a99afd5fd7b362feecd https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda#608e0ef8256b81d04456e8d211eee3e8 @@ -115,54 +111,26 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.cond https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.12-h4f16b4b_0.conda#db038ce880f100acc74dba10302b5630 https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.4-h04a3f94_2.conda#81096a80f03fc2f0fb2a230f5d028643 https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.4-hb9b18c6_4.conda#773c99d0dbe2b3704af165f97ff399e5 -https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 -https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.3-py313hd8ed1ab_101.conda#904a822cbd380adafb9070debf8579a8 -https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.8.0.87-hf36481c_1.conda#988b6d0f8a2660fdee429d3d0f761ed3 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.27-h54b06d7_7.conda#dce22f70b4e5a407ce88f2be046f4ceb -https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.12-py313h5dec8f5_0.conda#24a42a0c1cc33743e33572d63d489b54 -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e -https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 -https://conda.anaconda.org/conda-forge/linux-64/fastrlock-0.8.3-py313h9800cb9_1.conda#54dd71b3be2ed6ccc50f180347c901db -https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda#4547b39256e296bb758166893e909a7c -https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.3.2-pyhd8ed1ab_0.conda#9c40692c3d24c7aaf335f673ac09d308 -https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py313h33d0bda_0.conda#9862d13a5e466273d5a4738cffcb8d6c +https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_3.conda#5d08a0ac29e6a5a984817584775d4131 +https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda#cae723309a49399d2949362f4ab5c9e4 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.17-h717163a_0.conda#000e85703f0fd9594c81710dd5066471 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-31_h59b9bed_openblas.conda#728dbebd0f7a20337218beacffd37916 -https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 -https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.13.0-h332b0f4_0.conda#cbdc92ac0d93fe3c796e36ad65c7905c +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-32_h59b9bed_openblas.conda#2af9f3d5c2e39f417ce040f5a35c40c6 +https://conda.anaconda.org/conda-forge/linux-64/libcudnn-9.10.1.4-h7d33bf5_0.conda#93fe78190bc6fe40d5e7a737c8065286 +https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda#d4a250da4737ee127fb1fa6452a9002e +https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.14.1-h332b0f4_0.conda#45f6713cb00f124af300342512219182 https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.13.3-ha770c72_1.conda#51f5be229d83ecd401fb369ab96ae669 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.1-h2ff4ddf_0.conda#0305434da649d4fb48a425e588b79ea6 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.84.2-h3618099_0.conda#072ab14a02164b7c0c089055368ff776 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda#c8013e438185f33b13814c5c488acd5c https://conda.anaconda.org/conda-forge/linux-64/libhiredis-1.0.2-h2cc385e_0.tar.bz2#b34907d3a81a3cd8095ee83d174c074a -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.7-h4bc477f_1.conda#ad1f1f8238834cd3c88ceeaee8da444a -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py313h8060acc_1.conda#21b62c55924f01b6eef6827167b46acb -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.3-hee844dc_0.conda#4fe4c3b7ce84cda6508b6d78f0ce72e3 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.8-h4bc477f_0.conda#14dbe05b929e329dbaa6f2d0aa19466d https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda#2eeb50cab6652538eee8fc0bc3340c81 -https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda#3585aa87c43ab15b167b574cd73b057b -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyh267e887_2.conda#fd40bf7f7f4bc4b647dc8512053d9873 -https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.29-pthreads_h6ec200e_0.conda#7e4d48870b3258bea920d51b7f495a81 +https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.30-pthreads_h6ec200e_0.conda#15fa8c1f683e68ff08ef0ea106012add https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.3-h5fbd93e_0.conda#9e5816bc95d285c115a3ebc2f8563564 https://conda.anaconda.org/conda-forge/linux-64/orc-2.1.1-h2271f48_0.conda#67075ef2cb33079efee3abfe58127a3b -https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 -https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 -https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h9925aae_2.conda#e84ddf12bde691e8ec894b00ea829ddf -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 -https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 -https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f -https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 -https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.2-py313h536fd9c_0.conda#5f5cbdd527d2e74e270d8b6255ba714f -https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.13.2-pyh29332c3_0.conda#83fc6ae00127671e301c9f44254c31b8 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 -https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.44-hb9d3cd8_0.conda#7c91bfc90672888259675ad2ad28af9c +https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.45-hb9d3cd8_0.conda#397a013c2dc5145a70737871aaa87e98 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda#febbab7d15033c913d53c7a2c102309d https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.1-hb9d3cd8_0.conda#4bdb303603e9821baf5fe5fdff1dc8f8 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda#96d57aba173e878a2089d5638016dc5e @@ -170,27 +138,21 @@ https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.6-hd08a7f5_4.cond https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.12.2-h108da3e_2.conda#90e07c8bac8da6378ee1882ef0a9374a https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.14.0-h5cfcd09_0.conda#0a8838771cc2e985cd295e01ae83baf1 https://conda.anaconda.org/conda-forge/linux-64/ccache-4.11.3-h80c52d3_0.conda#eb517c6a2b960c3ccb6f1db1005f063a -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.8.0-py313h8060acc_0.conda#375064d30e709bf7c1d4580e70aaea61 -https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.57.0-py313h8060acc_0.conda#76b3a3367ac578a7cc43f4b7814e7e87 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h3c4dab8_0.conda#679616eb5ad4e521c83da4650860aba7 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.13.3-ha770c72_1.conda#9ccd736d31e0c6e41f54e704e5312811 -https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-31_he106b2a_openblas.conda#abb32c727da370c481a1c206f5159ce9 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-32_he106b2a_openblas.conda#3d3f9355e52f269cd8bc2c440d8a5263 +https://conda.anaconda.org/conda-forge/linux-64/libcudnn-dev-9.10.1.4-h0fdc2d1_0.conda#a0c0b44d26a4710e6ea577fcddbe09d1 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda#928b8be80851f5d8ffb016f9c81dae7a https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-h25350d4_2.conda#bfcedaf5f9b003029cc6abe9431f66bf -https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.2-default_h0d58e46_1001.conda#804ca9e91bcaea0824a341d55b1684f2 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-31_h7ac8fdf_openblas.conda#452b98eafe050ecff932f0ec832dd03f -https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.4-he9d0ab4_0.conda#96c33bbd084ef2b2463503fb7f1482ae -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.9.1-h65c71a3_0.conda#6e45090fe0eec179ecc8041a3a3fc9f8 +https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.2-default_h3d81e11_1002.conda#56aacccb6356b6b6134a79cdf5688506 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-32_h7ac8fdf_openblas.conda#6c3f04ccb6c578138e9f9899da0bd714 +https://conda.anaconda.org/conda-forge/linux-64/libllvm20-20.1.8-hecd9e04_0.conda#59a7b967b6ef5d63029b1712f8dcf661 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.10.0-h65c71a3_0.conda#fedf6bfe5d21d21d2b1785ec00a8889a https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda#aa14b9a5196a6d8dd364164b7ce56acf -https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.9-he970967_0.conda#ca2de8bbdc871bce41dbf59e51324165 +https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda#2e5bf4f1da39c0b32778561c3c4e5878 https://conda.anaconda.org/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda#a83f6a2fdc079e643237887a37460668 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e -https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.3-h4df99d1_101.conda#82c2641f2f0f513f7d2d1b847a2588e3 +https://conda.anaconda.org/conda-forge/linux-64/python-3.13.5-hec9711d_102_cp313.conda#89e07d92cf50743886f41638d58c4328 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.5-hb9d3cd8_0.conda#eb44b3b6deb1cab08d72cb61686fe64c https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda#d3c295b50f092ab525ffe3c2aa4b7413 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda#2ccd714aa2242315acaf0a67faea780b @@ -198,57 +160,97 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda#17dcc85db3c7886650b8908b183d6876 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda#2de7f99d6581a4a7adbff607b5c278ca https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.6-hb9d3cd8_0.conda#5efa5fa6243a622445fdfd72aee15efa -https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.13-h822ba82_2.conda#9cf2c3c13468f2209ee814be2c88655f https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.10.0-h113e628_0.conda#73f73f60854f325a55f1d31459f2ab73 https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.8.0-h736e048_1.conda#13de36be8de3ae3f05ba127631599213 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 +https://conda.anaconda.org/conda-forge/noarch/cpython-3.13.5-py313hd8ed1ab_102.conda#0401f31e3c9e48cebf215472aa3e7104 +https://conda.anaconda.org/conda-forge/linux-64/cudnn-9.10.1.4-haad7af6_0.conda#8382d957333e0d3280dcbf5691516dc1 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 +https://conda.anaconda.org/conda-forge/linux-64/cython-3.1.2-py313h5dec8f5_2.conda#790ba9e115dfa69fde25212a51fe3d30 +https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 +https://conda.anaconda.org/conda-forge/linux-64/fastrlock-0.8.3-py313h9800cb9_1.conda#54dd71b3be2ed6ccc50f180347c901db +https://conda.anaconda.org/conda-forge/noarch/filelock-3.18.0-pyhd8ed1ab_0.conda#4547b39256e296bb758166893e909a7c https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee +https://conda.anaconda.org/conda-forge/noarch/fsspec-2025.7.0-pyhd8ed1ab_0.conda#a31ce802cd0ebfce298f342c02757019 https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.2.1-py313h11186cd_0.conda#54d020e0eaacf1e99bfb2410b9aa2e5e -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.4-default_h1df26ce_0.conda#96f8d5b2e94c9ba4fef19f1adf068a15 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.4-default_he06ed0a_0.conda#2d933632c8004be47deb2be61bf013be +https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.8-py313h33d0bda_1.conda#6d8d806d9db877ace75ca67aa572bf84 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp20.1-20.1.8-default_hddf928d_0.conda#b939740734ad5a8e8f6c942374dee68d +https://conda.anaconda.org/conda-forge/linux-64/libclang13-20.1.8-default_ha444ac7_0.conda#783f9cdcb0255ed00e3f1be22e16de40 https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.36.0-h2b5623c_0.conda#c96ca58ad3352a964bfcb85de6cd1496 -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-31_he2f377e_openblas.conda#7e5fff7d0db69be3a266f7e79a3bb0e2 +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-32_he2f377e_openblas.conda#54e7f7896d0dbf56665bcb0078bfa9d2 https://conda.anaconda.org/conda-forge/linux-64/libmagma-2.9.0-h45b15fe_0.conda#703a1ab01e36111d8bb40bc7517e900b https://conda.anaconda.org/conda-forge/linux-64/libopentelemetry-cpp-1.18.0-hfcad708_1.conda#1f5a5d66e77a39dc5bd639ec953705cf -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.4-h27ae623_1.conda#37fba334855ef3b51549308e61ed7a3d -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.5-py313h17eae1a_0.conda#6ceeff9ed72e54e4a2f9a1c88f47bdde -https://conda.anaconda.org/conda-forge/linux-64/pillow-11.1.0-py313h8db990d_0.conda#1e86810c6c3fb6d6aebdba26564eb2e8 -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.1.1-pyhd8ed1ab_0.conda#1e35d8f975bc0e984a19819aa91c440a -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.5-h27ae623_0.conda#6458be24f09e1b034902ab44fe9de908 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py313h8060acc_1.conda#21b62c55924f01b6eef6827167b46acb +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_1.conda#3585aa87c43ab15b167b574cd73b057b +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/noarch/networkx-3.5-pyhe01879c_0.conda#16bff3d37a4f99e3aa089c36c2b8d650 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.2.6-py313h17eae1a_0.conda#7a2d2f9adecd86ed5c29c2115354f615 +https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.3.0-py313h8db990d_0.conda#114a74a6e184101112fdffd3a1cb5b8f +https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh145f28c_0.conda#01384ff1639c6330a0924791413b8714 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 +https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.2-pyhd8ed1ab_0.conda#88476ae6ebd24f39261e0854ac244f33 +https://conda.anaconda.org/conda-forge/noarch/pytz-2025.2-pyhd8ed1ab_0.conda#bc8e3267d44011051f2eb14d22fb0960 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e +https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.13.0-hceb3a55_1.conda#ba7726b8df7b9d34ea80e82b097a4893 +https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_1.conda#b0dd904de08b7db706167240bf37b164 +https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.1-py313h536fd9c_0.conda#e9434a5155db25c38ade26f71a2f5a48 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f -https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.3.1-pyhd8ed1ab_0.conda#11107d0aeb8c590a34fee0894909816b +https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.4-pyhe01879c_1.conda#61d4f8b95dac300a1b7f665bcc79653a https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.31.0-h55f77e1_4.conda#0627af705ed70681f5bede31e72348e5 https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.13.0-h3cf044e_1.conda#7eb66060455c7a47d9dcdbfa9f46579b -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-31_h1ea3ea9_openblas.conda#ba652ee0576396d4765e567f043c57f9 +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-32_h1ea3ea9_openblas.conda#34cb4b6753b38a62ae25f3a73efd16b0 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-h3394656_0.conda#09262e66b19567aff4f592fb53b28760 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.2-py313h33d0bda_0.conda#5dc81fffe102f63045225007a33d6199 -https://conda.anaconda.org/conda-forge/linux-64/cupy-core-13.4.1-py313hc2a895b_0.conda#46dd595e816b278b178e3bef8a6acf71 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.9.2-py313h8060acc_0.conda#5efd7abeadb3e88a6a219066682942de +https://conda.anaconda.org/conda-forge/linux-64/cupy-core-13.5.1-py313hc2a895b_1.conda#7930edc4011e8e228a315509ddf53d3f +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.59.0-py313h3dea7bd_0.conda#9ab0ef93a0904a39910d1835588e25cd +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhd8ed1ab_0.conda#446bd6c8cb26050d528881df495ce646 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.36.0-h0121fbd_0.conda#fc5efe1833a4d709953964037985bb72 https://conda.anaconda.org/conda-forge/linux-64/libmagma_sparse-2.9.0-h45b15fe_0.conda#beac0a5bbe0af75db6b16d3d8fd24f7e https://conda.anaconda.org/conda-forge/linux-64/mkl-2024.2.2-ha957f24_16.conda#1459379c79dda834673426504d52b319 -https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py313ha87cce1_3.conda#6248b529e537b1d4cb5ab3ef7f537795 -https://conda.anaconda.org/conda-forge/linux-64/polars-1.27.1-py39h2a4a510_3.conda#fba08963eaa1f954480045d033d1221e -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py313h86fcf2b_0.conda#ca68acd9febc86448eeed68d0c6c8643 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 +https://conda.anaconda.org/conda-forge/noarch/python-gil-3.13.5-h4df99d1_102.conda#2eabcede0db21acee23c181db58b4128 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.0-py313h86fcf2b_0.conda#8c60fe574a5abab59cd365d32e279872 https://conda.anaconda.org/conda-forge/noarch/sympy-1.14.0-pyh2585a3b_105.conda#8c09fac3785696e1c477156192d64b91 +https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda#aaa2a381ccc56eac91d63b6c1240312f https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.510-h37a5c72_3.conda#beb8577571033140c6897d257acc7724 https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.12.0-ha633028_1.conda#7c1980f89dd41b097549782121a73490 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.131-openblas.conda#38b2ec894c69bb4be0e66d2ef7fc60bf -https://conda.anaconda.org/conda-forge/linux-64/cupy-13.4.1-py313h66a2ee2_0.conda#784d6bd149ef2b5d9c733ea3dd4d15ad -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.1.0-h3beb420_0.conda#95e3bb97f9cdc251c0c68640e9c10ed3 +https://conda.anaconda.org/conda-forge/linux-64/blas-2.132-openblas.conda#9c4a27ab2463f9b1d9019e0a798a5b81 +https://conda.anaconda.org/conda-forge/linux-64/cupy-13.5.1-py313h66a2ee2_1.conda#f75aebc467badfd648a37dcafdf7a3b2 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-11.2.1-h3beb420_0.conda#0e6e192d4b3d95708ad192d957cf3163 https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.4.1-cuda118_mkl_hee7131c_306.conda#28b3b3da11973494ed0100aa50f47328 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.1-py313h129903b_0.conda#4e23b3fabf434b418e0d9c6975a6453f +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.3-py313h129903b_0.conda#4f8816d006b1c155ec416bcf7ff6cee2 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.1-py313h08cd8bf_0.conda#0b23bc9b44d838b88f3ec8ab780113f1 https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py313hf0ab243_1.conda#4c769bf3858f424cb2ecf952175ec600 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 https://conda.anaconda.org/conda-forge/linux-64/libarrow-19.0.1-hc7b3859_3_cpu.conda#9ed3ded6da29dec8417f2e1db68798f2 +https://conda.anaconda.org/conda-forge/linux-64/polars-default-1.31.0-py39hf521cc8_1.conda#85f9f61975ba5a8f3d40b477aef457cb +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.2.1-pyhd8ed1ab_0.conda#ce978e1b9ed8b8d49164e90a5cdc94cd +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.4.1-cuda118_mkl_py313_h909c4c2_306.conda#de6e45613bbdb51127e9ff483c31bf41 -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.0-h6441bc3_1.conda#4029a8dcb1d97ea241dbe5abfda1fad6 +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.9.1-h0384650_1.conda#3610aa92d2de36047886f30e99342f21 https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-19.0.1-hcb10f89_3_cpu.conda#8f8dc214d89e06933f1bc1dcd2310b9c https://conda.anaconda.org/conda-forge/linux-64/libparquet-19.0.1-h081d1f1_3_cpu.conda#1d04307cdb1d8aeb5f55b047d5d403ea +https://conda.anaconda.org/conda-forge/linux-64/polars-1.31.0-default_h70f2ef1_1.conda#0217d9e4176cf33942996a7ee3afac0e https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-19.0.1-py313he5f92c8_0_cpu.conda#7d8649531c807b24295c8f9a0a396a78 -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.0-py313h5f61773_0.conda#f51f25ec8fcbf777f8b186bb5deeed40 +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.9.1-py313h7dabd7a_0.conda#42a24d0f4fe3a2e8307de3838e162452 https://conda.anaconda.org/conda-forge/linux-64/pytorch-gpu-2.4.1-cuda118_mkl_hf8a3b2d_306.conda#b1802a39f1ca7ebed5f8c35755bffec1 https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-19.0.1-hcb10f89_3_cpu.conda#a28f04b6e68a1c76de76783108ad729d -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.1-py313h78bf25f_0.conda#d0c80dea550ca97fc0710b2ecef919ba +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.3-py313h78bf25f_0.conda#cc9324e614a297fdf23439d887d3513d https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-19.0.1-h08228c5_3_cpu.conda#a58e4763af8293deaac77b63bc7804d8 https://conda.anaconda.org/conda-forge/linux-64/pyarrow-19.0.1-py313h78bf25f_0.conda#e8efe6998a383dd149787c83d3d6a92e diff --git a/build_tools/github/pymin_conda_forge_arm_linux-aarch64_conda.lock b/build_tools/github/pymin_conda_forge_arm_linux-aarch64_conda.lock index 5f7bedbbfeaa8..2ef258d506c3c 100644 --- a/build_tools/github/pymin_conda_forge_arm_linux-aarch64_conda.lock +++ b/build_tools/github/pymin_conda_forge_arm_linux-aarch64_conda.lock @@ -1,138 +1,137 @@ # Generated by conda-lock. # platform: linux-aarch64 -# input_hash: 9226800dfe446f7b9ed783525101a7cf60f0da339c6c1fc6db00ea557831de1d +# input_hash: f12646c755adbf5f02f95c5d07e868bf1570777923e737bc27273eb1a5e40cd7 @EXPLICIT https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda#49023d73832ef61042f6a237cb2687e7 -https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_4.conda#80c9ad5e05e91bb6c0967af3880c9742 +https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.44-h5e2c951_1.conda#c10832808cf155953061892b3656470a https://conda.anaconda.org/conda-forge/linux-aarch64/libglvnd-1.7.0-hd24410f_2.conda#9e115653741810778c9a915a2f8439e7 -https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-14.2.0-he277a41_2.conda#b11c09d9463daf4cae492d29806b1889 -https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-7_cp310.conda#44e871cba2b162368476a84b8d040b6c +https://conda.anaconda.org/conda-forge/linux-aarch64/libgomp-15.1.0-he277a41_3.conda#b79b8a69669f9ac6311f9ff2e6bffdf2 +https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda#05e00f3b21e88bb3d658ac700b2ce58c https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda#4222072737ccff51314b5ece9c7d6f5a https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_gnu.tar.bz2#6168d71addc746e8f2b8d57dfd2edcea -https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.4.26-hbd8a1cb_0.conda#95db94f75ba080a22eb623590993167b +https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.7.14-hbd8a1cb_0.conda#d16c90324aef024877d8713c0b7fea5b https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 https://conda.anaconda.org/conda-forge/linux-aarch64/libegl-1.7.0-hd24410f_2.conda#cf105bce884e4ef8c8ccdca9fe6695e7 https://conda.anaconda.org/conda-forge/linux-aarch64/libopengl-1.7.0-hd24410f_2.conda#cf9d12bfab305e48d095a4c79002c922 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-14.2.0-he277a41_2.conda#6b4268a60b10f29257b51b9b67ff8d76 +https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-15.1.0-he277a41_3.conda#409b902521be20c2efb69d2e0c5e3bc8 https://conda.anaconda.org/conda-forge/linux-aarch64/alsa-lib-1.2.14-h86ecc28_0.conda#a696b24c1b473ecc4774bcb5a6ac6337 -https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_2.conda#3ee026955c688f551a9999840cff4c67 -https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.23-he377734_0.conda#308ad7cbe9fd92add59ef3d547a42c17 -https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.0-h5ad3122_0.conda#d41a057e7968705dae8dcb7c8ba2c8dd +https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlicommon-1.1.0-h86ecc28_3.conda#76295055ce278970227759bdf3490827 +https://conda.anaconda.org/conda-forge/linux-aarch64/libdeflate-1.24-he377734_0.conda#f0b3d6494663b3385bf87fc206d7451a +https://conda.anaconda.org/conda-forge/linux-aarch64/libexpat-2.7.1-hfae3067_0.conda#f75d19f3755461db2eb69401f5514f4c https://conda.anaconda.org/conda-forge/linux-aarch64/libffi-3.4.6-he21f813_1.conda#15a131f30cae36e9a655ca81fee9a285 -https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-14.2.0-he9431aa_2.conda#692c2bb75f32cfafb6799cf6d1c5d0e0 -https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-14.2.0-hb6113d0_2.conda#cd754566661513808ef2408c4ab99a2f +https://conda.anaconda.org/conda-forge/linux-aarch64/libgcc-ng-15.1.0-he9431aa_3.conda#831062d3b6a4cdfdde1015be90016102 +https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran5-15.1.0-hbc25352_3.conda#eb1421397fe5db5ad4c3f8d611dd5117 https://conda.anaconda.org/conda-forge/linux-aarch64/libiconv-1.18-hc99b53d_1.conda#81541d85a45fbf4d0a29346176f1f21c https://conda.anaconda.org/conda-forge/linux-aarch64/libjpeg-turbo-3.1.0-h86ecc28_0.conda#a689388210d502364b79e8b19e7fa2cb -https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_0.conda#775d36ea4e469b0c049a6f2cd6253d82 -https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-14.2.0-h3f4de04_2.conda#eadee2cda99697e29411c1013c187b92 -https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.5.0-h0886dbf_0.conda#95ef4a689b8cc1b7e18b53784d88f96b +https://conda.anaconda.org/conda-forge/linux-aarch64/liblzma-5.8.1-h86ecc28_2.conda#7d362346a479256857ab338588190da0 +https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h86ecc28_1.conda#d5d58b2dc3e57073fe22303f5fed4db7 +https://conda.anaconda.org/conda-forge/linux-aarch64/libpciaccess-0.18-h86ecc28_0.conda#5044e160c5306968d956c2a0a2a440d6 +https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-15.1.0-h3f4de04_3.conda#4e2d5a407e0ecfe493d8b2a65a437bd8 +https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.6.0-ha2e29f5_0.conda#24e92d0942c799db387f5c9d7b81f1af https://conda.anaconda.org/conda-forge/linux-aarch64/libzlib-1.3.1-h86ecc28_2.conda#08aad7cbe9f5a6b460d0976076b6ae64 https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-ha32ae93_3.conda#182afabe009dc78d8b73100255ee6868 -https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.0-hd08dc88_1.conda#ee68fdc3a8723e9c58bdd2f10544658f +https://conda.anaconda.org/conda-forge/linux-aarch64/openssl-3.5.1-hd08dc88_0.conda#cf2dfe9c774c20e65d42d87147903bdb https://conda.anaconda.org/conda-forge/linux-aarch64/pthread-stubs-0.4-h86ecc28_1002.conda#bb5a90c93e3bac3d5690acf76b4a6386 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libice-1.1.2-h86ecc28_0.conda#c8d8ec3e00cd0fd8a231789b91a7c5b7 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxau-1.0.12-h86ecc28_0.conda#d5397424399a66d33c80b1f2345a36a6 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxdmcp-1.1.5-h57736b2_0.conda#25a5a7b797fe6e084e04ffe2db02fc62 https://conda.anaconda.org/conda-forge/linux-aarch64/bzip2-1.0.8-h68df207_7.conda#56398c28220513b9ea13d7b450acfb20 https://conda.anaconda.org/conda-forge/linux-aarch64/double-conversion-3.3.1-h5ad3122_0.conda#399959d889e1a73fc99f12ce480e77e1 -https://conda.anaconda.org/conda-forge/linux-aarch64/expat-2.7.0-h5ad3122_0.conda#c22e14e241ade3d3a74c0409c3d582a2 +https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.14-h5ad3122_0.conda#087ecf989fc23fc50944a06fddf5f3bc https://conda.anaconda.org/conda-forge/linux-aarch64/keyutils-1.6.1-h4e544f5_0.tar.bz2#1f24853e59c68892452ef94ddd8afd4b https://conda.anaconda.org/conda-forge/linux-aarch64/lerc-4.0.0-hfdc4d58_1.conda#60dceb7e876f4d74a9cbd42bbbc6b9cf -https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_2.conda#e64d0f3b59c7c4047446b97a8624a72d -https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_2.conda#0e9bd365480c72b25c71a448257b537d +https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlidec-1.1.0-h86ecc28_3.conda#3a4b4fc0864a4dc0f4012ac1abe069a9 +https://conda.anaconda.org/conda-forge/linux-aarch64/libbrotlienc-1.1.0-h86ecc28_3.conda#2b8199de1016a56c49bfced37c7f0882 +https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.125-h86ecc28_0.conda#c5e4a8dad08e393b3616651e963304e5 https://conda.anaconda.org/conda-forge/linux-aarch64/libedit-3.1.20250104-pl5321h976ea20_0.conda#fb640d776fc92b682a14e001980825b1 -https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-14.2.0-he9431aa_2.conda#d8b9d9dc0c8cd97d375b48e55947ba70 -https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.conda#c14f32510f694e3185704d89967ec422 +https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-15.1.0-he9431aa_3.conda#2987b138ed84460e6898daab172e9798 https://conda.anaconda.org/conda-forge/linux-aarch64/libntlm-1.4-hf897c2e_1002.tar.bz2#835c7c4137821de5c309f4266a51ba89 -https://conda.anaconda.org/conda-forge/linux-aarch64/libpciaccess-0.18-h31becfc_0.conda#6d48179630f00e8c9ad9e30879ce1e54 -https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.47-hec79eb8_0.conda#c4b1ba0d7cef5002759d2f156722feee -https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.49.1-h5eb1b54_2.conda#7c45959e187fd3313f9f1734464baecc -https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_2.conda#c934c1fddad582fcc385b608eb06a70c +https://conda.anaconda.org/conda-forge/linux-aarch64/libpng-1.6.50-hec79eb8_0.conda#375b0e45424d5d77b8c572a5a1521b70 +https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.50.3-h022381a_0.conda#94498365d25343e93a7708add6ae86b0 +https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-15.1.0-hf1166c9_3.conda#f981af71cbd4c67c9e6acc7d4cc3f163 https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969 https://conda.anaconda.org/conda-forge/linux-aarch64/libxcb-1.17.0-h262b8f6_0.conda#cd14ee5cca2464a425b1dbfc24d90db2 https://conda.anaconda.org/conda-forge/linux-aarch64/libxcrypt-4.4.36-h31becfc_1.conda#b4df5d7d4b63579d081fd3a4cf99740e -https://conda.anaconda.org/conda-forge/linux-aarch64/mysql-common-9.2.0-h3f5c77f_0.conda#f9db1ad1a8897483edb3ac321d662e7b -https://conda.anaconda.org/conda-forge/linux-aarch64/ninja-1.12.1-h17cf362_1.conda#885414635e2a65ed06f284f6d569cdff -https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.46.0-h86a87f0_0.conda#1328d5bad76f7b31926ccd2a33e0d6ef +https://conda.anaconda.org/conda-forge/linux-aarch64/ninja-1.13.1-hdc560ac_0.conda#eff201e0dd7462df1f2a497cd0f1aa11 +https://conda.anaconda.org/conda-forge/linux-aarch64/pixman-0.46.2-h86a87f0_0.conda#019114cf59c0cce5a08f6661179a1d65 https://conda.anaconda.org/conda-forge/linux-aarch64/readline-8.2-h8382b9d_2.conda#c0f08fc2737967edde1a272d4bf41ed9 -https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc -https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.23.1-h698ed42_1.conda#229b00f81a229af79547a7e4776ccf6e +https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-noxft_h5688188_102.conda#2562c9bfd1de3f9c590f0fe53858d85c +https://conda.anaconda.org/conda-forge/linux-aarch64/wayland-1.24.0-h698ed42_0.conda#2a57237cee70cb13c402af1ef6f8e5f6 https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.7-hbcf94c1_2.conda#5be90c5a3e4b43c53e38f50a85e11527 -https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-bin-1.1.0-h86ecc28_2.conda#7d48b185fe1f722f8cda4539bb931f85 -https://conda.anaconda.org/conda-forge/linux-aarch64/graphite2-1.3.13-h2f0025b_1003.conda#f33009add6a08358bc12d114ceec1304 +https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-bin-1.1.0-h86ecc28_3.conda#e06eec5d869ddde3abbb8c9784425106 https://conda.anaconda.org/conda-forge/linux-aarch64/icu-75.1-hf9b3779_0.conda#268203e8b983fddb6412b36f2024e75c https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37 -https://conda.anaconda.org/conda-forge/linux-aarch64/libdrm-2.4.124-h86ecc28_0.conda#a8058bcb6b4fa195aaa20452437c7727 https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype6-2.13.3-he93130f_1.conda#51eae9012d75b8f7e4b0adfe61a83330 -https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-ng-14.2.0-he9431aa_2.conda#0980d7d931474a6a037ae66f1da4d2fe -https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.29-pthreads_h9d3fd7e_0.conda#a99e2bfcb1ad6362544c71281eb617e9 -https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h88f7998_4.conda#6edd78ac9bee9a972f25cb6e8c6e21ad -https://conda.anaconda.org/conda-forge/linux-aarch64/mysql-libs-9.2.0-h11569fd_0.conda#72f21962b1205535d810b82f8f0fa342 -https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.44-hf4ec17f_2.conda#ab9d0f9a3c9ce23e4fd2af4edc6fa245 -https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.10.17-h256493d_0_cpython.conda#c496213b6ede3c5a30ce1bf02bebf382 +https://conda.anaconda.org/conda-forge/linux-aarch64/libgfortran-ng-15.1.0-he9431aa_3.conda#f23422dc5b054e5ce5b29374c2d37057 +https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.30-pthreads_h9d3fd7e_0.conda#7c3670fbc19809070c27948efda30c4b +https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-h7c15681_5.conda#264a9aac20276b1784dac8c5f8d3704a +https://conda.anaconda.org/conda-forge/linux-aarch64/pcre2-10.45-hf4ec17f_0.conda#ad22a9a9497f7aedce73e0da53cd215f +https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.10.18-h256493d_0_cpython.conda#766640fd0208e1d277a26d3497cc4b63 https://conda.anaconda.org/conda-forge/linux-aarch64/qhull-2020.2-h70be974_5.conda#bb138086d938e2b64f5f364945793ebf -https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-0.4.1-h5c728e9_2.conda#b4cf8ba6cff9cdf1249bcfe1314222b0 +https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-0.4.1-hca56bd8_2.conda#159ffec8f7fab775669a538f0b29373a https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-keysyms-0.4.1-h5c728e9_0.conda#57ca8564599ddf8b633c4ea6afee6f3a https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-renderutil-0.3.10-h5c728e9_0.conda#7beeda4223c5484ef72d89fb66b7e8c1 https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-wm-0.4.2-h5c728e9_0.conda#f14dcda6894722e421da2b7dcffb0b78 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.6-h0808dbd_0.conda#2d1409c50882819cb1af2de82e2b7208 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.12-hca56bd8_0.conda#3df132f0048b9639bc091ef22937c111 -https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-1.1.0-h86ecc28_2.conda#5094acc34eb173f74205c0b55f0dd4a4 +https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-1.1.0-h86ecc28_3.conda#725908554f2bf8f68502bbade3ea3489 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda#962b9857ee8e7018c22f2776ffa0b2d7 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_1.conda#44600c4667a319d67dbe0681fc0bc833 -https://conda.anaconda.org/conda-forge/linux-aarch64/cyrus-sasl-2.1.27-hf6b2984_7.conda#7a85d417c8acd7a5215c082c5b9219e5 -https://conda.anaconda.org/conda-forge/linux-aarch64/cython-3.0.12-py310hc86cfe9_0.conda#4bd71650f315b643774841272d02911a -https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_1.conda#a16662747cdeb9abbac74d0057cc976e +https://conda.anaconda.org/conda-forge/linux-aarch64/cyrus-sasl-2.1.28-h6c5dea3_0.conda#b6d06b46e791add99cc39fbbc34530d5 +https://conda.anaconda.org/conda-forge/linux-aarch64/cython-3.1.2-py310hc86cfe9_2.conda#86a3ab2db622c5cb32d015c1645854a1 https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_1.conda#a71efeae2c160f6789900ba2631a2c90 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_1.conda#6837f3eff7dcea42ecd714ce1ac2b108 -https://conda.anaconda.org/conda-forge/linux-aarch64/kiwisolver-1.4.7-py310h5d7f10c_0.conda#b86d594bf17c9ad7a291593368ae8ba7 +https://conda.anaconda.org/conda-forge/linux-aarch64/kiwisolver-1.4.8-py310h5d7f10c_1.conda#7ff3753addbf5b590a51d01b238786bc https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.17-hc88f144_0.conda#b87b1abd2542cf65a00ad2e2461a3083 -https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-31_h1a9f1db_openblas.conda#48bd5bf15ccf3e409840be9caafc0ad5 -https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h405e4a8_4.conda#d42c670b0c96c1795fd859d5e0275a55 +https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-32_h1a9f1db_openblas.conda#833718ed1c0b597ce17e5f410bd9b017 +https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h5cdc715_5.conda#ac0333d338076ef19170938bbaf97582 https://conda.anaconda.org/conda-forge/linux-aarch64/libfreetype-2.13.3-h8af1aa0_1.conda#2d4a1c3dcabb80b4a56d5c34bdacea08 -https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.84.1-hc486b8e_0.conda#07cb059040220481ab9eda17cb86f644 +https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.84.2-hc022ef1_0.conda#51323eab8e9f049d001424828c4c25a4 https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-1.7.0-hd24410f_2.conda#1d4269e233636148696a67e2d30dad2a https://conda.anaconda.org/conda-forge/linux-aarch64/libhiredis-1.0.2-h05efe27_0.tar.bz2#a87f068744fd20334cd41489eb163bee -https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.7-he060846_1.conda#b461618b5dafbc95c6f9492043cd991a -https://conda.anaconda.org/conda-forge/noarch/meson-1.8.0-pyh29332c3_0.conda#8e25221b702272394b86b0f4d7217f77 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/linux-aarch64/openblas-0.3.29-pthreads_h3a8cbd8_0.conda#4ec5b6144709ced5e7933977675f61c6 +https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.8-he060846_0.conda#c73dfe6886cc8d39a09c357a36f91fb2 +https://conda.anaconda.org/conda-forge/noarch/meson-1.8.2-pyhe01879c_0.conda#f0e001c8de8d959926d98edf0458cb2d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda#37293a85a0f4f77bbd9cf7aaefc62609 +https://conda.anaconda.org/conda-forge/linux-aarch64/openblas-0.3.30-pthreads_h3a8cbd8_0.conda#17cd049c668bb66162801e95db37244c https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.3-h3f56577_0.conda#04231368e4af50d11184b50e14250993 https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda#58335b26c38bf4a20f399384c33cbcf9 -https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_1.conda#e9dcbce5f45f9ee500e728ae58b605b6 +https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhd8ed1ab_0.conda#7da7ccd349dbf6487a7778579d2bb971 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda#6b6ece66ebcae2d5f326c77ef2c5a066 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.3-pyhd8ed1ab_1.conda#513d3c262ee49b54a8fec85c5bc99764 -https://conda.anaconda.org/conda-forge/noarch/setuptools-80.1.0-pyhff2d567_0.conda#f6f72d0837c79eaec77661be43e8a691 +https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda#4de79c071274a53dcaf2a8c749d1499e https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhd8ed1ab_0.conda#a451d576819089b0d672f18768be0f65 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda#9d64911b31d57ca443e9f1e36b04385f https://conda.anaconda.org/conda-forge/noarch/tomli-2.2.1-pyhd8ed1ab_1.conda#ac944244f1fed2eb49bae07193ae8215 -https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.2-py310h78583b1_0.conda#68a2bd5dcbb6feac96dee39f4b49fe0f +https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.5.1-py310h78583b1_0.conda#e1e576b66cca7642b0a66310b675ea36 +https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.14.1-pyhe01879c_0.conda#e523f4f1e980ed7a4240d7e27e9ec81f https://conda.anaconda.org/conda-forge/linux-aarch64/unicodedata2-16.0.0-py310ha766c32_0.conda#2936ce19a675e162962f396c7b40b905 https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda#75cb7132eb58d97896e173ef12ac9986 https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-image-0.4.0-h5c728e9_2.conda#b82e5c78dbbfa931980e8bfe83bce913 -https://conda.anaconda.org/conda-forge/linux-aarch64/xkeyboard-config-2.44-h86ecc28_0.conda#4d91bf5ccb5b31be8e070fda2ed13c50 +https://conda.anaconda.org/conda-forge/linux-aarch64/xkeyboard-config-2.45-h86ecc28_0.conda#01251d1503a253e39be4fa9bcf447d63 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxext-1.3.6-h57736b2_0.conda#bd1e86dd8aa3afd78a4bfdb4ef918165 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxfixes-6.0.1-h57736b2_0.conda#78f8715c002cc66991d7c11e3cf66039 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrender-0.9.12-h86ecc28_0.conda#ae2c2dd0e2d38d249887727db2af960e https://conda.anaconda.org/conda-forge/linux-aarch64/ccache-4.11.3-h4889ad1_0.conda#e0b9e519da2bf0fb8c48381daf87a194 -https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.13.6-h12b9eeb_3.tar.bz2#f3d63805602166bac09386741e00935e -https://conda.anaconda.org/conda-forge/linux-aarch64/fonttools-4.57.0-py310heeae437_0.conda#548b750f1b3ec57d07b0014f8081e9c2 +https://conda.anaconda.org/conda-forge/linux-aarch64/dbus-1.16.2-heda779d_0.conda#9203b74bb1f3fa0d6f308094b3b44c1e +https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.0-pyhd8ed1ab_0.conda#72e42d28960d875c7654614f8b50939a +https://conda.anaconda.org/conda-forge/linux-aarch64/fonttools-4.59.0-py310h2d8da20_0.conda#5f93264842d77827a0dac712d0fd188e https://conda.anaconda.org/conda-forge/linux-aarch64/freetype-2.13.3-h8af1aa0_1.conda#71c4cbe1b384a8e7b56993394a435343 -https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.0-pyhd8ed1ab_0.conda#3d7257f0a61c9aa4ffa3e324a887416b -https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-31_hab92f65_openblas.conda#6b81dbae56a519f1ec2f25e0ee2f4334 +https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.1-pyhd8ed1ab_0.conda#fb1c14694de51a476ce8636d92b6f42c +https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-32_hab92f65_openblas.conda#2f02a3ea0960118a0a8d45cdd348b039 https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-1.7.0-hd24410f_2.conda#0d00176464ebb25af83d40736a2cd3bb -https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-31_h411afd4_openblas.conda#41dbff5eb805a75c120a7b7a1c744dc2 -https://conda.anaconda.org/conda-forge/linux-aarch64/libllvm20-20.1.4-h07bd352_0.conda#a83f31777ec098202198145883d86ffb -https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.9.1-hbab7b08_0.conda#49a02083d4ab2cda74584a64defb4b9d +https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-32_h411afd4_openblas.conda#8d143759d5a22e9975a996bd13eeb8f0 +https://conda.anaconda.org/conda-forge/linux-aarch64/libllvm20-20.1.8-h2b567e5_0.conda#b2ae284ba64d978316177c9ab68e3da5 +https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.10.0-hbab7b08_0.conda#36cd1db31e923c6068b7e0e6fce2cd7b https://conda.anaconda.org/conda-forge/linux-aarch64/libxslt-1.1.39-h1cc9640_0.conda#13e1d3f9188e85c6d59a98651aced002 -https://conda.anaconda.org/conda-forge/linux-aarch64/openldap-2.6.9-h30c48ee_0.conda#c07822a5de65ce9797b9afa257faa917 +https://conda.anaconda.org/conda-forge/linux-aarch64/openldap-2.6.10-h30c48ee_0.conda#48f31a61be512ec1929f4b4a9cedf4bd +https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.3.0-py310h34c99de_0.conda#91ea2cb93e2ac055f30b5a8e14cd6270 https://conda.anaconda.org/conda-forge/noarch/pip-25.1.1-pyh8b19718_0.conda#32d0781ace05105cc99af55d36cbec7c https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.1-pyhd8ed1ab_0.conda#22ae7c6ea81e0c8661ef32168dda929b -https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.5-pyhd8ed1ab_0.conda#c3c9316209dec74a705a36797970c6be -https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhff2d567_1.conda#5ba79d7c71f03c678c8ead841f347d6e +https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda#5b8d21249ff20967101ffa321cab24e8 https://conda.anaconda.org/conda-forge/linux-aarch64/xcb-util-cursor-0.1.5-h86ecc28_0.conda#d6bb2038d26fa118d5cbc2761116f3e5 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcomposite-0.4.6-h86ecc28_2.conda#86051eee0766c3542be24844a9c3cf36 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxcursor-1.2.3-h86ecc28_0.conda#f2054759c2203d12d0007005e1f1296d @@ -141,22 +140,22 @@ https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxi-1.8.2-h57736b2_0 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxrandr-1.5.4-h86ecc28_0.conda#dd3e74283a082381aa3860312e3c721e https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxxf86vm-1.1.6-h86ecc28_0.conda#d745faa2d7c15092652e40a22bb261ed https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.15.0-h8dda3cd_1.conda#112b71b6af28b47c624bcbeefeea685b -https://conda.anaconda.org/conda-forge/linux-aarch64/libclang-cpp20.1-20.1.4-default_h7d4303a_0.conda#d71665eccdb65183c72e149424ec3928 -https://conda.anaconda.org/conda-forge/linux-aarch64/libclang13-20.1.4-default_h9e36cb9_0.conda#6d587caa650694fa5f6d04fda1bcfee2 -https://conda.anaconda.org/conda-forge/linux-aarch64/liblapacke-3.9.0-31_hc659ca5_openblas.conda#256bb281d78e5b8927ff13a1cde9f6f5 -https://conda.anaconda.org/conda-forge/linux-aarch64/libpq-17.4-hf590da8_1.conda#10fdc78be541c9017e2144f86d092aa2 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_1.conda#7a02679229c6c2092571b4c025055440 -https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.2.5-py310h6e5608f_0.conda#5c521c566cbcf058769c613dee3a18d6 -https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.1.0-py310h34c99de_0.conda#c4fa80647a708505d65573c2353bc216 -https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_1.conda#59aad4fb37cabc0bacc73cf344612ddd +https://conda.anaconda.org/conda-forge/linux-aarch64/libclang-cpp20.1-20.1.8-default_hf07bfb7_0.conda#c7a64cd7dd2bf72956d2f3b1b1aa13a7 +https://conda.anaconda.org/conda-forge/linux-aarch64/libclang13-20.1.8-default_h173080d_0.conda#c9a9e8c0477f9c915f106fd6254b2a9c +https://conda.anaconda.org/conda-forge/linux-aarch64/liblapacke-3.9.0-32_hc659ca5_openblas.conda#1cd2cbdb80386aae8c584ab9f1175ca6 +https://conda.anaconda.org/conda-forge/linux-aarch64/libpq-17.5-hf590da8_0.conda#b5a01e5aa04651ccf5865c2d029affa3 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.18.0-pyh70fd9c4_0.conda#576c04b9d9f8e45285fb4d9452c26133 +https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.2.6-py310h6e5608f_0.conda#9e9f1f279eb02c41bda162a42861adc0 +https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.1-pyhd8ed1ab_0.conda#a49c2283f24696a7b30367b7346a0144 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxtst-1.2.5-h57736b2_3.conda#c05698071b5c8e0da82a282085845860 -https://conda.anaconda.org/conda-forge/linux-aarch64/blas-devel-3.9.0-31_h9678261_openblas.conda#a2cc143d7e25e52a915cb320e5b0d592 +https://conda.anaconda.org/conda-forge/linux-aarch64/blas-devel-3.9.0-32_h9678261_openblas.conda#9c18808e64a8557732e664eac92df74d https://conda.anaconda.org/conda-forge/linux-aarch64/cairo-1.18.4-h83712da_0.conda#cd55953a67ec727db5dc32b167201aa6 https://conda.anaconda.org/conda-forge/linux-aarch64/contourpy-1.3.2-py310hf54e67a_0.conda#779694434d1f0a67c5260db76b7b7907 +https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda#8375cfbda7c57fbceeda18229be10417 https://conda.anaconda.org/conda-forge/linux-aarch64/scipy-1.15.2-py310hf37559f_0.conda#5c9b72f10d2118d943a5eaaf2f396891 -https://conda.anaconda.org/conda-forge/linux-aarch64/blas-2.131-openblas.conda#51c5f346e1ebee750f76066490059df9 -https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-11.1.0-h405b6a2_0.conda#6fd48c127b76a95ed3858c47fa9db7b0 -https://conda.anaconda.org/conda-forge/linux-aarch64/matplotlib-base-3.10.1-py310h2cc5e2d_0.conda#5652e355346f4823f6b4bfdd4860359d -https://conda.anaconda.org/conda-forge/linux-aarch64/qt6-main-6.9.0-ha483c8b_1.conda#fb32973c68de1f23a7e4de3651442b15 -https://conda.anaconda.org/conda-forge/linux-aarch64/pyside6-6.9.0-py310hee8ad4f_0.conda#68f556281ac23f1780381f00de99d66d -https://conda.anaconda.org/conda-forge/linux-aarch64/matplotlib-3.10.1-py310hbbe02a8_0.conda#c6aa0ea00ec104d0ad260c2ed2bb5582 +https://conda.anaconda.org/conda-forge/linux-aarch64/blas-2.132-openblas.conda#2c1e3662c8c5e7b92a49fd6372bb659f +https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-11.2.1-h405b6a2_0.conda#b55680fc90e9747dc858e7ceb0abc2b2 +https://conda.anaconda.org/conda-forge/linux-aarch64/matplotlib-base-3.10.3-py310h2cc5e2d_0.conda#e29f4329f4f76cf14f74ed86dcc59bac +https://conda.anaconda.org/conda-forge/linux-aarch64/qt6-main-6.9.1-h13135bf_1.conda#def3ca3fcfa60a6c954bdd8f5bb00cd2 +https://conda.anaconda.org/conda-forge/linux-aarch64/pyside6-6.9.1-py310hd3bda28_0.conda#1a105dc54d3cd250526c9d52379133c9 +https://conda.anaconda.org/conda-forge/linux-aarch64/matplotlib-3.10.3-py310hbbe02a8_0.conda#08982f6ac753e962d59160b08839221b diff --git a/build_tools/update_environments_and_lock_files.py b/build_tools/update_environments_and_lock_files.py index 0edf62b5a0d7b..b99e0e8f8d416 100644 --- a/build_tools/update_environments_and_lock_files.py +++ b/build_tools/update_environments_and_lock_files.py @@ -83,7 +83,11 @@ docstring_test_dependencies = ["sphinx", "numpydoc"] -default_package_constraints = {} +default_package_constraints = { + # TODO: remove once https://github.com/numpy/numpydoc/issues/638 is fixed + # and released. + "numpydoc": "<1.9.0", +} def remove_from(alist, to_remove): @@ -147,26 +151,16 @@ def remove_from(alist, to_remove): }, }, { - "name": "pylatest_conda_mkl_no_openmp", + "name": "pylatest_conda_forge_mkl_no_openmp", "type": "conda", "tag": "main-ci", "folder": "build_tools/azure", "platform": "osx-64", - "channels": ["defaults"], - "conda_dependencies": remove_from( - common_dependencies, ["cython", "threadpoolctl", "meson-python"] - ) - + ["ccache"], + "channels": ["conda-forge"], + "conda_dependencies": common_dependencies + ["ccache"], "package_constraints": { "blas": "[build=mkl]", - # scipy 1.12.x crashes on this platform (https://github.com/scipy/scipy/pull/20086) - # TODO: release scipy constraint when 1.13 is available in the "default" - # channel. - "scipy": "<1.12", }, - # TODO: put cython, threadpoolctl and meson-python back to conda - # dependencies when required version is available on the main channel - "pip_dependencies": ["cython", "threadpoolctl", "meson-python", "meson"], }, { "name": "pymin_conda_forge_openblas_min_dependencies", @@ -175,7 +169,7 @@ def remove_from(alist, to_remove): "folder": "build_tools/azure", "platform": "linux-64", "channels": ["conda-forge"], - "conda_dependencies": common_dependencies + ["ccache", "polars"], + "conda_dependencies": common_dependencies + ["ccache", "polars", "pyarrow"], "package_constraints": { "python": "3.10", "blas": "[build=openblas]", @@ -189,6 +183,7 @@ def remove_from(alist, to_remove): "pandas": "min", "polars": "min", "pyamg": "min", + "pyarrow": "min", }, }, { @@ -214,7 +209,7 @@ def remove_from(alist, to_remove): "tag": "main-ci", "folder": "build_tools/azure", "platform": "linux-64", - "channels": ["defaults"], + "channels": ["conda-forge"], "conda_dependencies": ["python", "ccache"], "pip_dependencies": ( remove_from(common_dependencies, ["python", "blas", "pip"]) @@ -233,7 +228,7 @@ def remove_from(alist, to_remove): "tag": "scipy-dev", "folder": "build_tools/azure", "platform": "linux-64", - "channels": ["defaults"], + "channels": ["conda-forge"], "conda_dependencies": ["python", "ccache"], "pip_dependencies": ( remove_from( @@ -271,11 +266,8 @@ def remove_from(alist, to_remove): "conda_dependencies": [ "python-freethreading", "numpy", - # TODO add cython and scipy when there are conda-forge packages for - # them and remove dev version install in - # build_tools/azure/install.sh. Note that for now conda-lock does - # not deal with free-threaded wheels correctly, see - # https://github.com/conda/conda-lock/issues/754. + "scipy", + "cython", "joblib", "threadpoolctl", "pytest", @@ -287,7 +279,7 @@ def remove_from(alist, to_remove): ], }, { - "name": "pymin_conda_forge_mkl", + "name": "pymin_conda_forge_openblas", "type": "conda", "tag": "main-ci", "folder": "build_tools/azure", @@ -300,7 +292,7 @@ def remove_from(alist, to_remove): ], "package_constraints": { "python": "3.10", - "blas": "[build=mkl]", + "blas": "[build=openblas]", }, }, { @@ -324,13 +316,13 @@ def remove_from(alist, to_remove): "plotly", "polars", "pooch", + "sphinxext-opengraph", "sphinx-remove-toctrees", "sphinx-design", "pydata-sphinx-theme", "towncrier", ], "pip_dependencies": [ - "sphinxext-opengraph", "sphinxcontrib-sass", ], "package_constraints": { @@ -384,10 +376,10 @@ def remove_from(alist, to_remove): "sphinx-design", "pydata-sphinx-theme", "towncrier", - ], - "pip_dependencies": [ "jupyterlite-sphinx", "jupyterlite-pyodide-kernel", + ], + "pip_dependencies": [ "sphinxcontrib-sass", ], "package_constraints": { diff --git a/build_tools/wheels/build_wheels.sh b/build_tools/wheels/build_wheels.sh index 02b05bc8a2795..f29747cdc509d 100755 --- a/build_tools/wheels/build_wheels.sh +++ b/build_tools/wheels/build_wheels.sh @@ -49,13 +49,6 @@ if [[ $(uname) == "Darwin" ]]; then export LDFLAGS="$LDFLAGS -Wl,-rpath,$PREFIX/lib -L$PREFIX/lib -lomp" fi -if [[ "$CIBW_FREE_THREADED_SUPPORT" =~ [tT]rue ]]; then - # Numpy, scipy, Cython only have free-threaded wheels on scientific-python-nightly-wheels - # TODO: remove this after CPython 3.13 is released (scheduled October 2024) - # and our dependencies have free-threaded wheels on PyPI - export CIBW_BUILD_FRONTEND='pip; args: --pre --extra-index-url "https://pypi.anaconda.org/scientific-python-nightly-wheels/simple" --only-binary :all:' -fi - # The version of the built dependencies are specified # in the pyproject.toml file, while the tests are run # against the most recent version of the dependencies diff --git a/build_tools/wheels/check_license.py b/build_tools/wheels/check_license.py index 00fe4169be65d..bad33ae3cbc37 100644 --- a/build_tools/wheels/check_license.py +++ b/build_tools/wheels/check_license.py @@ -20,7 +20,7 @@ except StopIteration as e: raise RuntimeError("Unable to find scikit-learn's dist-info") from e -license_text = (distinfo_path / "COPYING").read_text() +license_text = (distinfo_path / "licenses" / "COPYING").read_text() assert "Copyright (c)" in license_text diff --git a/doc/about.rst b/doc/about.rst index 4db39f9709e73..ba265e21889df 100644 --- a/doc/about.rst +++ b/doc/about.rst @@ -32,10 +32,10 @@ The decision making process and governance structure of scikit-learn, like roles The people behind scikit-learn ============================== -Scikit-learn is a community project, developed by a large group of +scikit-learn is a community project, developed by a large group of people, all across the world. A few core contributor teams, listed below, have central roles, however a more complete list of contributors can be found `on -github +GitHub `__. Active Core Contributors @@ -93,6 +93,7 @@ Emeritus Maintainers Team The following people have been active contributors in the past, but are no longer active in the project: +.. rst-class:: grid-list-three-columns .. include:: maintainers_emeritus.rst Emeritus Communication Team @@ -158,12 +159,13 @@ Bibtex entry:: pages = {108--122}, } -Artwork -======= +Branding & Logos +================ -High quality PNG and SVG logos are available in the `doc/logos/ +High quality PNG and SVG logos are available in the `doc/logos `_ -source directory. +source directory. The color palette is available in the +`Branding Guide `_. .. image:: images/scikit-learn-logo-notext.png :align: center @@ -580,8 +582,43 @@ the past: |hf| + +Donations in Kind +----------------- +The following organizations provide non-financial contributions to the +scikit-learn project. + +.. raw:: html + + + + + + + + + + + + + + + + + + + + + + + + + + +
CompanyContribution
Anaconda IncStorage for our staging and nightly builds
CircleCICPU time on their Continuous Integration servers
GitHubTeams account
Microsoft AzureCPU time on their Continuous Integration servers
+ Coding Sprints -============== +-------------- The scikit-learn project has a long history of `open source coding sprints `_ with over 50 sprint @@ -593,40 +630,52 @@ list of events. Donating to the project ======================= -If you are interested in donating to the project or to one of our code-sprints, -please donate via the `NumFOCUS Donations Page -`_. +If you have found scikit-learn to be useful in your work, research, or company, +please consider making a donation to the project commensurate with your resources. +There are several options for making donations: .. raw:: html

- Help us, donate! + Donate via NumFOCUS + + + Donate via GitHub Sponsors + + + Donate via Benevity

-All donations will be handled by `NumFOCUS `_, a non-profit -organization which is managed by a board of `Scipy community members -`_. NumFOCUS's mission is to foster scientific -computing software, in particular in Python. As a fiscal home of scikit-learn, it -ensures that money is available when needed to keep the project funded and available -while in compliance with tax regulations. +**Donation Options:** -The received donations for the scikit-learn project mostly will go towards covering -travel-expenses for code sprints, as well as towards the organization budget of the -project [#f1]_. +* **NumFOCUS**: Donate via the `NumFOCUS Donations Page + `_, scikit-learn's fiscal sponsor. -.. rubric:: Notes +* **GitHub Sponsors**: Support the project directly through `GitHub Sponsors + `_. -.. [#f1] Regarding the organization budget, in particular, we might use some of - the donated funds to pay for other project expenses such as DNS, - hosting or continuous integration services. +* **Benevity**: If your company uses scikit-learn, you can also support the + project through Benevity, a platform to manage employee donations. It is + widely used by hundreds of Fortune 1000 companies to streamline and scale + their social impact initiatives. If your company uses Benevity, you are + able to make a donation with a company match as high as 100%. Our project + ID is `433725 `_. + +All donations are managed by `NumFOCUS `_, a 501(c)(3) +non-profit organization based in Austin, Texas, USA. The NumFOCUS board +consists of `SciPy community members `_. +Contributions are tax-deductible to the extent allowed by law. + +.. rubric:: Notes +Contributions support the maintenance of the project, including development, +documentation, infrastructure and coding sprints. -Infrastructure support -====================== -We would also like to thank `Microsoft Azure `_, -`CircleCl `_ for free CPU -time on their Continuous Integration servers, and `Anaconda Inc. `_ -for the storage they provide for our staging and nightly builds. +scikit-learn Swag +----------------- +Official scikit-learn swag is available for purchase at the `NumFOCUS online store +`_. +A portion of the proceeds from each sale goes to support the scikit-learn project. diff --git a/doc/common_pitfalls.rst b/doc/common_pitfalls.rst index 129f9b3990fd5..ff661b4d872be 100644 --- a/doc/common_pitfalls.rst +++ b/doc/common_pitfalls.rst @@ -356,7 +356,7 @@ lead to wrong conclusions. Estimators .......... -**Different `random_state` types lead to different cross-validation +**Different** `random_state` **types lead to different cross-validation procedures** Depending on the type of the `random_state` parameter, estimators will behave diff --git a/doc/conf.py b/doc/conf.py index 1113d4b2c100a..71c9ec5bb60c3 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -668,7 +668,7 @@ def notebook_modification_function(notebook_content, notebook_filename): if "seaborn" in notebook_content_str: code_lines.append("%pip install seaborn") if "plotly.express" in notebook_content_str: - code_lines.append("%pip install plotly") + code_lines.append("%pip install plotly nbformat") if "skimage" in notebook_content_str: code_lines.append("%pip install scikit-image") if "polars" in notebook_content_str: diff --git a/doc/developers/advanced_installation.rst b/doc/developers/advanced_installation.rst index 1a0c58de77f4e..d9bdeb50d325d 100644 --- a/doc/developers/advanced_installation.rst +++ b/doc/developers/advanced_installation.rst @@ -188,10 +188,6 @@ to enable OpenMP support: - or install `libomp` with Homebrew to extend the default Apple clang compiler. -For Apple Silicon M1 hardware, only the conda-forge method below is known to -work at the time of writing (January 2021). You can install the `macos/arm64` -distribution of conda using the `conda-forge installer -`_ macOS compilers from conda-forge ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -280,17 +276,6 @@ Install the LLVM OpenMP library: brew install libomp -Set the following environment variables: - -.. prompt:: bash $ - - export CC=/usr/bin/clang - export CXX=/usr/bin/clang++ - export CPPFLAGS="$CPPFLAGS -Xpreprocessor -fopenmp" - export CFLAGS="$CFLAGS -I/usr/local/opt/libomp/include" - export CXXFLAGS="$CXXFLAGS -I/usr/local/opt/libomp/include" - export LDFLAGS="$LDFLAGS -Wl,-rpath,/usr/local/opt/libomp/lib -L/usr/local/opt/libomp/lib -lomp" - Finally, build scikit-learn in verbose mode (to check for the presence of the ``-fopenmp`` flag in the compiler commands): diff --git a/doc/developers/contributing.rst b/doc/developers/contributing.rst index 34e8e6d3e2aca..bebeb93d86b0c 100644 --- a/doc/developers/contributing.rst +++ b/doc/developers/contributing.rst @@ -726,6 +726,19 @@ We are glad to accept any sort of documentation: .. dropdown:: Guidelines for writing docstrings + * You can use `pytest` to test docstrings, e.g. assuming the + `RandomForestClassifier` docstring has been modified, the following command + would test its docstring compliance: + + .. prompt:: bash + + pytest --doctest-modules sklearn/ensemble/_forest.py -k RandomForestClassifier + + * The correct order of sections is: Parameters, Returns, See Also, Notes, Examples. + See the `numpydoc documentation + `_ for + information on other possible sections. + * When documenting the parameters and attributes, here is a list of some well-formatted examples @@ -791,7 +804,15 @@ We are glad to accept any sort of documentation: SelectKBest : Select features based on the k highest scores. SelectFpr : Select features based on a false positive rate test. - * Add one or two snippets of code in "Example" section to show how it can be used. + * The "Notes" section is optional. It is meant to provide information on + specific behavior of a function/class/classmethod/method. + + * A `Note` can also be added to an attribute, but in that case it requires + using the `.. rubric:: Note` directive. + + * Add one or two **snippets** of code in "Example" section to show how it can + be used. The code should be runable as is, i.e. it should include all + required imports. Keep this section as brief as possible. .. dropdown:: Guidelines for writing the user guide and other reStructuredText documents diff --git a/doc/developers/maintainer.rst.template b/doc/developers/maintainer.rst.template index b7134d4170521..941f72aa20906 100644 --- a/doc/developers/maintainer.rst.template +++ b/doc/developers/maintainer.rst.template @@ -121,6 +121,9 @@ Reference Steps * [ ] Update the sklearn dev0 version in main branch {%- endif %} * [ ] Set the version number in the release branch + {%- if key == "rc" %} + * [ ] Set an upper bound on build dependencies in the release branch + {%- endif %} * [ ] Generate the changelog in the release branch * [ ] Check that the wheels for the release can be built successfully * [ ] Merge the PR with `[cd build]` commit message to upload wheels to the staging repo @@ -162,6 +165,17 @@ Reference Steps - In the release branch, change the version number `__version__` in `sklearn/__init__.py` to `{{ version_full }}`. + {% if key == "rc" %} + - Still in the release branch, set or update the upper bound on the build + dependencies in the `[build-system]` section of `pyproject.toml`. The goal is to + prevent future backward incompatible releases of the dependencies to break the + build in the maintenance branch. + + The upper bounds should match the latest already-released minor versions of the + dependencies and should allow future micro (bug-fix) versions. For instance, if + numpy 2.2.5 is the most recent version, its upper bound should be set to <2.3.0. + {% endif %} + - In the release branch, generate the changelog for the incoming version, i.e., `doc/whats_new/{{ version_short }}.rst`. {%- if key == "rc" %} diff --git a/doc/getting_started.rst b/doc/getting_started.rst index 14e0178f0826b..820b503b683d5 100644 --- a/doc/getting_started.rst +++ b/doc/getting_started.rst @@ -1,17 +1,18 @@ Getting Started =============== -The purpose of this guide is to illustrate some of the main features that -``scikit-learn`` provides. It assumes a very basic working knowledge of -machine learning practices (model fitting, predicting, cross-validation, -etc.). Please refer to our :ref:`installation instructions -` for installing ``scikit-learn``. - ``Scikit-learn`` is an open source machine learning library that supports supervised and unsupervised learning. It also provides various tools for model fitting, data preprocessing, model selection, model evaluation, and many other utilities. +The purpose of this guide is to illustrate some of the main features of +``scikit-learn``. It assumes basic working knowledge of machine learning +practices (model fitting, predicting, cross-validation, etc.). Please refer to +our :ref:`installation instructions ` to install +``scikit-learn``, or jump to the :ref:`next_steps` section for additional +guidance on using ``scikit-learn``. + Fitting and predicting: estimator basics ---------------------------------------- @@ -167,13 +168,17 @@ a :class:`~sklearn.ensemble.RandomForestRegressor` that has been fitted with the best set of parameters. Read more in the :ref:`User Guide `:: - >>> from sklearn.datasets import fetch_california_housing + >>> from sklearn.datasets import make_regression >>> from sklearn.ensemble import RandomForestRegressor >>> from sklearn.model_selection import RandomizedSearchCV >>> from sklearn.model_selection import train_test_split >>> from scipy.stats import randint ... - >>> X, y = fetch_california_housing(return_X_y=True) + >>> # create a synthetic dataset + >>> X, y = make_regression(n_samples=20640, + ... n_features=8, + ... noise=0.1, + ... random_state=0) >>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) ... >>> # define the parameter space that will be searched over @@ -196,7 +201,7 @@ the best set of parameters. Read more in the :ref:`User Guide >>> # the search object now acts like a normal random forest estimator >>> # with max_depth=9 and n_estimators=4 >>> search.score(X_test, y_test) - 0.73... + 0.84... .. note:: @@ -214,6 +219,7 @@ the best set of parameters. Read more in the :ref:`User Guide Using a pipeline for cross-validation and searching will largely keep you from this common pitfall. +.. _next_steps: Next steps ---------- @@ -228,4 +234,5 @@ provide. You can also find an exhaustive list of the public API in the :ref:`api_ref`. You can also look at our numerous :ref:`examples ` that -illustrate the use of ``scikit-learn`` in many different contexts. +illustrate the use of ``scikit-learn`` in many different contexts, or have +a look at the :ref:`external_resources` for learning materials. diff --git a/doc/glossary.rst b/doc/glossary.rst index caf6b952553c4..f522073f25e7e 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -940,8 +940,11 @@ Class APIs and Estimator Types :class:`ensemble.BaggingClassifier`. In a meta-estimator's :term:`fit` method, any contained estimators - should be :term:`cloned` before they are fit (although FIXME: Pipeline - and FeatureUnion do not do this currently). An exception to this is + should be :term:`cloned` before they are fit. + + .. FIXME: Pipeline and FeatureUnion do not do this currently + + An exception to this is that an estimator may explicitly document that it accepts a pre-fitted estimator (e.g. using ``prefit=True`` in :class:`feature_selection.SelectFromModel`). One known issue with this @@ -1188,7 +1191,7 @@ Target Types :term:`multiclass` targets, horizontally stacked into an array of shape ``(n_samples, n_outputs)``. - XXX: For simplicity, we may not always support string class labels + Note: For simplicity, we may not always support string class labels for multiclass multioutput, and integer class labels should be used. :mod:`~sklearn.multioutput` provides estimators which estimate multi-output @@ -1381,7 +1384,7 @@ Methods To clear the model, a new estimator should be constructed, for instance with :func:`base.clone`. - NOTE: Using ``partial_fit`` after ``fit`` results in undefined behavior. + Note: Using ``partial_fit`` after ``fit`` results in undefined behavior. ``predict`` Makes a prediction for each sample, usually only taking :term:`X` as @@ -1590,8 +1593,7 @@ functions or non-estimator constructors. estimators: some, but not all, use it to mean a single epoch (i.e. a pass over every sample in the data). - FIXME perhaps we should have some common tests about the relationship - between ConvergenceWarning and max_iter. + .. FIXME: perhaps we should have some common tests about the relationship between ConvergenceWarning and max_iter. ``memory`` Some estimators make use of :class:`joblib.Memory` to @@ -1611,7 +1613,7 @@ functions or non-estimator constructors. for some algorithms, an improper distance metric (one that does not obey the triangle inequality, such as Cosine Distance) may be used. - XXX: hierarchical clustering uses ``affinity`` with this meaning. + Note: Hierarchical clustering uses ``affinity`` with this meaning. We also use *metric* to refer to :term:`evaluation metrics`, but avoid using this sense as a parameter name. @@ -1859,12 +1861,11 @@ See concept :term:`sample property`. the weight. Weights may be specified as floats, so that sample weights are usually equivalent up to a constant positive scaling factor. - FIXME Is this interpretation always the case in practice? We have no - common tests. + .. FIXME: Is this interpretation always the case in practice? We have no common tests. Some estimators, such as decision trees, support negative weights. - FIXME: This feature or its absence may not be tested or documented in - many estimators. + + .. FIXME: This feature or its absence may not be tested or documented in many estimators. This is not entirely the case where other parameters of the model consider the number of samples in a region, as with ``min_samples`` in diff --git a/doc/governance.rst b/doc/governance.rst index 5601f80573651..cbe35c0ebe0a4 100644 --- a/doc/governance.rst +++ b/doc/governance.rst @@ -146,20 +146,22 @@ decision making process**". Decisions (in addition to adding core contributors and TC membership as above) are made according to the following rules: -* **Minor Documentation changes**, such as typo fixes, or addition / correction +* **Minor code and documentation changes**, such as small maintenance changes without + modification of code logic, typo fixes, or addition / correction of a sentence, but no change of the ``scikit-learn.org`` landing page or the - “about” page: Requires +1 by a maintainer, no -1 by a maintainer (lazy - consensus), happens on the issue or pull request page. Maintainers are - expected to give “reasonable time” to others to give their opinion on the + “about” page: Requires +1 by a core contributor, no -1 by a core contributor + (lazy consensus), happens on the issue or pull request page. Core contributors + are expected to give “reasonable time” to others to give their opinion on the pull request if they're not confident others would agree. * **Code changes and major documentation changes** - require +1 by two maintainers, no -1 by a maintainer (lazy + require +1 by two core contributors, no -1 by a core contributor (lazy consensus), happens on the issue of pull-request page. * **Changes to the API principles and changes to dependencies or supported - versions** happen via :ref:`slep` and follows the decision-making process - outlined above. + versions** follow the decision-making process outlined above. In particular + changes to API principles are backed via a :ref:`slep`. Smaller decisions + like supported versions can happen on a GitHub issue or pull request. * **Changes to the governance model** follow the process outlined in `SLEP020 `__. diff --git a/doc/maintainers_emeritus.rst b/doc/maintainers_emeritus.rst index f5640ab2caf31..9df0488d2d3b6 100644 --- a/doc/maintainers_emeritus.rst +++ b/doc/maintainers_emeritus.rst @@ -17,7 +17,7 @@ - Arnaud Joly - Thouis (Ray) Jones - Kyle Kastner -- manoj kumar +- Manoj Kumar - Robert Layton - Wei Li - Paolo Losi @@ -39,4 +39,4 @@ - Nelle Varoquaux - David Warde-Farley - Ron Weiss -- Roman Yurchak +- Roman Yurchak \ No newline at end of file diff --git a/doc/modules/array_api.rst b/doc/modules/array_api.rst index e7261ea35cc7c..79c4d43ad7bc0 100644 --- a/doc/modules/array_api.rst +++ b/doc/modules/array_api.rst @@ -30,7 +30,7 @@ data structures and automatically dispatch operations to the underlying namespac instead of relying on NumPy. At this stage, this support is **considered experimental** and must be enabled -explicitly as explained in the following. +explicitly by the `array_api_dispatch` configuration. See below for details. .. note:: Currently, only `array-api-strict`, `cupy`, and `PyTorch` are known to work @@ -45,7 +45,13 @@ and how it facilitates interoperability between array libraries: Example usage ============= -Here is an example code snippet to demonstrate how to use `CuPy +The configuration `array_api_dispatch=True` needs to be set to `True` to enable array +API support. We recommend setting this configuration globally to ensure consistent +behaviour and prevent accidental mixing of array namespaces. +Note that we set it with :func:`config_context` below to avoid having to call +:func:`set_config(array_api_dispatch=False)` at the end of every code snippet +that uses the array API. +The example code snippet below demonstrates how to use `CuPy `_ to run :class:`~discriminant_analysis.LinearDiscriminantAnalysis` on a GPU:: @@ -82,8 +88,7 @@ transfers an estimator attributes from Array API to a ndarray:: PyTorch Support --------------- -PyTorch Tensors are supported by setting `array_api_dispatch=True` and passing in -the tensors directly:: +PyTorch Tensors can also be passed directly:: >>> import torch >>> X_torch = torch.asarray(X_np, device="cuda", dtype=torch.float32) @@ -139,6 +144,7 @@ Metrics - :func:`sklearn.metrics.f1_score` - :func:`sklearn.metrics.fbeta_score` - :func:`sklearn.metrics.hamming_loss` +- :func:`sklearn.metrics.jaccard_score` - :func:`sklearn.metrics.max_error` - :func:`sklearn.metrics.mean_absolute_error` - :func:`sklearn.metrics.mean_absolute_percentage_error` @@ -177,41 +183,116 @@ Tools Coverage is expected to grow over time. Please follow the dedicated `meta-issue on GitHub `_ to track progress. -Type of return values and fitted attributes -------------------------------------------- +Input and output array type handling +==================================== -When calling functions or methods with Array API compatible inputs, the -convention is to return array values of the same array container type and +Estimators and scoring functions are able to accept input arrays +from different array libraries and/or devices. When a mixed set of input arrays is +passed, scikit-learn converts arrays as needed to make them all consistent. + +For estimators, the rule is **"everything follows** `X` **"** - mixed array inputs are +converted so that they all match the array library and device of `X`. +For scoring functions the rule is **"everything follows** `y_pred` **"** - mixed array +inputs are converted so that they all match the array library and device of `y_pred`. + +When a function or method has been called with array API compatible inputs, the +convention is to return arrays from the same array library and on the same device as the input data. -Similarly, when an estimator is fitted with Array API compatible inputs, the -fitted attributes will be arrays from the same library as the input and stored -on the same device. The `predict` and `transform` method subsequently expect +Estimators +---------- + +When an estimator is fitted with an array API compatible `X`, all other +array inputs, including constructor arguments, (e.g., `y`, `sample_weight`) +will be converted to match the array library and device of `X`, if they do not already. +This behaviour enables switching from processing on the CPU to processing +on the GPU at any point within a pipeline. + +This allows estimators to accept mixed input types, enabling `X` to be moved +to a different device within a pipeline, without explicitly moving `y`. +Note that scikit-learn pipelines do not allow transformation of `y` (to avoid +:ref:`leakage `). + +Take for example a pipeline where `X` and `y` both start on CPU, and go through +the following three steps: + +* :class:`~sklearn.preprocessing.TargetEncoder`, which will transform categorial + `X` but also requires `y`, meaning both `X` and `y` need to be on CPU. +* :class:`FunctionTransformer(func=partial(torch.asarray, device="cuda")) `, + which moves `X` to GPU, to improve performance in the next step. +* :class:`~sklearn.linear_model.Ridge`, whose performance can be improved when + passed arrays on a GPU, as they can handle large matrix operations very efficiently. + +`X` initially contains categorical string data (thus needs to be on CPU), which is +target encoded to numerical values in :class:`~sklearn.preprocessing.TargetEncoder`. +`X` is then explicitly moved to GPU to improve the performance of +:class:`~sklearn.linear_model.Ridge`. `y` cannot be transformed by the pipeline +(recall scikit-learn pipelines do not allow transformation of `y`) but as +:class:`~sklearn.linear_model.Ridge` is able to accept mixed input types, +this is not a problem and the pipeline is able to be run. + +The fitted attributes of an estimator fitted with an array API compatible `X`, will +be arrays from the same library as the input and stored on the same device. +The `predict` and `transform` method subsequently expect inputs from the same array library and device as the data passed to the `fit` method. -Note however that scoring functions that return scalar values return Python -scalars (typically a `float` instance) instead of an array scalar value. +Scoring functions +----------------- + +When an array API compatible `y_pred` is passed to a scoring function, +all other array inputs (e.g., `y_true`, `sample_weight`) will be converted +to match the array library and device of `y_pred`, if they do not already. +This allows scoring functions to accept mixed input types, enabling them to be +used within a :term:`meta-estimator` (or function that accepts estimators), with a +pipeline that moves input arrays between devices (e.g., CPU to GPU). + +For example, to be able to use the pipeline described above within e.g., +:func:`~sklearn.model_selection.cross_validate` or +:class:`~sklearn.model_selection.GridSearchCV`, the scoring function internally +called needs to be able to accept mixed input types. + +The output type of scoring functions depends on the number of output values. +When a scoring function returns a scalar value, it will return a Python +scalar (typically a `float` instance) instead of an array scalar value. +For scoring functions that support :term:`multiclass` or :term:`multioutput`, +an array from the same array library and device as `y_pred` will be returned when +multiple values need to be output. Common estimator checks ======================= Add the `array_api_support` tag to an estimator's set of tags to indicate that -it supports the Array API. This will enable dedicated checks as part of the +it supports the array API. This will enable dedicated checks as part of the common tests to verify that the estimators' results are the same when using -vanilla NumPy and Array API inputs. +vanilla NumPy and array API inputs. -To run the full set of checks you need to install both -`PyTorch `_ and `CuPy `_ and have +To run these checks you need to install +`array-api-strict `_ in your +test environment. This allows you to run checks without having a +GPU. To run the full set of checks you also need to install +`PyTorch `_, `CuPy `_ and have a GPU. Checks that can not be executed or have missing dependencies will be automatically skipped. Therefore it's important to run the tests with the `-v` flag to see which checks are skipped: .. prompt:: bash $ - pip install ... # selected libraries as needed + pip install array-api-strict # and other libraries as needed pytest -k "array_api" -v +Running the scikit-learn tests against `array-api-strict` should help reveal +most code problems related to handling multiple device inputs via the use of +simulated non-CPU devices. This allows for fast iterative development and debugging of +array API related code. + +However, to ensure full handling of PyTorch or CuPy inputs allocated on actual GPU +devices, it is necessary to run the tests against those libraries and hardware. +This can either be achieved by using +`Google Colab `_ +or leveraging our CI infrastructure on pull requests (manually triggered by maintainers +for cost reasons). + .. _mps_support: Note on MPS device support diff --git a/doc/modules/calibration.rst b/doc/modules/calibration.rst index a7b34065fe330..e8e6aa8b9953a 100644 --- a/doc/modules/calibration.rst +++ b/doc/modules/calibration.rst @@ -103,7 +103,7 @@ difficulty making predictions near 0 and 1 because variance in the underlying base models will bias predictions that should be near zero or one away from these values. Because predictions are restricted to the interval [0,1], errors caused by variance tend to be one-sided near zero and one. For -example, if a model should predict p = 0 for a case, the only way bagging +example, if a model should predict :math:`p = 0` for a case, the only way bagging can achieve this is if all bagged trees predict zero. If we add noise to the trees that bagging is averaging over, this noise will cause some trees to predict values larger than 0 for this case, thus moving the average @@ -146,7 +146,7 @@ Usage The :class:`CalibratedClassifierCV` class is used to calibrate a classifier. :class:`CalibratedClassifierCV` uses a cross-validation approach to ensure -unbiased data is always used to fit the calibrator. The data is split into k +unbiased data is always used to fit the calibrator. The data is split into :math:`k` `(train_set, test_set)` couples (as determined by `cv`). When `ensemble=True` (default), the following procedure is repeated independently for each cross-validation split: @@ -157,13 +157,13 @@ cross-validation split: regressor) (when the data is multiclass, a calibrator is fit for every class) This results in an -ensemble of k `(classifier, calibrator)` couples where each calibrator maps +ensemble of :math:`k` `(classifier, calibrator)` couples where each calibrator maps the output of its corresponding classifier into [0, 1]. Each couple is exposed in the `calibrated_classifiers_` attribute, where each entry is a calibrated classifier with a :term:`predict_proba` method that outputs calibrated probabilities. The output of :term:`predict_proba` for the main :class:`CalibratedClassifierCV` instance corresponds to the average of the -predicted probabilities of the `k` estimators in the `calibrated_classifiers_` +predicted probabilities of the :math:`k` estimators in the `calibrated_classifiers_` list. The output of :term:`predict` is the class that has the highest probability. @@ -244,12 +244,12 @@ subject to :math:`\hat{f}_i \geq \hat{f}_j` whenever :math:`f_i \geq f_j`. :math:`y_i` is the true label of sample :math:`i` and :math:`\hat{f}_i` is the output of the calibrated classifier for sample :math:`i` (i.e., the calibrated probability). -This method is more general when compared to 'sigmoid' as the only restriction +This method is more general when compared to `'sigmoid'` as the only restriction is that the mapping function is monotonically increasing. It is thus more powerful as it can correct any monotonic distortion of the un-calibrated model. However, it is more prone to overfitting, especially on small datasets [6]_. -Overall, 'isotonic' will perform as well as or better than 'sigmoid' when +Overall, `'isotonic'` will perform as well as or better than `'sigmoid'` when there is enough data (greater than ~ 1000 samples) to avoid overfitting [3]_. .. note:: Impact on ranking metrics like AUC diff --git a/doc/modules/classification_threshold.rst b/doc/modules/classification_threshold.rst index ee7028f469b5f..94a5e0a30b716 100644 --- a/doc/modules/classification_threshold.rst +++ b/doc/modules/classification_threshold.rst @@ -28,7 +28,7 @@ cut-off rules: a positive class is predicted when the conditional probability :math:`P(y|X)` is greater than 0.5 (obtained with :term:`predict_proba`) or if the decision score is greater than 0 (obtained with :term:`decision_function`). -Here, we show an example that illustrates the relation between conditional +Here, we show an example that illustrates the relatonship between conditional probability estimates :math:`P(y|X)` and class labels:: >>> from sklearn.datasets import make_classification diff --git a/doc/modules/compose.rst b/doc/modules/compose.rst index 3ef0d94236aa6..86e95c12f0940 100644 --- a/doc/modules/compose.rst +++ b/doc/modules/compose.rst @@ -286,12 +286,17 @@ the regressor that will be used for prediction, and the transformer that will be applied to the target variable:: >>> import numpy as np - >>> from sklearn.datasets import fetch_california_housing + >>> from sklearn.datasets import make_regression >>> from sklearn.compose import TransformedTargetRegressor >>> from sklearn.preprocessing import QuantileTransformer >>> from sklearn.linear_model import LinearRegression >>> from sklearn.model_selection import train_test_split - >>> X, y = fetch_california_housing(return_X_y=True) + >>> # create a synthetic dataset + >>> X, y = make_regression(n_samples=20640, + ... n_features=8, + ... noise=100.0, + ... random_state=0) + >>> y = np.exp( 1 + (y - y.min()) * (4 / (y.max() - y.min()))) >>> X, y = X[:2000, :], y[:2000] # select a subset of data >>> transformer = QuantileTransformer(output_distribution='normal') >>> regressor = LinearRegression() @@ -300,11 +305,11 @@ be applied to the target variable:: >>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) >>> regr.fit(X_train, y_train) TransformedTargetRegressor(...) - >>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test))) - R2 score: 0.61 + >>> print(f"R2 score: {regr.score(X_test, y_test):.2f}") + R2 score: 0.67 >>> raw_target_regr = LinearRegression().fit(X_train, y_train) - >>> print('R2 score: {0:.2f}'.format(raw_target_regr.score(X_test, y_test))) - R2 score: 0.59 + >>> print(f"R2 score: {raw_target_regr.score(X_test, y_test):.2f}") + R2 score: 0.64 For simple transformations, instead of a Transformer object, a pair of functions can be passed, defining the transformation and its inverse mapping:: @@ -321,8 +326,8 @@ Subsequently, the object is created as:: ... inverse_func=inverse_func) >>> regr.fit(X_train, y_train) TransformedTargetRegressor(...) - >>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test))) - R2 score: 0.51 + >>> print(f"R2 score: {regr.score(X_test, y_test):.2f}") + R2 score: 0.67 By default, the provided functions are checked at each fit to be the inverse of each other. However, it is possible to bypass this checking by setting @@ -336,8 +341,8 @@ each other. However, it is possible to bypass this checking by setting ... check_inverse=False) >>> regr.fit(X_train, y_train) TransformedTargetRegressor(...) - >>> print('R2 score: {0:.2f}'.format(regr.score(X_test, y_test))) - R2 score: -1.57 + >>> print(f"R2 score: {regr.score(X_test, y_test):.2f}") + R2 score: -3.02 .. note:: diff --git a/doc/modules/covariance.rst b/doc/modules/covariance.rst index 0eadfa2c8c584..98c5b7a8d88a6 100644 --- a/doc/modules/covariance.rst +++ b/doc/modules/covariance.rst @@ -35,10 +35,9 @@ The empirical covariance matrix of a sample can be computed using the :class:`EmpiricalCovariance` object to the data sample with the :meth:`EmpiricalCovariance.fit` method. Be careful that results depend on whether the data are centered, so one may want to use the -``assume_centered`` parameter accurately. More precisely, if -``assume_centered=False``, then the test set is supposed to have the -same mean vector as the training set. If not, both should be centered -by the user, and ``assume_centered=True`` should be used. +`assume_centered` parameter accurately. More precisely, if `assume_centered=True`, then +all features in the train and test sets should have a mean of zero. If not, both should +be centered by the user, or `assume_centered=False` should be used. .. rubric:: Examples diff --git a/doc/modules/cross_validation.rst b/doc/modules/cross_validation.rst index bfdee6c8a043d..b1c9ccec8f641 100644 --- a/doc/modules/cross_validation.rst +++ b/doc/modules/cross_validation.rst @@ -372,8 +372,7 @@ Thus, one can create the training/test sets using numpy indexing:: Repeated K-Fold ^^^^^^^^^^^^^^^ -:class:`RepeatedKFold` repeats K-Fold n times. It can be used when one -requires to run :class:`KFold` n times, producing different splits in +:class:`RepeatedKFold` repeats :class:`KFold` :math:`n` times, producing different splits in each repetition. Example of 2-fold K-Fold repeated 2 times:: @@ -392,7 +391,7 @@ Example of 2-fold K-Fold repeated 2 times:: [1 3] [0 2] -Similarly, :class:`RepeatedStratifiedKFold` repeats Stratified K-Fold n times +Similarly, :class:`RepeatedStratifiedKFold` repeats :class:`StratifiedKFold` :math:`n` times with different randomization in each repetition. .. _leave_one_out: @@ -434,10 +433,10 @@ folds are virtually identical to each other and to the model built from the entire training set. However, if the learning curve is steep for the training size in question, -then 5- or 10- fold cross validation can overestimate the generalization error. +then 5 or 10-fold cross validation can overestimate the generalization error. -As a general rule, most authors, and empirical evidence, suggest that 5- or 10- -fold cross validation should be preferred to LOO. +As a general rule, most authors and empirical evidence suggest that 5 or 10-fold +cross validation should be preferred to LOO. .. dropdown:: References @@ -553,10 +552,10 @@ relative class frequencies are approximately preserved in each fold. .. _stratified_k_fold: -Stratified k-fold +Stratified K-fold ^^^^^^^^^^^^^^^^^ -:class:`StratifiedKFold` is a variation of *k-fold* which returns *stratified* +:class:`StratifiedKFold` is a variation of *K-fold* which returns *stratified* folds: each set contains approximately the same percentage of samples of each target class as the complete set. @@ -648,10 +647,10 @@ parameter. .. _group_k_fold: -Group k-fold +Group K-fold ^^^^^^^^^^^^ -:class:`GroupKFold` is a variation of k-fold which ensures that the same group is +:class:`GroupKFold` is a variation of K-fold which ensures that the same group is not represented in both testing and training sets. For example if the data is obtained from different subjects with several samples per-subject and if the model is flexible enough to learn from highly person specific features it diff --git a/doc/modules/density.rst b/doc/modules/density.rst index 16c73bd5349a2..b629857827c74 100644 --- a/doc/modules/density.rst +++ b/doc/modules/density.rst @@ -90,7 +90,7 @@ Here we have used ``kernel='gaussian'``, as seen above. Mathematically, a kernel is a positive function :math:`K(x;h)` which is controlled by the bandwidth parameter :math:`h`. Given this kernel form, the density estimate at a point :math:`y` within -a group of points :math:`x_i; i=1\cdots N` is given by: +a group of points :math:`x_i; i=1, \cdots, N` is given by: .. math:: \rho_K(y) = \sum_{i=1}^{N} K(y - x_i; h) diff --git a/doc/modules/ensemble.rst b/doc/modules/ensemble.rst index f0f14c60e4867..3e85fe14326ed 100644 --- a/doc/modules/ensemble.rst +++ b/doc/modules/ensemble.rst @@ -308,7 +308,7 @@ values. all of the :math:`2^{K - 1} - 1` partitions, where :math:`K` is the number of categories. This can quickly become prohibitive when :math:`K` is large. Fortunately, since gradient boosting trees are always regression trees (even - for classification problems), there exist a faster strategy that can yield + for classification problems), there exists a faster strategy that can yield equivalent splits. First, the categories of a feature are sorted according to the variance of the target, for each category `k`. Once the categories are sorted, one can consider *continuous partitions*, i.e. treat the categories @@ -369,13 +369,17 @@ following modelling constraint: Also, monotonic constraints are not supported for multiclass classification. +For a practical implementation of monotonic constraints with the histogram-based +gradient boosting, including how they can improve generalization when domain knowledge +is available, see +:ref:`sphx_glr_auto_examples_ensemble_plot_monotonic_constraints.py`. + .. note:: Since categories are unordered quantities, it is not possible to enforce monotonic constraints on categorical features. .. rubric:: Examples -* :ref:`sphx_glr_auto_examples_ensemble_plot_monotonic_constraints.py` * :ref:`sphx_glr_auto_examples_ensemble_plot_hgbt_regression.py` .. _interaction_cst_hgbt: @@ -918,7 +922,7 @@ based on permutation of the features. Annals of Statistics, 29, 1189-1232. .. [Friedman2002] Friedman, J.H. (2002). `Stochastic gradient boosting. - `_. + `_. Computational Statistics & Data Analysis, 38, 367-378. .. [R2007] G. Ridgeway (2006). `Generalized Boosted Models: A guide to the gbm @@ -961,7 +965,7 @@ from a sample drawn with replacement (i.e., a bootstrap sample) from the training set. Furthermore, when splitting each node during the construction of a tree, the -best split is found through an exhaustive search of the features values of +best split is found through an exhaustive search of the feature values of either all input features or a random subset of size ``max_features``. (See the :ref:`parameter tuning guidelines ` for more details.) @@ -1587,7 +1591,7 @@ Note that it is also possible to get the output of the stacked In practice, a stacking predictor predicts as good as the best predictor of the base layer and even sometimes outperforms it by combining the different -strengths of the these predictors. However, training a stacking predictor is +strengths of these predictors. However, training a stacking predictor is computationally expensive. .. note:: diff --git a/doc/modules/gaussian_process.rst b/doc/modules/gaussian_process.rst index 46d04ac35d832..b8b3fd62709d6 100644 --- a/doc/modules/gaussian_process.rst +++ b/doc/modules/gaussian_process.rst @@ -337,7 +337,7 @@ of a :class:`Sum` kernel, where it modifies the mean of the Gaussian process. It depends on a parameter :math:`constant\_value`. It is defined as: .. math:: - k(x_i, x_j) = constant\_value \;\forall\; x_1, x_2 + k(x_i, x_j) = constant\_value \;\forall\; x_i, x_j The main use-case of the :class:`WhiteKernel` kernel is as part of a sum-kernel where it explains the noise-component of the signal. Tuning its diff --git a/doc/modules/kernel_approximation.rst b/doc/modules/kernel_approximation.rst index 0bbd19d05de33..334aecc9ac7f6 100644 --- a/doc/modules/kernel_approximation.rst +++ b/doc/modules/kernel_approximation.rst @@ -94,6 +94,8 @@ also the dimensionality of the features computed - is given by the parameter :ref:`sphx_glr_auto_examples_applications_plot_cyclical_feature_engineering.py`, that shows an efficient machine learning pipeline that uses a :class:`Nystroem` kernel. +* See :ref:`sphx_glr_auto_examples_miscellaneous_plot_kernel_approximation.py` + for a comparison of :class:`Nystroem` kernel with :class:`RBFSampler`. .. _rbf_kernel_approx: @@ -145,7 +147,9 @@ use of larger feature spaces more efficient. .. rubric:: Examples -* :ref:`sphx_glr_auto_examples_miscellaneous_plot_kernel_approximation.py` +* See :ref:`sphx_glr_auto_examples_miscellaneous_plot_kernel_approximation.py` for a + comparison of :class:`Nystroem` kernel with :class:`RBFSampler`. + .. _additive_chi_kernel_approx: diff --git a/doc/modules/kernel_ridge.rst b/doc/modules/kernel_ridge.rst index fcc19a49628c4..64267f4233a53 100644 --- a/doc/modules/kernel_ridge.rst +++ b/doc/modules/kernel_ridge.rst @@ -7,7 +7,7 @@ Kernel ridge regression .. currentmodule:: sklearn.kernel_ridge Kernel ridge regression (KRR) [M2012]_ combines :ref:`ridge_regression` -(linear least squares with l2-norm regularization) with the `kernel trick +(linear least squares with :math:`L_2`-norm regularization) with the `kernel trick `_. It thus learns a linear function in the space induced by the respective kernel and the data. For non-linear kernels, this corresponds to a non-linear function in the original @@ -16,7 +16,7 @@ space. The form of the model learned by :class:`KernelRidge` is identical to support vector regression (:class:`~sklearn.svm.SVR`). However, different loss functions are used: KRR uses squared error loss while support vector -regression uses :math:`\epsilon`-insensitive loss, both combined with l2 +regression uses :math:`\epsilon`-insensitive loss, both combined with :math:`L_2` regularization. In contrast to :class:`~sklearn.svm.SVR`, fitting :class:`KernelRidge` can be done in closed-form and is typically faster for medium-sized datasets. On the other hand, the learned model is non-sparse and @@ -31,7 +31,7 @@ plotted, where both complexity/regularization and bandwidth of the RBF kernel have been optimized using grid-search. The learned functions are very similar; however, fitting :class:`KernelRidge` is approximately seven times faster than fitting :class:`~sklearn.svm.SVR` (both with grid-search). -However, prediction of 100000 target values is more than three times faster +However, prediction of 100,000 target values is more than three times faster with :class:`~sklearn.svm.SVR` since it has learned a sparse model using only approximately 1/3 of the 100 training datapoints as support vectors. diff --git a/doc/modules/lda_qda.rst b/doc/modules/lda_qda.rst index 405ef8e5d3a8b..c18835d514a9f 100644 --- a/doc/modules/lda_qda.rst +++ b/doc/modules/lda_qda.rst @@ -173,11 +173,11 @@ In this scenario, the empirical sample covariance is a poor estimator, and shrinkage helps improving the generalization performance of the classifier. Shrinkage LDA can be used by setting the ``shrinkage`` parameter of -the :class:`~discriminant_analysis.LinearDiscriminantAnalysis` class to 'auto'. +the :class:`~discriminant_analysis.LinearDiscriminantAnalysis` class to `'auto'`. This automatically determines the optimal shrinkage parameter in an analytic way following the lemma introduced by Ledoit and Wolf [2]_. Note that -currently shrinkage only works when setting the ``solver`` parameter to 'lsqr' -or 'eigen'. +currently shrinkage only works when setting the ``solver`` parameter to `'lsqr'` +or `'eigen'`. The ``shrinkage`` parameter can also be manually set between 0 and 1. In particular, a value of 0 corresponds to no shrinkage (which means the empirical @@ -192,7 +192,7 @@ best choice. For example if the distribution of the data is normally distributed, the Oracle Approximating Shrinkage estimator :class:`sklearn.covariance.OAS` yields a smaller Mean Squared Error than the one given by Ledoit and Wolf's -formula used with shrinkage="auto". In LDA, the data are assumed to be gaussian +formula used with `shrinkage="auto"`. In LDA, the data are assumed to be gaussian conditionally to the class. If these assumptions hold, using LDA with the OAS estimator of covariance will yield a better classification accuracy than if Ledoit and Wolf or the empirical covariance estimator is used. @@ -239,7 +239,7 @@ computing :math:`S` and :math:`V` via the SVD of :math:`X` is enough. For LDA, two SVDs are computed: the SVD of the centered input matrix :math:`X` and the SVD of the class-wise mean vectors. -The 'lsqr' solver is an efficient algorithm that only works for +The `'lsqr'` solver is an efficient algorithm that only works for classification. It needs to explicitly compute the covariance matrix :math:`\Sigma`, and supports shrinkage and custom covariance estimators. This solver computes the coefficients @@ -247,9 +247,9 @@ This solver computes the coefficients \mu_k`, thus avoiding the explicit computation of the inverse :math:`\Sigma^{-1}`. -The 'eigen' solver is based on the optimization of the between class scatter to +The `'eigen'` solver is based on the optimization of the between class scatter to within class scatter ratio. It can be used for both classification and -transform, and it supports shrinkage. However, the 'eigen' solver needs to +transform, and it supports shrinkage. However, the `'eigen'` solver needs to compute the covariance matrix, so it might not be suitable for situations with a high number of features. diff --git a/doc/modules/linear_model.rst b/doc/modules/linear_model.rst index 69a2bf9b7f477..48acba45fec17 100644 --- a/doc/modules/linear_model.rst +++ b/doc/modules/linear_model.rst @@ -383,7 +383,7 @@ scikit-learn. For a linear Gaussian model, the maximum log-likelihood is defined as: .. math:: - \log(\hat{L}) = - \frac{n}{2} \log(2 \pi) - \frac{n}{2} \ln(\sigma^2) - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{2\sigma^2} + \log(\hat{L}) = - \frac{n}{2} \log(2 \pi) - \frac{n}{2} \log(\sigma^2) - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{2\sigma^2} where :math:`\sigma^2` is an estimate of the noise variance, :math:`y_i` and :math:`\hat{y}_i` are respectively the true and predicted @@ -654,7 +654,7 @@ or :func:`lars_path_gram`. .. rubric:: References * Original Algorithm is detailed in the paper `Least Angle Regression - `_ + `_ by Hastie et al. .. _omp: @@ -837,13 +837,11 @@ prior over all :math:`\lambda_i` is chosen to be the same gamma distribution given by the hyperparameters :math:`\lambda_1` and :math:`\lambda_2`. ARD is also known in the literature as *Sparse Bayesian Learning* and *Relevance -Vector Machine* [3]_ [4]_. For a worked-out comparison between ARD and `Bayesian -Ridge Regression`_, see the example below. +Vector Machine* [3]_ [4]_. -.. rubric:: Examples - -* :ref:`sphx_glr_auto_examples_linear_model_plot_ard.py` +See :ref:`sphx_glr_auto_examples_linear_model_plot_ard.py` for a worked-out comparison between ARD and `Bayesian Ridge Regression`_. +See :ref:`sphx_glr_auto_examples_linear_model_plot_lasso_and_elasticnet.py` for a comparison between various methods - Lasso, ARD and ElasticNet - on correlated data. .. rubric:: References @@ -971,7 +969,7 @@ logistic regression, see also `log-linear model .. dropdown:: Mathematical details - Let :math:`y_i \in {1, \ldots, K}` be the label (ordinal) encoded target variable for observation :math:`i`. + Let :math:`y_i \in \{1, \ldots, K\}` be the label (ordinal) encoded target variable for observation :math:`i`. Instead of a single coefficient vector, we now have a matrix of coefficients :math:`W` where each row vector :math:`W_k` corresponds to class :math:`k`. We aim at predicting the class probabilities :math:`P(y_i=k|X_i)` via @@ -1022,7 +1020,7 @@ The following table summarizes the penalties and multinomial multiclass supporte +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ | **Penalties** | **'lbfgs'** | **'liblinear'** | **'newton-cg'** | **'newton-cholesky'** | **'sag'** | **'saga'** | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ -| L2 penalty | yes | no | yes | no | yes | yes | +| L2 penalty | yes | yes | yes | yes | yes | yes | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ | L1 penalty | no | yes | no | no | no | yes | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ @@ -1032,7 +1030,7 @@ The following table summarizes the penalties and multinomial multiclass supporte +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ | **Multiclass support** | | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ -| multinomial multiclass | yes | no | yes | no | yes | yes | +| multinomial multiclass | yes | no | yes | yes | yes | yes | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ | **Behaviors** | | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ @@ -1043,8 +1041,11 @@ The following table summarizes the penalties and multinomial multiclass supporte | Robust to unscaled datasets | yes | yes | yes | yes | no | no | +------------------------------+-------------+-----------------+-----------------+-----------------------+-----------+------------+ -The "lbfgs" solver is used by default for its robustness. For large datasets -the "saga" solver is usually faster. +The "lbfgs" solver is used by default for its robustness. For +`n_samples >> n_features`, "newton-cholesky" is a good choice and can reach high +precision (tiny `tol` values). For large datasets +the "saga" solver is usually faster (than "lbfgs"), in particular for low precision +(high `tol`). For large dataset, you may also consider using :class:`SGDClassifier` with `loss="log_loss"`, which might be even faster but requires more tuning. @@ -1101,13 +1102,12 @@ zero, is likely to be an underfit, bad model and you are advised to set scaled datasets and on datasets with one-hot encoded categorical features with rare categories. - * The "newton-cholesky" solver is an exact Newton solver that calculates the hessian + * The "newton-cholesky" solver is an exact Newton solver that calculates the Hessian matrix and solves the resulting linear system. It is a very good choice for - `n_samples` >> `n_features`, but has a few shortcomings: Only :math:`\ell_2` - regularization is supported. Furthermore, because the hessian matrix is explicitly - computed, the memory usage has a quadratic dependency on `n_features` as well as on - `n_classes`. As a consequence, only the one-vs-rest scheme is implemented for the - multiclass case. + `n_samples` >> `n_features` and can reach high precision (tiny values of `tol`), + but has a few shortcomings: Only :math:`\ell_2` regularization is supported. + Furthermore, because the Hessian matrix is explicitly computed, the memory usage + has a quadratic dependency on `n_features` as well as on `n_classes`. For a comparison of some of these solvers, see [9]_. diff --git a/doc/modules/manifold.rst b/doc/modules/manifold.rst index fec6e96153323..aec992a8f9dc1 100644 --- a/doc/modules/manifold.rst +++ b/doc/modules/manifold.rst @@ -115,6 +115,9 @@ from the data itself, without the use of predetermined classifications. * See :ref:`sphx_glr_auto_examples_manifold_plot_manifold_sphere.py` for an example of manifold learning techniques applied to a spherical data-set. +* See :ref:`sphx_glr_auto_examples_manifold_plot_swissroll.py` for an example of using + manifold learning techniques on a Swiss Roll dataset. + The manifold learning implementations available in scikit-learn are summarized below diff --git a/doc/modules/model_evaluation.rst b/doc/modules/model_evaluation.rst index cf168295a6024..cca1ec88c23cd 100644 --- a/doc/modules/model_evaluation.rst +++ b/doc/modules/model_evaluation.rst @@ -1880,7 +1880,7 @@ In multilabel classification, the :func:`zero_one_loss` scores a subset as one if its labels strictly match the predictions, and as a zero if there are any errors. By default, the function returns the percentage of imperfectly predicted subsets. To get the count of such subsets instead, set -``normalize`` to ``False`` +``normalize`` to ``False``. If :math:`\hat{y}_i` is the predicted value of the :math:`i`-th sample and :math:`y_i` is the corresponding true value, @@ -1891,8 +1891,8 @@ then the 0-1 loss :math:`L_{0-1}` is defined as: L_{0-1}(y, \hat{y}) = \frac{1}{n_\text{samples}} \sum_{i=0}^{n_\text{samples}-1} 1(\hat{y}_i \not= y_i) where :math:`1(x)` is the `indicator function -`_. The zero one -loss can also be computed as :math:`zero-one loss = 1 - accuracy`. +`_. The zero-one +loss can also be computed as :math:`\text{zero-one loss} = 1 - \text{accuracy}`. >>> from sklearn.metrics import zero_one_loss @@ -1950,7 +1950,7 @@ achieves the best score only when the estimated probabilities equal the true ones. Note that in the binary case, the Brier score is usually divided by two and -ranges between :math:`[0,1]`. For binary targets :math:`y_i \in {0, 1}` and +ranges between :math:`[0,1]`. For binary targets :math:`y_i \in \{0, 1\}` and probability estimates :math:`\hat{p}_i \approx \operatorname{Pr}(y_i = 1)` for the positive class, the Brier score is then equal to: diff --git a/doc/modules/neighbors.rst b/doc/modules/neighbors.rst index 82caa397b60d2..f2f761f92f932 100644 --- a/doc/modules/neighbors.rst +++ b/doc/modules/neighbors.rst @@ -347,7 +347,7 @@ Alternatively, the user can work with the :class:`BallTree` class directly. .. dropdown:: References * `"Five Balltree Construction Algorithms" - `_, + `_, Omohundro, S.M., International Computer Science Institute Technical Report (1989) diff --git a/doc/modules/neural_networks_supervised.rst b/doc/modules/neural_networks_supervised.rst index 13611b7f52775..a2e3046abc1cf 100644 --- a/doc/modules/neural_networks_supervised.rst +++ b/doc/modules/neural_networks_supervised.rst @@ -22,7 +22,7 @@ Multi-layer Perceptron **Multi-layer Perceptron (MLP)** is a supervised learning algorithm that learns a function :math:`f: R^m \rightarrow R^o` by training on a dataset, where :math:`m` is the number of dimensions for input and :math:`o` is the -number of dimensions for output. Given a set of features :math:`X = {x_1, x_2, ..., x_m}` +number of dimensions for output. Given a set of features :math:`X = \{x_1, x_2, ..., x_m\}` and a target :math:`y`, it can learn a non-linear function approximator for either classification or regression. It is different from logistic regression, in that between the input and the output layer, there can be one or more non-linear @@ -78,7 +78,7 @@ Classification ============== Class :class:`MLPClassifier` implements a multi-layer perceptron (MLP) algorithm -that trains using `Backpropagation `_. +that trains using `Backpropagation `_. MLP trains on two arrays: array X of size (n_samples, n_features), which holds the training samples represented as floating point feature vectors; and array @@ -233,7 +233,7 @@ training. .. dropdown:: Mathematical formulation - Given a set of training examples :math:`(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)` + Given a set of training examples :math:`\{(x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n)\}` where :math:`x_i \in \mathbf{R}^n` and :math:`y_i \in \{0, 1\}`, a one hidden layer one hidden neuron MLP learns the function :math:`f(x) = W_2 g(W_1^T x + b_1) + b_2` where :math:`W_1 \in \mathbf{R}^m` and :math:`W_2, b_1, b_2 \in \mathbf{R}` are diff --git a/doc/modules/outlier_detection.rst b/doc/modules/outlier_detection.rst index 7de2da4f1818e..bdb6b1aeacdbf 100644 --- a/doc/modules/outlier_detection.rst +++ b/doc/modules/outlier_detection.rst @@ -411,7 +411,8 @@ Note that ``fit_predict`` is not available in this case to avoid inconsistencies The scores of abnormality of the training samples are always accessible through the ``negative_outlier_factor_`` attribute. -Novelty detection with Local Outlier Factor is illustrated below. +Novelty detection with :class:`neighbors.LocalOutlierFactor` is illustrated below +(see :ref:`sphx_glr_auto_examples_neighbors_plot_lof_novelty_detection.py`). .. figure:: ../auto_examples/neighbors/images/sphx_glr_plot_lof_novelty_detection_001.png :target: ../auto_examples/neighbors/plot_lof_novelty_detection.html diff --git a/doc/modules/partial_dependence.rst b/doc/modules/partial_dependence.rst index 083b23c1f1c91..7f30a3a7e6731 100644 --- a/doc/modules/partial_dependence.rst +++ b/doc/modules/partial_dependence.rst @@ -211,11 +211,11 @@ Computation methods =================== There are two main methods to approximate the integral above, namely the -'brute' and 'recursion' methods. The `method` parameter controls which method +`'brute'` and `'recursion'` methods. The `method` parameter controls which method to use. -The 'brute' method is a generic method that works with any estimator. Note that -computing ICE plots is only supported with the 'brute' method. It +The `'brute'` method is a generic method that works with any estimator. Note that +computing ICE plots is only supported with the `'brute'` method. It approximates the above integral by computing an average over the data `X`: .. math:: @@ -231,7 +231,7 @@ at :math:`x_{S}`. Computing this for multiple values of :math:`x_{S}`, one obtains a full ICE line. As one can see, the average of the ICE lines corresponds to the partial dependence line. -The 'recursion' method is faster than the 'brute' method, but it is only +The `'recursion'` method is faster than the `'brute'` method, but it is only supported for PDP plots by some tree-based estimators. It is computed as follows. For a given point :math:`x_S`, a weighted tree traversal is performed: if a split node involves an input feature of interest, the corresponding left @@ -240,12 +240,12 @@ being weighted by the fraction of training samples that entered that branch. Finally, the partial dependence is given by a weighted average of all the visited leaves' values. -With the 'brute' method, the parameter `X` is used both for generating the +With the `'brute'` method, the parameter `X` is used both for generating the grid of values :math:`x_S` and the complement feature values :math:`x_C`. However with the 'recursion' method, `X` is only used for the grid values: implicitly, the :math:`x_C` values are those of the training data. -By default, the 'recursion' method is used for plotting PDPs on tree-based +By default, the `'recursion'` method is used for plotting PDPs on tree-based estimators that support it, and 'brute' is used for the rest. .. _pdp_method_differences: @@ -253,10 +253,10 @@ estimators that support it, and 'brute' is used for the rest. .. note:: While both methods should be close in general, they might differ in some - specific settings. The 'brute' method assumes the existence of the + specific settings. The `'brute'` method assumes the existence of the data points :math:`(x_S, x_C^{(i)})`. When the features are correlated, - such artificial samples may have a very low probability mass. The 'brute' - and 'recursion' methods will likely disagree regarding the value of the + such artificial samples may have a very low probability mass. The `'brute'` + and `'recursion'` methods will likely disagree regarding the value of the partial dependence, because they will treat these unlikely samples differently. Remember, however, that the primary assumption for interpreting PDPs is that the features should be independent. diff --git a/doc/modules/sgd.rst b/doc/modules/sgd.rst index 103ae205387e3..360ba2f11c994 100644 --- a/doc/modules/sgd.rst +++ b/doc/modules/sgd.rst @@ -71,7 +71,7 @@ penalties for classification. Below is the decision boundary of a As other classifiers, SGD has to be fitted with two arrays: an array `X` of shape (n_samples, n_features) holding the training samples, and an -array y of shape (n_samples,) holding the target values (class labels) +array `y` of shape (n_samples,) holding the target values (class labels) for the training samples:: >>> from sklearn.linear_model import SGDClassifier @@ -114,8 +114,8 @@ parameter. :class:`SGDClassifier` supports the following loss functions: * ``loss="hinge"``: (soft-margin) linear Support Vector Machine, * ``loss="modified_huber"``: smoothed hinge loss, * ``loss="log_loss"``: logistic regression, -* and all regression losses below. In this case the target is encoded as -1 - or 1, and the problem is treated as a regression problem. The predicted +* and all regression losses below. In this case the target is encoded as :math:`-1` + or :math:`1`, and the problem is treated as a regression problem. The predicted class then corresponds to the sign of the predicted target. Please refer to the :ref:`mathematical section below @@ -123,7 +123,7 @@ Please refer to the :ref:`mathematical section below The first two loss functions are lazy, they only update the model parameters if an example violates the margin constraint, which makes training very efficient and may result in sparser models (i.e. with more zero -coefficients), even when L2 penalty is used. +coefficients), even when :math:`L_2` penalty is used. Using ``loss="log_loss"`` or ``loss="modified_huber"`` enables the ``predict_proba`` method, which gives a vector of probability estimates @@ -136,16 +136,16 @@ Using ``loss="log_loss"`` or ``loss="modified_huber"`` enables the The concrete penalty can be set via the ``penalty`` parameter. SGD supports the following penalties: -* ``penalty="l2"``: L2 norm penalty on ``coef_``. -* ``penalty="l1"``: L1 norm penalty on ``coef_``. -* ``penalty="elasticnet"``: Convex combination of L2 and L1; +* ``penalty="l2"``: :math:`L_2` norm penalty on ``coef_``. +* ``penalty="l1"``: :math:`L_1` norm penalty on ``coef_``. +* ``penalty="elasticnet"``: Convex combination of :math:`L_2` and :math:`L_1`; ``(1 - l1_ratio) * L2 + l1_ratio * L1``. -The default setting is ``penalty="l2"``. The L1 penalty leads to sparse +The default setting is ``penalty="l2"``. The :math:`L_1` penalty leads to sparse solutions, driving most coefficients to zero. The Elastic Net [#5]_ solves -some deficiencies of the L1 penalty in the presence of highly correlated +some deficiencies of the :math:`L_1` penalty in the presence of highly correlated attributes. The parameter ``l1_ratio`` controls the convex combination -of L1 and L2 penalty. +of :math:`L_1` and :math:`L_2` penalty. :class:`SGDClassifier` supports multi-class classification by combining multiple binary classifiers in a "one versus all" (OVA) scheme. For each @@ -164,8 +164,8 @@ the decision surface induced by the three classifiers. In the case of multi-class classification ``coef_`` is a two-dimensional array of shape (n_classes, n_features) and ``intercept_`` is a -one-dimensional array of shape (n_classes,). The i-th row of ``coef_`` holds -the weight vector of the OVA classifier for the i-th class; classes are +one-dimensional array of shape (n_classes,). The :math:`i`-th row of ``coef_`` holds +the weight vector of the OVA classifier for the :math:`i`-th class; classes are indexed in ascending order (see attribute ``classes_``). Note that, in principle, since they allow to create a probability model, ``loss="log_loss"`` and ``loss="modified_huber"`` are more suitable for @@ -227,10 +227,14 @@ description above in the classification section). :class:`SGDRegressor` also supports averaged SGD [#4]_ (here again, see description above in the classification section). -For regression with a squared loss and a l2 penalty, another variant of +For regression with a squared loss and a :math:`L_2` penalty, another variant of SGD with an averaging strategy is available with Stochastic Average Gradient (SAG) algorithm, available as a solver in :class:`Ridge`. +.. rubric:: Examples + +- :ref:`sphx_glr_auto_examples_applications_plot_prediction_latency.py` + .. _sgd_online_one_class_svm: Online One-Class SVM @@ -245,7 +249,7 @@ solution of a kernelized One-Class SVM, implemented in samples. Note that the complexity of a kernelized One-Class SVM is at best quadratic in the number of samples. :class:`sklearn.linear_model.SGDOneClassSVM` is thus well suited for datasets -with a large number of training samples (> 10,000) for which the SGD +with a large number of training samples (over 10,000) for which the SGD variant can be several orders of magnitude faster. .. dropdown:: Mathematical details @@ -280,7 +284,7 @@ variant can be several orders of magnitude faster. This is similar to the optimization problems studied in section :ref:`sgd_mathematical_formulation` with :math:`y_i = 1, 1 \leq i \leq n` and :math:`\alpha = \nu/2`, :math:`L` being the hinge loss function and :math:`R` - being the L2 norm. We just need to add the term :math:`b\nu` in the + being the :math:`L_2` norm. We just need to add the term :math:`b\nu` in the optimization loop. As :class:`SGDClassifier` and :class:`SGDRegressor`, :class:`SGDOneClassSVM` @@ -312,8 +316,9 @@ Complexity ========== The major advantage of SGD is its efficiency, which is basically -linear in the number of training examples. If X is a matrix of size (n, p) -training has a cost of :math:`O(k n \bar p)`, where k is the number +linear in the number of training examples. If :math:`X` is a matrix of size +:math:`n \times p` (with :math:`n` samples and :math:`p` features), +training has a cost of :math:`O(k n \bar p)`, where :math:`k` is the number of iterations (epochs) and :math:`\bar p` is the average number of non-zero attributes per sample. @@ -348,8 +353,8 @@ Tips on Practical Use * Stochastic Gradient Descent is sensitive to feature scaling, so it is highly recommended to scale your data. For example, scale each - attribute on the input vector X to [0,1] or [-1,+1], or standardize - it to have mean 0 and variance 1. Note that the *same* scaling must be + attribute on the input vector :math:`X` to :math:`[0,1]` or :math:`[-1,1]`, or standardize + it to have mean :math:`0` and variance :math:`1`. Note that the *same* scaling must be applied to the test vector to obtain meaningful results. This can be easily done using :class:`~sklearn.preprocessing.StandardScaler`:: @@ -375,16 +380,16 @@ Tips on Practical Use range ``10.0**-np.arange(1,7)``. * Empirically, we found that SGD converges after observing - approximately 10^6 training samples. Thus, a reasonable first guess + approximately :math:`10^6` training samples. Thus, a reasonable first guess for the number of iterations is ``max_iter = np.ceil(10**6 / n)``, where ``n`` is the size of the training set. * If you apply SGD to features extracted using PCA we found that it is often wise to scale the feature values by some constant `c` - such that the average L2 norm of the training data equals one. + such that the average :math:`L_2` norm of the training data equals one. * We found that Averaged SGD works best with a larger number of features - and a higher eta0. + and a higher `eta0`. .. rubric:: References @@ -400,7 +405,7 @@ Mathematical formulation We describe here the mathematical details of the SGD procedure. A good overview with convergence rates can be found in [#6]_. -Given a set of training examples :math:`(x_1, y_1), \ldots, (x_n, y_n)` where +Given a set of training examples :math:`\{(x_1, y_1), \ldots, (x_n, y_n)\}` where :math:`x_i \in \mathbf{R}^m` and :math:`y_i \in \mathbf{R}` (:math:`y_i \in \{-1, 1\}` for classification), our goal is to learn a linear scoring function @@ -452,11 +457,11 @@ misclassification error (Zero-one loss) as shown in the Figure below. Popular choices for the regularization term :math:`R` (the `penalty` parameter) include: -- L2 norm: :math:`R(w) := \frac{1}{2} \sum_{j=1}^{m} w_j^2 = ||w||_2^2`, -- L1 norm: :math:`R(w) := \sum_{j=1}^{m} |w_j|`, which leads to sparse +- :math:`L_2` norm: :math:`R(w) := \frac{1}{2} \sum_{j=1}^{m} w_j^2 = ||w||_2^2`, +- :math:`L_1` norm: :math:`R(w) := \sum_{j=1}^{m} |w_j|`, which leads to sparse solutions. - Elastic Net: :math:`R(w) := \frac{\rho}{2} \sum_{j=1}^{n} w_j^2 + - (1-\rho) \sum_{j=1}^{m} |w_j|`, a convex combination of L2 and L1, where + (1-\rho) \sum_{j=1}^{m} |w_j|`, a convex combination of :math:`L_2` and :math:`L_1`, where :math:`\rho` is given by ``1 - l1_ratio``. The Figure below shows the contours of the different regularization terms @@ -500,8 +505,8 @@ is given by where :math:`t` is the time step (there are a total of `n_samples * n_iter` time steps), :math:`t_0` is determined based on a heuristic proposed by Léon Bottou such that the expected initial updates are comparable with the expected -size of the weights (this assuming that the norm of the training samples is -approx. 1). The exact definition can be found in ``_init_t`` in `BaseSGD`. +size of the weights (this assumes that the norm of the training samples is +approximately 1). The exact definition can be found in ``_init_t`` in `BaseSGD`. For regression the default learning rate schedule is inverse scaling @@ -509,10 +514,10 @@ For regression the default learning rate schedule is inverse scaling .. math:: - \eta^{(t)} = \frac{eta_0}{t^{power\_t}} + \eta^{(t)} = \frac{\eta_0}{t^{power\_t}} -where :math:`eta_0` and :math:`power\_t` are hyperparameters chosen by the -user via ``eta0`` and ``power_t``, resp. +where :math:`\eta_0` and :math:`power\_t` are hyperparameters chosen by the +user via ``eta0`` and ``power_t``, respectively. For a constant learning rate use ``learning_rate='constant'`` and use ``eta0`` to specify the learning rate. @@ -520,7 +525,7 @@ to specify the learning rate. For an adaptively decreasing learning rate, use ``learning_rate='adaptive'`` and use ``eta0`` to specify the starting learning rate. When the stopping criterion is reached, the learning rate is divided by 5, and the algorithm -does not stop. The algorithm stops when the learning rate goes below 1e-6. +does not stop. The algorithm stops when the learning rate goes below `1e-6`. The model parameters can be accessed through the ``coef_`` and ``intercept_`` attributes: ``coef_`` holds the weights :math:`w` and @@ -540,7 +545,7 @@ The implementation of SGD is influenced by the `Stochastic Gradient SVM` of [#1]_. Similar to SvmSGD, the weight vector is represented as the product of a scalar and a vector -which allows an efficient weight update in the case of L2 regularization. +which allows an efficient weight update in the case of :math:`L_2` regularization. In the case of sparse input `X`, the intercept is updated with a smaller learning rate (multiplied by 0.01) to account for the fact that it is updated more frequently. Training examples are picked up sequentially @@ -548,7 +553,7 @@ and the learning rate is lowered after each observed example. We adopted the learning rate schedule from [#2]_. For multi-class classification, a "one versus all" approach is used. We use the truncated gradient algorithm proposed in [#3]_ -for L1 regularization (and the Elastic Net). +for :math:`L_1` regularization (and the Elastic Net). The code is written in Cython. .. rubric:: References diff --git a/doc/modules/svm.rst b/doc/modules/svm.rst index ac9fbdb12e58d..dc912a289ed46 100644 --- a/doc/modules/svm.rst +++ b/doc/modules/svm.rst @@ -119,15 +119,14 @@ properties of these support vectors can be found in attributes Multi-class classification -------------------------- -:class:`SVC` and :class:`NuSVC` implement the "one-versus-one" -approach for multi-class classification. In total, +:class:`SVC` and :class:`NuSVC` implement the "one-versus-one" ("ovo") +approach for multi-class classification, which constructs ``n_classes * (n_classes - 1) / 2`` -classifiers are constructed and each one trains data from two classes. -To provide a consistent interface with other classifiers, the -``decision_function_shape`` option allows to monotonically transform the -results of the "one-versus-one" classifiers to a "one-vs-rest" decision -function of shape ``(n_samples, n_classes)``, which is the default setting -of the parameter (default='ovr'). +classifiers, each trained on data from two classes. Internally, the solver +always uses this "ovo" strategy to train the models. However, by default, the +`decision_function_shape` parameter is set to `"ovr"` ("one-vs-rest"), to have +a consistent interface with other classifiers by monotonically transforming the "ovo" +decision function into an "ovr" decision function of shape ``(n_samples, n_classes)``. >>> X = [[0], [1], [2], [3]] >>> Y = [0, 1, 2, 3] @@ -142,7 +141,7 @@ of the parameter (default='ovr'). >>> dec.shape[1] # 4 classes 4 -On the other hand, :class:`LinearSVC` implements "one-vs-the-rest" +On the other hand, :class:`LinearSVC` implements a "one-vs-rest" ("ovr") multi-class strategy, thus training `n_classes` models. >>> lin_clf = svm.LinearSVC() diff --git a/doc/scss/custom.scss b/doc/scss/custom.scss index cac81b03e7ce2..ed95c15276e1f 100644 --- a/doc/scss/custom.scss +++ b/doc/scss/custom.scss @@ -251,3 +251,14 @@ div.sk-text-image-grid-small { div.sk-text-image-grid-large { @include sk-text-image-grid(100px); } + +/* Responsive three-column grid list */ +.grid-list-three-columns { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + + @media screen and (max-width: 500px) { + grid-template-columns: 1fr; + } +} diff --git a/doc/templates/index.html b/doc/templates/index.html index 0f0cecf7fed96..2c18e822f8cda 100644 --- a/doc/templates/index.html +++ b/doc/templates/index.html @@ -206,15 +206,15 @@

News

    -
  • On-going development: scikit-learn 1.7 (Changelog).
  • +
  • On-going development: scikit-learn 1.8 (Changelog).
  • +
  • September 2025. scikit-learn 1.7.2 is available for download (Changelog).
  • +
  • July 2025. scikit-learn 1.7.1 is available for download (Changelog).
  • +
  • June 2025. scikit-learn 1.7.0 is available for download (Changelog).
  • January 2025. scikit-learn 1.6.1 is available for download (Changelog).
  • December 2024. scikit-learn 1.6.0 is available for download (Changelog).
  • September 2024. scikit-learn 1.5.2 is available for download (Changelog).
  • July 2024. scikit-learn 1.5.1 is available for download (Changelog).
  • May 2024. scikit-learn 1.5.0 is available for download (Changelog).
  • -
  • April 2024. scikit-learn 1.4.2 is available for download (Changelog).
  • -
  • February 2024. scikit-learn 1.4.1.post1 is available for download (Changelog).
  • -
  • January 2024. scikit-learn 1.4.0 is available for download (Changelog).
  • All releases: What's new (Changelog).
@@ -224,7 +224,7 @@

Community

  • About us: See people and contributing
  • More Machine Learning: Find related projects
  • -
  • Questions? See FAQ, support, and stackoverflow
  • +
  • Questions? See FAQ, Support, Discussions, and Stack Overflow
  • Subscribe to the mailing list
  • Blog: blog.scikit-learn.org
  • Logos & Branding: logos and branding
  • diff --git a/doc/visualizations.rst b/doc/visualizations.rst index 412dfc001fab1..e9d38f25e1e0d 100644 --- a/doc/visualizations.rst +++ b/doc/visualizations.rst @@ -8,37 +8,86 @@ Scikit-learn defines a simple API for creating visualizations for machine learning. The key feature of this API is to allow for quick plotting and visual adjustments without recalculation. We provide `Display` classes that expose two methods for creating plots: `from_estimator` and -`from_predictions`. The `from_estimator` method will take a fitted estimator -and some data (`X` and `y`) and create a `Display` object. Sometimes, we would -like to only compute the predictions once and one should use `from_predictions` -instead. In the following example, we plot a ROC curve for a fitted support -vector machine: +`from_predictions`. + +The `from_estimator` method generates a `Display` object from a fitted estimator, +input data (`X`, `y`), and a plot. +The `from_predictions` method creates a `Display` object from true and predicted +values (`y_test`, `y_pred`), and a plot. + +Using `from_predictions` avoids having to recompute predictions, +but the user needs to take care that the prediction values passed correspond +to the `pos_label`. For :term:`predict_proba`, select the column corresponding +to the `pos_label` class while for :term:`decision_function`, revert the score +(i.e. multiply by -1) if `pos_label` is not the last class in the +`classes_` attribute of your estimator. + +The `Display` object stores the computed values (e.g., metric values or +feature importance) required for plotting with Matplotlib. These values are the +results derived from the raw predictions passed to `from_predictions`, or +an estimator and `X` passed to `from_estimator`. + +Display objects have a plot method that creates a matplotlib plot once the display +object has been initialized (note that we recommend that display objects are created +via `from_estimator` or `from_predictions` instead of initialized directly). +The plot method allows adding to an existing plot by passing the existing plots +:class:`matplotlib.axes.Axes` to the `ax` parameter. + +In the following example, we plot a ROC curve for a fitted Logistic Regression +model `from_estimator`: .. plot:: :context: close-figs :align: center from sklearn.model_selection import train_test_split - from sklearn.svm import SVC + from sklearn.linear_model import LogisticRegression from sklearn.metrics import RocCurveDisplay - from sklearn.datasets import load_wine + from sklearn.datasets import load_iris - X, y = load_wine(return_X_y=True) + X, y = load_iris(return_X_y=True) y = y == 2 # make binary - X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42) - svc = SVC(random_state=42) - svc.fit(X_train, y_train) + X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=.8, random_state=42 + ) + clf = LogisticRegression(random_state=42, C=.01) + clf.fit(X_train, y_train) - svc_disp = RocCurveDisplay.from_estimator(svc, X_test, y_test) + clf_disp = RocCurveDisplay.from_estimator(clf, X_test, y_test) -The returned `svc_disp` object allows us to continue using the already computed -ROC curve for SVC in future plots. In this case, the `svc_disp` is a -:class:`~sklearn.metrics.RocCurveDisplay` that stores the computed values as -attributes called `roc_auc`, `fpr`, and `tpr`. Be aware that we could get -the predictions from the support vector machine and then use `from_predictions` -instead of `from_estimator`. Next, we train a random forest classifier and plot -the previously computed ROC curve again by using the `plot` method of the -`Display` object. +If you already have the prediction values, you could instead use +`from_predictions` to do the same thing (and save on compute): + + +.. plot:: + :context: close-figs + :align: center + + from sklearn.model_selection import train_test_split + from sklearn.linear_model import LogisticRegression + from sklearn.metrics import RocCurveDisplay + from sklearn.datasets import load_iris + + X, y = load_iris(return_X_y=True) + y = y == 2 # make binary + X_train, X_test, y_train, y_test = train_test_split( + X, y, test_size=.8, random_state=42 + ) + clf = LogisticRegression(random_state=42, C=.01) + clf.fit(X_train, y_train) + + # select the probability of the class that we considered to be the positive label + y_pred = clf.predict_proba(X_test)[:, 1] + + clf_disp = RocCurveDisplay.from_predictions(y_test, y_pred) + + +The returned `clf_disp` object allows us to add another curve to the already computed +ROC curve. In this case, the `clf_disp` is a :class:`~sklearn.metrics.RocCurveDisplay` +that stores the computed values as attributes called `roc_auc`, `fpr`, and `tpr`. + +Next, we train a random forest classifier and plot the previously computed ROC curve +again by using the `plot` method of the `Display` object. .. plot:: :context: close-figs @@ -51,12 +100,15 @@ the previously computed ROC curve again by using the `plot` method of the rfc.fit(X_train, y_train) ax = plt.gca() - rfc_disp = RocCurveDisplay.from_estimator(rfc, X_test, y_test, ax=ax, alpha=0.8) - svc_disp.plot(ax=ax, alpha=0.8) + rfc_disp = RocCurveDisplay.from_estimator( + rfc, X_test, y_test, ax=ax, curve_kwargs={"alpha": 0.8} + ) + clf_disp.plot(ax=ax, curve_kwargs={"alpha": 0.8}) Notice that we pass `alpha=0.8` to the plot functions to adjust the alpha values of the curves. + .. rubric:: Examples * :ref:`sphx_glr_auto_examples_miscellaneous_plot_roc_curve_visualization_api.py` diff --git a/doc/whats_new/upcoming_changes/array-api/29519.feature.rst b/doc/whats_new/upcoming_changes/array-api/29519.feature.rst deleted file mode 100644 index 19f800ee45b4b..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/29519.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :func:`sklearn.utils.check_consistent_length` now supports Array API compatible - inputs. - By :user:`Stefanie Senger ` diff --git a/doc/whats_new/upcoming_changes/array-api/29978.feature.rst b/doc/whats_new/upcoming_changes/array-api/29978.feature.rst deleted file mode 100644 index 16cbd174a3dfa..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/29978.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :func:`sklearn.metrics.explained_variance_score` and - :func:`sklearn.metrics.mean_pinball_loss` now support Array API compatible inputs. - By :user:`Virgil Chan ` diff --git a/doc/whats_new/upcoming_changes/array-api/30340.other.rst b/doc/whats_new/upcoming_changes/array-api/30340.other.rst deleted file mode 100644 index 38053567080f4..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/30340.other.rst +++ /dev/null @@ -1,4 +0,0 @@ -- array-api-compat and array-api-extra are now vendored within the - scikit-learn source. Users of the experimental array API standard - support no longer need to install array-api-compat in their environment. - by :user:`Lucas Colley ` diff --git a/doc/whats_new/upcoming_changes/array-api/30395.feature.rst b/doc/whats_new/upcoming_changes/array-api/30395.feature.rst deleted file mode 100644 index 739ea20071dfc..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/30395.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func:`sklearn.metrics.fbeta_score`, - :func:`sklearn.metrics.precision_score` and - :func:`sklearn.metrics.recall_score` now support Array API compatible inputs. - By :user:`Omar Salman ` diff --git a/doc/whats_new/upcoming_changes/array-api/30819.feature.rst b/doc/whats_new/upcoming_changes/array-api/30819.feature.rst deleted file mode 100644 index 56955d73ae903..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/30819.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -- :func:`sklearn.utils.extmath.randomized_svd` now support Array API compatible inputs. - By :user:`Connor Lane ` and :user:`Jérémie du Boisberranger `. diff --git a/doc/whats_new/upcoming_changes/array-api/30838.feature.rst b/doc/whats_new/upcoming_changes/array-api/30838.feature.rst deleted file mode 100644 index f733f1c6476a6..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/30838.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -- :func:`sklearn.metrics.hamming_loss` now support Array API compatible inputs. - By :user:`Thomas Li ` diff --git a/doc/whats_new/upcoming_changes/array-api/31190.feature.rst b/doc/whats_new/upcoming_changes/array-api/31190.feature.rst deleted file mode 100644 index 15504c0e28fce..0000000000000 --- a/doc/whats_new/upcoming_changes/array-api/31190.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -- :class:`preprocessing.Binarizer` now supports Array API compatible inputs. - By :user:`Yaroslav Korobko `, :user:`Olivier Grisel `, and :user:`Thomas Li `. diff --git a/doc/whats_new/upcoming_changes/many-modules/30858.other.rst b/doc/whats_new/upcoming_changes/many-modules/30858.other.rst deleted file mode 100644 index 5e2441cf5c95e..0000000000000 --- a/doc/whats_new/upcoming_changes/many-modules/30858.other.rst +++ /dev/null @@ -1,7 +0,0 @@ - -- Sparse update: As part of the SciPy change from spmatrix to sparray, all - internal use of sparse now supports both sparray and spmatrix. - All manipulations of sparse objects should work for either spmatrix or sparray. - This is pass 1 of a migration toward sparray (see - `SciPy migration to sparray `_ - By :user:`Dan Schult ` diff --git a/doc/whats_new/upcoming_changes/metadata-routing/30833.feature.rst b/doc/whats_new/upcoming_changes/metadata-routing/30833.feature.rst deleted file mode 100644 index e46420e9ee2d2..0000000000000 --- a/doc/whats_new/upcoming_changes/metadata-routing/30833.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :class:`ensemble.BaggingClassifier` and :class:`ensemble.BaggingRegressor` now support - metadata routing through their `predict`, `predict_proba`, `predict_log_proba` and - `decision_function` methods and pass `**params` to the underlying estimators. - By :user:`Stefanie Senger `. diff --git a/doc/whats_new/upcoming_changes/sklearn.calibration/30873.fix.rst b/doc/whats_new/upcoming_changes/sklearn.calibration/30873.fix.rst deleted file mode 100644 index 3e438622f4918..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.calibration/30873.fix.rst +++ /dev/null @@ -1,7 +0,0 @@ -- :class:`~calibration.CalibratedClassifierCV` now raises `FutureWarning` - instead of `UserWarning` when passing `cv="prefit`". By - :user:`Olivier Grisel ` -- :class:`~calibration.CalibratedClassifierCV` with `method="sigmoid"` no - longer crashes when passing `float64`-dtyped `sample_weight` along with a - base estimator that outputs `float32`-dtyped predictions. By :user:`Olivier - Grisel ` diff --git a/doc/whats_new/upcoming_changes/sklearn.compose/31167.api.rst b/doc/whats_new/upcoming_changes/sklearn.compose/31167.api.rst deleted file mode 100644 index 5f25cbac65020..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.compose/31167.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -- The `force_int_remainder_cols` parameter of :class:`compose.ColumnTransformer` and - :func:`compose.make_column_transformer` is deprecated and will be removed in 1.9. - It has no effect. - By :user:`Jérémie du Boisberranger ` diff --git a/doc/whats_new/upcoming_changes/sklearn.covariance/30483.fix.rst b/doc/whats_new/upcoming_changes/sklearn.covariance/30483.fix.rst deleted file mode 100644 index 4329c5a2696fd..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.covariance/30483.fix.rst +++ /dev/null @@ -1,2 +0,0 @@ -- Support for ``n_samples == n_features`` in `sklearn.covariance.MinCovDet` has - been restored. By :user:`Antony Lee `. diff --git a/doc/whats_new/upcoming_changes/sklearn.datasets/30196.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.datasets/30196.enhancement.rst deleted file mode 100644 index d044d039badd2..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.datasets/30196.enhancement.rst +++ /dev/null @@ -1,3 +0,0 @@ -- New parameter ``return_X_y`` added to :func:`datasets.make_classification`. The - default value of the parameter does not change how the function behaves. - By :user:`Success Moses ` and :user:`Adam Cooper ` diff --git a/doc/whats_new/upcoming_changes/sklearn.decomposition/30443.feature.rst b/doc/whats_new/upcoming_changes/sklearn.decomposition/30443.feature.rst deleted file mode 100644 index 5678039b69065..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.decomposition/30443.feature.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :class:`~sklearn.decomposition.DictionaryLearning`, - :class:`~sklearn.decomposition.SparseCoder` and - :class:`~sklearn.decomposition.MiniBatchDictionaryLearning` now have a - ``inverse_transform`` method. By :user:`Rémi Flamary ` diff --git a/doc/whats_new/upcoming_changes/sklearn.ensemble/27124.feature.rst b/doc/whats_new/upcoming_changes/sklearn.ensemble/27124.feature.rst deleted file mode 100644 index 2087efb00d779..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.ensemble/27124.feature.rst +++ /dev/null @@ -1,6 +0,0 @@ -- :class:`ensemble.HistGradientBoostingClassifier` and - :class:`ensemble.HistGradientBoostingRegressor` allow for more control over the - validation set used for early stopping. You can now pass data to be used for - validation directly to `fit` via the arguments `X_val`, `y_val` and - `sample_weight_val`. - By :user:`Christian Lorentzen `. diff --git a/doc/whats_new/upcoming_changes/sklearn.ensemble/30649.fix.rst b/doc/whats_new/upcoming_changes/sklearn.ensemble/30649.fix.rst deleted file mode 100644 index 43ad381fb5ca8..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.ensemble/30649.fix.rst +++ /dev/null @@ -1,2 +0,0 @@ -- :class:`ensemble.VotingClassifier` and :class:`ensemble.VotingRegressor` - validate `estimators` to make sure it is a list of tuples. By `Thomas Fan`_. diff --git a/doc/whats_new/upcoming_changes/sklearn.feature_selection/30179.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.feature_selection/30179.enhancement.rst deleted file mode 100644 index 6eec68c0d95e7..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.feature_selection/30179.enhancement.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :class:`feature_selection.RFECV` now gives access to the ranking and support in each - iteration and cv step of feature selection. - By :user:`Marie S. ` diff --git a/doc/whats_new/upcoming_changes/sklearn.feature_selection/31107.fix.rst b/doc/whats_new/upcoming_changes/sklearn.feature_selection/31107.fix.rst deleted file mode 100644 index b5ca4ab283434..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.feature_selection/31107.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :class:`feature_selection.SelectFromModel` now correctly works when the estimator - is an instance of :class:`linear_model.ElasticNetCV` with its `l1_ratio` parameter - being an array-like. - By :user:`Vasco Pereira `. diff --git a/doc/whats_new/upcoming_changes/sklearn.gaussian_process/22227.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.gaussian_process/22227.enhancement.rst deleted file mode 100644 index bcc9825f30978..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.gaussian_process/22227.enhancement.rst +++ /dev/null @@ -1 +0,0 @@ -- :class:`gaussian_process.GaussianProcessClassifier` now includes a `latent_mean_and_variance` method that exposes the mean and the variance of the latent function, :math:`f`, used in the Laplace approximation. By :user:`Miguel González Duque ` diff --git a/doc/whats_new/upcoming_changes/sklearn.inspection/26202.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.inspection/26202.enhancement.rst deleted file mode 100644 index 666d55a24c577..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.inspection/26202.enhancement.rst +++ /dev/null @@ -1,5 +0,0 @@ -- Add `custom_values` parameter in :func:`inspection.partial_dependence`. It enables - users to pass their own grid of values at which the partial dependence should be - calculated. - By :user:`Freddy A. Boulton ` and :user:`Stephen Pardy - ` diff --git a/doc/whats_new/upcoming_changes/sklearn.inspection/29797.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.inspection/29797.enhancement.rst deleted file mode 100644 index 2b16d7e2bf6be..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.inspection/29797.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :class:`inspection.DecisionBoundaryDisplay` now supports - plotting all classes for multi-class problems when `response_method` is - 'decision_function', 'predict_proba' or 'auto'. - By :user:`Lucy Liu ` diff --git a/doc/whats_new/upcoming_changes/sklearn.inspection/30409.api.rst b/doc/whats_new/upcoming_changes/sklearn.inspection/30409.api.rst deleted file mode 100644 index ab73ed4bd1afd..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.inspection/30409.api.rst +++ /dev/null @@ -1,5 +0,0 @@ -- :func:`inspection.partial_dependence` does no longer accept integer dtype for - numerical feature columns. Explicit conversion to floating point values is - now required before calling this tool (and preferably even before fitting the - model to inspect). - By :user:`Olivier Grisel ` diff --git a/doc/whats_new/upcoming_changes/sklearn.inspection/31146.fix.rst b/doc/whats_new/upcoming_changes/sklearn.inspection/31146.fix.rst deleted file mode 100644 index 2cd7d6eed61f5..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.inspection/31146.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func:`inspection.partial_dependence` now raises an informative error when passing - an empty list as the `categorical_features` parameter. `None` should be used instead - to indicate that no categorical features are present. - By :user:`Pedro Lopes `. diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30057.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30057.fix.rst deleted file mode 100644 index 94ed332295b9b..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/30057.fix.rst +++ /dev/null @@ -1,5 +0,0 @@ -- :class:`linear_model.LogisticRegression` and - :class:`linear_model.LogisticRegressionCV` now properly pass sample weights to - :func:`utils.class_weight.compute_class_weight` when fit with - `class_weight="balanced"`. - By :user:`Shruti Nath ` and :user:`Olivier Grisel ` diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30521.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30521.fix.rst deleted file mode 100644 index 951da8f2627b4..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/30521.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- Added a new parameter `tol` to - :class:`linear_model.LinearRegression` that determines the precision of the - solution `coef_` when fitting on sparse data. - By :user:`Success Moses ` diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30616.api.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30616.api.rst deleted file mode 100644 index 2b9d30e445bcf..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/30616.api.rst +++ /dev/null @@ -1,9 +0,0 @@ -- The parameter `n_alphas` has been deprecated in the following classes: - :class:`linear_model.ElasticNetCV` and :class:`linear_model.LassoCV` - and :class:`linear_model.MultiTaskElasticNetCV` - and :class:`linear_model.MultiTaskLassoCV`, and will be removed in 1.9. The parameter - `alphas` now supports both integers and array-likes, removing the need for `n_alphas`. - From now on, only `alphas` should be set to either indicate the number of alphas to - automatically generate (int) or to provide a list of alphas (array-like) to test along - the regularization path. - By :user:`Siddharth Bansal `. diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30644.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30644.fix.rst deleted file mode 100644 index 9c8a85b080617..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/30644.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- The update and initialization of the hyperparameters now properly handle - sample weights in :class:`linear_model.BayesianRidge`. - By :user:`Antoine Baker `. diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30730.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30730.enhancement.rst deleted file mode 100644 index 91638cbcd9c7a..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/30730.enhancement.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :class:`linear_model.SGDClassifier` and :class:`linear_model.SGDRegressor` now accept - `l1_ratio=None` when `penalty` is not `"elasticnet"`. - By :user:`Marc Bresson `. diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/31094.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/31094.fix.rst deleted file mode 100644 index b65d96bccd7d2..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/31094.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :class:`linear_model.BayesianRidge` now uses the full SVD to correctly estimate - the posterior covariance matrix `sigma_` when `n_samples < n_features`. - By :user:`Antoine Baker ` diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/31241.api.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/31241.api.rst deleted file mode 100644 index 9cd97143e29c7..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.linear_model/31241.api.rst +++ /dev/null @@ -1,7 +0,0 @@ -- Using the `"liblinear"` solver for multiclass classification with a one-versus-rest - scheme in :class:`linear_model.LogisticRegression` and - :class:`linear_model.LogisticRegressionCV` is deprecated and will raise an error in - version 1.8. Either use a solver which supports the multinomial loss or wrap the - estimator in a :class:`sklearn.multiclass.OneVsRestClassifier` to keep applying a - one-versus-rest scheme. - By :user:`Jérémie du Boisberranger `. diff --git a/doc/whats_new/upcoming_changes/sklearn.manifold/30514.fix.rst b/doc/whats_new/upcoming_changes/sklearn.manifold/30514.fix.rst deleted file mode 100644 index 7f4e4104446dc..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.manifold/30514.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :class:`manifold.MDS` now correctly handles non-metric MDS. Furthermore, - the returned stress value now corresponds to the returned embedding and - normalized stress is now allowed for metric MDS. - By :user:`Dmitry Kobak ` diff --git a/doc/whats_new/upcoming_changes/sklearn.manifold/31117.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.manifold/31117.enhancement.rst deleted file mode 100644 index 87b6896890163..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.manifold/31117.enhancement.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :class:`manifold.MDS` will switch to use `n_init=1` by default, - starting from version 1.9. - By :user:`Dmitry Kobak ` diff --git a/doc/whats_new/upcoming_changes/sklearn.manifold/31117.fix.rst b/doc/whats_new/upcoming_changes/sklearn.manifold/31117.fix.rst deleted file mode 100644 index 6248a23b86546..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.manifold/31117.fix.rst +++ /dev/null @@ -1,5 +0,0 @@ -- :class:`manifold.MDS` now uses `eps=1e-6` by default and the convergence - criterion was adjusted to make sense for both metric and non-metric MDS - and to follow the reference R implementation. The formula for normalized - stress was adjusted to follow the original definition by Kruskal. - By :user:`Dmitry Kobak ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/22046.feature.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/22046.feature.rst deleted file mode 100644 index dbe9166aa1314..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/22046.feature.rst +++ /dev/null @@ -1,6 +0,0 @@ -- :func:`metrics.brier_score_loss` implements the Brier score for multiclass - classification problems and adds a `scale_by_half` argument. This metric is - notably useful to assess both sharpness and calibration of probabilistic - classifiers. See the docstrings for more details. By - :user:`Varun Aggarwal `, :user:`Olivier Grisel ` and - :user:`Antoine Baker `. diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/22046.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/22046.fix.rst deleted file mode 100644 index 7ba041f2686cf..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/22046.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :func:`metrics.log_loss` now raises a `ValueError` if values of `y_true` - are missing in `labels`. By :user:`Varun Aggarwal `, - :user:`Olivier Grisel ` and :user:`Antoine Baker `. diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/28981.api.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/28981.api.rst deleted file mode 100644 index 6cc771d6a0d45..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/28981.api.rst +++ /dev/null @@ -1,3 +0,0 @@ -- The `sparse` parameter of :func:`metrics.fowlkes_mallows_score` is deprecated and - will be removed in 1.9. It has no effect. - By :user:`Luc Rocher `. diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29151.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29151.enhancement.rst deleted file mode 100644 index fc552703f2512..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29151.enhancement.rst +++ /dev/null @@ -1,6 +0,0 @@ -- :func:`metrics.det_curve`, :class:`metrics.DetCurveDisplay.from_estimator`, - and :class:`metrics.DetCurveDisplay.from_estimator` now accept a - `drop_intermediate` option to drop thresholds where true positives (tp) do not - change from the previous or subsequent thresholds. All points with the same tp - value have the same `fnr` and thus same y coordinate in a DET curve. - By :user:`Arturo Amor ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29151.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29151.fix.rst deleted file mode 100644 index 61cf97e9b27f6..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29151.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func:`metrics.det_curve` and :class:`metrics.DetCurveDisplay` now return an - extra threshold at infinity where the classifier always predicts the negative - class i.e. tps = fps = 0. - By :user:`Arturo Amor ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29288.api.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29288.api.rst deleted file mode 100644 index 1c8e15d714391..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29288.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -- The `raise_warning` parameter of :func:`metrics.class_likelihood_ratios` is deprecated - and will be removed in 1.9. An `UndefinedMetricWarning` will always be raised in case - of a division by zero. - By :user:`Stefanie Senger `. diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29288.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29288.enhancement.rst deleted file mode 100644 index e6e682a333f86..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29288.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func:`~metrics.class_likelihood_ratios` now has a `replace_undefined_by` param. - When there is a division by zero, the metric is undefined and the set values are - returned for `LR+` and `LR-`. - By :user:`Stefanie Senger ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29288.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29288.fix.rst deleted file mode 100644 index 23237b3923668..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29288.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :func:`~metrics.class_likelihood_ratios` now raises `UndefinedMetricWarning` instead - of `UserWarning` when a division by zero occurs. - By :user:`Stefanie Senger ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29727.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29727.fix.rst deleted file mode 100644 index b25de83128504..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29727.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :class:`metrics.RocCurveDisplay` will no longer set a legend when - `label` is `None` in both the `line_kwargs` and the `chance_level_kw`. - By :user:`Arturo Amor ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29865.api.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29865.api.rst deleted file mode 100644 index 60ea7d83de71f..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/29865.api.rst +++ /dev/null @@ -1,4 +0,0 @@ -- In :meth:`sklearn.metrics.RocCurveDisplay.from_predictions`, - the argument `y_pred` has been renamed to `y_score` to better reflect its purpose. - `y_pred` will be removed in 1.9. - By :user:`Bagus Tris Atmaja ` in diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/30903.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/30903.fix.rst deleted file mode 100644 index 90250f427dc20..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/30903.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :func:`~metrics.d2_log_loss_score` now properly handles the case when `labels` is - passed and not all of the labels are present in `y_true`. - By :user:`Vassilis Margonis ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/31065.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/31065.fix.rst deleted file mode 100644 index 82126da7852cc..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/31065.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- Fix :func:`metrics.adjusted_mutual_info_score` numerical issue when number of - classes and samples is low. - By :user:`Hleb Levitski ` diff --git a/doc/whats_new/upcoming_changes/sklearn.mixture/28559.feature.rst b/doc/whats_new/upcoming_changes/sklearn.mixture/28559.feature.rst deleted file mode 100644 index 31da86d63c0f7..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.mixture/28559.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -- Added an attribute `lower_bounds_` in the :class:`mixture.BaseMixture` - class to save the list of lower bounds for each iteration thereby providing - insights into the convergence behavior of mixture models like - :class:`mixture.GaussianMixture`. - By :user:`Manideep Yenugula ` diff --git a/doc/whats_new/upcoming_changes/sklearn.mixture/30414.efficiency.rst b/doc/whats_new/upcoming_changes/sklearn.mixture/30414.efficiency.rst deleted file mode 100644 index 401ebb65916bb..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.mixture/30414.efficiency.rst +++ /dev/null @@ -1,4 +0,0 @@ -- Simplified redundant computation when estimating covariances in - :class:`~mixture.GaussianMixture` with a `covariance_type="spherical"` or - `covariance_type="diag"`. - By :user:`Leonce Mekinda ` and :user:`Olivier Grisel ` diff --git a/doc/whats_new/upcoming_changes/sklearn.mixture/30415.efficiency.rst b/doc/whats_new/upcoming_changes/sklearn.mixture/30415.efficiency.rst deleted file mode 100644 index 095ef66ce28c0..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.mixture/30415.efficiency.rst +++ /dev/null @@ -1,5 +0,0 @@ -- :class:`~mixture.GaussianMixture` now consistently operates at `float32` - precision when fitted with `float32` data to improve training speed and - memory efficiency. Previously, part of the computation would be implicitly - cast to `float64`. By :user:`Olivier Grisel ` and :user:`Omar Salman - `. diff --git a/doc/whats_new/upcoming_changes/sklearn.model_selection/30743.fix.rst b/doc/whats_new/upcoming_changes/sklearn.model_selection/30743.fix.rst deleted file mode 100644 index 8e091f55b2e31..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.model_selection/30743.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- Hyper-parameter optimizers such as :class:`model_selection.GridSearchCV` - now forward `sample_weight` to the scorer even when metadata routing is not enabled. - By :user:`Antoine Baker ` diff --git a/doc/whats_new/upcoming_changes/sklearn.multiclass/31228.fix.rst b/doc/whats_new/upcoming_changes/sklearn.multiclass/31228.fix.rst deleted file mode 100644 index 68056db580fd7..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.multiclass/31228.fix.rst +++ /dev/null @@ -1,5 +0,0 @@ -- The `predict_proba` method of :class:`sklearn.multiclass.OneVsRestClassifier` now - returns zero for all classes when all inner estimators never predict their positive - class. - By :user:`Luis M. B. Varona `, :user:`Marc Bresson `, and - :user:`Jérémie du Boisberranger `. diff --git a/doc/whats_new/upcoming_changes/sklearn.multioutput/30152.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.multioutput/30152.enhancement.rst deleted file mode 100644 index 3bc2ae2f6ced4..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.multioutput/30152.enhancement.rst +++ /dev/null @@ -1,3 +0,0 @@ -- The parameter `base_estimator` has been deprecated in favour of `estimator` for - :class:`multioutput.RegressorChain` and :class:`multioutput.ClassifierChain`. - By :user:`Success Moses ` and :user:`dikraMasrour ` diff --git a/doc/whats_new/upcoming_changes/sklearn.neural_network/24788.fix.rst b/doc/whats_new/upcoming_changes/sklearn.neural_network/24788.fix.rst deleted file mode 100644 index dc2742e9a04d8..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.neural_network/24788.fix.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :class:`neural_network.MLPRegressor` now raises an informative error when - `early_stopping` is set and the computed validation set is too small. - By :user:`David Shumway `. diff --git a/doc/whats_new/upcoming_changes/sklearn.neural_network/30155.feature.rst b/doc/whats_new/upcoming_changes/sklearn.neural_network/30155.feature.rst deleted file mode 100644 index 4fcf738072e5e..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.neural_network/30155.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -- Added support for `sample_weight` in :class:`neural_network.MLPClassifier` and - :class:`neural_network.MLPRegressor`. - By :user:`Zach Shu ` and :user:`Christian Lorentzen ` diff --git a/doc/whats_new/upcoming_changes/sklearn.neural_network/30712.feature.rst b/doc/whats_new/upcoming_changes/sklearn.neural_network/30712.feature.rst deleted file mode 100644 index e8ad9882ff0f0..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.neural_network/30712.feature.rst +++ /dev/null @@ -1,3 +0,0 @@ -- Added parameter for `loss` in :class:`neural_network.MLPRegressor` with options - `"squared_error"` (default) and `"poisson"` (new). - By :user:`Christian Lorentzen ` diff --git a/doc/whats_new/upcoming_changes/sklearn.pipeline/30406.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.pipeline/30406.enhancement.rst deleted file mode 100644 index 8e2a5f6242392..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.pipeline/30406.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- Expose the ``verbose_feature_names_out`` argument in the - :func:`pipeline.make_union` function, allowing users to control - feature name uniqueness in the :class:`pipeline.FeatureUnion`. - By :user:`Abhijeetsingh Meena ` diff --git a/doc/whats_new/upcoming_changes/sklearn.preprocessing/29907.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.preprocessing/29907.enhancement.rst deleted file mode 100644 index 0ce9249cc94fb..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.preprocessing/29907.enhancement.rst +++ /dev/null @@ -1,6 +0,0 @@ -- :class:`preprocessing.KBinsDiscretizer` with `strategy="uniform"` now - accepts `sample_weight`. Additionally with `strategy="quantile"` the - `quantile_method` can now be specified (in the future - `quantile_method="averaged_inverted_cdf"` will become the default). - By :user:`Shruti Nath ` and :user:`Olivier Grisel - ` diff --git a/doc/whats_new/upcoming_changes/sklearn.preprocessing/29907.fix.rst b/doc/whats_new/upcoming_changes/sklearn.preprocessing/29907.fix.rst deleted file mode 100644 index d2f61e099c5eb..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.preprocessing/29907.fix.rst +++ /dev/null @@ -1,6 +0,0 @@ -- :class:`preprocessing.KBinsDiscretizer` now uses weighted resampling when - sample weights are given and subsampling is used. This may change results - even when not using sample weights, although in absolute and not in terms - of statistical properties. - By :user:`Shruti Nath ` and :user:`Jérémie du Boisberranger - ` diff --git a/doc/whats_new/upcoming_changes/sklearn.preprocessing/31227.fix.rst b/doc/whats_new/upcoming_changes/sklearn.preprocessing/31227.fix.rst deleted file mode 100644 index 803517760a822..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.preprocessing/31227.fix.rst +++ /dev/null @@ -1,6 +0,0 @@ -- Now using ``scipy.stats.yeojohnson`` instead of our own implementation of the Yeo-Johnson transform. - Fixed numerical stability (mostly overflows) of the Yeo-Johnson transform with - `PowerTransformer(method="yeo-johnson")` when scipy version is `>= 1.12`. - Initial PR by :user:`Xuefeng Xu ` completed by :user:`Mohamed Yaich `, - :user:`Oussama Er-rabie `, :user:`Mohammed Yaslam Dlimi `, - :user:`Hamza Zaroual `, :user:`Amine Hannoun ` and :user:`Sylvain Marié `. \ No newline at end of file diff --git a/doc/whats_new/upcoming_changes/sklearn.svm/30057.fix.rst b/doc/whats_new/upcoming_changes/sklearn.svm/30057.fix.rst deleted file mode 100644 index 5951e0dd2a0c0..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.svm/30057.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :class:`svm.LinearSVC` now properly passes sample weights to - :func:`utils.class_weight.compute_class_weight` when fit with - `class_weight="balanced"`. - By :user:`Shruti Nath ` diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/26335.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.utils/26335.enhancement.rst deleted file mode 100644 index 9a82ab4f02675..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/26335.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func:`utils.multiclass.type_of_target` raises a warning when the number - of unique classes is greater than 50% of the number of samples. This warning is raised - only if `y` has more than 20 samples. - By :user:`Rahil Parikh `. diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/29907.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.utils/29907.enhancement.rst deleted file mode 100644 index 0a17e5d1d1ae1..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/29907.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func: `resample` now handles sample weights which allows - weighted resampling. - By :user:`Shruti Nath ` and :user:`Olivier Grisel - ` diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/30057.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.utils/30057.enhancement.rst deleted file mode 100644 index 8ca10c884c9b3..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/30057.enhancement.rst +++ /dev/null @@ -1,3 +0,0 @@ -- :func:`utils.class_weight.compute_class_weight` now properly accounts for - sample weights when using strategy "balanced" to calculate class weights. - By :user:`Shruti Nath ` diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/30380.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.utils/30380.enhancement.rst deleted file mode 100644 index bd1eaf9213257..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/30380.enhancement.rst +++ /dev/null @@ -1,2 +0,0 @@ -- Warning filters from the main process are propagated to joblib workers. - By `Thomas Fan`_ diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/30775.fix.rst b/doc/whats_new/upcoming_changes/sklearn.utils/30775.fix.rst deleted file mode 100644 index bd383a70c2bba..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/30775.fix.rst +++ /dev/null @@ -1,5 +0,0 @@ -- In :mod:`utils.estimator_checks` we now enforce for binary classifiers a - binary `y` by taking the minimum as the negative class instead of the first - element, which makes it robust to `y` shuffling. It prevents two checks from - wrongly failing on binary classifiers. - By :user:`Antoine Baker `. diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/30819.fix.rst b/doc/whats_new/upcoming_changes/sklearn.utils/30819.fix.rst deleted file mode 100644 index 81c7564023ac1..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/30819.fix.rst +++ /dev/null @@ -1,4 +0,0 @@ -- :func:`utils.extmath.randomized_svd` and :func:`utils.extmath.randomized_range_finder` - now validate their input array to fail early with an informative error message on - invalid input. - By :user:`Connor Lane `. diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/31040.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.utils/31040.enhancement.rst deleted file mode 100644 index 096a98cb176bc..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.utils/31040.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- The private helper function :func:`utils._safe_indexing` now officially supports - pyarrow data. For instance, passing a pyarrow `Table` as `X` in a - :class:`compose.ColumnTransformer` is now possible. - By :user:`Christian Lorentzen ` diff --git a/doc/whats_new/v0.20.rst b/doc/whats_new/v0.20.rst index 1bd4a6cd2af9a..a7d43d2d45d85 100644 --- a/doc/whats_new/v0.20.rst +++ b/doc/whats_new/v0.20.rst @@ -445,7 +445,7 @@ Miscellaneous - |API| Removed all mentions of ``sklearn.externals.joblib``, and deprecated joblib methods exposed in ``sklearn.utils``, except for - :func:`utils.parallel_backend` and :func:`utils.register_parallel_backend`, + `utils.parallel_backend` and `utils.register_parallel_backend`, which allow users to configure parallel computation in scikit-learn. Other functionalities are part of `joblib `_. package and should be used directly, by installing it. diff --git a/doc/whats_new/v1.3.rst b/doc/whats_new/v1.3.rst index f523c02e14447..e581f451fc741 100644 --- a/doc/whats_new/v1.3.rst +++ b/doc/whats_new/v1.3.rst @@ -770,7 +770,7 @@ Changelog :func:`model_selection.validation_curve`. :pr:`25120` by :user:`Guillaume Lemaitre `. -- |API| The parameter `log_scale` in the class +- |API| The parameter `log_scale` in the method `plot` of the class :class:`model_selection.LearningCurveDisplay` has been deprecated in 1.3 and will be removed in 1.5. The default scale can be overridden by setting it directly on the `ax` object and will be set automatically from the spacing diff --git a/doc/whats_new/v1.4.rst b/doc/whats_new/v1.4.rst index 3dfcde90c9e81..c90ffc5865af7 100644 --- a/doc/whats_new/v1.4.rst +++ b/doc/whats_new/v1.4.rst @@ -989,7 +989,7 @@ Changelog when the input is a Series instead of a DataFrame. :pr:`28090` by :user:`Stan Furrer ` and :user:`Yao Xiao `. -- |API| :func:`sklearn.extmath.log_logistic` is deprecated and will be removed in 1.6. +- |API| :func:`sklearn.utils.extmath.log_logistic` is deprecated and will be removed in 1.6. Use `-np.logaddexp(0, -x)` instead. :pr:`27544` by :user:`Christian Lorentzen `. diff --git a/doc/whats_new/v1.5.rst b/doc/whats_new/v1.5.rst index 1ce5aa4839426..502e669d1e702 100644 --- a/doc/whats_new/v1.5.rst +++ b/doc/whats_new/v1.5.rst @@ -347,12 +347,13 @@ Changelog the previous predicted values were not affected by this bug. :pr:`28612` by :user:`Guillaume Lemaitre `. -- |API| Deprecates `Y` in favor of `y` in the methods fit, transform and - inverse_transform of: - :class:`cross_decomposition.PLSRegression`. - :class:`cross_decomposition.PLSCanonical`, - :class:`cross_decomposition.CCA`, - and :class:`cross_decomposition.PLSSVD`. +- |API| Deprecates `Y` in favor of `y` in the methods `fit`, `transform` and + `inverse_transform` of: + :class:`cross_decomposition.PLSRegression`, + :class:`cross_decomposition.PLSCanonical`, + and :class:`cross_decomposition.CCA`, + and methods `fit` and `transform` of: + :class:`cross_decomposition.PLSSVD`. `Y` will be removed in version 1.7. :pr:`28604` by :user:`David Leon `. @@ -502,13 +503,13 @@ Changelog - |API| Parameter `multi_class` was deprecated in :class:`linear_model.LogisticRegression` and - :class:`linear_model.LogisticRegressionCV`. `multi_class` will be removed in 1.7, + :class:`linear_model.LogisticRegressionCV`. `multi_class` will be removed in 1.8, and internally, for 3 and more classes, it will always use multinomial. If you still want to use the one-vs-rest scheme, you can use `OneVsRestClassifier(LogisticRegression(..))`. :pr:`28703` by :user:`Christian Lorentzen `. -- |API| `store_cv_values` and `cv_values_` are deprecated in favor of +- |API| Parameters `store_cv_values` and `cv_values_` are deprecated in favor of `store_cv_results` and `cv_results_` in `~linear_model.RidgeCV` and `~linear_model.RidgeClassifierCV`. :pr:`28915` by :user:`Lucy Liu `. @@ -656,7 +657,7 @@ Changelog - |API| :func:`utils.tosequence` is deprecated and will be removed in version 1.7. :pr:`28763` by :user:`Jérémie du Boisberranger `. -- |API| :class:`utils.parallel_backend` and :func:`utils.register_parallel_backend` are +- |API| `utils.parallel_backend` and `utils.register_parallel_backend` are deprecated and will be removed in version 1.7. Use `joblib.parallel_backend` and `joblib.register_parallel_backend` instead. :pr:`28847` by :user:`Jérémie du Boisberranger `. diff --git a/doc/whats_new/v1.7.rst b/doc/whats_new/v1.7.rst index 9043f8ac6d0d4..feaa97a951b5c 100644 --- a/doc/whats_new/v1.7.rst +++ b/doc/whats_new/v1.7.rst @@ -8,27 +8,617 @@ Version 1.7 =========== -.. - -- UNCOMMENT WHEN 1.7.0 IS RELEASED -- - For a short description of the main highlights of the release, please refer to - :ref:`sphx_glr_auto_examples_release_highlights_plot_release_highlights_1_6_0.py`. - - -.. - DELETE WHEN 1.7.0 IS RELEASED - Since October 2024, DO NOT add your changelog entry in this file. -.. - Instead, create a file named `..rst` in the relevant sub-folder in - `doc/whats_new/upcoming_changes/`. For full details, see: - https://github.com/scikit-learn/scikit-learn/blob/main/doc/whats_new/upcoming_changes/README.md +For a short description of the main highlights of the release, please refer to +:ref:`sphx_glr_auto_examples_release_highlights_plot_release_highlights_1_7_0.py`. .. include:: changelog_legend.inc .. towncrier release notes start +.. _changes_1_7_2: + +Version 1.7.2 +============= + +**September 2025** + +:mod:`sklearn.compose` +---------------------- + +- |Fix| :class:`compose.TransformedTargetRegressor` now passes the transformed target to + the regressor with the same number of dimensions as the original target. + By :user:`kryggird `. :pr:`31563` + +:mod:`sklearn.feature_extraction` +--------------------------------- + +- |Fix| Set the tag `requires_fit=False` for the classes + :class:`feature_extraction.FeatureHasher` and + :class:`feature_extraction.text.HashingVectorizer`. + By :user:`hakan çanakcı `. :pr:`31851` + +:mod:`sklearn.impute` +--------------------- + +- |Fix| Fixed a bug in :class:`impute.SimpleImputer` with `strategy="most_frequent"` + when there is a tie in the most frequent value and the input data has mixed types. + By :user:`Alexandre Abraham `. :pr:`31820` + +:mod:`sklearn.linear_model` +--------------------------- + +- |Fix| Fixed a bug with `solver="newton-cholesky"` on multi-class problems in + :class:`linear_model.LogisticRegressionCV` and in + :class:`linear_model.LogisticRegression` when used with `warm_start=True`. The bug + appeared either with `fit_intercept=True` or with `penalty=None` (both resulting in + unpenalized parameters for the solver). The coefficients and intercepts of the last + class as provided by warm start were partially wrongly overwritten by zero. + By :user:`Christian Lorentzen `. :pr:`31866` + +:mod:`sklearn.pipeline` +----------------------- + +- |Fix| :class:`pipeline.FeatureUnion` now validates that all transformers return 2D + outputs and raises an informative error when transformers return 1D outputs, + preventing silent failures that previously produced meaningless concatenated results. + By :user:`gguiomar `. :pr:`31559` + +.. _changes_1_7_1: + +Version 1.7.1 +============= + +**July 2025** + +:mod:`sklearn.base` +------------------- + +- |Fix| Fix regression in HTML representation when detecting the non-default parameters + that where of array-like types. + By :user:`Dea María Léon ` :pr:`31528` + +:mod:`sklearn.compose` +---------------------- + +- |Fix| :class:`compose.ColumnTransformer` now correctly preserves non-default index + when mixing pandas Series and Dataframes. + By :user:`Nicolas Bolle `. :pr:`31079` + +:mod:`sklearn.datasets` +----------------------- + +- |Fix| Fixed a regression preventing to extract the downloaded dataset in + :func:`datasets.fetch_20newsgroups`, :func:`datasets.fetch_20newsgroups_vectorized`, + :func:`datasets.fetch_lfw_people` and :func:`datasets.fetch_lfw_pairs`. This + only affects Python versions `>=3.10.0,<=3.10.11` and `>=3.11.0,<=3.11.3`. + By :user:`Jérémie du Boisberranger `. :pr:`31685` + +:mod:`sklearn.inspection` +------------------------- + +- |Fix| Fix multiple issues in the multiclass setting of :class:`inspection.DecisionBoundaryDisplay`: + + - `contour` plotting now correctly shows the decision boundary. + - `cmap` and `colors` are now properly ignored in favor of `multiclass_colors`. + - Linear segmented colormaps are now fully supported. + + By :user:`Yunjie Lin ` :pr:`31553` + +:mod:`sklearn.naive_bayes` +-------------------------- + +- |Fix| :class:`naive_bayes.CategoricalNB` now correctly declares that it accepts + categorical features in the tags returned by its `__sklearn_tags__` method. + By :user:`Olivier Grisel ` :pr:`31556` + +:mod:`sklearn.utils` +-------------------- + +- |Fix| Fixed a spurious warning (about the number of unique classes being + greater than 50% of the number of samples) that could occur when + passing `classes` :func:`utils.multiclass.type_of_target`. + By :user:`Sascha D. Krauss `. :pr:`31584` + +.. _changes_1_7_0: + +Version 1.7.0 +============= + +**June 2025** + +Changed models +-------------- + +- |Fix| Change the `ConvergenceWarning` message of estimators that rely on the + `"lbfgs"` optimizer internally to be more informative and to avoid + suggesting to increase the maximum number of iterations when it is not + user-settable or when the convergence problem happens before reaching it. + By :user:`Olivier Grisel `. :pr:`31316` + +Changes impacting many modules +------------------------------ + +- Sparse update: As part of the SciPy change from spmatrix to sparray, all + internal use of sparse now supports both sparray and spmatrix. + All manipulations of sparse objects should work for either spmatrix or sparray. + This is pass 1 of a migration toward sparray (see + `SciPy migration to sparray `_ + By :user:`Dan Schult ` :pr:`30858` + +Support for Array API +--------------------- + +Additional estimators and functions have been updated to include support for all +`Array API `_ compliant inputs. + +See :ref:`array_api` for more details. + +- |Feature| :func:`sklearn.utils.check_consistent_length` now supports Array API compatible + inputs. + By :user:`Stefanie Senger ` :pr:`29519` + +- |Feature| :func:`sklearn.metrics.explained_variance_score` and + :func:`sklearn.metrics.mean_pinball_loss` now support Array API compatible inputs. + By :user:`Virgil Chan ` :pr:`29978` + +- |Feature| :func:`sklearn.metrics.fbeta_score`, + :func:`sklearn.metrics.precision_score` and + :func:`sklearn.metrics.recall_score` now support Array API compatible inputs. + By :user:`Omar Salman ` :pr:`30395` + +- |Feature| :func:`sklearn.utils.extmath.randomized_svd` now support Array API compatible inputs. + By :user:`Connor Lane ` and :user:`Jérémie du Boisberranger `. :pr:`30819` + +- |Feature| :func:`sklearn.metrics.hamming_loss` now support Array API compatible inputs. + By :user:`Thomas Li ` :pr:`30838` + +- |Feature| :class:`preprocessing.Binarizer` now supports Array API compatible inputs. + By :user:`Yaroslav Korobko `, :user:`Olivier Grisel `, and :user:`Thomas Li `. :pr:`31190` + +- |Feature| :func:`sklearn.metrics.jaccard_score` now supports Array API compatible inputs. + By :user:`Omar Salman ` :pr:`31204` + +- array-api-compat and array-api-extra are now vendored within the + scikit-learn source. Users of the experimental array API standard + support no longer need to install array-api-compat in their environment. + by :user:`Lucas Colley ` :pr:`30340` + +Metadata routing +---------------- + +Refer to the :ref:`Metadata Routing User Guide ` for +more details. + +- |Feature| :class:`ensemble.BaggingClassifier` and :class:`ensemble.BaggingRegressor` now support + metadata routing through their `predict`, `predict_proba`, `predict_log_proba` and + `decision_function` methods and pass `**params` to the underlying estimators. + By :user:`Stefanie Senger `. :pr:`30833` + +:mod:`sklearn.base` +------------------- + +- |Enhancement| :class:`base.BaseEstimator` now has a parameter table added to the + estimators HTML representation that can be visualized with jupyter. + By :user:`Guillaume Lemaitre ` and + :user:`Dea María Léon ` :pr:`30763` + +:mod:`sklearn.calibration` +-------------------------- + +- |Fix| :class:`~calibration.CalibratedClassifierCV` now raises `FutureWarning` + instead of `UserWarning` when passing `cv="prefit`". By + :user:`Olivier Grisel ` +- :class:`~calibration.CalibratedClassifierCV` with `method="sigmoid"` no + longer crashes when passing `float64`-dtyped `sample_weight` along with a + base estimator that outputs `float32`-dtyped predictions. By :user:`Olivier + Grisel ` :pr:`30873` + +:mod:`sklearn.compose` +---------------------- + +- |API| The `force_int_remainder_cols` parameter of :class:`compose.ColumnTransformer` and + :func:`compose.make_column_transformer` is deprecated and will be removed in 1.9. + It has no effect. + By :user:`Jérémie du Boisberranger ` :pr:`31167` + +:mod:`sklearn.covariance` +------------------------- + +- |Fix| Support for ``n_samples == n_features`` in `sklearn.covariance.MinCovDet` has + been restored. By :user:`Antony Lee `. :pr:`30483` + +:mod:`sklearn.datasets` +----------------------- + +- |Enhancement| New parameter ``return_X_y`` added to :func:`datasets.make_classification`. The + default value of the parameter does not change how the function behaves. + By :user:`Success Moses ` and :user:`Adam Cooper ` :pr:`30196` + +:mod:`sklearn.decomposition` +---------------------------- + +- |Feature| :class:`~sklearn.decomposition.DictionaryLearning`, + :class:`~sklearn.decomposition.SparseCoder` and + :class:`~sklearn.decomposition.MiniBatchDictionaryLearning` now have a + ``inverse_transform`` method. By :user:`Rémi Flamary ` :pr:`30443` + +:mod:`sklearn.ensemble` +----------------------- + +- |Feature| :class:`ensemble.HistGradientBoostingClassifier` and + :class:`ensemble.HistGradientBoostingRegressor` allow for more control over the + validation set used for early stopping. You can now pass data to be used for + validation directly to `fit` via the arguments `X_val`, `y_val` and + `sample_weight_val`. + By :user:`Christian Lorentzen `. :pr:`27124` + +- |Fix| :class:`ensemble.VotingClassifier` and :class:`ensemble.VotingRegressor` + validate `estimators` to make sure it is a list of tuples. By `Thomas Fan`_. :pr:`30649` + +:mod:`sklearn.feature_selection` +-------------------------------- + +- |Enhancement| :class:`feature_selection.RFECV` now gives access to the ranking and support in each + iteration and cv step of feature selection. + By :user:`Marie S. ` :pr:`30179` + +- |Fix| :class:`feature_selection.SelectFromModel` now correctly works when the estimator + is an instance of :class:`linear_model.ElasticNetCV` with its `l1_ratio` parameter + being an array-like. + By :user:`Vasco Pereira `. :pr:`31107` + +:mod:`sklearn.gaussian_process` +------------------------------- + +- |Enhancement| :class:`gaussian_process.GaussianProcessClassifier` now includes a `latent_mean_and_variance` method that exposes the mean and the variance of the latent function, :math:`f`, used in the Laplace approximation. By :user:`Miguel González Duque ` :pr:`22227` + +:mod:`sklearn.inspection` +------------------------- + +- |Enhancement| Add `custom_values` parameter in :func:`inspection.partial_dependence`. It enables + users to pass their own grid of values at which the partial dependence should be + calculated. + By :user:`Freddy A. Boulton ` and :user:`Stephen Pardy + ` :pr:`26202` + +- |Enhancement| :class:`inspection.DecisionBoundaryDisplay` now supports + plotting all classes for multi-class problems when `response_method` is + 'decision_function', 'predict_proba' or 'auto'. + By :user:`Lucy Liu ` :pr:`29797` + +- |Fix| :func:`inspection.partial_dependence` now raises an informative error when passing + an empty list as the `categorical_features` parameter. `None` should be used instead + to indicate that no categorical features are present. + By :user:`Pedro Lopes `. :pr:`31146` + +- |API| :func:`inspection.partial_dependence` does no longer accept integer dtype for + numerical feature columns. Explicit conversion to floating point values is + now required before calling this tool (and preferably even before fitting the + model to inspect). + By :user:`Olivier Grisel ` :pr:`30409` + +:mod:`sklearn.linear_model` +--------------------------- + +- |Enhancement| :class:`linear_model.SGDClassifier` and :class:`linear_model.SGDRegressor` now accept + `l1_ratio=None` when `penalty` is not `"elasticnet"`. + By :user:`Marc Bresson `. :pr:`30730` + +- |Enhancement| Fitting :class:`linear_model.Lasso` and :class:`linear_model.ElasticNet` with + `fit_intercept=True` is faster for sparse input `X` because an unnecessary + re-computation of the sum of residuals is avoided. + By :user:`Christian Lorentzen ` :pr:`31387` + +- |Fix| :class:`linear_model.LogisticRegression` and + :class:`linear_model.LogisticRegressionCV` now properly pass sample weights to + :func:`utils.class_weight.compute_class_weight` when fit with + `class_weight="balanced"`. + By :user:`Shruti Nath ` and :user:`Olivier Grisel ` :pr:`30057` + +- |Fix| Added a new parameter `tol` to + :class:`linear_model.LinearRegression` that determines the precision of the + solution `coef_` when fitting on sparse data. + By :user:`Success Moses ` :pr:`30521` + +- |Fix| The update and initialization of the hyperparameters now properly handle + sample weights in :class:`linear_model.BayesianRidge`. + By :user:`Antoine Baker `. :pr:`30644` + +- |Fix| :class:`linear_model.BayesianRidge` now uses the full SVD to correctly estimate + the posterior covariance matrix `sigma_` when `n_samples < n_features`. + By :user:`Antoine Baker ` :pr:`31094` + +- |API| The parameter `n_alphas` has been deprecated in the following classes: + :class:`linear_model.ElasticNetCV` and :class:`linear_model.LassoCV` + and :class:`linear_model.MultiTaskElasticNetCV` + and :class:`linear_model.MultiTaskLassoCV`, and will be removed in 1.9. The parameter + `alphas` now supports both integers and array-likes, removing the need for `n_alphas`. + From now on, only `alphas` should be set to either indicate the number of alphas to + automatically generate (int) or to provide a list of alphas (array-like) to test along + the regularization path. + By :user:`Siddharth Bansal `. :pr:`30616` + +- |API| Using the `"liblinear"` solver for multiclass classification with a one-versus-rest + scheme in :class:`linear_model.LogisticRegression` and + :class:`linear_model.LogisticRegressionCV` is deprecated and will raise an error in + version 1.8. Either use a solver which supports the multinomial loss or wrap the + estimator in a :class:`sklearn.multiclass.OneVsRestClassifier` to keep applying a + one-versus-rest scheme. + By :user:`Jérémie du Boisberranger `. :pr:`31241` + +:mod:`sklearn.manifold` +----------------------- + +- |Enhancement| :class:`manifold.MDS` will switch to use `n_init=1` by default, + starting from version 1.9. + By :user:`Dmitry Kobak ` :pr:`31117` + +- |Fix| :class:`manifold.MDS` now correctly handles non-metric MDS. Furthermore, + the returned stress value now corresponds to the returned embedding and + normalized stress is now allowed for metric MDS. + By :user:`Dmitry Kobak ` :pr:`30514` + +- |Fix| :class:`manifold.MDS` now uses `eps=1e-6` by default and the convergence + criterion was adjusted to make sense for both metric and non-metric MDS + and to follow the reference R implementation. The formula for normalized + stress was adjusted to follow the original definition by Kruskal. + By :user:`Dmitry Kobak ` :pr:`31117` + +:mod:`sklearn.metrics` +---------------------- + +- |Feature| :func:`metrics.brier_score_loss` implements the Brier score for multiclass + classification problems and adds a `scale_by_half` argument. This metric is + notably useful to assess both sharpness and calibration of probabilistic + classifiers. See the docstrings for more details. By + :user:`Varun Aggarwal `, :user:`Olivier Grisel ` and + :user:`Antoine Baker `. :pr:`22046` + +- |Feature| Add class method `from_cv_results` to :class:`metrics.RocCurveDisplay`, which allows + easy plotting of multiple ROC curves from :func:`model_selection.cross_validate` + results. + By :user:`Lucy Liu ` :pr:`30399` + +- |Enhancement| :func:`metrics.det_curve`, :class:`metrics.DetCurveDisplay.from_estimator`, + and :class:`metrics.DetCurveDisplay.from_estimator` now accept a + `drop_intermediate` option to drop thresholds where true positives (tp) do not + change from the previous or subsequent thresholds. All points with the same tp + value have the same `fnr` and thus same y coordinate in a DET curve. + By :user:`Arturo Amor ` :pr:`29151` + +- |Enhancement| :func:`~metrics.class_likelihood_ratios` now has a `replace_undefined_by` param. + When there is a division by zero, the metric is undefined and the set values are + returned for `LR+` and `LR-`. + By :user:`Stefanie Senger ` :pr:`29288` + +- |Fix| :func:`metrics.log_loss` now raises a `ValueError` if values of `y_true` + are missing in `labels`. By :user:`Varun Aggarwal `, + :user:`Olivier Grisel ` and :user:`Antoine Baker `. :pr:`22046` + +- |Fix| :func:`metrics.det_curve` and :class:`metrics.DetCurveDisplay` now return an + extra threshold at infinity where the classifier always predicts the negative + class i.e. tps = fps = 0. + By :user:`Arturo Amor ` :pr:`29151` + +- |Fix| :func:`~metrics.class_likelihood_ratios` now raises `UndefinedMetricWarning` instead + of `UserWarning` when a division by zero occurs. + By :user:`Stefanie Senger ` :pr:`29288` + +- |Fix| :class:`metrics.RocCurveDisplay` will no longer set a legend when + `label` is `None` in both the `line_kwargs` and the `chance_level_kw`. + By :user:`Arturo Amor ` :pr:`29727` + +- |Fix| Additional `sample_weight` checking has been added to + :func:`metrics.mean_absolute_error`, + :func:`metrics.mean_pinball_loss`, + :func:`metrics.mean_absolute_percentage_error`, + :func:`metrics.mean_squared_error`, + :func:`metrics.root_mean_squared_error`, + :func:`metrics.mean_squared_log_error`, + :func:`metrics.root_mean_squared_log_error`, + :func:`metrics.explained_variance_score`, + :func:`metrics.r2_score`, + :func:`metrics.mean_tweedie_deviance`, + :func:`metrics.mean_poisson_deviance`, + :func:`metrics.mean_gamma_deviance` and + :func:`metrics.d2_tweedie_score`. + `sample_weight` can only be 1D, consistent to `y_true` and `y_pred` in length + or a scalar. + By :user:`Lucy Liu `. :pr:`30886` + +- |Fix| :func:`~metrics.d2_log_loss_score` now properly handles the case when `labels` is + passed and not all of the labels are present in `y_true`. + By :user:`Vassilis Margonis ` :pr:`30903` + +- |Fix| Fix :func:`metrics.adjusted_mutual_info_score` numerical issue when number of + classes and samples is low. + By :user:`Hleb Levitski ` :pr:`31065` + +- |API| The `sparse` parameter of :func:`metrics.fowlkes_mallows_score` is deprecated and + will be removed in 1.9. It has no effect. + By :user:`Luc Rocher `. :pr:`28981` + +- |API| The `raise_warning` parameter of :func:`metrics.class_likelihood_ratios` is deprecated + and will be removed in 1.9. An `UndefinedMetricWarning` will always be raised in case + of a division by zero. + By :user:`Stefanie Senger `. :pr:`29288` + +- |API| In :meth:`sklearn.metrics.RocCurveDisplay.from_predictions`, + the argument `y_pred` has been renamed to `y_score` to better reflect its purpose. + `y_pred` will be removed in 1.9. + By :user:`Bagus Tris Atmaja ` in :pr:`29865` + +:mod:`sklearn.mixture` +---------------------- + +- |Feature| Added an attribute `lower_bounds_` in the :class:`mixture.BaseMixture` + class to save the list of lower bounds for each iteration thereby providing + insights into the convergence behavior of mixture models like + :class:`mixture.GaussianMixture`. + By :user:`Manideep Yenugula ` :pr:`28559` + +- |Efficiency| Simplified redundant computation when estimating covariances in + :class:`~mixture.GaussianMixture` with a `covariance_type="spherical"` or + `covariance_type="diag"`. + By :user:`Leonce Mekinda ` and :user:`Olivier Grisel ` :pr:`30414` + +- |Efficiency| :class:`~mixture.GaussianMixture` now consistently operates at `float32` + precision when fitted with `float32` data to improve training speed and + memory efficiency. Previously, part of the computation would be implicitly + cast to `float64`. By :user:`Olivier Grisel ` and :user:`Omar Salman + `. :pr:`30415` + +:mod:`sklearn.model_selection` +------------------------------ + +- |Fix| Hyper-parameter optimizers such as :class:`model_selection.GridSearchCV` + now forward `sample_weight` to the scorer even when metadata routing is not enabled. + By :user:`Antoine Baker ` :pr:`30743` + +:mod:`sklearn.multiclass` +------------------------- + +- |Fix| The `predict_proba` method of :class:`sklearn.multiclass.OneVsRestClassifier` now + returns zero for all classes when all inner estimators never predict their positive + class. + By :user:`Luis M. B. Varona `, :user:`Marc Bresson `, and + :user:`Jérémie du Boisberranger `. :pr:`31228` + +:mod:`sklearn.multioutput` +-------------------------- + +- |Enhancement| The parameter `base_estimator` has been deprecated in favour of `estimator` for + :class:`multioutput.RegressorChain` and :class:`multioutput.ClassifierChain`. + By :user:`Success Moses ` and :user:`dikraMasrour ` :pr:`30152` + +:mod:`sklearn.neural_network` +----------------------------- + +- |Feature| Added support for `sample_weight` in :class:`neural_network.MLPClassifier` and + :class:`neural_network.MLPRegressor`. + By :user:`Zach Shu ` and :user:`Christian Lorentzen ` :pr:`30155` + +- |Feature| Added parameter for `loss` in :class:`neural_network.MLPRegressor` with options + `"squared_error"` (default) and `"poisson"` (new). + By :user:`Christian Lorentzen ` :pr:`30712` + +- |Fix| :class:`neural_network.MLPRegressor` now raises an informative error when + `early_stopping` is set and the computed validation set is too small. + By :user:`David Shumway `. :pr:`24788` + +:mod:`sklearn.pipeline` +----------------------- + +- |Enhancement| Expose the ``verbose_feature_names_out`` argument in the + :func:`pipeline.make_union` function, allowing users to control + feature name uniqueness in the :class:`pipeline.FeatureUnion`. + By :user:`Abhijeetsingh Meena ` :pr:`30406` + +:mod:`sklearn.preprocessing` +---------------------------- + +- |Enhancement| :class:`preprocessing.KBinsDiscretizer` with `strategy="uniform"` now + accepts `sample_weight`. Additionally with `strategy="quantile"` the + `quantile_method` can now be specified (in the future + `quantile_method="averaged_inverted_cdf"` will become the default). + By :user:`Shruti Nath ` and :user:`Olivier Grisel + ` :pr:`29907` + +- |Fix| :class:`preprocessing.KBinsDiscretizer` now uses weighted resampling when + sample weights are given and subsampling is used. This may change results + even when not using sample weights, although in absolute and not in terms + of statistical properties. + By :user:`Shruti Nath ` and :user:`Jérémie du Boisberranger + ` :pr:`29907` + +- |Fix| Now using ``scipy.stats.yeojohnson`` instead of our own implementation of the Yeo-Johnson transform. + Fixed numerical stability (mostly overflows) of the Yeo-Johnson transform with + `PowerTransformer(method="yeo-johnson")` when scipy version is `>= 1.12`. + Initial PR by :user:`Xuefeng Xu ` completed by :user:`Mohamed Yaich `, + :user:`Oussama Er-rabie `, :user:`Mohammed Yaslam Dlimi `, + :user:`Hamza Zaroual `, :user:`Amine Hannoun ` and :user:`Sylvain Marié `. :pr:`31227` + +:mod:`sklearn.svm` +------------------ + +- |Fix| :class:`svm.LinearSVC` now properly passes sample weights to + :func:`utils.class_weight.compute_class_weight` when fit with + `class_weight="balanced"`. + By :user:`Shruti Nath ` :pr:`30057` + +:mod:`sklearn.utils` +-------------------- + +- |Enhancement| :func:`utils.multiclass.type_of_target` raises a warning when the number + of unique classes is greater than 50% of the number of samples. This warning is raised + only if `y` has more than 20 samples. + By :user:`Rahil Parikh `. :pr:`26335` + +- |Enhancement| :func: `resample` now handles sample weights which allows + weighted resampling. + By :user:`Shruti Nath ` and :user:`Olivier Grisel + ` :pr:`29907` + +- |Enhancement| :func:`utils.class_weight.compute_class_weight` now properly accounts for + sample weights when using strategy "balanced" to calculate class weights. + By :user:`Shruti Nath ` :pr:`30057` + +- |Enhancement| Warning filters from the main process are propagated to joblib workers. + By `Thomas Fan`_ :pr:`30380` + +- |Enhancement| The private helper function :func:`utils._safe_indexing` now officially supports + pyarrow data. For instance, passing a pyarrow `Table` as `X` in a + :class:`compose.ColumnTransformer` is now possible. + By :user:`Christian Lorentzen ` :pr:`31040` + +- |Fix| In :mod:`utils.estimator_checks` we now enforce for binary classifiers a + binary `y` by taking the minimum as the negative class instead of the first + element, which makes it robust to `y` shuffling. It prevents two checks from + wrongly failing on binary classifiers. + By :user:`Antoine Baker `. :pr:`30775` + +- |Fix| :func:`utils.extmath.randomized_svd` and :func:`utils.extmath.randomized_range_finder` + now validate their input array to fail early with an informative error message on + invalid input. + By :user:`Connor Lane `. :pr:`30819` + .. rubric:: Code and documentation contributors Thanks to everyone who has contributed to the maintenance and improvement of -the project since version 1.7, including: +the project since version 1.6, including: -TODO: update at the time of the release. +4hm3d, Aaron Schumacher, Abhijeetsingh Meena, Acciaro Gennaro Daniele, +Achraf Tasfaout, Adriano Leão, Adrien Linares, Adrin Jalali, Agriya Khetarpal, +Aiden Frank, Aitsaid Azzedine Idir, ajay-sentry, Akanksha Mhadolkar, Alexandre +Abraham, Alfredo Saucedo, Anderson Chaves, Andres Guzman-Ballen, Aniruddha +Saha, antoinebaker, Antony Lee, Arjun S, ArthurDbrn, Arturo, Arturo Amor, ash, +Ashton Powell, ayoub.agouzoul, Ayrat, Bagus Tris Atmaja, Benjamin Danek, Boney +Patel, Camille Troillard, Chems Ben, Christian Lorentzen, Christian Veenhuis, +Christine P. Chai, claudio, Code_Blooded, Colas, Colin Coe, Connor Lane, Corey +Farwell, Daniel Agyapong, Dan Schult, Dea María Léon, Deepak Saldanha, +dependabot[bot], Dhyey Findoriya, Dimitri Papadopoulos Orfanos, Dmitry Kobak, +Domenico, elenafillo, Elham Babaei, emelia-hdz, EmilyXinyi, Emma Carballal, +Eric Larson, Eugen-Bleck, Evgeni Burovski, fabianhenning, Gael Varoquaux, +GaetandeCast, Gil Ramot, Gonçalo Guiomar, Gordon Grey, Goutam, G Sreeja, +Guillaume Lemaitre, Haesun Park, hakan çanakçı, Hanjun Kim, Helder Geovane +Gomes de Lima, Henri Bonamy, Hleb Levitski, Hugo Boulenger, IlyaSolomatin, +Irene, Jérémie du Boisberranger, Jérôme Dockès, JoaoRodriguesIST, Joel +Nothman, Joris Van den Bossche, Josh, jshn9515, KALLA GANASEKHAR, Kevin Klein, +Krishnan Vignesh, kryggird, Loic Esteve, Lucas Colley, Luc Rocher, Lucy Liu, +Luis M. B. Varona, lunovian, Mamduh Zabidi, Marc Bresson, Marco Edward Gorelli, +Marco Maggi, Marek Pokropiński, Maren Westermann, Marie Sacksick, Marija +Vlajic, Martin Jurča, Mayank Raj, Michael Burkhart, Miguel González Duque, +Mihir Waknis, Miro Hrončok, Mohamed Ali SRIR, Mohamed DHIFALLAH, mohammed +benyamna, Mohit Singh Thakur, Mounir Lbath, myenugula, Natalia Mokeeva, Nicolas +Bolle, Olivier Grisel, omahs, Omar Salman, Pedro Lopes, Pedro Olivares, Peter +Holzer, Prashant Bansal, Preyas Shah, Radovenchyk, Rahil Parikh, Rémi Flamary, +Reshama Shaikh, Richard Harris, Rishab Saini, rolandrmgservices, SanchitD, +Santiago Castro, Santiago Víquez, saskra, scikit-learn-bot, Scott Huberty, +Shashank S, Shaurya Bisht, Shivam, Shruti Nath, Siddharth Bansal, SIKAI ZHANG, +Simarjot Sidhu, sisird864, SiyuJin-1, Somdutta Banerjee, Sortofamudkip, sotagg, +Sourabh Kumar, Stefan, Stefanie Senger, Stefano Gaspari, Steffen Rehberg, +Stephen Pardy, Success Moses, Sylvain Combettes, Tahar Allouche, Thomas J. Fan, +Thomas Li, ThorbenMaa, Tim Head, Tingwei Zhu, TJ Norred, Umberto Fasci, UV, +Vasco Pereira, Vassilis Margonis, Velislav Babatchev, Victoria Shevchenko, +viktor765, Vipsa Kamani, VirenPassi, Virgil Chan, vpz, Xiao Yuan, Yaich +Mohamed, Yair Shimony, Yao Xiao, Yaroslav Halchenko, Yulia Vilensky, Yuvi Panda diff --git a/examples/bicluster/plot_spectral_biclustering.py b/examples/bicluster/plot_spectral_biclustering.py index 469c3c71e17c6..b3eb1017b6217 100644 --- a/examples/bicluster/plot_spectral_biclustering.py +++ b/examples/bicluster/plot_spectral_biclustering.py @@ -26,7 +26,7 @@ # -------------------- # We generate the sample data using the # :func:`~sklearn.datasets.make_checkerboard` function. Each pixel within -# `shape=(300, 300)` represents with it's color a value from a uniform +# `shape=(300, 300)` represents with its color a value from a uniform # distribution. The noise is added from a normal distribution, where the value # chosen for `noise` is the standard deviation. # @@ -43,7 +43,7 @@ plt.matshow(data, cmap=plt.cm.Blues) plt.title("Original dataset") -_ = plt.show() +plt.show() # %% # We shuffle the data and the goal is to reconstruct it afterwards using @@ -62,7 +62,7 @@ plt.matshow(data, cmap=plt.cm.Blues) plt.title("Shuffled dataset") -_ = plt.show() +plt.show() # %% # Fitting `SpectralBiclustering` @@ -102,7 +102,7 @@ plt.matshow(reordered_data, cmap=plt.cm.Blues) plt.title("After biclustering; rearranged to show biclusters") -_ = plt.show() +plt.show() # %% # As a last step, we want to demonstrate the relationships between the row diff --git a/examples/calibration/plot_compare_calibration.py b/examples/calibration/plot_compare_calibration.py index aa60de1032765..fb41527eb0d44 100644 --- a/examples/calibration/plot_compare_calibration.py +++ b/examples/calibration/plot_compare_calibration.py @@ -271,12 +271,12 @@ def predict_proba(self, X): # Niculescu-Mizil & R. Caruana, ICML 2005 # # .. [2] `Beyond independence: Conditions for the optimality of the simple -# bayesian classifier +# Bayesian classifier # `_ # Domingos, P., & Pazzani, M., Proc. 13th Intl. Conf. Machine Learning. # 1996. # # .. [3] `Obtaining calibrated probability estimates from decision trees and # naive Bayesian classifiers -# `_ +# `_ # Zadrozny, Bianca, and Charles Elkan. Icml. Vol. 1. 2001. diff --git a/examples/cluster/plot_agglomerative_clustering_metrics.py b/examples/cluster/plot_agglomerative_clustering_metrics.py index c565a5859d093..dbf929d9576e1 100644 --- a/examples/cluster/plot_agglomerative_clustering_metrics.py +++ b/examples/cluster/plot_agglomerative_clustering_metrics.py @@ -18,7 +18,7 @@ We add observation noise to these waveforms. We generate very sparse noise: only 6% of the time points contain noise. As a result, the -l1 norm of this noise (ie "cityblock" distance) is much smaller than it's +l1 norm of this noise (ie "cityblock" distance) is much smaller than its l2 norm ("euclidean" distance). This can be seen on the inter-class distance matrices: the values on the diagonal, that characterize the spread of the class, are much bigger for the Euclidean distance than for diff --git a/examples/ensemble/plot_forest_importances.py b/examples/ensemble/plot_forest_importances.py index b77e30c327fb4..5fb8f21364450 100644 --- a/examples/ensemble/plot_forest_importances.py +++ b/examples/ensemble/plot_forest_importances.py @@ -102,10 +102,10 @@ forest_importances = pd.Series(result.importances_mean, index=feature_names) # %% -# The computation for full permutation importance is more costly. Features are -# shuffled n times and the model refitted to estimate the importance of it. -# Please see :ref:`permutation_importance` for more details. We can now plot -# the importance ranking. +# The computation for full permutation importance is more costly. Each feature is +# shuffled n times and the model is used to make predictions on the permuted data to see +# the drop in performance. Please see :ref:`permutation_importance` for more details. +# We can now plot the importance ranking. fig, ax = plt.subplots() forest_importances.plot.bar(yerr=result.importances_std, ax=ax) diff --git a/examples/feature_selection/plot_rfe_with_cross_validation.py b/examples/feature_selection/plot_rfe_with_cross_validation.py index 951b82bffa46d..307707c5aa069 100644 --- a/examples/feature_selection/plot_rfe_with_cross_validation.py +++ b/examples/feature_selection/plot_rfe_with_cross_validation.py @@ -105,7 +105,7 @@ for i in range(cv.n_splits): mask = rfecv.cv_results_[f"split{i}_support"][ - rfecv.n_features_ + rfecv.n_features_ - 1 ] # mask of features selected by the RFE features_selected = np.ma.compressed(np.ma.masked_array(feat_names, mask=1 - mask)) print(f"Features selected in fold {i}: {features_selected}") diff --git a/examples/frozen/plot_frozen_examples.py b/examples/frozen/plot_frozen_examples.py index 373e47ff2d68c..7237003090d13 100644 --- a/examples/frozen/plot_frozen_examples.py +++ b/examples/frozen/plot_frozen_examples.py @@ -3,7 +3,7 @@ Examples of Using `FrozenEstimator` =================================== -This examples showcases some use cases of :class:`~sklearn.frozen.FrozenEstimator`. +This example showcases some use cases of :class:`~sklearn.frozen.FrozenEstimator`. :class:`~sklearn.frozen.FrozenEstimator` is a utility class that allows to freeze a fitted estimator. This is useful, for instance, when we want to pass a fitted estimator diff --git a/examples/impute/plot_iterative_imputer_variants_comparison.py b/examples/impute/plot_iterative_imputer_variants_comparison.py index d2a68d351ce8a..854d443b229d0 100644 --- a/examples/impute/plot_iterative_imputer_variants_comparison.py +++ b/examples/impute/plot_iterative_imputer_variants_comparison.py @@ -13,7 +13,7 @@ imputation with :class:`~impute.IterativeImputer`: * :class:`~linear_model.BayesianRidge`: regularized linear regression -* :class:`~ensemble.RandomForestRegressor`: Forests of randomized trees regression +* :class:`~ensemble.RandomForestRegressor`: forests of randomized trees regression * :func:`~pipeline.make_pipeline` (:class:`~kernel_approximation.Nystroem`, :class:`~linear_model.Ridge`): a pipeline with the expansion of a degree 2 polynomial kernel and regularized linear regression @@ -62,11 +62,10 @@ from sklearn.model_selection import cross_val_score from sklearn.neighbors import KNeighborsRegressor from sklearn.pipeline import make_pipeline +from sklearn.preprocessing import RobustScaler N_SPLITS = 5 -rng = np.random.RandomState(0) - X_full, y_full = fetch_california_housing(return_X_y=True) # ~2k samples is enough for the purpose of the example. # Remove the following two lines for a slower run with different error bars. @@ -74,16 +73,28 @@ y_full = y_full[::10] n_samples, n_features = X_full.shape + +def compute_score_for(X, y, imputer=None): + # We scale data before imputation and training a target estimator, + # because our target estimator and some of the imputers assume + # that the features have similar scales. + if imputer is None: + estimator = make_pipeline(RobustScaler(), BayesianRidge()) + else: + estimator = make_pipeline(RobustScaler(), imputer, BayesianRidge()) + return cross_val_score( + estimator, X, y, scoring="neg_mean_squared_error", cv=N_SPLITS + ) + + # Estimate the score on the entire dataset, with no missing values -br_estimator = BayesianRidge() score_full_data = pd.DataFrame( - cross_val_score( - br_estimator, X_full, y_full, scoring="neg_mean_squared_error", cv=N_SPLITS - ), + compute_score_for(X_full, y_full), columns=["Full Data"], ) # Add a single missing value to each row +rng = np.random.RandomState(0) X_missing = X_full.copy() y_missing = y_full missing_samples = np.arange(n_samples) @@ -93,48 +104,52 @@ # Estimate the score after imputation (mean and median strategies) score_simple_imputer = pd.DataFrame() for strategy in ("mean", "median"): - estimator = make_pipeline( - SimpleImputer(missing_values=np.nan, strategy=strategy), br_estimator - ) - score_simple_imputer[strategy] = cross_val_score( - estimator, X_missing, y_missing, scoring="neg_mean_squared_error", cv=N_SPLITS + score_simple_imputer[strategy] = compute_score_for( + X_missing, y_missing, SimpleImputer(strategy=strategy) ) # Estimate the score after iterative imputation of the missing values # with different estimators -estimators = [ - BayesianRidge(), - RandomForestRegressor( - # We tuned the hyperparameters of the RandomForestRegressor to get a good - # enough predictive performance for a restricted execution time. - n_estimators=4, - max_depth=10, - bootstrap=True, - max_samples=0.5, - n_jobs=2, - random_state=0, +named_estimators = [ + ("Bayesian Ridge", BayesianRidge()), + ( + "Random Forest", + RandomForestRegressor( + # We tuned the hyperparameters of the RandomForestRegressor to get a good + # enough predictive performance for a restricted execution time. + n_estimators=5, + max_depth=10, + bootstrap=True, + max_samples=0.5, + n_jobs=2, + random_state=0, + ), ), - make_pipeline( - Nystroem(kernel="polynomial", degree=2, random_state=0), Ridge(alpha=1e3) + ( + "Nystroem + Ridge", + make_pipeline( + Nystroem(kernel="polynomial", degree=2, random_state=0), Ridge(alpha=1e4) + ), + ), + ( + "k-NN", + KNeighborsRegressor(n_neighbors=10), ), - KNeighborsRegressor(n_neighbors=15), ] score_iterative_imputer = pd.DataFrame() -# iterative imputer is sensible to the tolerance and +# Iterative imputer is sensitive to the tolerance and # dependent on the estimator used internally. -# we tuned the tolerance to keep this example run with limited computational +# We tuned the tolerance to keep this example run with limited computational # resources while not changing the results too much compared to keeping the # stricter default value for the tolerance parameter. tolerances = (1e-3, 1e-1, 1e-1, 1e-2) -for impute_estimator, tol in zip(estimators, tolerances): - estimator = make_pipeline( +for (name, impute_estimator), tol in zip(named_estimators, tolerances): + score_iterative_imputer[name] = compute_score_for( + X_missing, + y_missing, IterativeImputer( - random_state=0, estimator=impute_estimator, max_iter=25, tol=tol + random_state=0, estimator=impute_estimator, max_iter=40, tol=tol ), - br_estimator, - ) - score_iterative_imputer[impute_estimator.__class__.__name__] = cross_val_score( - estimator, X_missing, y_missing, scoring="neg_mean_squared_error", cv=N_SPLITS ) scores = pd.concat( diff --git a/examples/impute/plot_missing_values.py b/examples/impute/plot_missing_values.py index 851bfd419453b..c7474eb338357 100644 --- a/examples/impute/plot_missing_values.py +++ b/examples/impute/plot_missing_values.py @@ -9,14 +9,15 @@ In this example we will investigate different imputation techniques: - imputation by the constant value 0 -- imputation by the mean value of each feature combined with a missing-ness - indicator auxiliary variable +- imputation by the mean value of each feature - k nearest neighbor imputation - iterative imputation +In all the cases, for each feature, we add a new feature indicating the missingness. + We will use two datasets: Diabetes dataset which consists of 10 feature variables collected from diabetes patients with an aim to predict disease -progression and California Housing dataset for which the target is the median +progression and California housing dataset for which the target is the median house value for California districts. As neither of these datasets have missing values, we will remove some @@ -36,9 +37,9 @@ # ############################################## # # First we download the two datasets. Diabetes dataset is shipped with -# scikit-learn. It has 442 entries, each with 10 features. California Housing +# scikit-learn. It has 442 entries, each with 10 features. California housing # dataset is much larger with 20640 entries and 8 features. It needs to be -# downloaded. We will only use the first 400 entries for the sake of speeding +# downloaded. We will only use the first 300 entries for the sake of speeding # up the calculations but feel free to use the whole dataset. # @@ -46,17 +47,16 @@ from sklearn.datasets import fetch_california_housing, load_diabetes -rng = np.random.RandomState(42) - X_diabetes, y_diabetes = load_diabetes(return_X_y=True) X_california, y_california = fetch_california_housing(return_X_y=True) -X_california = X_california[:300] -y_california = y_california[:300] + X_diabetes = X_diabetes[:300] y_diabetes = y_diabetes[:300] +X_california = X_california[:300] +y_california = y_california[:300] -def add_missing_values(X_full, y_full): +def add_missing_values(X_full, y_full, rng): n_samples, n_features = X_full.shape # Add missing values in 75% of the lines @@ -75,20 +75,22 @@ def add_missing_values(X_full, y_full): return X_missing, y_missing -X_miss_california, y_miss_california = add_missing_values(X_california, y_california) - -X_miss_diabetes, y_miss_diabetes = add_missing_values(X_diabetes, y_diabetes) +rng = np.random.RandomState(42) +X_miss_diabetes, y_miss_diabetes = add_missing_values(X_diabetes, y_diabetes, rng) +X_miss_california, y_miss_california = add_missing_values( + X_california, y_california, rng +) # %% # Impute the missing data and score # ################################# # Now we will write a function which will score the results on the differently -# imputed data. Let's look at each imputer separately: +# imputed data, including the case of no imputation for full data. +# We will use :class:`~sklearn.ensemble.RandomForestRegressor` for the target +# regression. # -rng = np.random.RandomState(0) - from sklearn.ensemble import RandomForestRegressor # To use the experimental IterativeImputer, we need to explicitly ask for it: @@ -96,33 +98,29 @@ def add_missing_values(X_full, y_full): from sklearn.impute import IterativeImputer, KNNImputer, SimpleImputer from sklearn.model_selection import cross_val_score from sklearn.pipeline import make_pipeline +from sklearn.preprocessing import RobustScaler N_SPLITS = 4 -regressor = RandomForestRegressor(random_state=0) - -# %% -# Missing information -# ------------------- -# In addition to imputing the missing values, the imputers have an -# `add_indicator` parameter that marks the values that were missing, which -# might carry some information. -# -def get_scores_for_imputer(imputer, X_missing, y_missing): - estimator = make_pipeline(imputer, regressor) - impute_scores = cross_val_score( - estimator, X_missing, y_missing, scoring="neg_mean_squared_error", cv=N_SPLITS +def get_score(X, y, imputer=None): + regressor = RandomForestRegressor(random_state=0) + if imputer is not None: + estimator = make_pipeline(imputer, regressor) + else: + estimator = regressor + scores = cross_val_score( + estimator, X, y, scoring="neg_mean_squared_error", cv=N_SPLITS ) - return impute_scores + return scores.mean(), scores.std() x_labels = [] -mses_california = np.zeros(5) -stds_california = np.zeros(5) mses_diabetes = np.zeros(5) stds_diabetes = np.zeros(5) +mses_california = np.zeros(5) +stds_california = np.zeros(5) # %% # Estimate the score @@ -131,16 +129,9 @@ def get_scores_for_imputer(imputer, X_missing, y_missing): # -def get_full_score(X_full, y_full): - full_scores = cross_val_score( - regressor, X_full, y_full, scoring="neg_mean_squared_error", cv=N_SPLITS - ) - return full_scores.mean(), full_scores.std() - - -mses_california[0], stds_california[0] = get_full_score(X_california, y_california) -mses_diabetes[0], stds_diabetes[0] = get_full_score(X_diabetes, y_diabetes) -x_labels.append("Full data") +mses_diabetes[0], stds_diabetes[0] = get_score(X_diabetes, y_diabetes) +mses_california[0], stds_california[0] = get_score(X_california, y_california) +x_labels.append("Full Data") # %% @@ -151,22 +142,28 @@ def get_full_score(X_full, y_full): # replaced by 0: # +imputer = SimpleImputer(strategy="constant", fill_value=0, add_indicator=True) +mses_diabetes[1], stds_diabetes[1] = get_score( + X_miss_diabetes, y_miss_diabetes, imputer +) +mses_california[1], stds_california[1] = get_score( + X_miss_california, y_miss_california, imputer +) +x_labels.append("Zero Imputation") -def get_impute_zero_score(X_missing, y_missing): - imputer = SimpleImputer( - missing_values=np.nan, add_indicator=True, strategy="constant", fill_value=0 - ) - zero_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing) - return zero_impute_scores.mean(), zero_impute_scores.std() - +# %% +# Impute missing values with mean +# ------------------------------- +# -mses_california[1], stds_california[1] = get_impute_zero_score( - X_miss_california, y_miss_california +imputer = SimpleImputer(strategy="mean", add_indicator=True) +mses_diabetes[2], stds_diabetes[2] = get_score( + X_miss_diabetes, y_miss_diabetes, imputer ) -mses_diabetes[1], stds_diabetes[1] = get_impute_zero_score( - X_miss_diabetes, y_miss_diabetes +mses_california[2], stds_california[2] = get_score( + X_miss_california, y_miss_california, imputer ) -x_labels.append("Zero imputation") +x_labels.append("Mean Imputation") # %% @@ -174,74 +171,41 @@ def get_impute_zero_score(X_missing, y_missing): # ------------------------------------ # # :class:`~sklearn.impute.KNNImputer` imputes missing values using the weighted -# or unweighted mean of the desired number of nearest neighbors. - - -def get_impute_knn_score(X_missing, y_missing): - imputer = KNNImputer(missing_values=np.nan, add_indicator=True) - knn_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing) - return knn_impute_scores.mean(), knn_impute_scores.std() - +# or unweighted mean of the desired number of nearest neighbors. If your features +# have vastly different scales (as in the California housing dataset), +# consider re-scaling them to potentially improve performance. +# -mses_california[2], stds_california[2] = get_impute_knn_score( - X_miss_california, y_miss_california +imputer = KNNImputer(add_indicator=True) +mses_diabetes[3], stds_diabetes[3] = get_score( + X_miss_diabetes, y_miss_diabetes, imputer ) -mses_diabetes[2], stds_diabetes[2] = get_impute_knn_score( - X_miss_diabetes, y_miss_diabetes +mses_california[3], stds_california[3] = get_score( + X_miss_california, y_miss_california, make_pipeline(RobustScaler(), imputer) ) x_labels.append("KNN Imputation") -# %% -# Impute missing values with mean -# ------------------------------- -# - - -def get_impute_mean(X_missing, y_missing): - imputer = SimpleImputer(missing_values=np.nan, strategy="mean", add_indicator=True) - mean_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing) - return mean_impute_scores.mean(), mean_impute_scores.std() - - -mses_california[3], stds_california[3] = get_impute_mean( - X_miss_california, y_miss_california -) -mses_diabetes[3], stds_diabetes[3] = get_impute_mean(X_miss_diabetes, y_miss_diabetes) -x_labels.append("Mean Imputation") - - # %% # Iterative imputation of the missing values # ------------------------------------------ # # Another option is the :class:`~sklearn.impute.IterativeImputer`. This uses -# round-robin linear regression, modeling each feature with missing values as a -# function of other features, in turn. -# The version implemented assumes Gaussian (output) variables. If your features -# are obviously non-normal, consider transforming them to look more normal -# to potentially improve performance. +# round-robin regression, modeling each feature with missing values as a +# function of other features, in turn. We use the class's default choice +# of the regressor model (:class:`~sklearn.linear_model.BayesianRidge`) +# to predict missing feature values. The performance of the predictor +# may be negatively affected by vastly different scales of the features, +# so we re-scale the features in the California housing dataset. # +imputer = IterativeImputer(add_indicator=True) -def get_impute_iterative(X_missing, y_missing): - imputer = IterativeImputer( - missing_values=np.nan, - add_indicator=True, - random_state=0, - n_nearest_features=3, - max_iter=1, - sample_posterior=True, - ) - iterative_impute_scores = get_scores_for_imputer(imputer, X_missing, y_missing) - return iterative_impute_scores.mean(), iterative_impute_scores.std() - - -mses_california[4], stds_california[4] = get_impute_iterative( - X_miss_california, y_miss_california +mses_diabetes[4], stds_diabetes[4] = get_score( + X_miss_diabetes, y_miss_diabetes, imputer ) -mses_diabetes[4], stds_diabetes[4] = get_impute_iterative( - X_miss_diabetes, y_miss_diabetes +mses_california[4], stds_california[4] = get_score( + X_miss_california, y_miss_california, make_pipeline(RobustScaler(), imputer) ) x_labels.append("Iterative Imputation") diff --git a/examples/linear_model/plot_ridge_path.py b/examples/linear_model/plot_ridge_path.py index d3c19acd9e18c..eca65bb509c7b 100644 --- a/examples/linear_model/plot_ridge_path.py +++ b/examples/linear_model/plot_ridge_path.py @@ -22,7 +22,7 @@ squared loss function and the coefficients tend to zero. At the end of the path, as alpha tends toward zero and the solution tends towards the ordinary least squares, coefficients -exhibit big oscillations. In practise it is necessary to tune alpha +exhibit big oscillations. In practice it is necessary to tune alpha in such a way that a balance is maintained between both. """ @@ -63,6 +63,9 @@ ax.set_xlim(ax.get_xlim()[::-1]) # reverse axis plt.xlabel("alpha") plt.ylabel("weights") -plt.title("Ridge coefficients as a function of the regularization") +plt.title("Ridge Coefficients vs Regularization Strength (alpha)") plt.axis("tight") +plt.legend( + [f"Feature {i + 1}" for i in range(X.shape[1])], loc="best", fontsize="small" +) plt.show() diff --git a/examples/miscellaneous/plot_outlier_detection_bench.py b/examples/miscellaneous/plot_outlier_detection_bench.py index 600eceb1a06b3..933902500ef8b 100644 --- a/examples/miscellaneous/plot_outlier_detection_bench.py +++ b/examples/miscellaneous/plot_outlier_detection_bench.py @@ -355,8 +355,7 @@ def fit_predict(estimator, X): ax=ax, plot_chance_level=(model_idx == len(n_neighbors_list) - 1), chance_level_kw={"linestyle": (0, (1, 10))}, - linestyle=linestyle, - linewidth=2, + curve_kwargs=dict(linestyle=linestyle, linewidth=2), ) _ = ax.set_title("RobustScaler with varying n_neighbors\non forestcover dataset") @@ -395,8 +394,7 @@ def fit_predict(estimator, X): ax=ax, plot_chance_level=(model_idx == len(preprocessor_list) - 1), chance_level_kw={"linestyle": (0, (1, 10))}, - linestyle=linestyle, - linewidth=2, + curve_kwargs=dict(linestyle=linestyle, linewidth=2), ) _ = ax.set_title("Fixed n_neighbors with varying preprocessing\non forestcover dataset") @@ -447,8 +445,7 @@ def fit_predict(estimator, X): ax=ax, plot_chance_level=(model_idx == len(preprocessor_list) - 1), chance_level_kw={"linestyle": (0, (1, 10))}, - linestyle=linestyle, - linewidth=2, + curve_kwargs=dict(linestyle=linestyle, linewidth=2), ) ax.set_title( "Fixed n_neighbors with varying preprocessing\non cardiotocography dataset" diff --git a/examples/miscellaneous/plot_roc_curve_visualization_api.py b/examples/miscellaneous/plot_roc_curve_visualization_api.py index d377d321e061e..1aacbd9de3631 100644 --- a/examples/miscellaneous/plot_roc_curve_visualization_api.py +++ b/examples/miscellaneous/plot_roc_curve_visualization_api.py @@ -54,6 +54,8 @@ rfc = RandomForestClassifier(n_estimators=10, random_state=42) rfc.fit(X_train, y_train) ax = plt.gca() -rfc_disp = RocCurveDisplay.from_estimator(rfc, X_test, y_test, ax=ax, alpha=0.8) -svc_disp.plot(ax=ax, alpha=0.8) +rfc_disp = RocCurveDisplay.from_estimator( + rfc, X_test, y_test, ax=ax, curve_kwargs=dict(alpha=0.8) +) +svc_disp.plot(ax=ax, curve_kwargs=dict(alpha=0.8)) plt.show() diff --git a/examples/model_selection/plot_cost_sensitive_learning.py b/examples/model_selection/plot_cost_sensitive_learning.py index 9845d27661374..6b5b651463b05 100644 --- a/examples/model_selection/plot_cost_sensitive_learning.py +++ b/examples/model_selection/plot_cost_sensitive_learning.py @@ -321,8 +321,7 @@ def plot_roc_pr_curves(vanilla_model, tuned_model, *, title): X_test, y_test, pos_label=pos_label, - linestyle=linestyle, - color=color, + curve_kwargs=dict(linestyle=linestyle, color=color), ax=axs[1], name=name, plot_chance_level=idx == 1, diff --git a/examples/model_selection/plot_det.py b/examples/model_selection/plot_det.py index 873d00d696d95..4a22cdcd44eb8 100644 --- a/examples/model_selection/plot_det.py +++ b/examples/model_selection/plot_det.py @@ -103,7 +103,12 @@ ) clf.fit(X_train, y_train) RocCurveDisplay.from_estimator( - clf, X_test, y_test, ax=ax_roc, name=name, color=color, linestyle=linestyle + clf, + X_test, + y_test, + ax=ax_roc, + name=name, + curve_kwargs=dict(color=color, linestyle=linestyle), ) DetCurveDisplay.from_estimator( clf, X_test, y_test, ax=ax_det, name=name, color=color, linestyle=linestyle diff --git a/examples/model_selection/plot_grid_search_refit_callable.py b/examples/model_selection/plot_grid_search_refit_callable.py index 2b13ee5ad584c..945daf32b41ff 100644 --- a/examples/model_selection/plot_grid_search_refit_callable.py +++ b/examples/model_selection/plot_grid_search_refit_callable.py @@ -3,19 +3,20 @@ Balance model complexity and cross-validated score ================================================== -This example balances model complexity and cross-validated score by -finding a decent accuracy within 1 standard deviation of the best accuracy -score while minimising the number of PCA components [1]. +This example demonstrates how to balance model complexity and cross-validated score by +finding a decent accuracy within 1 standard deviation of the best accuracy score while +minimising the number of :class:`~sklearn.decomposition.PCA` components [1]. It uses +:class:`~sklearn.model_selection.GridSearchCV` with a custom refit callable to select +the optimal model. The figure shows the trade-off between cross-validated score and the number -of PCA components. The balanced case is when n_components=10 and accuracy=0.88, +of PCA components. The balanced case is when `n_components=10` and `accuracy=0.88`, which falls into the range within 1 standard deviation of the best accuracy score. [1] Hastie, T., Tibshirani, R.,, Friedman, J. (2001). Model Assessment and Selection. The Elements of Statistical Learning (pp. 219-260). New York, NY, USA: Springer New York Inc.. - """ # Authors: The scikit-learn developers @@ -23,12 +24,33 @@ import matplotlib.pyplot as plt import numpy as np +import polars as pl from sklearn.datasets import load_digits from sklearn.decomposition import PCA -from sklearn.model_selection import GridSearchCV +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import GridSearchCV, ShuffleSplit from sklearn.pipeline import Pipeline -from sklearn.svm import LinearSVC + +# %% +# Introduction +# ------------ +# +# When tuning hyperparameters, we often want to balance model complexity and +# performance. The "one-standard-error" rule is a common approach: select the simplest +# model whose performance is within one standard error of the best model's performance. +# This helps to avoid overfitting by preferring simpler models when their performance is +# statistically comparable to more complex ones. + +# %% +# Helper functions +# ---------------- +# +# We define two helper functions: +# 1. `lower_bound`: Calculates the threshold for acceptable performance +# (best score - 1 std) +# 2. `best_low_complexity`: Selects the model with the fewest PCA components that +# exceeds this threshold def lower_bound(cv_results): @@ -79,49 +101,280 @@ def best_low_complexity(cv_results): return best_idx +# %% +# Set up the pipeline and parameter grid +# -------------------------------------- +# +# We create a pipeline with two steps: +# 1. Dimensionality reduction using PCA +# 2. Classification using LogisticRegression +# +# We'll search over different numbers of PCA components to find the optimal complexity. + pipe = Pipeline( [ ("reduce_dim", PCA(random_state=42)), - ("classify", LinearSVC(random_state=42, C=0.01)), + ("classify", LogisticRegression(random_state=42, C=0.01, max_iter=1000)), ] ) -param_grid = {"reduce_dim__n_components": [6, 8, 10, 12, 14]} +param_grid = {"reduce_dim__n_components": [6, 8, 10, 15, 20, 25, 35, 45, 55]} + +# %% +# Perform the search with GridSearchCV +# ------------------------------------ +# +# We use `GridSearchCV` with our custom `best_low_complexity` function as the refit +# parameter. This function will select the model with the fewest PCA components that +# still performs within one standard deviation of the best model. grid = GridSearchCV( pipe, - cv=10, - n_jobs=1, + # Use a non-stratified CV strategy to make sure that the inter-fold + # standard deviation of the test scores is informative. + cv=ShuffleSplit(n_splits=30, random_state=0), + n_jobs=1, # increase this on your machine to use more physical cores param_grid=param_grid, scoring="accuracy", refit=best_low_complexity, + return_train_score=True, ) + +# %% +# Load the digits dataset and fit the model +# ----------------------------------------- + X, y = load_digits(return_X_y=True) grid.fit(X, y) +# %% +# Visualize the results +# --------------------- +# +# We'll create a bar chart showing the test scores for different numbers of PCA +# components, along with horizontal lines indicating the best score and the +# one-standard-deviation threshold. + n_components = grid.cv_results_["param_reduce_dim__n_components"] test_scores = grid.cv_results_["mean_test_score"] -plt.figure() -plt.bar(n_components, test_scores, width=1.3, color="b") +# Create a polars DataFrame for better data manipulation and visualization +results_df = pl.DataFrame( + { + "n_components": n_components, + "mean_test_score": test_scores, + "std_test_score": grid.cv_results_["std_test_score"], + "mean_train_score": grid.cv_results_["mean_train_score"], + "std_train_score": grid.cv_results_["std_train_score"], + "mean_fit_time": grid.cv_results_["mean_fit_time"], + "rank_test_score": grid.cv_results_["rank_test_score"], + } +) -lower = lower_bound(grid.cv_results_) -plt.axhline(np.max(test_scores), linestyle="--", color="y", label="Best score") -plt.axhline(lower, linestyle="--", color=".5", label="Best score - 1 std") +# Sort by number of components +results_df = results_df.sort("n_components") -plt.title("Balance model complexity and cross-validated score") -plt.xlabel("Number of PCA components used") -plt.ylabel("Digit classification accuracy") -plt.xticks(n_components.tolist()) -plt.ylim((0, 1.0)) -plt.legend(loc="upper left") +# Calculate the lower bound threshold +lower = lower_bound(grid.cv_results_) +# Get the best model information best_index_ = grid.best_index_ +best_components = n_components[best_index_] +best_score = grid.cv_results_["mean_test_score"][best_index_] + +# Add a column to mark the selected model +results_df = results_df.with_columns( + pl.when(pl.col("n_components") == best_components) + .then(pl.lit("Selected")) + .otherwise(pl.lit("Regular")) + .alias("model_type") +) + +# Get the number of CV splits from the results +n_splits = sum( + 1 + for key in grid.cv_results_.keys() + if key.startswith("split") and key.endswith("test_score") +) + +# Extract individual scores for each split +test_scores = np.array( + [ + [grid.cv_results_[f"split{i}_test_score"][j] for i in range(n_splits)] + for j in range(len(n_components)) + ] +) +train_scores = np.array( + [ + [grid.cv_results_[f"split{i}_train_score"][j] for i in range(n_splits)] + for j in range(len(n_components)) + ] +) + +# Calculate mean and std of test scores +mean_test_scores = np.mean(test_scores, axis=1) +std_test_scores = np.std(test_scores, axis=1) + +# Find best score and threshold +best_mean_score = np.max(mean_test_scores) +threshold = best_mean_score - std_test_scores[np.argmax(mean_test_scores)] + +# Create a single figure for visualization +fig, ax = plt.subplots(figsize=(12, 8)) -print("The best_index_ is %d" % best_index_) -print("The n_components selected is %d" % n_components[best_index_]) -print( - "The corresponding accuracy score is %.2f" - % grid.cv_results_["mean_test_score"][best_index_] +# Plot individual points +for i, comp in enumerate(n_components): + # Plot individual test points + plt.scatter( + [comp] * n_splits, + test_scores[i], + alpha=0.2, + color="blue", + s=20, + label="Individual test scores" if i == 0 else "", + ) + # Plot individual train points + plt.scatter( + [comp] * n_splits, + train_scores[i], + alpha=0.2, + color="green", + s=20, + label="Individual train scores" if i == 0 else "", + ) + +# Plot mean lines with error bands +plt.plot( + n_components, + np.mean(test_scores, axis=1), + "-", + color="blue", + linewidth=2, + label="Mean test score", +) +plt.fill_between( + n_components, + np.mean(test_scores, axis=1) - np.std(test_scores, axis=1), + np.mean(test_scores, axis=1) + np.std(test_scores, axis=1), + alpha=0.15, + color="blue", +) + +plt.plot( + n_components, + np.mean(train_scores, axis=1), + "-", + color="green", + linewidth=2, + label="Mean train score", +) +plt.fill_between( + n_components, + np.mean(train_scores, axis=1) - np.std(train_scores, axis=1), + np.mean(train_scores, axis=1) + np.std(train_scores, axis=1), + alpha=0.15, + color="green", ) + +# Add threshold lines +plt.axhline( + best_mean_score, + color="#9b59b6", # Purple + linestyle="--", + label="Best score", + linewidth=2, +) +plt.axhline( + threshold, + color="#e67e22", # Orange + linestyle="--", + label="Best score - 1 std", + linewidth=2, +) + +# Highlight selected model +plt.axvline( + best_components, + color="#9b59b6", # Purple + alpha=0.2, + linewidth=8, + label="Selected model", +) + +# Set titles and labels +plt.xlabel("Number of PCA components", fontsize=12) +plt.ylabel("Score", fontsize=12) +plt.title("Model Selection: Balancing Complexity and Performance", fontsize=14) +plt.grid(True, linestyle="--", alpha=0.7) +plt.legend( + bbox_to_anchor=(1.02, 1), + loc="upper left", + borderaxespad=0, +) + +# Set axis properties +plt.xticks(n_components) +plt.ylim((0.85, 1.0)) + +# # Adjust layout +plt.tight_layout() + +# %% +# Print the results +# ----------------- +# +# We print information about the selected model, including its complexity and +# performance. We also show a summary table of all models using polars. + +print("Best model selected by the one-standard-error rule:") +print(f"Number of PCA components: {best_components}") +print(f"Accuracy score: {best_score:.4f}") +print(f"Best possible accuracy: {np.max(test_scores):.4f}") +print(f"Accuracy threshold (best - 1 std): {lower:.4f}") + +# Create a summary table with polars +summary_df = results_df.select( + pl.col("n_components"), + pl.col("mean_test_score").round(4).alias("test_score"), + pl.col("std_test_score").round(4).alias("test_std"), + pl.col("mean_train_score").round(4).alias("train_score"), + pl.col("std_train_score").round(4).alias("train_std"), + pl.col("mean_fit_time").round(3).alias("fit_time"), + pl.col("rank_test_score").alias("rank"), +) + +# Add a column to mark the selected model +summary_df = summary_df.with_columns( + pl.when(pl.col("n_components") == best_components) + .then(pl.lit("*")) + .otherwise(pl.lit("")) + .alias("selected") +) + +print("\nModel comparison table:") +print(summary_df) + +# %% +# Conclusion +# ---------- +# +# The one-standard-error rule helps us select a simpler model (fewer PCA components) +# while maintaining performance statistically comparable to the best model. +# This approach can help prevent overfitting and improve model interpretability +# and efficiency. +# +# In this example, we've seen how to implement this rule using a custom refit +# callable with :class:`~sklearn.model_selection.GridSearchCV`. +# +# Key takeaways: +# 1. The one-standard-error rule provides a good rule of thumb to select simpler models +# 2. Custom refit callables in :class:`~sklearn.model_selection.GridSearchCV` allow for +# flexible model selection strategies +# 3. Visualizing both train and test scores helps identify potential overfitting +# +# This approach can be applied to other model selection scenarios where balancing +# complexity and performance is important, or in cases where a use-case specific +# selection of the "best" model is desired. + +# Display the figure plt.show() diff --git a/examples/model_selection/plot_roc.py b/examples/model_selection/plot_roc.py index a482ad5f4ab95..9e659b9a2aa64 100644 --- a/examples/model_selection/plot_roc.py +++ b/examples/model_selection/plot_roc.py @@ -129,7 +129,7 @@ y_onehot_test[:, class_id], y_score[:, class_id], name=f"{class_of_interest} vs the rest", - color="darkorange", + curve_kwargs=dict(color="darkorange"), plot_chance_level=True, despine=True, ) @@ -165,7 +165,7 @@ y_onehot_test.ravel(), y_score.ravel(), name="micro-average OvR", - color="darkorange", + curve_kwargs=dict(color="darkorange"), plot_chance_level=True, despine=True, ) @@ -290,7 +290,7 @@ y_onehot_test[:, class_id], y_score[:, class_id], name=f"ROC curve for {target_names[class_id]}", - color=color, + curve_kwargs=dict(color=color), ax=ax, plot_chance_level=(class_id == 2), despine=True, diff --git a/examples/model_selection/plot_roc_crossval.py b/examples/model_selection/plot_roc_crossval.py index fb6432a71ed79..3c5c3fc9119b7 100644 --- a/examples/model_selection/plot_roc_crossval.py +++ b/examples/model_selection/plot_roc_crossval.py @@ -62,8 +62,9 @@ # Classification and ROC analysis # ------------------------------- # -# Here we run a :class:`~sklearn.svm.SVC` classifier with cross-validation and -# plot the ROC curves fold-wise. Notice that the baseline to define the chance +# Here we run :func:`~sklearn.model_selection.cross_validate` on a +# :class:`~sklearn.svm.SVC` classifier, then use the computed cross-validation results +# to plot the ROC curves fold-wise. Notice that the baseline to define the chance # level (dashed ROC curve) is a classifier that would always predict the most # frequent class. @@ -71,38 +72,46 @@ from sklearn import svm from sklearn.metrics import RocCurveDisplay, auc -from sklearn.model_selection import StratifiedKFold +from sklearn.model_selection import StratifiedKFold, cross_validate n_splits = 6 cv = StratifiedKFold(n_splits=n_splits) classifier = svm.SVC(kernel="linear", probability=True, random_state=random_state) +cv_results = cross_validate( + classifier, X, y, cv=cv, return_estimator=True, return_indices=True +) + +prop_cycle = plt.rcParams["axes.prop_cycle"] +colors = prop_cycle.by_key()["color"] +curve_kwargs_list = [ + dict(alpha=0.3, lw=1, color=colors[fold % len(colors)]) for fold in range(n_splits) +] +names = [f"ROC fold {idx}" for idx in range(n_splits)] -tprs = [] -aucs = [] mean_fpr = np.linspace(0, 1, 100) +interp_tprs = [] + +_, ax = plt.subplots(figsize=(6, 6)) +viz = RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + ax=ax, + name=names, + curve_kwargs=curve_kwargs_list, + plot_chance_level=True, +) -fig, ax = plt.subplots(figsize=(6, 6)) -for fold, (train, test) in enumerate(cv.split(X, y)): - classifier.fit(X[train], y[train]) - viz = RocCurveDisplay.from_estimator( - classifier, - X[test], - y[test], - name=f"ROC fold {fold}", - alpha=0.3, - lw=1, - ax=ax, - plot_chance_level=(fold == n_splits - 1), - ) - interp_tpr = np.interp(mean_fpr, viz.fpr, viz.tpr) +for idx in range(n_splits): + interp_tpr = np.interp(mean_fpr, viz.fpr[idx], viz.tpr[idx]) interp_tpr[0] = 0.0 - tprs.append(interp_tpr) - aucs.append(viz.roc_auc) + interp_tprs.append(interp_tpr) -mean_tpr = np.mean(tprs, axis=0) +mean_tpr = np.mean(interp_tprs, axis=0) mean_tpr[-1] = 1.0 mean_auc = auc(mean_fpr, mean_tpr) -std_auc = np.std(aucs) +std_auc = np.std(viz.roc_auc) + ax.plot( mean_fpr, mean_tpr, @@ -112,7 +121,7 @@ alpha=0.8, ) -std_tpr = np.std(tprs, axis=0) +std_tpr = np.std(interp_tprs, axis=0) tprs_upper = np.minimum(mean_tpr + std_tpr, 1) tprs_lower = np.maximum(mean_tpr - std_tpr, 0) ax.fill_between( diff --git a/examples/release_highlights/plot_release_highlights_1_7_0.py b/examples/release_highlights/plot_release_highlights_1_7_0.py new file mode 100644 index 0000000000000..06c2f10e70b28 --- /dev/null +++ b/examples/release_highlights/plot_release_highlights_1_7_0.py @@ -0,0 +1,115 @@ +# ruff: noqa: CPY001 +""" +======================================= +Release Highlights for scikit-learn 1.7 +======================================= + +.. currentmodule:: sklearn + +We are pleased to announce the release of scikit-learn 1.7! Many bug fixes +and improvements were added, as well as some key new features. Below we +detail the highlights of this release. **For an exhaustive list of +all the changes**, please refer to the :ref:`release notes `. + +To install the latest version (with pip):: + + pip install --upgrade scikit-learn + +or with conda:: + + conda install -c conda-forge scikit-learn + +""" + +# %% +# Improved estimator's HTML representation +# ---------------------------------------- +# The HTML representation of estimators now includes a section containing the list of +# parameters and their values. Non-default parameters are highlighted in orange. A copy +# button is also available to copy the "fully-qualified" parameter name without the +# need to call the `get_params` method. It is particularly useful when defining a +# parameter grid for a grid-search or a randomized-search with a complex pipeline. +# +# See the example below and click on the different estimator's blocks to see the +# improved HTML representation. + +from sklearn.linear_model import LogisticRegression +from sklearn.pipeline import make_pipeline +from sklearn.preprocessing import StandardScaler + +model = make_pipeline(StandardScaler(with_std=False), LogisticRegression(C=2.0)) +model + +# %% +# Custom validation set for histogram-based Gradient Boosting estimators +# ---------------------------------------------------------------------- +# The :class:`ensemble.HistGradientBoostingClassifier` and +# :class:`ensemble.HistGradientBoostingRegressor` now support directly passing a custom +# validation set for early stopping to the `fit` method, using the `X_val`, `y_val`, and +# `sample_weight_val` parameters. +# In a :class:`pipeline.Pipeline`, the validation set `X_val` can be transformed along +# with `X` using the `transform_input` parameter. + +import sklearn +from sklearn.datasets import make_classification +from sklearn.ensemble import HistGradientBoostingClassifier +from sklearn.model_selection import train_test_split +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import StandardScaler + +sklearn.set_config(enable_metadata_routing=True) + +X, y = make_classification(random_state=0) +X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=0) + +clf = HistGradientBoostingClassifier() +clf.set_fit_request(X_val=True, y_val=True) + +model = Pipeline([("sc", StandardScaler()), ("clf", clf)], transform_input=["X_val"]) +model.fit(X, y, X_val=X_val, y_val=y_val) + +# %% +# Plotting ROC curves from cross-validation results +# ------------------------------------------------- +# The class :class:`metrics.RocCurveDisplay` has a new class method `from_cv_results` +# that allows to easily plot multiple ROC curves from the results of +# :func:`model_selection.cross_validate`. + +from sklearn.datasets import make_classification +from sklearn.linear_model import LogisticRegression +from sklearn.metrics import RocCurveDisplay +from sklearn.model_selection import cross_validate + +X, y = make_classification(n_samples=150, random_state=0) +clf = LogisticRegression(random_state=0) +cv_results = cross_validate(clf, X, y, cv=5, return_estimator=True, return_indices=True) +_ = RocCurveDisplay.from_cv_results(cv_results, X, y) + +# %% +# Array API support +# ----------------- +# Several functions have been updated to support array API compatible inputs since +# version 1.6, especially metrics from the :mod:`sklearn.metrics` module. +# +# In addition, it is no longer required to install the `array-api-compat` package to use +# the experimental array API support in scikit-learn. +# +# Please refer to the :ref:`array API support` page for instructions to use +# scikit-learn with array API compatible libraries such as PyTorch or CuPy. + +# %% +# Improved API consistency of Multi-layer Perceptron +# -------------------------------------------------- +# The :class:`neural_network.MLPRegressor` has a new parameter `loss` and now supports +# the "poisson" loss in addition to the default "squared_error" loss. +# Moreover, the :class:`neural_network.MLPClassifier` and +# :class:`neural_network.MLPRegressor` estimators now support sample weights. +# These improvements have been made to improve the consistency of these estimators +# with regard to the other estimators in scikit-learn. + +# %% +# Migration toward sparse arrays +# ------------------------------ +# In order to prepare `SciPy migration from sparse matrices to sparse arrays `_, +# all scikit-learn estimators that accept sparse matrices as input now also accept +# sparse arrays. diff --git a/examples/svm/plot_svm_kernels.py b/examples/svm/plot_svm_kernels.py index df29d198abcbc..6173a2e7642be 100644 --- a/examples/svm/plot_svm_kernels.py +++ b/examples/svm/plot_svm_kernels.py @@ -79,7 +79,7 @@ scatter = ax.scatter(X[:, 0], X[:, 1], s=150, c=y, label=y, edgecolors="k") ax.legend(*scatter.legend_elements(), loc="upper right", title="Classes") ax.set_title("Samples in two-dimensional feature space") -_ = plt.show() +plt.show() # %% # We can see that the samples are not clearly separable by a straight line. @@ -255,7 +255,7 @@ def plot_training_data_with_decision_boundary( # that may not generalize well to unseen data. From this example it becomes # obvious, that the sigmoid kernel has very specific use cases, when dealing # with data that exhibits a sigmoidal shape. In this example, careful fine -# tuning might find more generalizable decision boundaries. Because of it's +# tuning might find more generalizable decision boundaries. Because of its # specificity, the sigmoid kernel is less commonly used in practice compared to # other kernels. # diff --git a/maint_tools/vendor_array_api_compat.sh b/maint_tools/vendor_array_api_compat.sh index 52fa4c570a534..51056ce477cbb 100755 --- a/maint_tools/vendor_array_api_compat.sh +++ b/maint_tools/vendor_array_api_compat.sh @@ -6,7 +6,7 @@ set -o nounset set -o errexit URL="https://github.com/data-apis/array-api-compat.git" -VERSION="1.11.2" +VERSION="1.12" ROOT_DIR=sklearn/externals/array_api_compat diff --git a/pyproject.toml b/pyproject.toml index 9a1c7c96241c7..f7cf10f452afb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,11 +13,11 @@ dependencies = [ "threadpoolctl>=3.1.0", ] requires-python = ">=3.10" -license = {file = "COPYING"} +license = "BSD-3-Clause" +license-files = ["COPYING"] classifiers=[ "Intended Audience :: Science/Research", "Intended Audience :: Developers", - "License :: OSI Approved :: BSD License", "Programming Language :: C", "Programming Language :: Python", "Topic :: Software Development", @@ -43,7 +43,7 @@ tracker = "https://github.com/scikit-learn/scikit-learn/issues" "release notes" = "https://scikit-learn.org/stable/whats_new" [project.optional-dependencies] -build = ["numpy>=1.22.0", "scipy>=1.8.0", "cython>=3.0.10", "meson-python>=0.16.0"] +build = ["numpy>=1.22.0", "scipy>=1.8.0", "cython>=3.0.10", "meson-python>=0.17.1"] install = ["numpy>=1.22.0", "scipy>=1.8.0", "joblib>=1.2.0", "threadpoolctl>=3.1.0"] benchmark = ["matplotlib>=3.5.0", "pandas>=1.4.0", "memory_profiler>=0.57.0"] docs = [ @@ -91,16 +91,16 @@ tests = [ "numpydoc>=1.2.0", "pooch>=1.6.0", ] -maintenance = ["conda-lock==2.5.7"] +maintenance = ["conda-lock==3.0.1"] [build-system] build-backend = "mesonpy" # Minimum requirements for the build system to execute. requires = [ - "meson-python>=0.16.0", - "Cython>=3.0.10", - "numpy>=2", - "scipy>=1.8.0", + "meson-python>=0.16.0,<0.19.0", + "Cython>=3.0.10,<3.2.0", + "numpy>=2,<2.4.0", + "scipy>=1.8.0,<1.17.0", ] [tool.pytest.ini_options] @@ -194,6 +194,7 @@ notice-rgx = "\\#\\ Authors:\\ The\\ scikit\\-learn\\ developers\\\r?\\\n\\#\\ S "examples/svm/plot_rbf_parameters.py"=["CPY001"] # __all__ has un-imported names "sklearn/__init__.py"=["F822"] +"sklearn/utils/_metadata_requests.py"=["CPY001"] [tool.mypy] ignore_missing_imports = true diff --git a/sklearn/__init__.py b/sklearn/__init__.py index 8ea5aacf84cf3..1064d6564ce6d 100644 --- a/sklearn/__init__.py +++ b/sklearn/__init__.py @@ -42,7 +42,7 @@ # Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer. # 'X.Y.dev0' is the canonical version of 'X.Y.dev' # -__version__ = "1.7.dev0" +__version__ = "1.7.2" # On OSX, we can get a runtime error due to multiple OpenMP libraries loaded diff --git a/sklearn/_config.py b/sklearn/_config.py index 05549c88a9ddc..66d119e02d1a3 100644 --- a/sklearn/_config.py +++ b/sklearn/_config.py @@ -33,7 +33,10 @@ def _get_threadlocal_config(): def get_config(): - """Retrieve current values for configuration set by :func:`set_config`. + """Retrieve the current scikit-learn configuration. + + This reflects the effective global configurations as established by default upon + library import, or modified via :func:`set_config` or :func:`config_context`. Returns ------- @@ -71,6 +74,15 @@ def set_config( ): """Set global scikit-learn configuration. + These settings control the behaviour of scikit-learn functions during a library + usage session. Global configuration defaults (as described in the parameter list + below) take effect when scikit-learn is imported. + + This function can be used to modify the global scikit-learn configuration at + runtime. Passing `None` as an argument (the default) leaves the corresponding + setting unchanged. This allows users to selectively update the global configuration + values without affecting the others. + .. versionadded:: 0.19 Parameters @@ -79,7 +91,7 @@ def set_config( If True, validation for finiteness will be skipped, saving time, but leading to potential crashes. If False, validation for finiteness will be performed, - avoiding error. Global default: False. + avoiding error. Global default: False. .. versionadded:: 0.19 @@ -96,20 +108,22 @@ def set_config( values will be printed when printing an estimator. For example, ``print(SVC())`` while True will only print 'SVC()' while the default behaviour would be to print 'SVC(C=1.0, cache_size=200, ...)' with - all the non-changed parameters. + all the non-changed parameters. Global default: True. .. versionadded:: 0.21 + .. versionchanged:: 0.23 + Global default configuration changed from False to True. display : {'text', 'diagram'}, default=None If 'diagram', estimators will be displayed as a diagram in a Jupyter lab or notebook context. If 'text', estimators will be displayed as - text. Default is 'diagram'. + text. Global default: 'diagram'. .. versionadded:: 0.23 pairwise_dist_chunk_size : int, default=None The number of row vectors per chunk for the accelerated pairwise- - distances reduction backend. Default is 256 (suitable for most of + distances reduction backend. Global default: 256 (suitable for most of modern laptops' caches and architectures). Intended for easier benchmarking and testing of scikit-learn internals. @@ -130,7 +144,7 @@ def set_config( array_api_dispatch : bool, default=None Use Array API dispatching when inputs follow the Array API standard. - Default is False. + Global default: False. See the :ref:`User Guide ` for more details. @@ -147,6 +161,8 @@ def set_config( - `"polars"`: Polars output - `None`: Transform configuration is unchanged + Global default: "default". + .. versionadded:: 1.2 .. versionadded:: 1.4 `"polars"` option was added. @@ -161,6 +177,8 @@ def set_config( - `False`: Metadata routing is disabled, use the old syntax. - `None`: Configuration is unchanged + Global default: False. + .. versionadded:: 1.3 skip_parameter_validation : bool, default=None @@ -168,6 +186,7 @@ def set_config( the fit method of estimators and for arguments passed to public helper functions. It can save time in some situations but can lead to low level crashes and exceptions with confusing error messages. + Global default: False. Note that for data parameters, such as `X` and `y`, only type validation is skipped but validation with `check_array` will continue to run. @@ -225,7 +244,14 @@ def config_context( enable_metadata_routing=None, skip_parameter_validation=None, ): - """Context manager for global scikit-learn configuration. + """Context manager to temporarily change the global scikit-learn configuration. + + This context manager can be used to apply scikit-learn configuration changes within + the scope of the with statement. Once the context exits, the global configuration is + restored again. + + The default global configurations (which take effect when scikit-learn is imported) + are defined below in the parameter list. Parameters ---------- @@ -233,38 +259,38 @@ def config_context( If True, validation for finiteness will be skipped, saving time, but leading to potential crashes. If False, validation for finiteness will be performed, - avoiding error. If None, the existing value won't change. - The default value is False. + avoiding error. If None, the existing configuration won't change. + Global default: False. working_memory : int, default=None If set, scikit-learn will attempt to limit the size of temporary arrays to this number of MiB (per job when parallelised), often saving both computation time and memory on expensive operations that can be - performed in chunks. If None, the existing value won't change. - The default value is 1024. + performed in chunks. If None, the existing configuration won't change. + Global default: 1024. print_changed_only : bool, default=None If True, only the parameters that were set to non-default values will be printed when printing an estimator. For example, ``print(SVC())`` while True will only print 'SVC()', but would print 'SVC(C=1.0, cache_size=200, ...)' with all the non-changed parameters - when False. If None, the existing value won't change. - The default value is True. + when False. If None, the existing configuration won't change. + Global default: True. .. versionchanged:: 0.23 - Default changed from False to True. + Global default configuration changed from False to True. display : {'text', 'diagram'}, default=None If 'diagram', estimators will be displayed as a diagram in a Jupyter lab or notebook context. If 'text', estimators will be displayed as - text. If None, the existing value won't change. - The default value is 'diagram'. + text. If None, the existing configuration won't change. + Global default: 'diagram'. .. versionadded:: 0.23 pairwise_dist_chunk_size : int, default=None The number of row vectors per chunk for the accelerated pairwise- - distances reduction backend. Default is 256 (suitable for most of + distances reduction backend. Global default: 256 (suitable for most of modern laptops' caches and architectures). Intended for easier benchmarking and testing of scikit-learn internals. @@ -285,7 +311,7 @@ def config_context( array_api_dispatch : bool, default=None Use Array API dispatching when inputs follow the Array API standard. - Default is False. + Global default: False. See the :ref:`User Guide ` for more details. @@ -302,6 +328,8 @@ def config_context( - `"polars"`: Polars output - `None`: Transform configuration is unchanged + Global default: "default". + .. versionadded:: 1.2 .. versionadded:: 1.4 `"polars"` option was added. @@ -316,6 +344,8 @@ def config_context( - `False`: Metadata routing is disabled, use the old syntax. - `None`: Configuration is unchanged + Global default: False. + .. versionadded:: 1.3 skip_parameter_validation : bool, default=None @@ -323,6 +353,7 @@ def config_context( the fit method of estimators and for arguments passed to public helper functions. It can save time in some situations but can lead to low level crashes and exceptions with confusing error messages. + Global default: False. Note that for data parameters, such as `X` and `y`, only type validation is skipped but validation with `check_array` will continue to run. diff --git a/sklearn/_loss/_loss.pyx.tp b/sklearn/_loss/_loss.pyx.tp index 6054d4c9472ca..44d5acd530a7f 100644 --- a/sklearn/_loss/_loss.pyx.tp +++ b/sklearn/_loss/_loss.pyx.tp @@ -121,7 +121,7 @@ doc_HalfTweedieLoss = ( - y_true * exp((1-p) * raw_prediction) / (1-p) Notes: - - Poisson with p=1 and and Gamma with p=2 have different terms dropped such + - Poisson with p=1 and Gamma with p=2 have different terms dropped such that cHalfTweedieLoss is not continuous in p=power at p=1 and p=2. - While the Tweedie distribution only exists for p<=0 or p>=1, the range 0>> clustering SpectralCoclustering(n_clusters=2, random_state=0) + + For a more detailed example, see the following: + :ref:`sphx_glr_auto_examples_bicluster_plot_spectral_coclustering.py`. """ _parameter_constraints: dict = { diff --git a/sklearn/cluster/_hdbscan/_tree.pyx b/sklearn/cluster/_hdbscan/_tree.pyx index 161092033b915..3c8e93abaaf8f 100644 --- a/sklearn/cluster/_hdbscan/_tree.pyx +++ b/sklearn/cluster/_hdbscan/_tree.pyx @@ -783,7 +783,7 @@ cdef tuple _get_clusters( else: is_cluster[c] = False - clusters = set([c for c in is_cluster if is_cluster[c]]) + clusters = {c for c in is_cluster if is_cluster[c]} cluster_map = {c: n for n, c in enumerate(sorted(list(clusters)))} reverse_cluster_map = {n: c for c, n in cluster_map.items()} diff --git a/sklearn/cluster/_hdbscan/meson.build b/sklearn/cluster/_hdbscan/meson.build index f2e3ac91b1eb2..8d880b39a4db5 100644 --- a/sklearn/cluster/_hdbscan/meson.build +++ b/sklearn/cluster/_hdbscan/meson.build @@ -1,7 +1,7 @@ cluster_hdbscan_extension_metadata = { '_linkage': {'sources': [cython_gen.process('_linkage.pyx'), metrics_cython_tree]}, '_reachability': {'sources': [cython_gen.process('_reachability.pyx')]}, - '_tree': {'sources': ['_tree.pyx']} + '_tree': {'sources': [cython_gen.process('_tree.pyx')]} } foreach ext_name, ext_dict : cluster_hdbscan_extension_metadata diff --git a/sklearn/cluster/_optics.py b/sklearn/cluster/_optics.py index 0cd32023de46c..4a1a80c9065c2 100644 --- a/sklearn/cluster/_optics.py +++ b/sklearn/cluster/_optics.py @@ -34,19 +34,21 @@ class OPTICS(ClusterMixin, BaseEstimator): """Estimate clustering structure from vector array. OPTICS (Ordering Points To Identify the Clustering Structure), closely - related to DBSCAN, finds core sample of high density and expands clusters - from them [1]_. Unlike DBSCAN, keeps cluster hierarchy for a variable + related to DBSCAN, finds core samples of high density and expands clusters + from them [1]_. Unlike DBSCAN, it keeps cluster hierarchy for a variable neighborhood radius. Better suited for usage on large datasets than the - current sklearn implementation of DBSCAN. + current scikit-learn implementation of DBSCAN. - Clusters are then extracted using a DBSCAN-like method - (cluster_method = 'dbscan') or an automatic + Clusters are then extracted from the cluster-order using a + DBSCAN-like method (cluster_method = 'dbscan') or an automatic technique proposed in [1]_ (cluster_method = 'xi'). This implementation deviates from the original OPTICS by first performing - k-nearest-neighborhood searches on all points to identify core sizes, then - computing only the distances to unprocessed points when constructing the - cluster order. Note that we do not employ a heap to manage the expansion + k-nearest-neighborhood searches on all points to identify core sizes of + all points (instead of computing neighbors while looping through points). + Reachability distances to only unprocessed points are then computed, to + construct the cluster order, similar to the original OPTICS. + Note that we do not employ a heap to manage the expansion candidates, so the time complexity will be O(n^2). Read more in the :ref:`User Guide `. @@ -68,9 +70,9 @@ class OPTICS(ClusterMixin, BaseEstimator): metric : str or callable, default='minkowski' Metric to use for distance computation. Any metric from scikit-learn - or scipy.spatial.distance can be used. + or :mod:`scipy.spatial.distance` can be used. - If metric is a callable function, it is called on each + If `metric` is a callable function, it is called on each pair of instances (rows) and the resulting value recorded. The callable should take two arrays as input and return one value indicating the distance between them. This works for Scipy's metrics, but is less @@ -90,8 +92,7 @@ class OPTICS(ClusterMixin, BaseEstimator): 'yule'] Sparse matrices are only supported by scikit-learn metrics. - See the documentation for scipy.spatial.distance for details on these - metrics. + See :mod:`scipy.spatial.distance` for details on these metrics. .. note:: `'kulsinski'` is deprecated from SciPy 1.9 and will be removed in SciPy 1.11. @@ -105,9 +106,9 @@ class OPTICS(ClusterMixin, BaseEstimator): metric_params : dict, default=None Additional keyword arguments for the metric function. - cluster_method : str, default='xi' + cluster_method : {'xi', 'dbscan'}, default='xi' The extraction method used to extract clusters using the calculated - reachability and ordering. Possible values are "xi" and "dbscan". + reachability and ordering. eps : float, default=None The maximum distance between two samples for one to be considered as diff --git a/sklearn/cluster/tests/test_optics.py b/sklearn/cluster/tests/test_optics.py index cf7d36f7848af..02184ea454d65 100644 --- a/sklearn/cluster/tests/test_optics.py +++ b/sklearn/cluster/tests/test_optics.py @@ -258,6 +258,12 @@ def test_warn_if_metric_bool_data_no_bool(): msg = f"Data will be converted to boolean for metric {pairwise_metric}" with pytest.warns(DataConversionWarning, match=msg) as warn_record: + # Silence a DeprecationWarning from joblib <= 1.5.1 in Python 3.14+. + warnings.filterwarnings( + "ignore", + message="'asyncio.iscoroutinefunction' is deprecated", + category=DeprecationWarning, + ) OPTICS(metric=pairwise_metric).fit(X) assert len(warn_record) == 1 diff --git a/sklearn/compose/_column_transformer.py b/sklearn/compose/_column_transformer.py index 8e3938c49be32..940d9194dd976 100644 --- a/sklearn/compose/_column_transformer.py +++ b/sklearn/compose/_column_transformer.py @@ -20,10 +20,10 @@ from ..pipeline import _fit_transform_one, _name_estimators, _transform_one from ..preprocessing import FunctionTransformer from ..utils import Bunch -from ..utils._estimator_html_repr import _VisualBlock from ..utils._indexing import _determine_key_type, _get_column_indices, _safe_indexing from ..utils._metadata_requests import METHODS from ..utils._param_validation import HasMethods, Hidden, Interval, StrOptions +from ..utils._repr_html.estimator import _VisualBlock from ..utils._set_output import ( _get_container_adapter, _get_output_config, @@ -1344,7 +1344,12 @@ def _is_empty_column_selection(column): boolean array). """ - if hasattr(column, "dtype") and np.issubdtype(column.dtype, np.bool_): + if ( + hasattr(column, "dtype") + # Not necessarily a numpy dtype, can be a pandas dtype as well + and isinstance(column.dtype, np.dtype) + and np.issubdtype(column.dtype, np.bool_) + ): return not column.any() elif hasattr(column, "__len__"): return len(column) == 0 or ( diff --git a/sklearn/compose/_target.py b/sklearn/compose/_target.py index 86fc6294878b9..7f713767b30cb 100644 --- a/sklearn/compose/_target.py +++ b/sklearn/compose/_target.py @@ -281,7 +281,7 @@ def fit(self, X, y, **fit_params): # FIXME: a FunctionTransformer can return a 1D array even when validate # is set to True. Therefore, we need to check the number of dimension # first. - if y_trans.ndim == 2 and y_trans.shape[1] == 1: + if y_trans.ndim == 2 and y_trans.shape[1] == 1 and self._training_dim == 1: y_trans = y_trans.squeeze(axis=1) self.regressor_ = self._get_regressor(get_clone=True) diff --git a/sklearn/compose/tests/test_column_transformer.py b/sklearn/compose/tests/test_column_transformer.py index daa4111c9393d..c7c69c657f2ea 100644 --- a/sklearn/compose/tests/test_column_transformer.py +++ b/sklearn/compose/tests/test_column_transformer.py @@ -20,6 +20,7 @@ make_column_transformer, ) from sklearn.exceptions import NotFittedError +from sklearn.feature_extraction import DictVectorizer from sklearn.feature_selection import VarianceThreshold from sklearn.preprocessing import ( FunctionTransformer, @@ -1375,10 +1376,10 @@ def test_n_features_in(): "cols, pattern, include, exclude", [ (["col_int", "col_float"], None, np.number, None), - (["col_int", "col_float"], None, None, object), + (["col_int", "col_float"], None, None, [object, "string"]), (["col_int", "col_float"], None, [int, float], None), - (["col_str"], None, [object], None), - (["col_str"], None, object, None), + (["col_str"], None, [object, "string"], None), + (["col_float"], None, [float], None), (["col_float"], None, float, None), (["col_float"], "at$", [np.number], None), (["col_int"], None, [int], None), @@ -1386,7 +1387,12 @@ def test_n_features_in(): (["col_float", "col_str"], "float|str", None, None), (["col_str"], "^col_s", None, [int]), ([], "str$", float, None), - (["col_int", "col_float", "col_str"], None, [np.number, object], None), + ( + ["col_int", "col_float", "col_str"], + None, + [np.number, object, "string"], + None, + ), ], ) def test_make_column_selector_with_select_dtypes(cols, pattern, include, exclude): @@ -1422,7 +1428,7 @@ def test_column_transformer_with_make_column_selector(): ) X_df["col_str"] = X_df["col_str"].astype("category") - cat_selector = make_column_selector(dtype_include=["category", object]) + cat_selector = make_column_selector(dtype_include=["category", object, "string"]) num_selector = make_column_selector(dtype_include=np.number) ohe = OneHotEncoder() @@ -2619,6 +2625,29 @@ def test_column_transformer_auto_memmap(): assert_allclose(Xt, StandardScaler().fit_transform(X[:, [0]])) +def test_column_transformer_non_default_index(): + """Check index handling when both pd.Series and pd.DataFrame slices are used in + ColumnTransformer. + + Non-regression test for issue #31546. + """ + pd = pytest.importorskip("pandas") + df = pd.DataFrame( + { + "dict_col": [{"foo": 1, "bar": 2}, {"foo": 3, "baz": 1}], + "dummy_col": [1, 2], + }, + index=[1, 2], + ) + t = make_column_transformer( + (DictVectorizer(sparse=False), "dict_col"), + (FunctionTransformer(), ["dummy_col"]), + ) + t.set_output(transform="pandas") + X = t.fit_transform(df) + assert list(X.index) == [1, 2] + + # Metadata Routing Tests # ====================== diff --git a/sklearn/compose/tests/test_target.py b/sklearn/compose/tests/test_target.py index e65b950f04007..19dcfb5dc7f03 100644 --- a/sklearn/compose/tests/test_target.py +++ b/sklearn/compose/tests/test_target.py @@ -410,3 +410,30 @@ def test_transform_target_regressor_not_warns_with_global_output_set(output_form TransformedTargetRegressor( regressor=LinearRegression(), func=np.log, inverse_func=np.exp ).fit(X, y) + + +class ValidateDimensionRegressor(BaseEstimator): + """A regressor that expects the target to have a specific number of dimensions.""" + + def __init__(self, ndim): + self.ndim = ndim + + def fit(self, X, y): + assert y.ndim == self.ndim + + def predict(self, X): + pass # pragma: no cover + + +@pytest.mark.parametrize("ndim", [1, 2]) +def test_transform_target_regressor_preserves_input_shape(ndim): + """Check that TransformedTargetRegressor internally preserves the shape of the input + + non-regression test for issue #26530. + """ + X, y = datasets.make_regression(n_samples=10, n_features=5, random_state=42) + if ndim == 2: + y = y.reshape(-1, 1) + + regr = TransformedTargetRegressor(regressor=ValidateDimensionRegressor(ndim)) + regr.fit(X, y) diff --git a/sklearn/covariance/_empirical_covariance.py b/sklearn/covariance/_empirical_covariance.py index 7c4db63b4e363..c8ee198cc4772 100644 --- a/sklearn/covariance/_empirical_covariance.py +++ b/sklearn/covariance/_empirical_covariance.py @@ -135,7 +135,7 @@ class EmpiricalCovariance(BaseEstimator): Estimated location, i.e. the estimated mean. covariance_ : ndarray of shape (n_features, n_features) - Estimated covariance matrix + Estimated covariance matrix. precision_ : ndarray of shape (n_features, n_features) Estimated pseudo-inverse matrix. @@ -343,6 +343,9 @@ def error_norm(self, comp_cov, norm="frobenius", scaling=True, squared=True): def mahalanobis(self, X): """Compute the squared Mahalanobis distances of given observations. + For a detailed example of how outliers affects the Mahalanobis distance, + see :ref:`sphx_glr_auto_examples_covariance_plot_mahalanobis_distances.py`. + Parameters ---------- X : array-like of shape (n_samples, n_features) diff --git a/sklearn/covariance/_robust_covariance.py b/sklearn/covariance/_robust_covariance.py index f386879e693fb..81fc194c6e410 100644 --- a/sklearn/covariance/_robust_covariance.py +++ b/sklearn/covariance/_robust_covariance.py @@ -633,6 +633,10 @@ class MinCovDet(EmpiricalCovariance): location_ : ndarray of shape (n_features,) Estimated robust location. + For an example of comparing raw robust estimates with + the true location and covariance, refer to + :ref:`sphx_glr_auto_examples_covariance_plot_robust_vs_empirical_covariance.py`. + covariance_ : ndarray of shape (n_features, n_features) Estimated robust covariance matrix. diff --git a/sklearn/datasets/_lfw.py b/sklearn/datasets/_lfw.py index 06420c41ed246..4f725b9250cc5 100644 --- a/sklearn/datasets/_lfw.py +++ b/sklearn/datasets/_lfw.py @@ -19,6 +19,7 @@ from ..utils import Bunch from ..utils._param_validation import Hidden, Interval, StrOptions, validate_params +from ..utils.fixes import tarfile_extractall from ._base import ( RemoteFileMetadata, _fetch_remote, @@ -117,10 +118,7 @@ def _check_fetch_lfw( logger.debug("Decompressing the data archive to %s", data_folder_path) with tarfile.open(archive_path, "r:gz") as fp: - # Use filter="data" to prevent the most dangerous security issues. - # For more details, see - # https://docs.python.org/3.9/library/tarfile.html#tarfile.TarFile.extractall - fp.extractall(path=lfw_home, filter="data") + tarfile_extractall(fp, path=lfw_home) remove(archive_path) diff --git a/sklearn/datasets/_openml.py b/sklearn/datasets/_openml.py index 47ecdcd14de9d..537f6cde499a2 100644 --- a/sklearn/datasets/_openml.py +++ b/sklearn/datasets/_openml.py @@ -109,6 +109,10 @@ def wrapper(*args, **kwargs): warn( f"A network error occurred while downloading {url}. Retrying..." ) + # Avoid a ResourceWarning on Python 3.14 and later. + if isinstance(e, HTTPError): + e.close() + retry_counter -= 1 time.sleep(delay) diff --git a/sklearn/datasets/_samples_generator.py b/sklearn/datasets/_samples_generator.py index e2d80422e7df7..c3b4622d6a91b 100644 --- a/sklearn/datasets/_samples_generator.py +++ b/sklearn/datasets/_samples_generator.py @@ -1864,6 +1864,8 @@ def make_swiss_roll(n_samples=100, *, noise=0.0, random_state=None, hole=False): Read more in the :ref:`User Guide `. + Adapted with permission from Stephen Marsland's code [1]. + Parameters ---------- n_samples : int, default=100 diff --git a/sklearn/datasets/_twenty_newsgroups.py b/sklearn/datasets/_twenty_newsgroups.py index 62db8c5cbdc8e..1dc5fb6244f1b 100644 --- a/sklearn/datasets/_twenty_newsgroups.py +++ b/sklearn/datasets/_twenty_newsgroups.py @@ -43,6 +43,7 @@ from ..feature_extraction.text import CountVectorizer from ..utils import Bunch, check_random_state from ..utils._param_validation import Interval, StrOptions, validate_params +from ..utils.fixes import tarfile_extractall from . import get_data_home, load_files from ._base import ( RemoteFileMetadata, @@ -81,10 +82,7 @@ def _download_20newsgroups(target_dir, cache_path, n_retries, delay): logger.debug("Decompressing %s", archive_path) with tarfile.open(archive_path, "r:gz") as fp: - # Use filter="data" to prevent the most dangerous security issues. - # For more details, see - # https://docs.python.org/3.9/library/tarfile.html#tarfile.TarFile.extractall - fp.extractall(path=target_dir, filter="data") + tarfile_extractall(fp, path=target_dir) with suppress(FileNotFoundError): os.remove(archive_path) diff --git a/sklearn/datasets/data/boston_house_prices.csv b/sklearn/datasets/data/boston_house_prices.csv deleted file mode 100644 index 61193a5d646cc..0000000000000 --- a/sklearn/datasets/data/boston_house_prices.csv +++ /dev/null @@ -1,508 +0,0 @@ -506,13,,,,,,,,,,,, -"CRIM","ZN","INDUS","CHAS","NOX","RM","AGE","DIS","RAD","TAX","PTRATIO","B","LSTAT","MEDV" -0.00632,18,2.31,0,0.538,6.575,65.2,4.09,1,296,15.3,396.9,4.98,24 -0.02731,0,7.07,0,0.469,6.421,78.9,4.9671,2,242,17.8,396.9,9.14,21.6 -0.02729,0,7.07,0,0.469,7.185,61.1,4.9671,2,242,17.8,392.83,4.03,34.7 -0.03237,0,2.18,0,0.458,6.998,45.8,6.0622,3,222,18.7,394.63,2.94,33.4 -0.06905,0,2.18,0,0.458,7.147,54.2,6.0622,3,222,18.7,396.9,5.33,36.2 -0.02985,0,2.18,0,0.458,6.43,58.7,6.0622,3,222,18.7,394.12,5.21,28.7 -0.08829,12.5,7.87,0,0.524,6.012,66.6,5.5605,5,311,15.2,395.6,12.43,22.9 -0.14455,12.5,7.87,0,0.524,6.172,96.1,5.9505,5,311,15.2,396.9,19.15,27.1 -0.21124,12.5,7.87,0,0.524,5.631,100,6.0821,5,311,15.2,386.63,29.93,16.5 -0.17004,12.5,7.87,0,0.524,6.004,85.9,6.5921,5,311,15.2,386.71,17.1,18.9 -0.22489,12.5,7.87,0,0.524,6.377,94.3,6.3467,5,311,15.2,392.52,20.45,15 -0.11747,12.5,7.87,0,0.524,6.009,82.9,6.2267,5,311,15.2,396.9,13.27,18.9 -0.09378,12.5,7.87,0,0.524,5.889,39,5.4509,5,311,15.2,390.5,15.71,21.7 -0.62976,0,8.14,0,0.538,5.949,61.8,4.7075,4,307,21,396.9,8.26,20.4 -0.63796,0,8.14,0,0.538,6.096,84.5,4.4619,4,307,21,380.02,10.26,18.2 -0.62739,0,8.14,0,0.538,5.834,56.5,4.4986,4,307,21,395.62,8.47,19.9 -1.05393,0,8.14,0,0.538,5.935,29.3,4.4986,4,307,21,386.85,6.58,23.1 -0.7842,0,8.14,0,0.538,5.99,81.7,4.2579,4,307,21,386.75,14.67,17.5 -0.80271,0,8.14,0,0.538,5.456,36.6,3.7965,4,307,21,288.99,11.69,20.2 -0.7258,0,8.14,0,0.538,5.727,69.5,3.7965,4,307,21,390.95,11.28,18.2 -1.25179,0,8.14,0,0.538,5.57,98.1,3.7979,4,307,21,376.57,21.02,13.6 -0.85204,0,8.14,0,0.538,5.965,89.2,4.0123,4,307,21,392.53,13.83,19.6 -1.23247,0,8.14,0,0.538,6.142,91.7,3.9769,4,307,21,396.9,18.72,15.2 -0.98843,0,8.14,0,0.538,5.813,100,4.0952,4,307,21,394.54,19.88,14.5 -0.75026,0,8.14,0,0.538,5.924,94.1,4.3996,4,307,21,394.33,16.3,15.6 -0.84054,0,8.14,0,0.538,5.599,85.7,4.4546,4,307,21,303.42,16.51,13.9 -0.67191,0,8.14,0,0.538,5.813,90.3,4.682,4,307,21,376.88,14.81,16.6 -0.95577,0,8.14,0,0.538,6.047,88.8,4.4534,4,307,21,306.38,17.28,14.8 -0.77299,0,8.14,0,0.538,6.495,94.4,4.4547,4,307,21,387.94,12.8,18.4 -1.00245,0,8.14,0,0.538,6.674,87.3,4.239,4,307,21,380.23,11.98,21 -1.13081,0,8.14,0,0.538,5.713,94.1,4.233,4,307,21,360.17,22.6,12.7 -1.35472,0,8.14,0,0.538,6.072,100,4.175,4,307,21,376.73,13.04,14.5 -1.38799,0,8.14,0,0.538,5.95,82,3.99,4,307,21,232.6,27.71,13.2 -1.15172,0,8.14,0,0.538,5.701,95,3.7872,4,307,21,358.77,18.35,13.1 -1.61282,0,8.14,0,0.538,6.096,96.9,3.7598,4,307,21,248.31,20.34,13.5 -0.06417,0,5.96,0,0.499,5.933,68.2,3.3603,5,279,19.2,396.9,9.68,18.9 -0.09744,0,5.96,0,0.499,5.841,61.4,3.3779,5,279,19.2,377.56,11.41,20 -0.08014,0,5.96,0,0.499,5.85,41.5,3.9342,5,279,19.2,396.9,8.77,21 -0.17505,0,5.96,0,0.499,5.966,30.2,3.8473,5,279,19.2,393.43,10.13,24.7 -0.02763,75,2.95,0,0.428,6.595,21.8,5.4011,3,252,18.3,395.63,4.32,30.8 -0.03359,75,2.95,0,0.428,7.024,15.8,5.4011,3,252,18.3,395.62,1.98,34.9 -0.12744,0,6.91,0,0.448,6.77,2.9,5.7209,3,233,17.9,385.41,4.84,26.6 -0.1415,0,6.91,0,0.448,6.169,6.6,5.7209,3,233,17.9,383.37,5.81,25.3 -0.15936,0,6.91,0,0.448,6.211,6.5,5.7209,3,233,17.9,394.46,7.44,24.7 -0.12269,0,6.91,0,0.448,6.069,40,5.7209,3,233,17.9,389.39,9.55,21.2 -0.17142,0,6.91,0,0.448,5.682,33.8,5.1004,3,233,17.9,396.9,10.21,19.3 -0.18836,0,6.91,0,0.448,5.786,33.3,5.1004,3,233,17.9,396.9,14.15,20 -0.22927,0,6.91,0,0.448,6.03,85.5,5.6894,3,233,17.9,392.74,18.8,16.6 -0.25387,0,6.91,0,0.448,5.399,95.3,5.87,3,233,17.9,396.9,30.81,14.4 -0.21977,0,6.91,0,0.448,5.602,62,6.0877,3,233,17.9,396.9,16.2,19.4 -0.08873,21,5.64,0,0.439,5.963,45.7,6.8147,4,243,16.8,395.56,13.45,19.7 -0.04337,21,5.64,0,0.439,6.115,63,6.8147,4,243,16.8,393.97,9.43,20.5 -0.0536,21,5.64,0,0.439,6.511,21.1,6.8147,4,243,16.8,396.9,5.28,25 -0.04981,21,5.64,0,0.439,5.998,21.4,6.8147,4,243,16.8,396.9,8.43,23.4 -0.0136,75,4,0,0.41,5.888,47.6,7.3197,3,469,21.1,396.9,14.8,18.9 -0.01311,90,1.22,0,0.403,7.249,21.9,8.6966,5,226,17.9,395.93,4.81,35.4 -0.02055,85,0.74,0,0.41,6.383,35.7,9.1876,2,313,17.3,396.9,5.77,24.7 -0.01432,100,1.32,0,0.411,6.816,40.5,8.3248,5,256,15.1,392.9,3.95,31.6 -0.15445,25,5.13,0,0.453,6.145,29.2,7.8148,8,284,19.7,390.68,6.86,23.3 -0.10328,25,5.13,0,0.453,5.927,47.2,6.932,8,284,19.7,396.9,9.22,19.6 -0.14932,25,5.13,0,0.453,5.741,66.2,7.2254,8,284,19.7,395.11,13.15,18.7 -0.17171,25,5.13,0,0.453,5.966,93.4,6.8185,8,284,19.7,378.08,14.44,16 -0.11027,25,5.13,0,0.453,6.456,67.8,7.2255,8,284,19.7,396.9,6.73,22.2 -0.1265,25,5.13,0,0.453,6.762,43.4,7.9809,8,284,19.7,395.58,9.5,25 -0.01951,17.5,1.38,0,0.4161,7.104,59.5,9.2229,3,216,18.6,393.24,8.05,33 -0.03584,80,3.37,0,0.398,6.29,17.8,6.6115,4,337,16.1,396.9,4.67,23.5 -0.04379,80,3.37,0,0.398,5.787,31.1,6.6115,4,337,16.1,396.9,10.24,19.4 -0.05789,12.5,6.07,0,0.409,5.878,21.4,6.498,4,345,18.9,396.21,8.1,22 -0.13554,12.5,6.07,0,0.409,5.594,36.8,6.498,4,345,18.9,396.9,13.09,17.4 -0.12816,12.5,6.07,0,0.409,5.885,33,6.498,4,345,18.9,396.9,8.79,20.9 -0.08826,0,10.81,0,0.413,6.417,6.6,5.2873,4,305,19.2,383.73,6.72,24.2 -0.15876,0,10.81,0,0.413,5.961,17.5,5.2873,4,305,19.2,376.94,9.88,21.7 -0.09164,0,10.81,0,0.413,6.065,7.8,5.2873,4,305,19.2,390.91,5.52,22.8 -0.19539,0,10.81,0,0.413,6.245,6.2,5.2873,4,305,19.2,377.17,7.54,23.4 -0.07896,0,12.83,0,0.437,6.273,6,4.2515,5,398,18.7,394.92,6.78,24.1 -0.09512,0,12.83,0,0.437,6.286,45,4.5026,5,398,18.7,383.23,8.94,21.4 -0.10153,0,12.83,0,0.437,6.279,74.5,4.0522,5,398,18.7,373.66,11.97,20 -0.08707,0,12.83,0,0.437,6.14,45.8,4.0905,5,398,18.7,386.96,10.27,20.8 -0.05646,0,12.83,0,0.437,6.232,53.7,5.0141,5,398,18.7,386.4,12.34,21.2 -0.08387,0,12.83,0,0.437,5.874,36.6,4.5026,5,398,18.7,396.06,9.1,20.3 -0.04113,25,4.86,0,0.426,6.727,33.5,5.4007,4,281,19,396.9,5.29,28 -0.04462,25,4.86,0,0.426,6.619,70.4,5.4007,4,281,19,395.63,7.22,23.9 -0.03659,25,4.86,0,0.426,6.302,32.2,5.4007,4,281,19,396.9,6.72,24.8 -0.03551,25,4.86,0,0.426,6.167,46.7,5.4007,4,281,19,390.64,7.51,22.9 -0.05059,0,4.49,0,0.449,6.389,48,4.7794,3,247,18.5,396.9,9.62,23.9 -0.05735,0,4.49,0,0.449,6.63,56.1,4.4377,3,247,18.5,392.3,6.53,26.6 -0.05188,0,4.49,0,0.449,6.015,45.1,4.4272,3,247,18.5,395.99,12.86,22.5 -0.07151,0,4.49,0,0.449,6.121,56.8,3.7476,3,247,18.5,395.15,8.44,22.2 -0.0566,0,3.41,0,0.489,7.007,86.3,3.4217,2,270,17.8,396.9,5.5,23.6 -0.05302,0,3.41,0,0.489,7.079,63.1,3.4145,2,270,17.8,396.06,5.7,28.7 -0.04684,0,3.41,0,0.489,6.417,66.1,3.0923,2,270,17.8,392.18,8.81,22.6 -0.03932,0,3.41,0,0.489,6.405,73.9,3.0921,2,270,17.8,393.55,8.2,22 -0.04203,28,15.04,0,0.464,6.442,53.6,3.6659,4,270,18.2,395.01,8.16,22.9 -0.02875,28,15.04,0,0.464,6.211,28.9,3.6659,4,270,18.2,396.33,6.21,25 -0.04294,28,15.04,0,0.464,6.249,77.3,3.615,4,270,18.2,396.9,10.59,20.6 -0.12204,0,2.89,0,0.445,6.625,57.8,3.4952,2,276,18,357.98,6.65,28.4 -0.11504,0,2.89,0,0.445,6.163,69.6,3.4952,2,276,18,391.83,11.34,21.4 -0.12083,0,2.89,0,0.445,8.069,76,3.4952,2,276,18,396.9,4.21,38.7 -0.08187,0,2.89,0,0.445,7.82,36.9,3.4952,2,276,18,393.53,3.57,43.8 -0.0686,0,2.89,0,0.445,7.416,62.5,3.4952,2,276,18,396.9,6.19,33.2 -0.14866,0,8.56,0,0.52,6.727,79.9,2.7778,5,384,20.9,394.76,9.42,27.5 -0.11432,0,8.56,0,0.52,6.781,71.3,2.8561,5,384,20.9,395.58,7.67,26.5 -0.22876,0,8.56,0,0.52,6.405,85.4,2.7147,5,384,20.9,70.8,10.63,18.6 -0.21161,0,8.56,0,0.52,6.137,87.4,2.7147,5,384,20.9,394.47,13.44,19.3 -0.1396,0,8.56,0,0.52,6.167,90,2.421,5,384,20.9,392.69,12.33,20.1 -0.13262,0,8.56,0,0.52,5.851,96.7,2.1069,5,384,20.9,394.05,16.47,19.5 -0.1712,0,8.56,0,0.52,5.836,91.9,2.211,5,384,20.9,395.67,18.66,19.5 -0.13117,0,8.56,0,0.52,6.127,85.2,2.1224,5,384,20.9,387.69,14.09,20.4 -0.12802,0,8.56,0,0.52,6.474,97.1,2.4329,5,384,20.9,395.24,12.27,19.8 -0.26363,0,8.56,0,0.52,6.229,91.2,2.5451,5,384,20.9,391.23,15.55,19.4 -0.10793,0,8.56,0,0.52,6.195,54.4,2.7778,5,384,20.9,393.49,13,21.7 -0.10084,0,10.01,0,0.547,6.715,81.6,2.6775,6,432,17.8,395.59,10.16,22.8 -0.12329,0,10.01,0,0.547,5.913,92.9,2.3534,6,432,17.8,394.95,16.21,18.8 -0.22212,0,10.01,0,0.547,6.092,95.4,2.548,6,432,17.8,396.9,17.09,18.7 -0.14231,0,10.01,0,0.547,6.254,84.2,2.2565,6,432,17.8,388.74,10.45,18.5 -0.17134,0,10.01,0,0.547,5.928,88.2,2.4631,6,432,17.8,344.91,15.76,18.3 -0.13158,0,10.01,0,0.547,6.176,72.5,2.7301,6,432,17.8,393.3,12.04,21.2 -0.15098,0,10.01,0,0.547,6.021,82.6,2.7474,6,432,17.8,394.51,10.3,19.2 -0.13058,0,10.01,0,0.547,5.872,73.1,2.4775,6,432,17.8,338.63,15.37,20.4 -0.14476,0,10.01,0,0.547,5.731,65.2,2.7592,6,432,17.8,391.5,13.61,19.3 -0.06899,0,25.65,0,0.581,5.87,69.7,2.2577,2,188,19.1,389.15,14.37,22 -0.07165,0,25.65,0,0.581,6.004,84.1,2.1974,2,188,19.1,377.67,14.27,20.3 -0.09299,0,25.65,0,0.581,5.961,92.9,2.0869,2,188,19.1,378.09,17.93,20.5 -0.15038,0,25.65,0,0.581,5.856,97,1.9444,2,188,19.1,370.31,25.41,17.3 -0.09849,0,25.65,0,0.581,5.879,95.8,2.0063,2,188,19.1,379.38,17.58,18.8 -0.16902,0,25.65,0,0.581,5.986,88.4,1.9929,2,188,19.1,385.02,14.81,21.4 -0.38735,0,25.65,0,0.581,5.613,95.6,1.7572,2,188,19.1,359.29,27.26,15.7 -0.25915,0,21.89,0,0.624,5.693,96,1.7883,4,437,21.2,392.11,17.19,16.2 -0.32543,0,21.89,0,0.624,6.431,98.8,1.8125,4,437,21.2,396.9,15.39,18 -0.88125,0,21.89,0,0.624,5.637,94.7,1.9799,4,437,21.2,396.9,18.34,14.3 -0.34006,0,21.89,0,0.624,6.458,98.9,2.1185,4,437,21.2,395.04,12.6,19.2 -1.19294,0,21.89,0,0.624,6.326,97.7,2.271,4,437,21.2,396.9,12.26,19.6 -0.59005,0,21.89,0,0.624,6.372,97.9,2.3274,4,437,21.2,385.76,11.12,23 -0.32982,0,21.89,0,0.624,5.822,95.4,2.4699,4,437,21.2,388.69,15.03,18.4 -0.97617,0,21.89,0,0.624,5.757,98.4,2.346,4,437,21.2,262.76,17.31,15.6 -0.55778,0,21.89,0,0.624,6.335,98.2,2.1107,4,437,21.2,394.67,16.96,18.1 -0.32264,0,21.89,0,0.624,5.942,93.5,1.9669,4,437,21.2,378.25,16.9,17.4 -0.35233,0,21.89,0,0.624,6.454,98.4,1.8498,4,437,21.2,394.08,14.59,17.1 -0.2498,0,21.89,0,0.624,5.857,98.2,1.6686,4,437,21.2,392.04,21.32,13.3 -0.54452,0,21.89,0,0.624,6.151,97.9,1.6687,4,437,21.2,396.9,18.46,17.8 -0.2909,0,21.89,0,0.624,6.174,93.6,1.6119,4,437,21.2,388.08,24.16,14 -1.62864,0,21.89,0,0.624,5.019,100,1.4394,4,437,21.2,396.9,34.41,14.4 -3.32105,0,19.58,1,0.871,5.403,100,1.3216,5,403,14.7,396.9,26.82,13.4 -4.0974,0,19.58,0,0.871,5.468,100,1.4118,5,403,14.7,396.9,26.42,15.6 -2.77974,0,19.58,0,0.871,4.903,97.8,1.3459,5,403,14.7,396.9,29.29,11.8 -2.37934,0,19.58,0,0.871,6.13,100,1.4191,5,403,14.7,172.91,27.8,13.8 -2.15505,0,19.58,0,0.871,5.628,100,1.5166,5,403,14.7,169.27,16.65,15.6 -2.36862,0,19.58,0,0.871,4.926,95.7,1.4608,5,403,14.7,391.71,29.53,14.6 -2.33099,0,19.58,0,0.871,5.186,93.8,1.5296,5,403,14.7,356.99,28.32,17.8 -2.73397,0,19.58,0,0.871,5.597,94.9,1.5257,5,403,14.7,351.85,21.45,15.4 -1.6566,0,19.58,0,0.871,6.122,97.3,1.618,5,403,14.7,372.8,14.1,21.5 -1.49632,0,19.58,0,0.871,5.404,100,1.5916,5,403,14.7,341.6,13.28,19.6 -1.12658,0,19.58,1,0.871,5.012,88,1.6102,5,403,14.7,343.28,12.12,15.3 -2.14918,0,19.58,0,0.871,5.709,98.5,1.6232,5,403,14.7,261.95,15.79,19.4 -1.41385,0,19.58,1,0.871,6.129,96,1.7494,5,403,14.7,321.02,15.12,17 -3.53501,0,19.58,1,0.871,6.152,82.6,1.7455,5,403,14.7,88.01,15.02,15.6 -2.44668,0,19.58,0,0.871,5.272,94,1.7364,5,403,14.7,88.63,16.14,13.1 -1.22358,0,19.58,0,0.605,6.943,97.4,1.8773,5,403,14.7,363.43,4.59,41.3 -1.34284,0,19.58,0,0.605,6.066,100,1.7573,5,403,14.7,353.89,6.43,24.3 -1.42502,0,19.58,0,0.871,6.51,100,1.7659,5,403,14.7,364.31,7.39,23.3 -1.27346,0,19.58,1,0.605,6.25,92.6,1.7984,5,403,14.7,338.92,5.5,27 -1.46336,0,19.58,0,0.605,7.489,90.8,1.9709,5,403,14.7,374.43,1.73,50 -1.83377,0,19.58,1,0.605,7.802,98.2,2.0407,5,403,14.7,389.61,1.92,50 -1.51902,0,19.58,1,0.605,8.375,93.9,2.162,5,403,14.7,388.45,3.32,50 -2.24236,0,19.58,0,0.605,5.854,91.8,2.422,5,403,14.7,395.11,11.64,22.7 -2.924,0,19.58,0,0.605,6.101,93,2.2834,5,403,14.7,240.16,9.81,25 -2.01019,0,19.58,0,0.605,7.929,96.2,2.0459,5,403,14.7,369.3,3.7,50 -1.80028,0,19.58,0,0.605,5.877,79.2,2.4259,5,403,14.7,227.61,12.14,23.8 -2.3004,0,19.58,0,0.605,6.319,96.1,2.1,5,403,14.7,297.09,11.1,23.8 -2.44953,0,19.58,0,0.605,6.402,95.2,2.2625,5,403,14.7,330.04,11.32,22.3 -1.20742,0,19.58,0,0.605,5.875,94.6,2.4259,5,403,14.7,292.29,14.43,17.4 -2.3139,0,19.58,0,0.605,5.88,97.3,2.3887,5,403,14.7,348.13,12.03,19.1 -0.13914,0,4.05,0,0.51,5.572,88.5,2.5961,5,296,16.6,396.9,14.69,23.1 -0.09178,0,4.05,0,0.51,6.416,84.1,2.6463,5,296,16.6,395.5,9.04,23.6 -0.08447,0,4.05,0,0.51,5.859,68.7,2.7019,5,296,16.6,393.23,9.64,22.6 -0.06664,0,4.05,0,0.51,6.546,33.1,3.1323,5,296,16.6,390.96,5.33,29.4 -0.07022,0,4.05,0,0.51,6.02,47.2,3.5549,5,296,16.6,393.23,10.11,23.2 -0.05425,0,4.05,0,0.51,6.315,73.4,3.3175,5,296,16.6,395.6,6.29,24.6 -0.06642,0,4.05,0,0.51,6.86,74.4,2.9153,5,296,16.6,391.27,6.92,29.9 -0.0578,0,2.46,0,0.488,6.98,58.4,2.829,3,193,17.8,396.9,5.04,37.2 -0.06588,0,2.46,0,0.488,7.765,83.3,2.741,3,193,17.8,395.56,7.56,39.8 -0.06888,0,2.46,0,0.488,6.144,62.2,2.5979,3,193,17.8,396.9,9.45,36.2 -0.09103,0,2.46,0,0.488,7.155,92.2,2.7006,3,193,17.8,394.12,4.82,37.9 -0.10008,0,2.46,0,0.488,6.563,95.6,2.847,3,193,17.8,396.9,5.68,32.5 -0.08308,0,2.46,0,0.488,5.604,89.8,2.9879,3,193,17.8,391,13.98,26.4 -0.06047,0,2.46,0,0.488,6.153,68.8,3.2797,3,193,17.8,387.11,13.15,29.6 -0.05602,0,2.46,0,0.488,7.831,53.6,3.1992,3,193,17.8,392.63,4.45,50 -0.07875,45,3.44,0,0.437,6.782,41.1,3.7886,5,398,15.2,393.87,6.68,32 -0.12579,45,3.44,0,0.437,6.556,29.1,4.5667,5,398,15.2,382.84,4.56,29.8 -0.0837,45,3.44,0,0.437,7.185,38.9,4.5667,5,398,15.2,396.9,5.39,34.9 -0.09068,45,3.44,0,0.437,6.951,21.5,6.4798,5,398,15.2,377.68,5.1,37 -0.06911,45,3.44,0,0.437,6.739,30.8,6.4798,5,398,15.2,389.71,4.69,30.5 -0.08664,45,3.44,0,0.437,7.178,26.3,6.4798,5,398,15.2,390.49,2.87,36.4 -0.02187,60,2.93,0,0.401,6.8,9.9,6.2196,1,265,15.6,393.37,5.03,31.1 -0.01439,60,2.93,0,0.401,6.604,18.8,6.2196,1,265,15.6,376.7,4.38,29.1 -0.01381,80,0.46,0,0.422,7.875,32,5.6484,4,255,14.4,394.23,2.97,50 -0.04011,80,1.52,0,0.404,7.287,34.1,7.309,2,329,12.6,396.9,4.08,33.3 -0.04666,80,1.52,0,0.404,7.107,36.6,7.309,2,329,12.6,354.31,8.61,30.3 -0.03768,80,1.52,0,0.404,7.274,38.3,7.309,2,329,12.6,392.2,6.62,34.6 -0.0315,95,1.47,0,0.403,6.975,15.3,7.6534,3,402,17,396.9,4.56,34.9 -0.01778,95,1.47,0,0.403,7.135,13.9,7.6534,3,402,17,384.3,4.45,32.9 -0.03445,82.5,2.03,0,0.415,6.162,38.4,6.27,2,348,14.7,393.77,7.43,24.1 -0.02177,82.5,2.03,0,0.415,7.61,15.7,6.27,2,348,14.7,395.38,3.11,42.3 -0.0351,95,2.68,0,0.4161,7.853,33.2,5.118,4,224,14.7,392.78,3.81,48.5 -0.02009,95,2.68,0,0.4161,8.034,31.9,5.118,4,224,14.7,390.55,2.88,50 -0.13642,0,10.59,0,0.489,5.891,22.3,3.9454,4,277,18.6,396.9,10.87,22.6 -0.22969,0,10.59,0,0.489,6.326,52.5,4.3549,4,277,18.6,394.87,10.97,24.4 -0.25199,0,10.59,0,0.489,5.783,72.7,4.3549,4,277,18.6,389.43,18.06,22.5 -0.13587,0,10.59,1,0.489,6.064,59.1,4.2392,4,277,18.6,381.32,14.66,24.4 -0.43571,0,10.59,1,0.489,5.344,100,3.875,4,277,18.6,396.9,23.09,20 -0.17446,0,10.59,1,0.489,5.96,92.1,3.8771,4,277,18.6,393.25,17.27,21.7 -0.37578,0,10.59,1,0.489,5.404,88.6,3.665,4,277,18.6,395.24,23.98,19.3 -0.21719,0,10.59,1,0.489,5.807,53.8,3.6526,4,277,18.6,390.94,16.03,22.4 -0.14052,0,10.59,0,0.489,6.375,32.3,3.9454,4,277,18.6,385.81,9.38,28.1 -0.28955,0,10.59,0,0.489,5.412,9.8,3.5875,4,277,18.6,348.93,29.55,23.7 -0.19802,0,10.59,0,0.489,6.182,42.4,3.9454,4,277,18.6,393.63,9.47,25 -0.0456,0,13.89,1,0.55,5.888,56,3.1121,5,276,16.4,392.8,13.51,23.3 -0.07013,0,13.89,0,0.55,6.642,85.1,3.4211,5,276,16.4,392.78,9.69,28.7 -0.11069,0,13.89,1,0.55,5.951,93.8,2.8893,5,276,16.4,396.9,17.92,21.5 -0.11425,0,13.89,1,0.55,6.373,92.4,3.3633,5,276,16.4,393.74,10.5,23 -0.35809,0,6.2,1,0.507,6.951,88.5,2.8617,8,307,17.4,391.7,9.71,26.7 -0.40771,0,6.2,1,0.507,6.164,91.3,3.048,8,307,17.4,395.24,21.46,21.7 -0.62356,0,6.2,1,0.507,6.879,77.7,3.2721,8,307,17.4,390.39,9.93,27.5 -0.6147,0,6.2,0,0.507,6.618,80.8,3.2721,8,307,17.4,396.9,7.6,30.1 -0.31533,0,6.2,0,0.504,8.266,78.3,2.8944,8,307,17.4,385.05,4.14,44.8 -0.52693,0,6.2,0,0.504,8.725,83,2.8944,8,307,17.4,382,4.63,50 -0.38214,0,6.2,0,0.504,8.04,86.5,3.2157,8,307,17.4,387.38,3.13,37.6 -0.41238,0,6.2,0,0.504,7.163,79.9,3.2157,8,307,17.4,372.08,6.36,31.6 -0.29819,0,6.2,0,0.504,7.686,17,3.3751,8,307,17.4,377.51,3.92,46.7 -0.44178,0,6.2,0,0.504,6.552,21.4,3.3751,8,307,17.4,380.34,3.76,31.5 -0.537,0,6.2,0,0.504,5.981,68.1,3.6715,8,307,17.4,378.35,11.65,24.3 -0.46296,0,6.2,0,0.504,7.412,76.9,3.6715,8,307,17.4,376.14,5.25,31.7 -0.57529,0,6.2,0,0.507,8.337,73.3,3.8384,8,307,17.4,385.91,2.47,41.7 -0.33147,0,6.2,0,0.507,8.247,70.4,3.6519,8,307,17.4,378.95,3.95,48.3 -0.44791,0,6.2,1,0.507,6.726,66.5,3.6519,8,307,17.4,360.2,8.05,29 -0.33045,0,6.2,0,0.507,6.086,61.5,3.6519,8,307,17.4,376.75,10.88,24 -0.52058,0,6.2,1,0.507,6.631,76.5,4.148,8,307,17.4,388.45,9.54,25.1 -0.51183,0,6.2,0,0.507,7.358,71.6,4.148,8,307,17.4,390.07,4.73,31.5 -0.08244,30,4.93,0,0.428,6.481,18.5,6.1899,6,300,16.6,379.41,6.36,23.7 -0.09252,30,4.93,0,0.428,6.606,42.2,6.1899,6,300,16.6,383.78,7.37,23.3 -0.11329,30,4.93,0,0.428,6.897,54.3,6.3361,6,300,16.6,391.25,11.38,22 -0.10612,30,4.93,0,0.428,6.095,65.1,6.3361,6,300,16.6,394.62,12.4,20.1 -0.1029,30,4.93,0,0.428,6.358,52.9,7.0355,6,300,16.6,372.75,11.22,22.2 -0.12757,30,4.93,0,0.428,6.393,7.8,7.0355,6,300,16.6,374.71,5.19,23.7 -0.20608,22,5.86,0,0.431,5.593,76.5,7.9549,7,330,19.1,372.49,12.5,17.6 -0.19133,22,5.86,0,0.431,5.605,70.2,7.9549,7,330,19.1,389.13,18.46,18.5 -0.33983,22,5.86,0,0.431,6.108,34.9,8.0555,7,330,19.1,390.18,9.16,24.3 -0.19657,22,5.86,0,0.431,6.226,79.2,8.0555,7,330,19.1,376.14,10.15,20.5 -0.16439,22,5.86,0,0.431,6.433,49.1,7.8265,7,330,19.1,374.71,9.52,24.5 -0.19073,22,5.86,0,0.431,6.718,17.5,7.8265,7,330,19.1,393.74,6.56,26.2 -0.1403,22,5.86,0,0.431,6.487,13,7.3967,7,330,19.1,396.28,5.9,24.4 -0.21409,22,5.86,0,0.431,6.438,8.9,7.3967,7,330,19.1,377.07,3.59,24.8 -0.08221,22,5.86,0,0.431,6.957,6.8,8.9067,7,330,19.1,386.09,3.53,29.6 -0.36894,22,5.86,0,0.431,8.259,8.4,8.9067,7,330,19.1,396.9,3.54,42.8 -0.04819,80,3.64,0,0.392,6.108,32,9.2203,1,315,16.4,392.89,6.57,21.9 -0.03548,80,3.64,0,0.392,5.876,19.1,9.2203,1,315,16.4,395.18,9.25,20.9 -0.01538,90,3.75,0,0.394,7.454,34.2,6.3361,3,244,15.9,386.34,3.11,44 -0.61154,20,3.97,0,0.647,8.704,86.9,1.801,5,264,13,389.7,5.12,50 -0.66351,20,3.97,0,0.647,7.333,100,1.8946,5,264,13,383.29,7.79,36 -0.65665,20,3.97,0,0.647,6.842,100,2.0107,5,264,13,391.93,6.9,30.1 -0.54011,20,3.97,0,0.647,7.203,81.8,2.1121,5,264,13,392.8,9.59,33.8 -0.53412,20,3.97,0,0.647,7.52,89.4,2.1398,5,264,13,388.37,7.26,43.1 -0.52014,20,3.97,0,0.647,8.398,91.5,2.2885,5,264,13,386.86,5.91,48.8 -0.82526,20,3.97,0,0.647,7.327,94.5,2.0788,5,264,13,393.42,11.25,31 -0.55007,20,3.97,0,0.647,7.206,91.6,1.9301,5,264,13,387.89,8.1,36.5 -0.76162,20,3.97,0,0.647,5.56,62.8,1.9865,5,264,13,392.4,10.45,22.8 -0.7857,20,3.97,0,0.647,7.014,84.6,2.1329,5,264,13,384.07,14.79,30.7 -0.57834,20,3.97,0,0.575,8.297,67,2.4216,5,264,13,384.54,7.44,50 -0.5405,20,3.97,0,0.575,7.47,52.6,2.872,5,264,13,390.3,3.16,43.5 -0.09065,20,6.96,1,0.464,5.92,61.5,3.9175,3,223,18.6,391.34,13.65,20.7 -0.29916,20,6.96,0,0.464,5.856,42.1,4.429,3,223,18.6,388.65,13,21.1 -0.16211,20,6.96,0,0.464,6.24,16.3,4.429,3,223,18.6,396.9,6.59,25.2 -0.1146,20,6.96,0,0.464,6.538,58.7,3.9175,3,223,18.6,394.96,7.73,24.4 -0.22188,20,6.96,1,0.464,7.691,51.8,4.3665,3,223,18.6,390.77,6.58,35.2 -0.05644,40,6.41,1,0.447,6.758,32.9,4.0776,4,254,17.6,396.9,3.53,32.4 -0.09604,40,6.41,0,0.447,6.854,42.8,4.2673,4,254,17.6,396.9,2.98,32 -0.10469,40,6.41,1,0.447,7.267,49,4.7872,4,254,17.6,389.25,6.05,33.2 -0.06127,40,6.41,1,0.447,6.826,27.6,4.8628,4,254,17.6,393.45,4.16,33.1 -0.07978,40,6.41,0,0.447,6.482,32.1,4.1403,4,254,17.6,396.9,7.19,29.1 -0.21038,20,3.33,0,0.4429,6.812,32.2,4.1007,5,216,14.9,396.9,4.85,35.1 -0.03578,20,3.33,0,0.4429,7.82,64.5,4.6947,5,216,14.9,387.31,3.76,45.4 -0.03705,20,3.33,0,0.4429,6.968,37.2,5.2447,5,216,14.9,392.23,4.59,35.4 -0.06129,20,3.33,1,0.4429,7.645,49.7,5.2119,5,216,14.9,377.07,3.01,46 -0.01501,90,1.21,1,0.401,7.923,24.8,5.885,1,198,13.6,395.52,3.16,50 -0.00906,90,2.97,0,0.4,7.088,20.8,7.3073,1,285,15.3,394.72,7.85,32.2 -0.01096,55,2.25,0,0.389,6.453,31.9,7.3073,1,300,15.3,394.72,8.23,22 -0.01965,80,1.76,0,0.385,6.23,31.5,9.0892,1,241,18.2,341.6,12.93,20.1 -0.03871,52.5,5.32,0,0.405,6.209,31.3,7.3172,6,293,16.6,396.9,7.14,23.2 -0.0459,52.5,5.32,0,0.405,6.315,45.6,7.3172,6,293,16.6,396.9,7.6,22.3 -0.04297,52.5,5.32,0,0.405,6.565,22.9,7.3172,6,293,16.6,371.72,9.51,24.8 -0.03502,80,4.95,0,0.411,6.861,27.9,5.1167,4,245,19.2,396.9,3.33,28.5 -0.07886,80,4.95,0,0.411,7.148,27.7,5.1167,4,245,19.2,396.9,3.56,37.3 -0.03615,80,4.95,0,0.411,6.63,23.4,5.1167,4,245,19.2,396.9,4.7,27.9 -0.08265,0,13.92,0,0.437,6.127,18.4,5.5027,4,289,16,396.9,8.58,23.9 -0.08199,0,13.92,0,0.437,6.009,42.3,5.5027,4,289,16,396.9,10.4,21.7 -0.12932,0,13.92,0,0.437,6.678,31.1,5.9604,4,289,16,396.9,6.27,28.6 -0.05372,0,13.92,0,0.437,6.549,51,5.9604,4,289,16,392.85,7.39,27.1 -0.14103,0,13.92,0,0.437,5.79,58,6.32,4,289,16,396.9,15.84,20.3 -0.06466,70,2.24,0,0.4,6.345,20.1,7.8278,5,358,14.8,368.24,4.97,22.5 -0.05561,70,2.24,0,0.4,7.041,10,7.8278,5,358,14.8,371.58,4.74,29 -0.04417,70,2.24,0,0.4,6.871,47.4,7.8278,5,358,14.8,390.86,6.07,24.8 -0.03537,34,6.09,0,0.433,6.59,40.4,5.4917,7,329,16.1,395.75,9.5,22 -0.09266,34,6.09,0,0.433,6.495,18.4,5.4917,7,329,16.1,383.61,8.67,26.4 -0.1,34,6.09,0,0.433,6.982,17.7,5.4917,7,329,16.1,390.43,4.86,33.1 -0.05515,33,2.18,0,0.472,7.236,41.1,4.022,7,222,18.4,393.68,6.93,36.1 -0.05479,33,2.18,0,0.472,6.616,58.1,3.37,7,222,18.4,393.36,8.93,28.4 -0.07503,33,2.18,0,0.472,7.42,71.9,3.0992,7,222,18.4,396.9,6.47,33.4 -0.04932,33,2.18,0,0.472,6.849,70.3,3.1827,7,222,18.4,396.9,7.53,28.2 -0.49298,0,9.9,0,0.544,6.635,82.5,3.3175,4,304,18.4,396.9,4.54,22.8 -0.3494,0,9.9,0,0.544,5.972,76.7,3.1025,4,304,18.4,396.24,9.97,20.3 -2.63548,0,9.9,0,0.544,4.973,37.8,2.5194,4,304,18.4,350.45,12.64,16.1 -0.79041,0,9.9,0,0.544,6.122,52.8,2.6403,4,304,18.4,396.9,5.98,22.1 -0.26169,0,9.9,0,0.544,6.023,90.4,2.834,4,304,18.4,396.3,11.72,19.4 -0.26938,0,9.9,0,0.544,6.266,82.8,3.2628,4,304,18.4,393.39,7.9,21.6 -0.3692,0,9.9,0,0.544,6.567,87.3,3.6023,4,304,18.4,395.69,9.28,23.8 -0.25356,0,9.9,0,0.544,5.705,77.7,3.945,4,304,18.4,396.42,11.5,16.2 -0.31827,0,9.9,0,0.544,5.914,83.2,3.9986,4,304,18.4,390.7,18.33,17.8 -0.24522,0,9.9,0,0.544,5.782,71.7,4.0317,4,304,18.4,396.9,15.94,19.8 -0.40202,0,9.9,0,0.544,6.382,67.2,3.5325,4,304,18.4,395.21,10.36,23.1 -0.47547,0,9.9,0,0.544,6.113,58.8,4.0019,4,304,18.4,396.23,12.73,21 -0.1676,0,7.38,0,0.493,6.426,52.3,4.5404,5,287,19.6,396.9,7.2,23.8 -0.18159,0,7.38,0,0.493,6.376,54.3,4.5404,5,287,19.6,396.9,6.87,23.1 -0.35114,0,7.38,0,0.493,6.041,49.9,4.7211,5,287,19.6,396.9,7.7,20.4 -0.28392,0,7.38,0,0.493,5.708,74.3,4.7211,5,287,19.6,391.13,11.74,18.5 -0.34109,0,7.38,0,0.493,6.415,40.1,4.7211,5,287,19.6,396.9,6.12,25 -0.19186,0,7.38,0,0.493,6.431,14.7,5.4159,5,287,19.6,393.68,5.08,24.6 -0.30347,0,7.38,0,0.493,6.312,28.9,5.4159,5,287,19.6,396.9,6.15,23 -0.24103,0,7.38,0,0.493,6.083,43.7,5.4159,5,287,19.6,396.9,12.79,22.2 -0.06617,0,3.24,0,0.46,5.868,25.8,5.2146,4,430,16.9,382.44,9.97,19.3 -0.06724,0,3.24,0,0.46,6.333,17.2,5.2146,4,430,16.9,375.21,7.34,22.6 -0.04544,0,3.24,0,0.46,6.144,32.2,5.8736,4,430,16.9,368.57,9.09,19.8 -0.05023,35,6.06,0,0.4379,5.706,28.4,6.6407,1,304,16.9,394.02,12.43,17.1 -0.03466,35,6.06,0,0.4379,6.031,23.3,6.6407,1,304,16.9,362.25,7.83,19.4 -0.05083,0,5.19,0,0.515,6.316,38.1,6.4584,5,224,20.2,389.71,5.68,22.2 -0.03738,0,5.19,0,0.515,6.31,38.5,6.4584,5,224,20.2,389.4,6.75,20.7 -0.03961,0,5.19,0,0.515,6.037,34.5,5.9853,5,224,20.2,396.9,8.01,21.1 -0.03427,0,5.19,0,0.515,5.869,46.3,5.2311,5,224,20.2,396.9,9.8,19.5 -0.03041,0,5.19,0,0.515,5.895,59.6,5.615,5,224,20.2,394.81,10.56,18.5 -0.03306,0,5.19,0,0.515,6.059,37.3,4.8122,5,224,20.2,396.14,8.51,20.6 -0.05497,0,5.19,0,0.515,5.985,45.4,4.8122,5,224,20.2,396.9,9.74,19 -0.06151,0,5.19,0,0.515,5.968,58.5,4.8122,5,224,20.2,396.9,9.29,18.7 -0.01301,35,1.52,0,0.442,7.241,49.3,7.0379,1,284,15.5,394.74,5.49,32.7 -0.02498,0,1.89,0,0.518,6.54,59.7,6.2669,1,422,15.9,389.96,8.65,16.5 -0.02543,55,3.78,0,0.484,6.696,56.4,5.7321,5,370,17.6,396.9,7.18,23.9 -0.03049,55,3.78,0,0.484,6.874,28.1,6.4654,5,370,17.6,387.97,4.61,31.2 -0.03113,0,4.39,0,0.442,6.014,48.5,8.0136,3,352,18.8,385.64,10.53,17.5 -0.06162,0,4.39,0,0.442,5.898,52.3,8.0136,3,352,18.8,364.61,12.67,17.2 -0.0187,85,4.15,0,0.429,6.516,27.7,8.5353,4,351,17.9,392.43,6.36,23.1 -0.01501,80,2.01,0,0.435,6.635,29.7,8.344,4,280,17,390.94,5.99,24.5 -0.02899,40,1.25,0,0.429,6.939,34.5,8.7921,1,335,19.7,389.85,5.89,26.6 -0.06211,40,1.25,0,0.429,6.49,44.4,8.7921,1,335,19.7,396.9,5.98,22.9 -0.0795,60,1.69,0,0.411,6.579,35.9,10.7103,4,411,18.3,370.78,5.49,24.1 -0.07244,60,1.69,0,0.411,5.884,18.5,10.7103,4,411,18.3,392.33,7.79,18.6 -0.01709,90,2.02,0,0.41,6.728,36.1,12.1265,5,187,17,384.46,4.5,30.1 -0.04301,80,1.91,0,0.413,5.663,21.9,10.5857,4,334,22,382.8,8.05,18.2 -0.10659,80,1.91,0,0.413,5.936,19.5,10.5857,4,334,22,376.04,5.57,20.6 -8.98296,0,18.1,1,0.77,6.212,97.4,2.1222,24,666,20.2,377.73,17.6,17.8 -3.8497,0,18.1,1,0.77,6.395,91,2.5052,24,666,20.2,391.34,13.27,21.7 -5.20177,0,18.1,1,0.77,6.127,83.4,2.7227,24,666,20.2,395.43,11.48,22.7 -4.26131,0,18.1,0,0.77,6.112,81.3,2.5091,24,666,20.2,390.74,12.67,22.6 -4.54192,0,18.1,0,0.77,6.398,88,2.5182,24,666,20.2,374.56,7.79,25 -3.83684,0,18.1,0,0.77,6.251,91.1,2.2955,24,666,20.2,350.65,14.19,19.9 -3.67822,0,18.1,0,0.77,5.362,96.2,2.1036,24,666,20.2,380.79,10.19,20.8 -4.22239,0,18.1,1,0.77,5.803,89,1.9047,24,666,20.2,353.04,14.64,16.8 -3.47428,0,18.1,1,0.718,8.78,82.9,1.9047,24,666,20.2,354.55,5.29,21.9 -4.55587,0,18.1,0,0.718,3.561,87.9,1.6132,24,666,20.2,354.7,7.12,27.5 -3.69695,0,18.1,0,0.718,4.963,91.4,1.7523,24,666,20.2,316.03,14,21.9 -13.5222,0,18.1,0,0.631,3.863,100,1.5106,24,666,20.2,131.42,13.33,23.1 -4.89822,0,18.1,0,0.631,4.97,100,1.3325,24,666,20.2,375.52,3.26,50 -5.66998,0,18.1,1,0.631,6.683,96.8,1.3567,24,666,20.2,375.33,3.73,50 -6.53876,0,18.1,1,0.631,7.016,97.5,1.2024,24,666,20.2,392.05,2.96,50 -9.2323,0,18.1,0,0.631,6.216,100,1.1691,24,666,20.2,366.15,9.53,50 -8.26725,0,18.1,1,0.668,5.875,89.6,1.1296,24,666,20.2,347.88,8.88,50 -11.1081,0,18.1,0,0.668,4.906,100,1.1742,24,666,20.2,396.9,34.77,13.8 -18.4982,0,18.1,0,0.668,4.138,100,1.137,24,666,20.2,396.9,37.97,13.8 -19.6091,0,18.1,0,0.671,7.313,97.9,1.3163,24,666,20.2,396.9,13.44,15 -15.288,0,18.1,0,0.671,6.649,93.3,1.3449,24,666,20.2,363.02,23.24,13.9 -9.82349,0,18.1,0,0.671,6.794,98.8,1.358,24,666,20.2,396.9,21.24,13.3 -23.6482,0,18.1,0,0.671,6.38,96.2,1.3861,24,666,20.2,396.9,23.69,13.1 -17.8667,0,18.1,0,0.671,6.223,100,1.3861,24,666,20.2,393.74,21.78,10.2 -88.9762,0,18.1,0,0.671,6.968,91.9,1.4165,24,666,20.2,396.9,17.21,10.4 -15.8744,0,18.1,0,0.671,6.545,99.1,1.5192,24,666,20.2,396.9,21.08,10.9 -9.18702,0,18.1,0,0.7,5.536,100,1.5804,24,666,20.2,396.9,23.6,11.3 -7.99248,0,18.1,0,0.7,5.52,100,1.5331,24,666,20.2,396.9,24.56,12.3 -20.0849,0,18.1,0,0.7,4.368,91.2,1.4395,24,666,20.2,285.83,30.63,8.8 -16.8118,0,18.1,0,0.7,5.277,98.1,1.4261,24,666,20.2,396.9,30.81,7.2 -24.3938,0,18.1,0,0.7,4.652,100,1.4672,24,666,20.2,396.9,28.28,10.5 -22.5971,0,18.1,0,0.7,5,89.5,1.5184,24,666,20.2,396.9,31.99,7.4 -14.3337,0,18.1,0,0.7,4.88,100,1.5895,24,666,20.2,372.92,30.62,10.2 -8.15174,0,18.1,0,0.7,5.39,98.9,1.7281,24,666,20.2,396.9,20.85,11.5 -6.96215,0,18.1,0,0.7,5.713,97,1.9265,24,666,20.2,394.43,17.11,15.1 -5.29305,0,18.1,0,0.7,6.051,82.5,2.1678,24,666,20.2,378.38,18.76,23.2 -11.5779,0,18.1,0,0.7,5.036,97,1.77,24,666,20.2,396.9,25.68,9.7 -8.64476,0,18.1,0,0.693,6.193,92.6,1.7912,24,666,20.2,396.9,15.17,13.8 -13.3598,0,18.1,0,0.693,5.887,94.7,1.7821,24,666,20.2,396.9,16.35,12.7 -8.71675,0,18.1,0,0.693,6.471,98.8,1.7257,24,666,20.2,391.98,17.12,13.1 -5.87205,0,18.1,0,0.693,6.405,96,1.6768,24,666,20.2,396.9,19.37,12.5 -7.67202,0,18.1,0,0.693,5.747,98.9,1.6334,24,666,20.2,393.1,19.92,8.5 -38.3518,0,18.1,0,0.693,5.453,100,1.4896,24,666,20.2,396.9,30.59,5 -9.91655,0,18.1,0,0.693,5.852,77.8,1.5004,24,666,20.2,338.16,29.97,6.3 -25.0461,0,18.1,0,0.693,5.987,100,1.5888,24,666,20.2,396.9,26.77,5.6 -14.2362,0,18.1,0,0.693,6.343,100,1.5741,24,666,20.2,396.9,20.32,7.2 -9.59571,0,18.1,0,0.693,6.404,100,1.639,24,666,20.2,376.11,20.31,12.1 -24.8017,0,18.1,0,0.693,5.349,96,1.7028,24,666,20.2,396.9,19.77,8.3 -41.5292,0,18.1,0,0.693,5.531,85.4,1.6074,24,666,20.2,329.46,27.38,8.5 -67.9208,0,18.1,0,0.693,5.683,100,1.4254,24,666,20.2,384.97,22.98,5 -20.7162,0,18.1,0,0.659,4.138,100,1.1781,24,666,20.2,370.22,23.34,11.9 -11.9511,0,18.1,0,0.659,5.608,100,1.2852,24,666,20.2,332.09,12.13,27.9 -7.40389,0,18.1,0,0.597,5.617,97.9,1.4547,24,666,20.2,314.64,26.4,17.2 -14.4383,0,18.1,0,0.597,6.852,100,1.4655,24,666,20.2,179.36,19.78,27.5 -51.1358,0,18.1,0,0.597,5.757,100,1.413,24,666,20.2,2.6,10.11,15 -14.0507,0,18.1,0,0.597,6.657,100,1.5275,24,666,20.2,35.05,21.22,17.2 -18.811,0,18.1,0,0.597,4.628,100,1.5539,24,666,20.2,28.79,34.37,17.9 -28.6558,0,18.1,0,0.597,5.155,100,1.5894,24,666,20.2,210.97,20.08,16.3 -45.7461,0,18.1,0,0.693,4.519,100,1.6582,24,666,20.2,88.27,36.98,7 -18.0846,0,18.1,0,0.679,6.434,100,1.8347,24,666,20.2,27.25,29.05,7.2 -10.8342,0,18.1,0,0.679,6.782,90.8,1.8195,24,666,20.2,21.57,25.79,7.5 -25.9406,0,18.1,0,0.679,5.304,89.1,1.6475,24,666,20.2,127.36,26.64,10.4 -73.5341,0,18.1,0,0.679,5.957,100,1.8026,24,666,20.2,16.45,20.62,8.8 -11.8123,0,18.1,0,0.718,6.824,76.5,1.794,24,666,20.2,48.45,22.74,8.4 -11.0874,0,18.1,0,0.718,6.411,100,1.8589,24,666,20.2,318.75,15.02,16.7 -7.02259,0,18.1,0,0.718,6.006,95.3,1.8746,24,666,20.2,319.98,15.7,14.2 -12.0482,0,18.1,0,0.614,5.648,87.6,1.9512,24,666,20.2,291.55,14.1,20.8 -7.05042,0,18.1,0,0.614,6.103,85.1,2.0218,24,666,20.2,2.52,23.29,13.4 -8.79212,0,18.1,0,0.584,5.565,70.6,2.0635,24,666,20.2,3.65,17.16,11.7 -15.8603,0,18.1,0,0.679,5.896,95.4,1.9096,24,666,20.2,7.68,24.39,8.3 -12.2472,0,18.1,0,0.584,5.837,59.7,1.9976,24,666,20.2,24.65,15.69,10.2 -37.6619,0,18.1,0,0.679,6.202,78.7,1.8629,24,666,20.2,18.82,14.52,10.9 -7.36711,0,18.1,0,0.679,6.193,78.1,1.9356,24,666,20.2,96.73,21.52,11 -9.33889,0,18.1,0,0.679,6.38,95.6,1.9682,24,666,20.2,60.72,24.08,9.5 -8.49213,0,18.1,0,0.584,6.348,86.1,2.0527,24,666,20.2,83.45,17.64,14.5 -10.0623,0,18.1,0,0.584,6.833,94.3,2.0882,24,666,20.2,81.33,19.69,14.1 -6.44405,0,18.1,0,0.584,6.425,74.8,2.2004,24,666,20.2,97.95,12.03,16.1 -5.58107,0,18.1,0,0.713,6.436,87.9,2.3158,24,666,20.2,100.19,16.22,14.3 -13.9134,0,18.1,0,0.713,6.208,95,2.2222,24,666,20.2,100.63,15.17,11.7 -11.1604,0,18.1,0,0.74,6.629,94.6,2.1247,24,666,20.2,109.85,23.27,13.4 -14.4208,0,18.1,0,0.74,6.461,93.3,2.0026,24,666,20.2,27.49,18.05,9.6 -15.1772,0,18.1,0,0.74,6.152,100,1.9142,24,666,20.2,9.32,26.45,8.7 -13.6781,0,18.1,0,0.74,5.935,87.9,1.8206,24,666,20.2,68.95,34.02,8.4 -9.39063,0,18.1,0,0.74,5.627,93.9,1.8172,24,666,20.2,396.9,22.88,12.8 -22.0511,0,18.1,0,0.74,5.818,92.4,1.8662,24,666,20.2,391.45,22.11,10.5 -9.72418,0,18.1,0,0.74,6.406,97.2,2.0651,24,666,20.2,385.96,19.52,17.1 -5.66637,0,18.1,0,0.74,6.219,100,2.0048,24,666,20.2,395.69,16.59,18.4 -9.96654,0,18.1,0,0.74,6.485,100,1.9784,24,666,20.2,386.73,18.85,15.4 -12.8023,0,18.1,0,0.74,5.854,96.6,1.8956,24,666,20.2,240.52,23.79,10.8 -10.6718,0,18.1,0,0.74,6.459,94.8,1.9879,24,666,20.2,43.06,23.98,11.8 -6.28807,0,18.1,0,0.74,6.341,96.4,2.072,24,666,20.2,318.01,17.79,14.9 -9.92485,0,18.1,0,0.74,6.251,96.6,2.198,24,666,20.2,388.52,16.44,12.6 -9.32909,0,18.1,0,0.713,6.185,98.7,2.2616,24,666,20.2,396.9,18.13,14.1 -7.52601,0,18.1,0,0.713,6.417,98.3,2.185,24,666,20.2,304.21,19.31,13 -6.71772,0,18.1,0,0.713,6.749,92.6,2.3236,24,666,20.2,0.32,17.44,13.4 -5.44114,0,18.1,0,0.713,6.655,98.2,2.3552,24,666,20.2,355.29,17.73,15.2 -5.09017,0,18.1,0,0.713,6.297,91.8,2.3682,24,666,20.2,385.09,17.27,16.1 -8.24809,0,18.1,0,0.713,7.393,99.3,2.4527,24,666,20.2,375.87,16.74,17.8 -9.51363,0,18.1,0,0.713,6.728,94.1,2.4961,24,666,20.2,6.68,18.71,14.9 -4.75237,0,18.1,0,0.713,6.525,86.5,2.4358,24,666,20.2,50.92,18.13,14.1 -4.66883,0,18.1,0,0.713,5.976,87.9,2.5806,24,666,20.2,10.48,19.01,12.7 -8.20058,0,18.1,0,0.713,5.936,80.3,2.7792,24,666,20.2,3.5,16.94,13.5 -7.75223,0,18.1,0,0.713,6.301,83.7,2.7831,24,666,20.2,272.21,16.23,14.9 -6.80117,0,18.1,0,0.713,6.081,84.4,2.7175,24,666,20.2,396.9,14.7,20 -4.81213,0,18.1,0,0.713,6.701,90,2.5975,24,666,20.2,255.23,16.42,16.4 -3.69311,0,18.1,0,0.713,6.376,88.4,2.5671,24,666,20.2,391.43,14.65,17.7 -6.65492,0,18.1,0,0.713,6.317,83,2.7344,24,666,20.2,396.9,13.99,19.5 -5.82115,0,18.1,0,0.713,6.513,89.9,2.8016,24,666,20.2,393.82,10.29,20.2 -7.83932,0,18.1,0,0.655,6.209,65.4,2.9634,24,666,20.2,396.9,13.22,21.4 -3.1636,0,18.1,0,0.655,5.759,48.2,3.0665,24,666,20.2,334.4,14.13,19.9 -3.77498,0,18.1,0,0.655,5.952,84.7,2.8715,24,666,20.2,22.01,17.15,19 -4.42228,0,18.1,0,0.584,6.003,94.5,2.5403,24,666,20.2,331.29,21.32,19.1 -15.5757,0,18.1,0,0.58,5.926,71,2.9084,24,666,20.2,368.74,18.13,19.1 -13.0751,0,18.1,0,0.58,5.713,56.7,2.8237,24,666,20.2,396.9,14.76,20.1 -4.34879,0,18.1,0,0.58,6.167,84,3.0334,24,666,20.2,396.9,16.29,19.9 -4.03841,0,18.1,0,0.532,6.229,90.7,3.0993,24,666,20.2,395.33,12.87,19.6 -3.56868,0,18.1,0,0.58,6.437,75,2.8965,24,666,20.2,393.37,14.36,23.2 -4.64689,0,18.1,0,0.614,6.98,67.6,2.5329,24,666,20.2,374.68,11.66,29.8 -8.05579,0,18.1,0,0.584,5.427,95.4,2.4298,24,666,20.2,352.58,18.14,13.8 -6.39312,0,18.1,0,0.584,6.162,97.4,2.206,24,666,20.2,302.76,24.1,13.3 -4.87141,0,18.1,0,0.614,6.484,93.6,2.3053,24,666,20.2,396.21,18.68,16.7 -15.0234,0,18.1,0,0.614,5.304,97.3,2.1007,24,666,20.2,349.48,24.91,12 -10.233,0,18.1,0,0.614,6.185,96.7,2.1705,24,666,20.2,379.7,18.03,14.6 -14.3337,0,18.1,0,0.614,6.229,88,1.9512,24,666,20.2,383.32,13.11,21.4 -5.82401,0,18.1,0,0.532,6.242,64.7,3.4242,24,666,20.2,396.9,10.74,23 -5.70818,0,18.1,0,0.532,6.75,74.9,3.3317,24,666,20.2,393.07,7.74,23.7 -5.73116,0,18.1,0,0.532,7.061,77,3.4106,24,666,20.2,395.28,7.01,25 -2.81838,0,18.1,0,0.532,5.762,40.3,4.0983,24,666,20.2,392.92,10.42,21.8 -2.37857,0,18.1,0,0.583,5.871,41.9,3.724,24,666,20.2,370.73,13.34,20.6 -3.67367,0,18.1,0,0.583,6.312,51.9,3.9917,24,666,20.2,388.62,10.58,21.2 -5.69175,0,18.1,0,0.583,6.114,79.8,3.5459,24,666,20.2,392.68,14.98,19.1 -4.83567,0,18.1,0,0.583,5.905,53.2,3.1523,24,666,20.2,388.22,11.45,20.6 -0.15086,0,27.74,0,0.609,5.454,92.7,1.8209,4,711,20.1,395.09,18.06,15.2 -0.18337,0,27.74,0,0.609,5.414,98.3,1.7554,4,711,20.1,344.05,23.97,7 -0.20746,0,27.74,0,0.609,5.093,98,1.8226,4,711,20.1,318.43,29.68,8.1 -0.10574,0,27.74,0,0.609,5.983,98.8,1.8681,4,711,20.1,390.11,18.07,13.6 -0.11132,0,27.74,0,0.609,5.983,83.5,2.1099,4,711,20.1,396.9,13.35,20.1 -0.17331,0,9.69,0,0.585,5.707,54,2.3817,6,391,19.2,396.9,12.01,21.8 -0.27957,0,9.69,0,0.585,5.926,42.6,2.3817,6,391,19.2,396.9,13.59,24.5 -0.17899,0,9.69,0,0.585,5.67,28.8,2.7986,6,391,19.2,393.29,17.6,23.1 -0.2896,0,9.69,0,0.585,5.39,72.9,2.7986,6,391,19.2,396.9,21.14,19.7 -0.26838,0,9.69,0,0.585,5.794,70.6,2.8927,6,391,19.2,396.9,14.1,18.3 -0.23912,0,9.69,0,0.585,6.019,65.3,2.4091,6,391,19.2,396.9,12.92,21.2 -0.17783,0,9.69,0,0.585,5.569,73.5,2.3999,6,391,19.2,395.77,15.1,17.5 -0.22438,0,9.69,0,0.585,6.027,79.7,2.4982,6,391,19.2,396.9,14.33,16.8 -0.06263,0,11.93,0,0.573,6.593,69.1,2.4786,1,273,21,391.99,9.67,22.4 -0.04527,0,11.93,0,0.573,6.12,76.7,2.2875,1,273,21,396.9,9.08,20.6 -0.06076,0,11.93,0,0.573,6.976,91,2.1675,1,273,21,396.9,5.64,23.9 -0.10959,0,11.93,0,0.573,6.794,89.3,2.3889,1,273,21,393.45,6.48,22 -0.04741,0,11.93,0,0.573,6.03,80.8,2.505,1,273,21,396.9,7.88,11.9 diff --git a/sklearn/datasets/tests/test_openml.py b/sklearn/datasets/tests/test_openml.py index 40e086ec6f6d3..3c29a526a008b 100644 --- a/sklearn/datasets/tests/test_openml.py +++ b/sklearn/datasets/tests/test_openml.py @@ -1540,9 +1540,11 @@ def _mock_urlopen_network_error(request, *args, **kwargs): f" {invalid_openml_url}. Retrying..." ), ) as record: - with pytest.raises(HTTPError, match="Simulated network error"): + with pytest.raises(HTTPError, match="Simulated network error") as exc_info: _open_openml_url(invalid_openml_url, None, delay=0) assert len(record) == 3 + # Avoid a ResourceWarning on Python 3.14 and later. + exc_info.value.close() ############################################################################### diff --git a/sklearn/decomposition/_kernel_pca.py b/sklearn/decomposition/_kernel_pca.py index 79573651eeb84..cd862079a1682 100644 --- a/sklearn/decomposition/_kernel_pca.py +++ b/sklearn/decomposition/_kernel_pca.py @@ -471,7 +471,7 @@ def fit_transform(self, X, y=None, **params): Returns ------- X_new : ndarray of shape (n_samples, n_components) - Returns the instance itself. + Transformed values. """ self.fit(X, **params) @@ -495,7 +495,8 @@ def transform(self, X): Returns ------- X_new : ndarray of shape (n_samples, n_components) - Returns the instance itself. + Projection of X in the first principal components, where `n_samples` + is the number of samples and `n_components` is the number of the components. """ check_is_fitted(self) X = validate_data(self, X, accept_sparse="csr", reset=False) @@ -545,7 +546,8 @@ def inverse_transform(self, X): Returns ------- X_original : ndarray of shape (n_samples, n_features) - Returns the instance itself. + Original data, where `n_samples` is the number of samples + and `n_features` is the number of features. References ---------- diff --git a/sklearn/decomposition/tests/test_incremental_pca.py b/sklearn/decomposition/tests/test_incremental_pca.py index 6bca13d0ad627..c4ea1c222901c 100644 --- a/sklearn/decomposition/tests/test_incremental_pca.py +++ b/sklearn/decomposition/tests/test_incremental_pca.py @@ -87,9 +87,9 @@ def test_incremental_pca_sparse(sparse_container): ipca.partial_fit(X_sparse) -def test_incremental_pca_check_projection(): +def test_incremental_pca_check_projection(global_random_seed): # Test that the projection of data is correct. - rng = np.random.RandomState(1999) + rng = np.random.RandomState(global_random_seed) n, p = 100, 3 X = rng.randn(n, p) * 0.1 X[:10] += np.array([3, 4, 5]) @@ -108,9 +108,9 @@ def test_incremental_pca_check_projection(): assert_almost_equal(np.abs(Yt[0][0]), 1.0, 1) -def test_incremental_pca_inverse(): +def test_incremental_pca_inverse(global_random_seed): # Test that the projection of data can be inverted. - rng = np.random.RandomState(1999) + rng = np.random.RandomState(global_random_seed) n, p = 50, 3 X = rng.randn(n, p) # spherical data X[:, 1] *= 0.00001 # make middle component relatively small @@ -217,9 +217,9 @@ def test_incremental_pca_num_features_change(): ipca.partial_fit(X2) -def test_incremental_pca_batch_signs(): +def test_incremental_pca_batch_signs(global_random_seed): # Test that components_ sign is stable over batch sizes. - rng = np.random.RandomState(1999) + rng = np.random.RandomState(global_random_seed) n_samples = 100 n_features = 3 X = rng.randn(n_samples, n_features) @@ -254,9 +254,9 @@ def test_incremental_pca_partial_fit_small_batch(): assert_allclose(pca.components_, pipca.components_, atol=1e-3) -def test_incremental_pca_batch_values(): +def test_incremental_pca_batch_values(global_random_seed): # Test that components_ values are stable over batch sizes. - rng = np.random.RandomState(1999) + rng = np.random.RandomState(global_random_seed) n_samples = 100 n_features = 3 X = rng.randn(n_samples, n_features) @@ -286,9 +286,9 @@ def test_incremental_pca_batch_rank(): assert_allclose_dense_sparse(components_i, components_j) -def test_incremental_pca_partial_fit(): +def test_incremental_pca_partial_fit(global_random_seed): # Test that fit and partial_fit get equivalent results. - rng = np.random.RandomState(1999) + rng = np.random.RandomState(global_random_seed) n, p = 50, 3 X = rng.randn(n, p) # spherical data X[:, 1] *= 0.00001 # make middle component relatively small @@ -316,9 +316,9 @@ def test_incremental_pca_against_pca_iris(): assert_almost_equal(np.abs(Y_pca), np.abs(Y_ipca), 1) -def test_incremental_pca_against_pca_random_data(): +def test_incremental_pca_against_pca_random_data(global_random_seed): # Test that IncrementalPCA and PCA are approximate (to a sign flip). - rng = np.random.RandomState(1999) + rng = np.random.RandomState(global_random_seed) n_samples = 100 n_features = 3 X = rng.randn(n_samples, n_features) + 5 * rng.rand(1, n_features) @@ -348,10 +348,10 @@ def test_explained_variances(): assert_almost_equal(pca.noise_variance_, ipca.noise_variance_, decimal=prec) -def test_singular_values(): +def test_singular_values(global_random_seed): # Check that the IncrementalPCA output has the correct singular values - rng = np.random.RandomState(0) + rng = np.random.RandomState(global_random_seed) n_samples = 1000 n_features = 100 @@ -360,7 +360,7 @@ def test_singular_values(): ) pca = PCA(n_components=10, svd_solver="full", random_state=rng).fit(X) - ipca = IncrementalPCA(n_components=10, batch_size=100).fit(X) + ipca = IncrementalPCA(n_components=10, batch_size=150).fit(X) assert_array_almost_equal(pca.singular_values_, ipca.singular_values_, 2) # Compare to the Frobenius norm @@ -382,7 +382,7 @@ def test_singular_values(): ) # Set the singular values and see what we get back - rng = np.random.RandomState(0) + rng = np.random.RandomState(global_random_seed) n_samples = 100 n_features = 110 diff --git a/sklearn/ensemble/_forest.py b/sklearn/ensemble/_forest.py index 5def6ac60816b..5b27e789b1d13 100644 --- a/sklearn/ensemble/_forest.py +++ b/sklearn/ensemble/_forest.py @@ -1298,6 +1298,9 @@ class RandomForestClassifier(ForestClassifier): Provide a callable with signature `metric(y_true, y_pred)` to use a custom metric. Only available if `bootstrap=True`. + For an illustration of out-of-bag (OOB) error estimation, see the example + :ref:`sphx_glr_auto_examples_ensemble_plot_ensemble_oob.py`. + n_jobs : int, default=None The number of jobs to run in parallel. :meth:`fit`, :meth:`predict`, :meth:`decision_path` and :meth:`apply` are all parallelized over the @@ -1709,6 +1712,9 @@ class RandomForestRegressor(ForestRegressor): Provide a callable with signature `metric(y_true, y_pred)` to use a custom metric. Only available if `bootstrap=True`. + For an illustration of out-of-bag (OOB) error estimation, see the example + :ref:`sphx_glr_auto_examples_ensemble_plot_ensemble_oob.py`. + n_jobs : int, default=None The number of jobs to run in parallel. :meth:`fit`, :meth:`predict`, :meth:`decision_path` and :meth:`apply` are all parallelized over the @@ -2054,6 +2060,9 @@ class ExtraTreesClassifier(ForestClassifier): Provide a callable with signature `metric(y_true, y_pred)` to use a custom metric. Only available if `bootstrap=True`. + For an illustration of out-of-bag (OOB) error estimation, see the example + :ref:`sphx_glr_auto_examples_ensemble_plot_ensemble_oob.py`. + n_jobs : int, default=None The number of jobs to run in parallel. :meth:`fit`, :meth:`predict`, :meth:`decision_path` and :meth:`apply` are all parallelized over the @@ -2449,6 +2458,9 @@ class ExtraTreesRegressor(ForestRegressor): Provide a callable with signature `metric(y_true, y_pred)` to use a custom metric. Only available if `bootstrap=True`. + For an illustration of out-of-bag (OOB) error estimation, see the example + :ref:`sphx_glr_auto_examples_ensemble_plot_ensemble_oob.py`. + n_jobs : int, default=None The number of jobs to run in parallel. :meth:`fit`, :meth:`predict`, :meth:`decision_path` and :meth:`apply` are all parallelized over the diff --git a/sklearn/ensemble/_stacking.py b/sklearn/ensemble/_stacking.py index d7491be2f666f..2894d8f174c13 100644 --- a/sklearn/ensemble/_stacking.py +++ b/sklearn/ensemble/_stacking.py @@ -24,8 +24,8 @@ from ..model_selection import check_cv, cross_val_predict from ..preprocessing import LabelEncoder from ..utils import Bunch -from ..utils._estimator_html_repr import _VisualBlock from ..utils._param_validation import HasMethods, StrOptions +from ..utils._repr_html.estimator import _VisualBlock from ..utils.metadata_routing import ( MetadataRouter, MethodMapping, diff --git a/sklearn/ensemble/_voting.py b/sklearn/ensemble/_voting.py index e7e670dd869b6..369d3f0f5553e 100644 --- a/sklearn/ensemble/_voting.py +++ b/sklearn/ensemble/_voting.py @@ -24,8 +24,8 @@ from ..exceptions import NotFittedError from ..preprocessing import LabelEncoder from ..utils import Bunch -from ..utils._estimator_html_repr import _VisualBlock from ..utils._param_validation import StrOptions +from ..utils._repr_html.estimator import _VisualBlock from ..utils.metadata_routing import ( MetadataRouter, MethodMapping, diff --git a/sklearn/externals/array_api_compat/__init__.py b/sklearn/externals/array_api_compat/__init__.py index 96b061e721808..653cb40a37607 100644 --- a/sklearn/externals/array_api_compat/__init__.py +++ b/sklearn/externals/array_api_compat/__init__.py @@ -17,6 +17,6 @@ this implementation for the default when working with NumPy arrays. """ -__version__ = '1.11.2' +__version__ = '1.12.0' from .common import * # noqa: F401, F403 diff --git a/sklearn/externals/array_api_compat/_internal.py b/sklearn/externals/array_api_compat/_internal.py index 170a1ff9e6459..cd8d939f36de2 100644 --- a/sklearn/externals/array_api_compat/_internal.py +++ b/sklearn/externals/array_api_compat/_internal.py @@ -2,10 +2,16 @@ Internal helpers """ +from collections.abc import Callable from functools import wraps from inspect import signature +from types import ModuleType +from typing import TypeVar -def get_xp(xp): +_T = TypeVar("_T") + + +def get_xp(xp: ModuleType) -> Callable[[Callable[..., _T]], Callable[..., _T]]: """ Decorator to automatically replace xp with the corresponding array module. @@ -22,14 +28,14 @@ def func(x, /, xp, kwarg=None): """ - def inner(f): + def inner(f: Callable[..., _T], /) -> Callable[..., _T]: @wraps(f) - def wrapped_f(*args, **kwargs): + def wrapped_f(*args: object, **kwargs: object) -> object: return f(*args, xp=xp, **kwargs) sig = signature(f) new_sig = sig.replace( - parameters=[sig.parameters[i] for i in sig.parameters if i != "xp"] + parameters=[par for i, par in sig.parameters.items() if i != "xp"] ) if wrapped_f.__doc__ is None: @@ -40,7 +46,14 @@ def wrapped_f(*args, **kwargs): specification for more details. """ - wrapped_f.__signature__ = new_sig - return wrapped_f + wrapped_f.__signature__ = new_sig # pyright: ignore[reportAttributeAccessIssue] + return wrapped_f # pyright: ignore[reportReturnType] return inner + + +__all__ = ["get_xp"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/common/__init__.py b/sklearn/externals/array_api_compat/common/__init__.py index 91ab1c405e1d7..8236080738175 100644 --- a/sklearn/externals/array_api_compat/common/__init__.py +++ b/sklearn/externals/array_api_compat/common/__init__.py @@ -1 +1 @@ -from ._helpers import * # noqa: F403 +from ._helpers import * # noqa: F403 diff --git a/sklearn/externals/array_api_compat/common/_aliases.py b/sklearn/externals/array_api_compat/common/_aliases.py index 35262d3a93538..8ea9162a9edc8 100644 --- a/sklearn/externals/array_api_compat/common/_aliases.py +++ b/sklearn/externals/array_api_compat/common/_aliases.py @@ -4,142 +4,171 @@ from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from typing import Optional, Sequence, Tuple, Union - from ._typing import ndarray, Device, Dtype - -from typing import NamedTuple import inspect +from typing import TYPE_CHECKING, Any, NamedTuple, Optional, Sequence, cast -from ._helpers import array_namespace, _check_device, device, is_cupy_namespace +from ._helpers import _check_device, array_namespace +from ._helpers import device as _get_device +from ._helpers import is_cupy_namespace as _is_cupy_namespace +from ._typing import Array, Device, DType, Namespace + +if TYPE_CHECKING: + # TODO: import from typing (requires Python >=3.13) + from typing_extensions import TypeIs # These functions are modified from the NumPy versions. -# Creation functions add the device keyword (which does nothing for NumPy) +# Creation functions add the device keyword (which does nothing for NumPy and Dask) + def arange( - start: Union[int, float], + start: float, /, - stop: Optional[Union[int, float]] = None, - step: Union[int, float] = 1, + stop: float | None = None, + step: float = 1, *, - xp, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs -) -> ndarray: + xp: Namespace, + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.arange(start, stop=stop, step=step, dtype=dtype, **kwargs) + def empty( - shape: Union[int, Tuple[int, ...]], - xp, + shape: int | tuple[int, ...], + xp: Namespace, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.empty(shape, dtype=dtype, **kwargs) + def empty_like( - x: ndarray, /, xp, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, - **kwargs -) -> ndarray: + x: Array, + /, + xp: Namespace, + *, + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.empty_like(x, dtype=dtype, **kwargs) + def eye( n_rows: int, - n_cols: Optional[int] = None, + n_cols: int | None = None, /, *, - xp, + xp: Namespace, k: int = 0, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs, -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.eye(n_rows, M=n_cols, k=k, dtype=dtype, **kwargs) + def full( - shape: Union[int, Tuple[int, ...]], - fill_value: Union[int, float], - xp, + shape: int | tuple[int, ...], + fill_value: complex, + xp: Namespace, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs, -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.full(shape, fill_value, dtype=dtype, **kwargs) + def full_like( - x: ndarray, + x: Array, /, - fill_value: Union[int, float], + fill_value: complex, *, - xp, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs, -) -> ndarray: + xp: Namespace, + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.full_like(x, fill_value, dtype=dtype, **kwargs) + def linspace( - start: Union[int, float], - stop: Union[int, float], + start: float, + stop: float, /, num: int, *, - xp, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, + xp: Namespace, + dtype: DType | None = None, + device: Device | None = None, endpoint: bool = True, - **kwargs, -) -> ndarray: + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.linspace(start, stop, num, dtype=dtype, endpoint=endpoint, **kwargs) + def ones( - shape: Union[int, Tuple[int, ...]], - xp, + shape: int | tuple[int, ...], + xp: Namespace, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs, -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.ones(shape, dtype=dtype, **kwargs) + def ones_like( - x: ndarray, /, xp, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, - **kwargs, -) -> ndarray: + x: Array, + /, + xp: Namespace, + *, + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.ones_like(x, dtype=dtype, **kwargs) + def zeros( - shape: Union[int, Tuple[int, ...]], - xp, + shape: int | tuple[int, ...], + xp: Namespace, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs, -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.zeros(shape, dtype=dtype, **kwargs) + def zeros_like( - x: ndarray, /, xp, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, - **kwargs, -) -> ndarray: + x: Array, + /, + xp: Namespace, + *, + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, +) -> Array: _check_device(xp, device) return xp.zeros_like(x, dtype=dtype, **kwargs) + # np.unique() is split into four functions in the array API: # unique_all, unique_counts, unique_inverse, and unique_values (this is done # to remove polymorphic return types). @@ -147,35 +176,37 @@ def zeros_like( # The functions here return namedtuples (np.unique() returns a normal # tuple). + # Note that these named tuples aren't actually part of the standard namespace, # but I don't see any issue with exporting the names here regardless. class UniqueAllResult(NamedTuple): - values: ndarray - indices: ndarray - inverse_indices: ndarray - counts: ndarray + values: Array + indices: Array + inverse_indices: Array + counts: Array class UniqueCountsResult(NamedTuple): - values: ndarray - counts: ndarray + values: Array + counts: Array class UniqueInverseResult(NamedTuple): - values: ndarray - inverse_indices: ndarray + values: Array + inverse_indices: Array -def _unique_kwargs(xp): +def _unique_kwargs(xp: Namespace) -> dict[str, bool]: # Older versions of NumPy and CuPy do not have equal_nan. Rather than # trying to parse version numbers, just check if equal_nan is in the # signature. s = inspect.signature(xp.unique) - if 'equal_nan' in s.parameters: - return {'equal_nan': False} + if "equal_nan" in s.parameters: + return {"equal_nan": False} return {} -def unique_all(x: ndarray, /, xp) -> UniqueAllResult: + +def unique_all(x: Array, /, xp: Namespace) -> UniqueAllResult: kwargs = _unique_kwargs(xp) values, indices, inverse_indices, counts = xp.unique( x, @@ -195,20 +226,16 @@ def unique_all(x: ndarray, /, xp) -> UniqueAllResult: ) -def unique_counts(x: ndarray, /, xp) -> UniqueCountsResult: +def unique_counts(x: Array, /, xp: Namespace) -> UniqueCountsResult: kwargs = _unique_kwargs(xp) res = xp.unique( - x, - return_counts=True, - return_index=False, - return_inverse=False, - **kwargs + x, return_counts=True, return_index=False, return_inverse=False, **kwargs ) return UniqueCountsResult(*res) -def unique_inverse(x: ndarray, /, xp) -> UniqueInverseResult: +def unique_inverse(x: Array, /, xp: Namespace) -> UniqueInverseResult: kwargs = _unique_kwargs(xp) values, inverse_indices = xp.unique( x, @@ -223,7 +250,7 @@ def unique_inverse(x: ndarray, /, xp) -> UniqueInverseResult: return UniqueInverseResult(values, inverse_indices) -def unique_values(x: ndarray, /, xp) -> ndarray: +def unique_values(x: Array, /, xp: Namespace) -> Array: kwargs = _unique_kwargs(xp) return xp.unique( x, @@ -233,51 +260,58 @@ def unique_values(x: ndarray, /, xp) -> ndarray: **kwargs, ) + # These functions have different keyword argument names + def std( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - axis: Optional[Union[int, Tuple[int, ...]]] = None, - correction: Union[int, float] = 0.0, # correction instead of ddof + axis: int | tuple[int, ...] | None = None, + correction: float = 0.0, # correction instead of ddof keepdims: bool = False, - **kwargs, -) -> ndarray: + **kwargs: object, +) -> Array: return xp.std(x, axis=axis, ddof=correction, keepdims=keepdims, **kwargs) + def var( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - axis: Optional[Union[int, Tuple[int, ...]]] = None, - correction: Union[int, float] = 0.0, # correction instead of ddof + axis: int | tuple[int, ...] | None = None, + correction: float = 0.0, # correction instead of ddof keepdims: bool = False, - **kwargs, -) -> ndarray: + **kwargs: object, +) -> Array: return xp.var(x, axis=axis, ddof=correction, keepdims=keepdims, **kwargs) + # cumulative_sum is renamed from cumsum, and adds the include_initial keyword # argument + def cumulative_sum( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - axis: Optional[int] = None, - dtype: Optional[Dtype] = None, + axis: int | None = None, + dtype: DType | None = None, include_initial: bool = False, - **kwargs -) -> ndarray: + **kwargs: object, +) -> Array: wrapped_xp = array_namespace(x) # TODO: The standard is not clear about what should happen when x.ndim == 0. if axis is None: if x.ndim > 1: - raise ValueError("axis must be specified in cumulative_sum for more than one dimension") + raise ValueError( + "axis must be specified in cumulative_sum for more than one dimension" + ) axis = 0 res = xp.cumsum(x, axis=axis, dtype=dtype, **kwargs) @@ -287,27 +321,34 @@ def cumulative_sum( initial_shape = list(x.shape) initial_shape[axis] = 1 res = xp.concatenate( - [wrapped_xp.zeros(shape=initial_shape, dtype=res.dtype, device=device(res)), res], + [ + wrapped_xp.zeros( + shape=initial_shape, dtype=res.dtype, device=_get_device(res) + ), + res, + ], axis=axis, ) return res def cumulative_prod( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - axis: Optional[int] = None, - dtype: Optional[Dtype] = None, + axis: int | None = None, + dtype: DType | None = None, include_initial: bool = False, - **kwargs -) -> ndarray: + **kwargs: object, +) -> Array: wrapped_xp = array_namespace(x) if axis is None: if x.ndim > 1: - raise ValueError("axis must be specified in cumulative_prod for more than one dimension") + raise ValueError( + "axis must be specified in cumulative_prod for more than one dimension" + ) axis = 0 res = xp.cumprod(x, axis=axis, dtype=dtype, **kwargs) @@ -317,25 +358,32 @@ def cumulative_prod( initial_shape = list(x.shape) initial_shape[axis] = 1 res = xp.concatenate( - [wrapped_xp.ones(shape=initial_shape, dtype=res.dtype, device=device(res)), res], + [ + wrapped_xp.ones( + shape=initial_shape, dtype=res.dtype, device=_get_device(res) + ), + res, + ], axis=axis, ) return res + # The min and max argument names in clip are different and not optional in numpy, and type # promotion behavior is different. def clip( - x: ndarray, + x: Array, /, - min: Optional[Union[int, float, ndarray]] = None, - max: Optional[Union[int, float, ndarray]] = None, + min: float | Array | None = None, + max: float | Array | None = None, *, - xp, + xp: Namespace, # TODO: np.clip has other ufunc kwargs - out: Optional[ndarray] = None, -) -> ndarray: - def _isscalar(a): + out: Array | None = None, +) -> Array: + def _isscalar(a: object) -> TypeIs[int | float | None]: return isinstance(a, (int, float, type(None))) + min_shape = () if _isscalar(min) else min.shape max_shape = () if _isscalar(max) else max.shape @@ -360,7 +408,6 @@ def _isscalar(a): # but an answer of 0 might be preferred. See # https://github.com/numpy/numpy/issues/24976 for more discussion on this issue. - # At least handle the case of Python integers correctly (see # https://github.com/numpy/numpy/pull/26892). if wrapped_xp.isdtype(x.dtype, "integral"): @@ -369,9 +416,10 @@ def _isscalar(a): if type(max) is int and max >= wrapped_xp.iinfo(x.dtype).max: max = None - dev = device(x) + dev = _get_device(x) if out is None: out = wrapped_xp.empty(result_shape, dtype=x.dtype, device=dev) + assert out is not None # workaround for a type-narrowing issue in pyright out[()] = x if min is not None: @@ -389,16 +437,22 @@ def _isscalar(a): # Return a scalar for 0-D return out[()] + # Unlike transpose(), the axes argument to permute_dims() is required. -def permute_dims(x: ndarray, /, axes: Tuple[int, ...], xp) -> ndarray: +def permute_dims(x: Array, /, axes: tuple[int, ...], xp: Namespace) -> Array: return xp.transpose(x, axes) + # np.reshape calls the keyword argument 'newshape' instead of 'shape' -def reshape(x: ndarray, - /, - shape: Tuple[int, ...], - xp, copy: Optional[bool] = None, - **kwargs) -> ndarray: +def reshape( + x: Array, + /, + shape: tuple[int, ...], + xp: Namespace, + *, + copy: Optional[bool] = None, + **kwargs: object, +) -> Array: if copy is True: x = x.copy() elif copy is False: @@ -407,17 +461,24 @@ def reshape(x: ndarray, return y return xp.reshape(x, shape, **kwargs) + # The descending keyword is new in sort and argsort, and 'kind' replaced with # 'stable' def argsort( - x: ndarray, /, xp, *, axis: int = -1, descending: bool = False, stable: bool = True, - **kwargs, -) -> ndarray: + x: Array, + /, + xp: Namespace, + *, + axis: int = -1, + descending: bool = False, + stable: bool = True, + **kwargs: object, +) -> Array: # Note: this keyword argument is different, and the default is different. # We set it in kwargs like this because numpy.sort uses kind='quicksort' # as the default whereas cupy.sort uses kind=None. if stable: - kwargs['kind'] = "stable" + kwargs["kind"] = "stable" if not descending: res = xp.argsort(x, axis=axis, **kwargs) else: @@ -434,69 +495,87 @@ def argsort( res = max_i - res return res + def sort( - x: ndarray, /, xp, *, axis: int = -1, descending: bool = False, stable: bool = True, - **kwargs, -) -> ndarray: + x: Array, + /, + xp: Namespace, + *, + axis: int = -1, + descending: bool = False, + stable: bool = True, + **kwargs: object, +) -> Array: # Note: this keyword argument is different, and the default is different. # We set it in kwargs like this because numpy.sort uses kind='quicksort' # as the default whereas cupy.sort uses kind=None. if stable: - kwargs['kind'] = "stable" + kwargs["kind"] = "stable" res = xp.sort(x, axis=axis, **kwargs) if descending: res = xp.flip(res, axis=axis) return res + # nonzero should error for zero-dimensional arrays -def nonzero(x: ndarray, /, xp, **kwargs) -> Tuple[ndarray, ...]: +def nonzero(x: Array, /, xp: Namespace, **kwargs: object) -> tuple[Array, ...]: if x.ndim == 0: raise ValueError("nonzero() does not support zero-dimensional arrays") return xp.nonzero(x, **kwargs) + # ceil, floor, and trunc return integers for integer inputs -def ceil(x: ndarray, /, xp, **kwargs) -> ndarray: + +def ceil(x: Array, /, xp: Namespace, **kwargs: object) -> Array: if xp.issubdtype(x.dtype, xp.integer): return x return xp.ceil(x, **kwargs) -def floor(x: ndarray, /, xp, **kwargs) -> ndarray: + +def floor(x: Array, /, xp: Namespace, **kwargs: object) -> Array: if xp.issubdtype(x.dtype, xp.integer): return x return xp.floor(x, **kwargs) -def trunc(x: ndarray, /, xp, **kwargs) -> ndarray: + +def trunc(x: Array, /, xp: Namespace, **kwargs: object) -> Array: if xp.issubdtype(x.dtype, xp.integer): return x return xp.trunc(x, **kwargs) + # linear algebra functions -def matmul(x1: ndarray, x2: ndarray, /, xp, **kwargs) -> ndarray: + +def matmul(x1: Array, x2: Array, /, xp: Namespace, **kwargs: object) -> Array: return xp.matmul(x1, x2, **kwargs) + # Unlike transpose, matrix_transpose only transposes the last two axes. -def matrix_transpose(x: ndarray, /, xp) -> ndarray: +def matrix_transpose(x: Array, /, xp: Namespace) -> Array: if x.ndim < 2: raise ValueError("x must be at least 2-dimensional for matrix_transpose") return xp.swapaxes(x, -1, -2) -def tensordot(x1: ndarray, - x2: ndarray, - /, - xp, - *, - axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, - **kwargs, -) -> ndarray: + +def tensordot( + x1: Array, + x2: Array, + /, + xp: Namespace, + *, + axes: int | tuple[Sequence[int], Sequence[int]] = 2, + **kwargs: object, +) -> Array: return xp.tensordot(x1, x2, axes=axes, **kwargs) -def vecdot(x1: ndarray, x2: ndarray, /, xp, *, axis: int = -1) -> ndarray: + +def vecdot(x1: Array, x2: Array, /, xp: Namespace, *, axis: int = -1) -> Array: if x1.shape[axis] != x2.shape[axis]: raise ValueError("x1 and x2 must have the same size along the given axis") - if hasattr(xp, 'broadcast_tensors'): + if hasattr(xp, "broadcast_tensors"): _broadcast = xp.broadcast_tensors else: _broadcast = xp.broadcast_arrays @@ -508,11 +587,16 @@ def vecdot(x1: ndarray, x2: ndarray, /, xp, *, axis: int = -1) -> ndarray: res = xp.conj(x1_[..., None, :]) @ x2_[..., None] return res[..., 0, 0] + # isdtype is a new function in the 2022.12 array API specification. + def isdtype( - dtype: Dtype, kind: Union[Dtype, str, Tuple[Union[Dtype, str], ...]], xp, - *, _tuple=True, # Disallow nested tuples + dtype: DType, + kind: DType | str | tuple[DType | str, ...], + xp: Namespace, + *, + _tuple: bool = True, # Disallow nested tuples ) -> bool: """ Returns a boolean indicating whether a provided dtype is of a specified data type ``kind``. @@ -525,21 +609,24 @@ def isdtype( for more details """ if isinstance(kind, tuple) and _tuple: - return any(isdtype(dtype, k, xp, _tuple=False) for k in kind) + return any( + isdtype(dtype, k, xp, _tuple=False) + for k in cast("tuple[DType | str, ...]", kind) + ) elif isinstance(kind, str): - if kind == 'bool': + if kind == "bool": return dtype == xp.bool_ - elif kind == 'signed integer': + elif kind == "signed integer": return xp.issubdtype(dtype, xp.signedinteger) - elif kind == 'unsigned integer': + elif kind == "unsigned integer": return xp.issubdtype(dtype, xp.unsignedinteger) - elif kind == 'integral': + elif kind == "integral": return xp.issubdtype(dtype, xp.integer) - elif kind == 'real floating': + elif kind == "real floating": return xp.issubdtype(dtype, xp.floating) - elif kind == 'complex floating': + elif kind == "complex floating": return xp.issubdtype(dtype, xp.complexfloating) - elif kind == 'numeric': + elif kind == "numeric": return xp.issubdtype(dtype, xp.number) else: raise ValueError(f"Unrecognized data type kind: {kind!r}") @@ -550,32 +637,91 @@ def isdtype( # array_api_strict implementation will be very strict. return dtype == kind + # unstack is a new function in the 2023.12 array API standard -def unstack(x: ndarray, /, xp, *, axis: int = 0) -> Tuple[ndarray, ...]: +def unstack(x: Array, /, xp: Namespace, *, axis: int = 0) -> tuple[Array, ...]: if x.ndim == 0: raise ValueError("Input array must be at least 1-d.") return tuple(xp.moveaxis(x, axis, 0)) + # numpy 1.26 does not use the standard definition for sign on complex numbers -def sign(x: ndarray, /, xp, **kwargs) -> ndarray: - if isdtype(x.dtype, 'complex floating', xp=xp): - out = (x/xp.abs(x, **kwargs))[...] + +def sign(x: Array, /, xp: Namespace, **kwargs: object) -> Array: + if isdtype(x.dtype, "complex floating", xp=xp): + out = (x / xp.abs(x, **kwargs))[...] # sign(0) = 0 but the above formula would give nan - out[x == 0+0j] = 0+0j + out[x == 0j] = 0j else: out = xp.sign(x, **kwargs) # CuPy sign() does not propagate nans. See # https://github.com/data-apis/array-api-compat/issues/136 - if is_cupy_namespace(xp) and isdtype(x.dtype, 'real floating', xp=xp): + if _is_cupy_namespace(xp) and isdtype(x.dtype, "real floating", xp=xp): out[xp.isnan(x)] = xp.nan return out[()] -__all__ = ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', - 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like', - 'UniqueAllResult', 'UniqueCountsResult', 'UniqueInverseResult', - 'unique_all', 'unique_counts', 'unique_inverse', 'unique_values', - 'std', 'var', 'cumulative_sum', 'cumulative_prod','clip', 'permute_dims', - 'reshape', 'argsort', 'sort', 'nonzero', 'ceil', 'floor', 'trunc', - 'matmul', 'matrix_transpose', 'tensordot', 'vecdot', 'isdtype', - 'unstack', 'sign'] + +def finfo(type_: DType | Array, /, xp: Namespace) -> Any: + # It is surprisingly difficult to recognize a dtype apart from an array. + # np.int64 is not the same as np.asarray(1).dtype! + try: + return xp.finfo(type_) + except (ValueError, TypeError): + return xp.finfo(type_.dtype) + + +def iinfo(type_: DType | Array, /, xp: Namespace) -> Any: + try: + return xp.iinfo(type_) + except (ValueError, TypeError): + return xp.iinfo(type_.dtype) + + +__all__ = [ + "arange", + "empty", + "empty_like", + "eye", + "full", + "full_like", + "linspace", + "ones", + "ones_like", + "zeros", + "zeros_like", + "UniqueAllResult", + "UniqueCountsResult", + "UniqueInverseResult", + "unique_all", + "unique_counts", + "unique_inverse", + "unique_values", + "std", + "var", + "cumulative_sum", + "cumulative_prod", + "clip", + "permute_dims", + "reshape", + "argsort", + "sort", + "nonzero", + "ceil", + "floor", + "trunc", + "matmul", + "matrix_transpose", + "tensordot", + "vecdot", + "isdtype", + "unstack", + "sign", + "finfo", + "iinfo", +] +_all_ignore = ["inspect", "array_namespace", "NamedTuple"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/common/_fft.py b/sklearn/externals/array_api_compat/common/_fft.py index e5caebef732c1..18839d37f8494 100644 --- a/sklearn/externals/array_api_compat/common/_fft.py +++ b/sklearn/externals/array_api_compat/common/_fft.py @@ -1,149 +1,150 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union, Optional, Literal +from collections.abc import Sequence +from typing import Literal, TypeAlias -if TYPE_CHECKING: - from ._typing import Device, ndarray, DType - from collections.abc import Sequence +from ._typing import Array, Device, DType, Namespace + +_Norm: TypeAlias = Literal["backward", "ortho", "forward"] # Note: NumPy fft functions improperly upcast float32 and complex64 to # complex128, which is why we require wrapping them all here. def fft( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - n: Optional[int] = None, + n: int | None = None, axis: int = -1, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + norm: _Norm = "backward", +) -> Array: res = xp.fft.fft(x, n=n, axis=axis, norm=norm) if x.dtype in [xp.float32, xp.complex64]: return res.astype(xp.complex64) return res def ifft( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - n: Optional[int] = None, + n: int | None = None, axis: int = -1, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + norm: _Norm = "backward", +) -> Array: res = xp.fft.ifft(x, n=n, axis=axis, norm=norm) if x.dtype in [xp.float32, xp.complex64]: return res.astype(xp.complex64) return res def fftn( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + s: Sequence[int] | None = None, + axes: Sequence[int] | None = None, + norm: _Norm = "backward", +) -> Array: res = xp.fft.fftn(x, s=s, axes=axes, norm=norm) if x.dtype in [xp.float32, xp.complex64]: return res.astype(xp.complex64) return res def ifftn( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + s: Sequence[int] | None = None, + axes: Sequence[int] | None = None, + norm: _Norm = "backward", +) -> Array: res = xp.fft.ifftn(x, s=s, axes=axes, norm=norm) if x.dtype in [xp.float32, xp.complex64]: return res.astype(xp.complex64) return res def rfft( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - n: Optional[int] = None, + n: int | None = None, axis: int = -1, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + norm: _Norm = "backward", +) -> Array: res = xp.fft.rfft(x, n=n, axis=axis, norm=norm) if x.dtype == xp.float32: return res.astype(xp.complex64) return res def irfft( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - n: Optional[int] = None, + n: int | None = None, axis: int = -1, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + norm: _Norm = "backward", +) -> Array: res = xp.fft.irfft(x, n=n, axis=axis, norm=norm) if x.dtype == xp.complex64: return res.astype(xp.float32) return res def rfftn( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + s: Sequence[int] | None = None, + axes: Sequence[int] | None = None, + norm: _Norm = "backward", +) -> Array: res = xp.fft.rfftn(x, s=s, axes=axes, norm=norm) if x.dtype == xp.float32: return res.astype(xp.complex64) return res def irfftn( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - s: Sequence[int] = None, - axes: Sequence[int] = None, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + s: Sequence[int] | None = None, + axes: Sequence[int] | None = None, + norm: _Norm = "backward", +) -> Array: res = xp.fft.irfftn(x, s=s, axes=axes, norm=norm) if x.dtype == xp.complex64: return res.astype(xp.float32) return res def hfft( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - n: Optional[int] = None, + n: int | None = None, axis: int = -1, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + norm: _Norm = "backward", +) -> Array: res = xp.fft.hfft(x, n=n, axis=axis, norm=norm) if x.dtype in [xp.float32, xp.complex64]: return res.astype(xp.float32) return res def ihfft( - x: ndarray, + x: Array, /, - xp, + xp: Namespace, *, - n: Optional[int] = None, + n: int | None = None, axis: int = -1, - norm: Literal["backward", "ortho", "forward"] = "backward", -) -> ndarray: + norm: _Norm = "backward", +) -> Array: res = xp.fft.ihfft(x, n=n, axis=axis, norm=norm) if x.dtype in [xp.float32, xp.complex64]: return res.astype(xp.complex64) @@ -152,12 +153,12 @@ def ihfft( def fftfreq( n: int, /, - xp, + xp: Namespace, *, d: float = 1.0, - dtype: Optional[DType] = None, - device: Optional[Device] = None -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, +) -> Array: if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") res = xp.fft.fftfreq(n, d=d) @@ -168,12 +169,12 @@ def fftfreq( def rfftfreq( n: int, /, - xp, + xp: Namespace, *, d: float = 1.0, - dtype: Optional[DType] = None, - device: Optional[Device] = None -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, +) -> Array: if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") res = xp.fft.rfftfreq(n, d=d) @@ -181,10 +182,14 @@ def rfftfreq( return res.astype(dtype) return res -def fftshift(x: ndarray, /, xp, *, axes: Union[int, Sequence[int]] = None) -> ndarray: +def fftshift( + x: Array, /, xp: Namespace, *, axes: int | Sequence[int] | None = None +) -> Array: return xp.fft.fftshift(x, axes=axes) -def ifftshift(x: ndarray, /, xp, *, axes: Union[int, Sequence[int]] = None) -> ndarray: +def ifftshift( + x: Array, /, xp: Namespace, *, axes: int | Sequence[int] | None = None +) -> Array: return xp.fft.ifftshift(x, axes=axes) __all__ = [ @@ -203,3 +208,6 @@ def ifftshift(x: ndarray, /, xp, *, axes: Union[int, Sequence[int]] = None) -> n "fftshift", "ifftshift", ] + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/common/_helpers.py b/sklearn/externals/array_api_compat/common/_helpers.py index 970450e8ff2e9..77175d0d1e974 100644 --- a/sklearn/externals/array_api_compat/common/_helpers.py +++ b/sklearn/externals/array_api_compat/common/_helpers.py @@ -5,35 +5,97 @@ that are in __all__ are intended as additional helper functions for use by end users of the compat library. """ + from __future__ import annotations -from typing import TYPE_CHECKING +import inspect +import math +import sys +import warnings +from collections.abc import Collection, Hashable +from functools import lru_cache +from typing import ( + TYPE_CHECKING, + Any, + Final, + Literal, + SupportsIndex, + TypeAlias, + TypeGuard, + TypeVar, + cast, + overload, +) + +from ._typing import Array, Device, HasShape, Namespace, SupportsArrayNamespace if TYPE_CHECKING: - from typing import Optional, Union, Any - from ._typing import Array, Device, Namespace -import sys -import math -import inspect -import warnings + import dask.array as da + import jax + import ndonnx as ndx + import numpy as np + import numpy.typing as npt + import sparse # pyright: ignore[reportMissingTypeStubs] + import torch + + # TODO: import from typing (requires Python >=3.13) + from typing_extensions import TypeIs, TypeVar + + _SizeT = TypeVar("_SizeT", bound = int | None) -def _is_jax_zero_gradient_array(x: object) -> bool: + _ZeroGradientArray: TypeAlias = npt.NDArray[np.void] + _CupyArray: TypeAlias = Any # cupy has no py.typed + + _ArrayApiObj: TypeAlias = ( + npt.NDArray[Any] + | da.Array + | jax.Array + | ndx.Array + | sparse.SparseArray + | torch.Tensor + | SupportsArrayNamespace[Any] + | _CupyArray + ) + +_API_VERSIONS_OLD: Final = frozenset({"2021.12", "2022.12", "2023.12"}) +_API_VERSIONS: Final = _API_VERSIONS_OLD | frozenset({"2024.12"}) + + +@lru_cache(100) +def _issubclass_fast(cls: type, modname: str, clsname: str) -> bool: + try: + mod = sys.modules[modname] + except KeyError: + return False + parent_cls = getattr(mod, clsname) + return issubclass(cls, parent_cls) + + +def _is_jax_zero_gradient_array(x: object) -> TypeGuard[_ZeroGradientArray]: """Return True if `x` is a zero-gradient array. These arrays are a design quirk of Jax that may one day be removed. See https://github.com/google/jax/issues/20620. """ - if 'numpy' not in sys.modules or 'jax' not in sys.modules: + # Fast exit + try: + dtype = x.dtype # type: ignore[attr-defined] + except AttributeError: + return False + cls = cast(Hashable, type(dtype)) + if not _issubclass_fast(cls, "numpy.dtypes", "VoidDType"): return False - import numpy as np - import jax + if "jax" not in sys.modules: + return False - return isinstance(x, np.ndarray) and x.dtype == jax.float0 + import jax + # jax.float0 is a np.dtype([('float0', 'V')]) + return dtype == jax.float0 -def is_numpy_array(x: object) -> bool: +def is_numpy_array(x: object) -> TypeGuard[npt.NDArray[Any]]: """ Return True if `x` is a NumPy array. @@ -54,15 +116,12 @@ def is_numpy_array(x: object) -> bool: is_jax_array is_pydata_sparse_array """ - # Avoid importing NumPy if it isn't already - if 'numpy' not in sys.modules: - return False - - import numpy as np - # TODO: Should we reject ndarray subclasses? - return (isinstance(x, (np.ndarray, np.generic)) - and not _is_jax_zero_gradient_array(x)) + cls = cast(Hashable, type(x)) + return ( + _issubclass_fast(cls, "numpy", "ndarray") + or _issubclass_fast(cls, "numpy", "generic") + ) and not _is_jax_zero_gradient_array(x) def is_cupy_array(x: object) -> bool: @@ -86,17 +145,11 @@ def is_cupy_array(x: object) -> bool: is_jax_array is_pydata_sparse_array """ - # Avoid importing CuPy if it isn't already - if 'cupy' not in sys.modules: - return False - - import cupy as cp - - # TODO: Should we reject ndarray subclasses? - return isinstance(x, cp.ndarray) + cls = cast(Hashable, type(x)) + return _issubclass_fast(cls, "cupy", "ndarray") -def is_torch_array(x: object) -> bool: +def is_torch_array(x: object) -> TypeIs[torch.Tensor]: """ Return True if `x` is a PyTorch tensor. @@ -114,17 +167,11 @@ def is_torch_array(x: object) -> bool: is_jax_array is_pydata_sparse_array """ - # Avoid importing torch if it isn't already - if 'torch' not in sys.modules: - return False - - import torch - - # TODO: Should we reject ndarray subclasses? - return isinstance(x, torch.Tensor) + cls = cast(Hashable, type(x)) + return _issubclass_fast(cls, "torch", "Tensor") -def is_ndonnx_array(x: object) -> bool: +def is_ndonnx_array(x: object) -> TypeIs[ndx.Array]: """ Return True if `x` is a ndonnx Array. @@ -143,16 +190,11 @@ def is_ndonnx_array(x: object) -> bool: is_jax_array is_pydata_sparse_array """ - # Avoid importing torch if it isn't already - if 'ndonnx' not in sys.modules: - return False - - import ndonnx as ndx - - return isinstance(x, ndx.Array) + cls = cast(Hashable, type(x)) + return _issubclass_fast(cls, "ndonnx", "Array") -def is_dask_array(x: object) -> bool: +def is_dask_array(x: object) -> TypeIs[da.Array]: """ Return True if `x` is a dask.array Array. @@ -171,16 +213,11 @@ def is_dask_array(x: object) -> bool: is_jax_array is_pydata_sparse_array """ - # Avoid importing dask if it isn't already - if 'dask.array' not in sys.modules: - return False - - import dask.array - - return isinstance(x, dask.array.Array) + cls = cast(Hashable, type(x)) + return _issubclass_fast(cls, "dask.array", "Array") -def is_jax_array(x: object) -> bool: +def is_jax_array(x: object) -> TypeIs[jax.Array]: """ Return True if `x` is a JAX array. @@ -200,16 +237,11 @@ def is_jax_array(x: object) -> bool: is_dask_array is_pydata_sparse_array """ - # Avoid importing jax if it isn't already - if 'jax' not in sys.modules: - return False - - import jax - - return isinstance(x, jax.Array) or _is_jax_zero_gradient_array(x) + cls = cast(Hashable, type(x)) + return _issubclass_fast(cls, "jax", "Array") or _is_jax_zero_gradient_array(x) -def is_pydata_sparse_array(x) -> bool: +def is_pydata_sparse_array(x: object) -> TypeIs[sparse.SparseArray]: """ Return True if `x` is an array from the `sparse` package. @@ -229,17 +261,12 @@ def is_pydata_sparse_array(x) -> bool: is_dask_array is_jax_array """ - # Avoid importing jax if it isn't already - if 'sparse' not in sys.modules: - return False - - import sparse - # TODO: Account for other backends. - return isinstance(x, sparse.SparseArray) + cls = cast(Hashable, type(x)) + return _issubclass_fast(cls, "sparse", "SparseArray") -def is_array_api_obj(x: object) -> bool: +def is_array_api_obj(x: object) -> TypeIs[_ArrayApiObj]: # pyright: ignore[reportUnknownParameterType] """ Return True if `x` is an array API compatible array object. @@ -254,21 +281,34 @@ def is_array_api_obj(x: object) -> bool: is_dask_array is_jax_array """ - return is_numpy_array(x) \ - or is_cupy_array(x) \ - or is_torch_array(x) \ - or is_dask_array(x) \ - or is_jax_array(x) \ - or is_pydata_sparse_array(x) \ - or hasattr(x, '__array_namespace__') + return ( + hasattr(x, '__array_namespace__') + or _is_array_api_cls(cast(Hashable, type(x))) + ) + + +@lru_cache(100) +def _is_array_api_cls(cls: type) -> bool: + return ( + # TODO: drop support for numpy<2 which didn't have __array_namespace__ + _issubclass_fast(cls, "numpy", "ndarray") + or _issubclass_fast(cls, "numpy", "generic") + or _issubclass_fast(cls, "cupy", "ndarray") + or _issubclass_fast(cls, "torch", "Tensor") + or _issubclass_fast(cls, "dask.array", "Array") + or _issubclass_fast(cls, "sparse", "SparseArray") + # TODO: drop support for jax<0.4.32 which didn't have __array_namespace__ + or _issubclass_fast(cls, "jax", "Array") + ) def _compat_module_name() -> str: - assert __name__.endswith('.common._helpers') - return __name__.removesuffix('.common._helpers') + assert __name__.endswith(".common._helpers") + return __name__.removesuffix(".common._helpers") -def is_numpy_namespace(xp) -> bool: +@lru_cache(100) +def is_numpy_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is a NumPy namespace. @@ -286,10 +326,11 @@ def is_numpy_namespace(xp) -> bool: is_pydata_sparse_namespace is_array_api_strict_namespace """ - return xp.__name__ in {'numpy', _compat_module_name() + '.numpy'} + return xp.__name__ in {"numpy", _compat_module_name() + ".numpy"} -def is_cupy_namespace(xp) -> bool: +@lru_cache(100) +def is_cupy_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is a CuPy namespace. @@ -307,10 +348,11 @@ def is_cupy_namespace(xp) -> bool: is_pydata_sparse_namespace is_array_api_strict_namespace """ - return xp.__name__ in {'cupy', _compat_module_name() + '.cupy'} + return xp.__name__ in {"cupy", _compat_module_name() + ".cupy"} -def is_torch_namespace(xp) -> bool: +@lru_cache(100) +def is_torch_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is a PyTorch namespace. @@ -328,10 +370,10 @@ def is_torch_namespace(xp) -> bool: is_pydata_sparse_namespace is_array_api_strict_namespace """ - return xp.__name__ in {'torch', _compat_module_name() + '.torch'} + return xp.__name__ in {"torch", _compat_module_name() + ".torch"} -def is_ndonnx_namespace(xp) -> bool: +def is_ndonnx_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is an NDONNX namespace. @@ -347,10 +389,11 @@ def is_ndonnx_namespace(xp) -> bool: is_pydata_sparse_namespace is_array_api_strict_namespace """ - return xp.__name__ == 'ndonnx' + return xp.__name__ == "ndonnx" -def is_dask_namespace(xp) -> bool: +@lru_cache(100) +def is_dask_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is a Dask namespace. @@ -368,10 +411,10 @@ def is_dask_namespace(xp) -> bool: is_pydata_sparse_namespace is_array_api_strict_namespace """ - return xp.__name__ in {'dask.array', _compat_module_name() + '.dask.array'} + return xp.__name__ in {"dask.array", _compat_module_name() + ".dask.array"} -def is_jax_namespace(xp) -> bool: +def is_jax_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is a JAX namespace. @@ -390,10 +433,10 @@ def is_jax_namespace(xp) -> bool: is_pydata_sparse_namespace is_array_api_strict_namespace """ - return xp.__name__ in {'jax.numpy', 'jax.experimental.array_api'} + return xp.__name__ in {"jax.numpy", "jax.experimental.array_api"} -def is_pydata_sparse_namespace(xp) -> bool: +def is_pydata_sparse_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is a pydata/sparse namespace. @@ -409,10 +452,10 @@ def is_pydata_sparse_namespace(xp) -> bool: is_jax_namespace is_array_api_strict_namespace """ - return xp.__name__ == 'sparse' + return xp.__name__ == "sparse" -def is_array_api_strict_namespace(xp) -> bool: +def is_array_api_strict_namespace(xp: Namespace) -> bool: """ Returns True if `xp` is an array-api-strict namespace. @@ -428,18 +471,25 @@ def is_array_api_strict_namespace(xp) -> bool: is_jax_namespace is_pydata_sparse_namespace """ - return xp.__name__ == 'array_api_strict' + return xp.__name__ == "array_api_strict" -def _check_api_version(api_version: str) -> None: - if api_version in ['2021.12', '2022.12', '2023.12']: - warnings.warn(f"The {api_version} version of the array API specification was requested but the returned namespace is actually version 2024.12") - elif api_version is not None and api_version not in ['2021.12', '2022.12', - '2023.12', '2024.12']: - raise ValueError("Only the 2024.12 version of the array API specification is currently supported") +def _check_api_version(api_version: str | None) -> None: + if api_version in _API_VERSIONS_OLD: + warnings.warn( + f"The {api_version} version of the array API specification was requested but the returned namespace is actually version 2024.12" + ) + elif api_version is not None and api_version not in _API_VERSIONS: + raise ValueError( + "Only the 2024.12 version of the array API specification is currently supported" + ) -def array_namespace(*xs, api_version=None, use_compat=None) -> Namespace: +def array_namespace( + *xs: Array | complex | None, + api_version: str | None = None, + use_compat: bool | None = None, +) -> Namespace: """ Get the array API compatible namespace for the arrays `xs`. @@ -508,11 +558,13 @@ def your_function(x, y): _use_compat = use_compat in [None, True] - namespaces = set() + namespaces: set[Namespace] = set() for x in xs: if is_numpy_array(x): - from .. import numpy as numpy_namespace import numpy as np + + from .. import numpy as numpy_namespace + if use_compat is True: _check_api_version(api_version) namespaces.add(numpy_namespace) @@ -526,25 +578,31 @@ def your_function(x, y): if _use_compat: _check_api_version(api_version) from .. import cupy as cupy_namespace + namespaces.add(cupy_namespace) else: - import cupy as cp + import cupy as cp # pyright: ignore[reportMissingTypeStubs] + namespaces.add(cp) elif is_torch_array(x): if _use_compat: _check_api_version(api_version) from .. import torch as torch_namespace + namespaces.add(torch_namespace) else: import torch + namespaces.add(torch) elif is_dask_array(x): if _use_compat: _check_api_version(api_version) from ..dask import array as dask_namespace + namespaces.add(dask_namespace) else: import dask.array as da + namespaces.add(da) elif is_jax_array(x): if use_compat is True: @@ -556,23 +614,27 @@ def your_function(x, y): # JAX v0.4.32 and newer implements the array API directly in jax.numpy. # For older JAX versions, it is available via jax.experimental.array_api. import jax.numpy + if hasattr(jax.numpy, "__array_api_version__"): jnp = jax.numpy else: - import jax.experimental.array_api as jnp + import jax.experimental.array_api as jnp # pyright: ignore[reportMissingImports] namespaces.add(jnp) elif is_pydata_sparse_array(x): if use_compat is True: _check_api_version(api_version) raise ValueError("`sparse` does not have an array-api-compat wrapper") else: - import sparse + import sparse # pyright: ignore[reportMissingTypeStubs] # `sparse` is already an array namespace. We do not have a wrapper # submodule for it. namespaces.add(sparse) - elif hasattr(x, '__array_namespace__'): + elif hasattr(x, "__array_namespace__"): if use_compat is True: - raise ValueError("The given array does not have an array-api-compat wrapper") + raise ValueError( + "The given array does not have an array-api-compat wrapper" + ) + x = cast("SupportsArrayNamespace[Any]", x) namespaces.add(x.__array_namespace__(api_version=api_version)) elif isinstance(x, (bool, int, float, complex, type(None))): continue @@ -586,34 +648,55 @@ def your_function(x, y): if len(namespaces) != 1: raise TypeError(f"Multiple namespaces for array inputs: {namespaces}") - xp, = namespaces + (xp,) = namespaces return xp + # backwards compatibility alias get_namespace = array_namespace -def _check_device(xp, device): - if xp == sys.modules.get('numpy'): - if device not in ["cpu", None]: + +def _check_device(bare_xp: Namespace, device: Device) -> None: # pyright: ignore[reportUnusedFunction] + """ + Validate dummy device on device-less array backends. + + Notes + ----- + This function is also invoked by CuPy, which does have multiple devices + if there are multiple GPUs available. + However, CuPy multi-device support is currently impossible + without using the global device or a context manager: + + https://github.com/data-apis/array-api-compat/pull/293 + """ + if bare_xp is sys.modules.get("numpy"): + if device not in ("cpu", None): raise ValueError(f"Unsupported device for NumPy: {device!r}") + elif bare_xp is sys.modules.get("dask.array"): + if device not in ("cpu", _DASK_DEVICE, None): + raise ValueError(f"Unsupported device for Dask: {device!r}") + + # Placeholder object to represent the dask device # when the array backend is not the CPU. # (since it is not easy to tell which device a dask array is on) class _dask_device: - def __repr__(self): + def __repr__(self) -> Literal["DASK_DEVICE"]: return "DASK_DEVICE" + _DASK_DEVICE = _dask_device() + # device() is not on numpy.ndarray or dask.array and to_device() is not on numpy.ndarray # or cupy.ndarray. They are not included in array objects of this library # because this library just reuses the respective ndarray classes without # wrapping or subclassing them. These helper functions can be used instead of # the wrapper functions for libraries that need to support both NumPy/CuPy and # other libraries that use devices. -def device(x: Array, /) -> Device: +def device(x: _ArrayApiObj, /) -> Device: """ Hardware device the array data resides on. @@ -649,7 +732,7 @@ def device(x: Array, /) -> Device: return "cpu" elif is_dask_array(x): # Peek at the metadata of the Dask array to determine type - if is_numpy_array(x._meta): + if is_numpy_array(x._meta): # pyright: ignore # Must be on CPU since backed by numpy return "cpu" return _DASK_DEVICE @@ -659,7 +742,7 @@ def device(x: Array, /) -> Device: # Return None in this case. Note that this workaround breaks # the standard and will result in new arrays being created on the # default device instead of the same device as the input array(s). - x_device = getattr(x, 'device', None) + x_device = getattr(x, "device", None) # Older JAX releases had .device() as a method, which has been replaced # with a property in accordance with the standard. if inspect.ismethod(x_device): @@ -668,66 +751,66 @@ def device(x: Array, /) -> Device: return x_device elif is_pydata_sparse_array(x): # `sparse` will gain `.device`, so check for this first. - x_device = getattr(x, 'device', None) + x_device = getattr(x, "device", None) if x_device is not None: return x_device # Everything but DOK has this attr. try: - inner = x.data + inner = x.data # pyright: ignore except AttributeError: return "cpu" # Return the device of the constituent array - return device(inner) - return x.device + return device(inner) # pyright: ignore + return x.device # pyright: ignore + # Prevent shadowing, used below _device = device + # Based on cupy.array_api.Array.to_device -def _cupy_to_device(x, device, /, stream=None): +def _cupy_to_device( + x: _CupyArray, + device: Device, + /, + stream: int | Any | None = None, +) -> _CupyArray: import cupy as cp - from cupy.cuda import Device as _Device - from cupy.cuda import stream as stream_module - from cupy_backends.cuda.api import runtime - if device == x.device: - return x - elif device == "cpu": + if device == "cpu": # allowing us to use `to_device(x, "cpu")` # is useful for portable test swapping between # host and device backends return x.get() - elif not isinstance(device, _Device): - raise ValueError(f"Unsupported device {device!r}") - else: - # see cupy/cupy#5985 for the reason how we handle device/stream here - prev_device = runtime.getDevice() - prev_stream: stream_module.Stream = None - if stream is not None: - prev_stream = stream_module.get_current_stream() - # stream can be an int as specified in __dlpack__, or a CuPy stream - if isinstance(stream, int): - stream = cp.cuda.ExternalStream(stream) - elif isinstance(stream, cp.cuda.Stream): - pass - else: - raise ValueError('the input stream is not recognized') - stream.use() - try: - runtime.setDevice(device.id) - arr = x.copy() - finally: - runtime.setDevice(prev_device) - if stream is not None: - prev_stream.use() - return arr - -def _torch_to_device(x, device, /, stream=None): + if not isinstance(device, cp.cuda.Device): + raise TypeError(f"Unsupported device type {device!r}") + + if stream is None: + with device: + return cp.asarray(x) + + # stream can be an int as specified in __dlpack__, or a CuPy stream + if isinstance(stream, int): + stream = cp.cuda.ExternalStream(stream) + elif not isinstance(stream, cp.cuda.Stream): + raise TypeError(f"Unsupported stream type {stream!r}") + + with device, stream: + return cp.asarray(x) + + +def _torch_to_device( + x: torch.Tensor, + device: torch.device | str | int, + /, + stream: None = None, +) -> torch.Tensor: if stream is not None: raise NotImplementedError return x.to(device) -def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] = None) -> Array: + +def to_device(x: Array, device: Device, /, *, stream: int | Any | None = None) -> Array: """ Copy the array from the device on which it currently resides to the specified ``device``. @@ -747,7 +830,7 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] a ``device`` object (see the `Device Support `__ section of the array API specification). - stream: Optional[Union[int, Any]] + stream: int | Any | None stream object to use during copy. In addition to the types supported in ``array.__dlpack__``, implementations may choose to support any library-specific stream object with the caveat that any code using @@ -779,25 +862,26 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] if is_numpy_array(x): if stream is not None: raise ValueError("The stream argument to to_device() is not supported") - if device == 'cpu': + if device == "cpu": return x raise ValueError(f"Unsupported device {device!r}") elif is_cupy_array(x): # cupy does not yet have to_device return _cupy_to_device(x, device, stream=stream) elif is_torch_array(x): - return _torch_to_device(x, device, stream=stream) + return _torch_to_device(x, device, stream=stream) # pyright: ignore[reportArgumentType] elif is_dask_array(x): if stream is not None: raise ValueError("The stream argument to to_device() is not supported") # TODO: What if our array is on the GPU already? - if device == 'cpu': + if device == "cpu": return x raise ValueError(f"Unsupported device {device!r}") elif is_jax_array(x): if not hasattr(x, "__array_namespace__"): # In JAX v0.4.31 and older, this import adds to_device method to x... - import jax.experimental.array_api # noqa: F401 + import jax.experimental.array_api # noqa: F401 # pyright: ignore + # ... but only on eager JAX. It won't work inside jax.jit. if not hasattr(x, "to_device"): return x @@ -806,10 +890,16 @@ def to_device(x: Array, device: Device, /, *, stream: Optional[Union[int, Any]] # Perform trivial check to return the same array if # device is same instead of err-ing. return x - return x.to_device(device, stream=stream) + return x.to_device(device, stream=stream) # pyright: ignore -def size(x: Array) -> int | None: +@overload +def size(x: HasShape[Collection[SupportsIndex]]) -> int: ... +@overload +def size(x: HasShape[Collection[None]]) -> None: ... +@overload +def size(x: HasShape[Collection[SupportsIndex | None]]) -> int | None: ... +def size(x: HasShape[Collection[SupportsIndex | None]]) -> int | None: """ Return the total number of elements of x. @@ -824,11 +914,24 @@ def size(x: Array) -> int | None: # Lazy API compliant arrays, such as ndonnx, can contain None in their shape if None in x.shape: return None - out = math.prod(x.shape) + out = math.prod(cast("Collection[SupportsIndex]", x.shape)) # dask.array.Array.shape can contain NaN return None if math.isnan(out) else out +@lru_cache(100) +def _is_writeable_cls(cls: type) -> bool | None: + if ( + _issubclass_fast(cls, "numpy", "generic") + or _issubclass_fast(cls, "jax", "Array") + or _issubclass_fast(cls, "sparse", "SparseArray") + ): + return False + if _is_array_api_cls(cls): + return True + return None + + def is_writeable_array(x: object) -> bool: """ Return False if ``x.__setitem__`` is expected to raise; True otherwise. @@ -839,11 +942,32 @@ def is_writeable_array(x: object) -> bool: As there is no standard way to check if an array is writeable without actually writing to it, this function blindly returns True for all unknown array types. """ - if is_numpy_array(x): - return x.flags.writeable - if is_jax_array(x) or is_pydata_sparse_array(x): + cls = cast(Hashable, type(x)) + if _issubclass_fast(cls, "numpy", "ndarray"): + return cast("npt.NDArray", x).flags.writeable + res = _is_writeable_cls(cls) + if res is not None: + return res + return hasattr(x, '__array_namespace__') + + +@lru_cache(100) +def _is_lazy_cls(cls: type) -> bool | None: + if ( + _issubclass_fast(cls, "numpy", "ndarray") + or _issubclass_fast(cls, "numpy", "generic") + or _issubclass_fast(cls, "cupy", "ndarray") + or _issubclass_fast(cls, "torch", "Tensor") + or _issubclass_fast(cls, "sparse", "SparseArray") + ): return False - return is_array_api_obj(x) + if ( + _issubclass_fast(cls, "jax", "Array") + or _issubclass_fast(cls, "dask.array", "Array") + or _issubclass_fast(cls, "ndonnx", "Array") + ): + return True + return None def is_lazy_array(x: object) -> bool: @@ -859,14 +983,6 @@ def is_lazy_array(x: object) -> bool: This function errs on the side of caution for array types that may or may not be lazy, e.g. JAX arrays, by always returning True for them. """ - if ( - is_numpy_array(x) - or is_cupy_array(x) - or is_torch_array(x) - or is_pydata_sparse_array(x) - ): - return False - # **JAX note:** while it is possible to determine if you're inside or outside # jax.jit by testing the subclass of a jax.Array object, as well as testing bool() # as we do below for unknown arrays, this is not recommended by JAX best practices. @@ -876,10 +992,14 @@ def is_lazy_array(x: object) -> bool: # compatibility, is highly detrimental to performance as the whole graph will end # up being computed multiple times. - if is_jax_array(x) or is_dask_array(x) or is_ndonnx_array(x): - return True + # Note: skipping reclassification of JAX zero gradient arrays, as one will + # exclusively get them once they leave a jax.grad JIT context. + cls = cast(Hashable, type(x)) + res = _is_lazy_cls(cls) + if res is not None: + return res - if not is_array_api_obj(x): + if not hasattr(x, "__array_namespace__"): return False # Unknown Array API compatible object. Note that this test may have dire consequences @@ -887,7 +1007,7 @@ def is_lazy_array(x: object) -> bool: # on __bool__ (dask is one such example, which however is special-cased above). # Select a single point of the array - s = size(x) + s = size(cast("HasShape[Collection[SupportsIndex | None]]", x)) if s is None: return True xp = array_namespace(x) @@ -899,7 +1019,7 @@ def is_lazy_array(x: object) -> bool: try: bool(x) return False - # The Array API standard dictates that __bool__ should raise TypeError if the + # The Array API standard dictactes that __bool__ should raise TypeError if the # output cannot be defined. # Here we allow for it to raise arbitrary exceptions, e.g. like Dask does. except Exception: @@ -932,4 +1052,7 @@ def is_lazy_array(x: object) -> bool: "to_device", ] -_all_ignore = ['sys', 'math', 'inspect', 'warnings'] +_all_ignore = ['lru_cache', 'sys', 'math', 'inspect', 'warnings'] + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/common/_linalg.py b/sklearn/externals/array_api_compat/common/_linalg.py index bfa1f1b937fdd..7ad87a1be9105 100644 --- a/sklearn/externals/array_api_compat/common/_linalg.py +++ b/sklearn/externals/array_api_compat/common/_linalg.py @@ -1,85 +1,114 @@ from __future__ import annotations -from typing import TYPE_CHECKING, NamedTuple -if TYPE_CHECKING: - from typing import Literal, Optional, Tuple, Union - from ._typing import ndarray - import math +from typing import Literal, NamedTuple, cast import numpy as np + if np.__version__[0] == "2": from numpy.lib.array_utils import normalize_axis_tuple else: from numpy.core.numeric import normalize_axis_tuple -from ._aliases import matmul, matrix_transpose, tensordot, vecdot, isdtype from .._internal import get_xp +from ._aliases import isdtype, matmul, matrix_transpose, tensordot, vecdot +from ._typing import Array, DType, JustFloat, JustInt, Namespace + # These are in the main NumPy namespace but not in numpy.linalg -def cross(x1: ndarray, x2: ndarray, /, xp, *, axis: int = -1, **kwargs) -> ndarray: +def cross( + x1: Array, + x2: Array, + /, + xp: Namespace, + *, + axis: int = -1, + **kwargs: object, +) -> Array: return xp.cross(x1, x2, axis=axis, **kwargs) -def outer(x1: ndarray, x2: ndarray, /, xp, **kwargs) -> ndarray: +def outer(x1: Array, x2: Array, /, xp: Namespace, **kwargs: object) -> Array: return xp.outer(x1, x2, **kwargs) class EighResult(NamedTuple): - eigenvalues: ndarray - eigenvectors: ndarray + eigenvalues: Array + eigenvectors: Array class QRResult(NamedTuple): - Q: ndarray - R: ndarray + Q: Array + R: Array class SlogdetResult(NamedTuple): - sign: ndarray - logabsdet: ndarray + sign: Array + logabsdet: Array class SVDResult(NamedTuple): - U: ndarray - S: ndarray - Vh: ndarray + U: Array + S: Array + Vh: Array # These functions are the same as their NumPy counterparts except they return # a namedtuple. -def eigh(x: ndarray, /, xp, **kwargs) -> EighResult: +def eigh(x: Array, /, xp: Namespace, **kwargs: object) -> EighResult: return EighResult(*xp.linalg.eigh(x, **kwargs)) -def qr(x: ndarray, /, xp, *, mode: Literal['reduced', 'complete'] = 'reduced', - **kwargs) -> QRResult: +def qr( + x: Array, + /, + xp: Namespace, + *, + mode: Literal["reduced", "complete"] = "reduced", + **kwargs: object, +) -> QRResult: return QRResult(*xp.linalg.qr(x, mode=mode, **kwargs)) -def slogdet(x: ndarray, /, xp, **kwargs) -> SlogdetResult: +def slogdet(x: Array, /, xp: Namespace, **kwargs: object) -> SlogdetResult: return SlogdetResult(*xp.linalg.slogdet(x, **kwargs)) -def svd(x: ndarray, /, xp, *, full_matrices: bool = True, **kwargs) -> SVDResult: +def svd( + x: Array, + /, + xp: Namespace, + *, + full_matrices: bool = True, + **kwargs: object, +) -> SVDResult: return SVDResult(*xp.linalg.svd(x, full_matrices=full_matrices, **kwargs)) # These functions have additional keyword arguments # The upper keyword argument is new from NumPy -def cholesky(x: ndarray, /, xp, *, upper: bool = False, **kwargs) -> ndarray: +def cholesky( + x: Array, + /, + xp: Namespace, + *, + upper: bool = False, + **kwargs: object, +) -> Array: L = xp.linalg.cholesky(x, **kwargs) if upper: U = get_xp(xp)(matrix_transpose)(L) if get_xp(xp)(isdtype)(U.dtype, 'complex floating'): - U = xp.conj(U) + U = xp.conj(U) # pyright: ignore[reportConstantRedefinition] return U return L # The rtol keyword argument of matrix_rank() and pinv() is new from NumPy. # Note that it has a different semantic meaning from tol and rcond. -def matrix_rank(x: ndarray, - /, - xp, - *, - rtol: Optional[Union[float, ndarray]] = None, - **kwargs) -> ndarray: +def matrix_rank( + x: Array, + /, + xp: Namespace, + *, + rtol: float | Array | None = None, + **kwargs: object, +) -> Array: # this is different from xp.linalg.matrix_rank, which supports 1 # dimensional arrays. if x.ndim < 2: raise xp.linalg.LinAlgError("1-dimensional array given. Array must be at least two-dimensional") - S = get_xp(xp)(svdvals)(x, **kwargs) + S: Array = get_xp(xp)(svdvals)(x, **kwargs) if rtol is None: tol = S.max(axis=-1, keepdims=True) * max(x.shape[-2:]) * xp.finfo(S.dtype).eps else: @@ -88,7 +117,14 @@ def matrix_rank(x: ndarray, tol = S.max(axis=-1, keepdims=True)*xp.asarray(rtol)[..., xp.newaxis] return xp.count_nonzero(S > tol, axis=-1) -def pinv(x: ndarray, /, xp, *, rtol: Optional[Union[float, ndarray]] = None, **kwargs) -> ndarray: +def pinv( + x: Array, + /, + xp: Namespace, + *, + rtol: float | Array | None = None, + **kwargs: object, +) -> Array: # this is different from xp.linalg.pinv, which does not multiply the # default tolerance by max(M, N). if rtol is None: @@ -97,15 +133,30 @@ def pinv(x: ndarray, /, xp, *, rtol: Optional[Union[float, ndarray]] = None, **k # These functions are new in the array API spec -def matrix_norm(x: ndarray, /, xp, *, keepdims: bool = False, ord: Optional[Union[int, float, Literal['fro', 'nuc']]] = 'fro') -> ndarray: +def matrix_norm( + x: Array, + /, + xp: Namespace, + *, + keepdims: bool = False, + ord: Literal[1, 2, -1, -2] | JustFloat | Literal["fro", "nuc"] | None = "fro", +) -> Array: return xp.linalg.norm(x, axis=(-2, -1), keepdims=keepdims, ord=ord) # svdvals is not in NumPy (but it is in SciPy). It is equivalent to # xp.linalg.svd(compute_uv=False). -def svdvals(x: ndarray, /, xp) -> Union[ndarray, Tuple[ndarray, ...]]: +def svdvals(x: Array, /, xp: Namespace) -> Array | tuple[Array, ...]: return xp.linalg.svd(x, compute_uv=False) -def vector_norm(x: ndarray, /, xp, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, ord: Optional[Union[int, float]] = 2) -> ndarray: +def vector_norm( + x: Array, + /, + xp: Namespace, + *, + axis: int | tuple[int, ...] | None = None, + keepdims: bool = False, + ord: JustInt | JustFloat = 2, +) -> Array: # xp.linalg.norm tries to do a matrix norm whenever axis is a 2-tuple or # when axis=None and the input is 2-D, so to force a vector norm, we make # it so the input is 1-D (for axis=None), or reshape so that norm is done @@ -117,7 +168,10 @@ def vector_norm(x: ndarray, /, xp, *, axis: Optional[Union[int, Tuple[int, ...]] elif isinstance(axis, tuple): # Note: The axis argument supports any number of axes, whereas # xp.linalg.norm() only supports a single axis for vector norm. - normalized_axis = normalize_axis_tuple(axis, x.ndim) + normalized_axis = cast( + "tuple[int, ...]", + normalize_axis_tuple(axis, x.ndim), # pyright: ignore[reportCallIssue] + ) rest = tuple(i for i in range(x.ndim) if i not in normalized_axis) newshape = axis + rest _x = xp.transpose(x, newshape).reshape( @@ -133,7 +187,13 @@ def vector_norm(x: ndarray, /, xp, *, axis: Optional[Union[int, Tuple[int, ...]] # We can't reuse xp.linalg.norm(keepdims) because of the reshape hacks # above to avoid matrix norm logic. shape = list(x.shape) - _axis = normalize_axis_tuple(range(x.ndim) if axis is None else axis, x.ndim) + _axis = cast( + "tuple[int, ...]", + normalize_axis_tuple( # pyright: ignore[reportCallIssue] + range(x.ndim) if axis is None else axis, + x.ndim, + ), + ) for i in _axis: shape[i] = 1 res = xp.reshape(res, tuple(shape)) @@ -143,14 +203,30 @@ def vector_norm(x: ndarray, /, xp, *, axis: Optional[Union[int, Tuple[int, ...]] # xp.diagonal and xp.trace operate on the first two axes whereas these # operates on the last two -def diagonal(x: ndarray, /, xp, *, offset: int = 0, **kwargs) -> ndarray: +def diagonal(x: Array, /, xp: Namespace, *, offset: int = 0, **kwargs: object) -> Array: return xp.diagonal(x, offset=offset, axis1=-2, axis2=-1, **kwargs) -def trace(x: ndarray, /, xp, *, offset: int = 0, dtype=None, **kwargs) -> ndarray: - return xp.asarray(xp.trace(x, offset=offset, dtype=dtype, axis1=-2, axis2=-1, **kwargs)) +def trace( + x: Array, + /, + xp: Namespace, + *, + offset: int = 0, + dtype: DType | None = None, + **kwargs: object, +) -> Array: + return xp.asarray( + xp.trace(x, offset=offset, dtype=dtype, axis1=-2, axis2=-1, **kwargs) + ) __all__ = ['cross', 'matmul', 'outer', 'tensordot', 'EighResult', 'QRResult', 'SlogdetResult', 'SVDResult', 'eigh', 'qr', 'slogdet', 'svd', 'cholesky', 'matrix_rank', 'pinv', 'matrix_norm', 'matrix_transpose', 'svdvals', 'vecdot', 'vector_norm', 'diagonal', 'trace'] + +_all_ignore = ['math', 'normalize_axis_tuple', 'get_xp', 'np', 'isdtype'] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/common/_typing.py b/sklearn/externals/array_api_compat/common/_typing.py index d8acdef7060d9..cd26feeba4dff 100644 --- a/sklearn/externals/array_api_compat/common/_typing.py +++ b/sklearn/externals/array_api_compat/common/_typing.py @@ -1,26 +1,192 @@ from __future__ import annotations -__all__ = [ - "NestedSequence", - "SupportsBufferProtocol", -] - -from types import ModuleType +from collections.abc import Mapping +from types import ModuleType as Namespace from typing import ( - Any, - TypeVar, + TYPE_CHECKING, + Literal, Protocol, + TypeAlias, + TypedDict, + TypeVar, + final, ) +if TYPE_CHECKING: + from _typeshed import Incomplete + + SupportsBufferProtocol: TypeAlias = Incomplete + Array: TypeAlias = Incomplete + Device: TypeAlias = Incomplete + DType: TypeAlias = Incomplete +else: + SupportsBufferProtocol = object + Array = object + Device = object + DType = object + + _T_co = TypeVar("_T_co", covariant=True) + +# These "Just" types are equivalent to the `Just` type from the `optype` library, +# apart from them not being `@runtime_checkable`. +# - docs: https://github.com/jorenham/optype/blob/master/README.md#just +# - code: https://github.com/jorenham/optype/blob/master/optype/_core/_just.py +@final +class JustInt(Protocol): + @property + def __class__(self, /) -> type[int]: ... + @__class__.setter + def __class__(self, value: type[int], /) -> None: ... # pyright: ignore[reportIncompatibleMethodOverride] + + +@final +class JustFloat(Protocol): + @property + def __class__(self, /) -> type[float]: ... + @__class__.setter + def __class__(self, value: type[float], /) -> None: ... # pyright: ignore[reportIncompatibleMethodOverride] + + +@final +class JustComplex(Protocol): + @property + def __class__(self, /) -> type[complex]: ... + @__class__.setter + def __class__(self, value: type[complex], /) -> None: ... # pyright: ignore[reportIncompatibleMethodOverride] + + +# + + class NestedSequence(Protocol[_T_co]): def __getitem__(self, key: int, /) -> _T_co | NestedSequence[_T_co]: ... def __len__(self, /) -> int: ... -SupportsBufferProtocol = Any -Array = Any -Device = Any -DType = Any -Namespace = ModuleType +class SupportsArrayNamespace(Protocol[_T_co]): + def __array_namespace__(self, /, *, api_version: str | None) -> _T_co: ... + + +class HasShape(Protocol[_T_co]): + @property + def shape(self, /) -> _T_co: ... + + +# Return type of `__array_namespace_info__.default_dtypes` +Capabilities = TypedDict( + "Capabilities", + { + "boolean indexing": bool, + "data-dependent shapes": bool, + "max dimensions": int, + }, +) + +# Return type of `__array_namespace_info__.default_dtypes` +DefaultDTypes = TypedDict( + "DefaultDTypes", + { + "real floating": DType, + "complex floating": DType, + "integral": DType, + "indexing": DType, + }, +) + + +_DTypeKind: TypeAlias = Literal[ + "bool", + "signed integer", + "unsigned integer", + "integral", + "real floating", + "complex floating", + "numeric", +] +# Type of the `kind` parameter in `__array_namespace_info__.dtypes` +DTypeKind: TypeAlias = _DTypeKind | tuple[_DTypeKind, ...] + + +# `__array_namespace_info__.dtypes(kind="bool")` +class DTypesBool(TypedDict): + bool: DType + + +# `__array_namespace_info__.dtypes(kind="signed integer")` +class DTypesSigned(TypedDict): + int8: DType + int16: DType + int32: DType + int64: DType + + +# `__array_namespace_info__.dtypes(kind="unsigned integer")` +class DTypesUnsigned(TypedDict): + uint8: DType + uint16: DType + uint32: DType + uint64: DType + + +# `__array_namespace_info__.dtypes(kind="integral")` +class DTypesIntegral(DTypesSigned, DTypesUnsigned): + pass + + +# `__array_namespace_info__.dtypes(kind="real floating")` +class DTypesReal(TypedDict): + float32: DType + float64: DType + + +# `__array_namespace_info__.dtypes(kind="complex floating")` +class DTypesComplex(TypedDict): + complex64: DType + complex128: DType + + +# `__array_namespace_info__.dtypes(kind="numeric")` +class DTypesNumeric(DTypesIntegral, DTypesReal, DTypesComplex): + pass + + +# `__array_namespace_info__.dtypes(kind=None)` (default) +class DTypesAll(DTypesBool, DTypesNumeric): + pass + + +# `__array_namespace_info__.dtypes(kind=?)` (fallback) +DTypesAny: TypeAlias = Mapping[str, DType] + + +__all__ = [ + "Array", + "Capabilities", + "DType", + "DTypeKind", + "DTypesAny", + "DTypesAll", + "DTypesBool", + "DTypesNumeric", + "DTypesIntegral", + "DTypesSigned", + "DTypesUnsigned", + "DTypesReal", + "DTypesComplex", + "DefaultDTypes", + "Device", + "HasShape", + "Namespace", + "JustInt", + "JustFloat", + "JustComplex", + "NestedSequence", + "SupportsArrayNamespace", + "SupportsBufferProtocol", +] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/cupy/__init__.py b/sklearn/externals/array_api_compat/cupy/__init__.py index 59e010582c6ed..9a30f95ddf12c 100644 --- a/sklearn/externals/array_api_compat/cupy/__init__.py +++ b/sklearn/externals/array_api_compat/cupy/__init__.py @@ -8,9 +8,6 @@ # See the comment in the numpy __init__.py __import__(__package__ + '.linalg') - __import__(__package__ + '.fft') -from ..common._helpers import * # noqa: F401,F403 - __array_api_version__ = '2024.12' diff --git a/sklearn/externals/array_api_compat/cupy/_aliases.py b/sklearn/externals/array_api_compat/cupy/_aliases.py index 30d9fe48cb451..90b48f059bafa 100644 --- a/sklearn/externals/array_api_compat/cupy/_aliases.py +++ b/sklearn/externals/array_api_compat/cupy/_aliases.py @@ -1,16 +1,14 @@ from __future__ import annotations +from typing import Optional + import cupy as cp from ..common import _aliases, _helpers +from ..common._typing import NestedSequence, SupportsBufferProtocol from .._internal import get_xp - from ._info import __array_namespace_info__ - -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from typing import Optional, Union - from ._typing import ndarray, Device, Dtype, NestedSequence, SupportsBufferProtocol +from ._typing import Array, Device, DType bool = cp.bool_ @@ -63,26 +61,25 @@ matrix_transpose = get_xp(cp)(_aliases.matrix_transpose) tensordot = get_xp(cp)(_aliases.tensordot) sign = get_xp(cp)(_aliases.sign) +finfo = get_xp(cp)(_aliases.finfo) +iinfo = get_xp(cp)(_aliases.iinfo) -_copy_default = object() # asarray also adds the copy keyword, which is not present in numpy 1.0. def asarray( - obj: Union[ - ndarray, - bool, - int, - float, - NestedSequence[bool | int | float], - SupportsBufferProtocol, - ], + obj: ( + Array + | bool | int | float | complex + | NestedSequence[bool | int | float | complex] + | SupportsBufferProtocol + ), /, *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - copy: Optional[bool] = _copy_default, + copy: Optional[bool] = None, **kwargs, -) -> ndarray: +) -> Array: """ Array API compatibility wrapper for asarray(). @@ -90,35 +87,23 @@ def asarray( specification for more details. """ with cp.cuda.Device(device): - # cupy is like NumPy 1.26 (except without _CopyMode). See the comments - # in asarray in numpy/_aliases.py. - if copy is not _copy_default: - # A future version of CuPy will change the meaning of copy=False - # to mean no-copy. We don't know for certain what version it will - # be yet, so to avoid breaking that version, we use a different - # default value for copy so asarray(obj) with no copy kwarg will - # always do the copy-if-needed behavior. - - # This will still need to be updated to remove the - # NotImplementedError for copy=False, but at least this won't - # break the default or existing behavior. - if copy is None: - copy = False - elif copy is False: - raise NotImplementedError("asarray(copy=False) is not yet supported in cupy") - kwargs['copy'] = copy - - return cp.array(obj, dtype=dtype, **kwargs) + if copy is None: + return cp.asarray(obj, dtype=dtype, **kwargs) + else: + res = cp.array(obj, dtype=dtype, copy=copy, **kwargs) + if not copy and res is not obj: + raise ValueError("Unable to avoid copy while creating an array as requested") + return res def astype( - x: ndarray, - dtype: Dtype, + x: Array, + dtype: DType, /, *, copy: bool = True, device: Optional[Device] = None, -) -> ndarray: +) -> Array: if device is None: return x.astype(dtype=dtype, copy=copy) out = _helpers.to_device(x.astype(dtype=dtype, copy=False), device) @@ -127,10 +112,10 @@ def astype( # cupy.count_nonzero does not have keepdims def count_nonzero( - x: ndarray, + x: Array, axis=None, keepdims=False -) -> ndarray: +) -> Array: result = cp.count_nonzero(x, axis) if keepdims: if axis is None: @@ -139,6 +124,11 @@ def count_nonzero( return result +# take_along_axis: axis defaults to -1 but in cupy (and numpy) axis is a required arg +def take_along_axis(x: Array, indices: Array, /, *, axis: int = -1): + return cp.take_along_axis(x, indices, axis=axis) + + # These functions are completely new here. If the library already has them # (i.e., numpy 2.0), use the library version instead of our wrapper. if hasattr(cp, 'vecdot'): @@ -160,6 +150,7 @@ def count_nonzero( 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_right_shift', - 'bool', 'concat', 'count_nonzero', 'pow', 'sign'] + 'bool', 'concat', 'count_nonzero', 'pow', 'sign', + 'take_along_axis'] _all_ignore = ['cp', 'get_xp'] diff --git a/sklearn/externals/array_api_compat/cupy/_info.py b/sklearn/externals/array_api_compat/cupy/_info.py index 790621e4f7c36..78e48a3358cf5 100644 --- a/sklearn/externals/array_api_compat/cupy/_info.py +++ b/sklearn/externals/array_api_compat/cupy/_info.py @@ -26,6 +26,7 @@ complex128, ) + class __array_namespace_info__: """ Get the array API inspection namespace for CuPy. @@ -49,7 +50,7 @@ class __array_namespace_info__: Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_dtypes() {'real floating': cupy.float64, 'complex floating': cupy.complex128, @@ -94,13 +95,13 @@ def capabilities(self): >>> info = xp.__array_namespace_info__() >>> info.capabilities() {'boolean indexing': True, - 'data-dependent shapes': True} + 'data-dependent shapes': True, + 'max dimensions': 64} """ return { "boolean indexing": True, "data-dependent shapes": True, - # 'max rank' will be part of the 2024.12 standard "max dimensions": 64, } @@ -117,7 +118,7 @@ def default_device(self): Returns ------- - device : str + device : Device The default device used for new CuPy arrays. Examples @@ -126,6 +127,15 @@ def default_device(self): >>> info.default_device() Device(0) + Notes + ----- + This method returns the static default device when CuPy is initialized. + However, the *current* device used by creation functions (``empty`` etc.) + can be changed globally or with a context manager. + + See Also + -------- + https://github.com/data-apis/array-api/issues/835 """ return cuda.Device(0) @@ -312,7 +322,7 @@ def devices(self): Returns ------- - devices : list of str + devices : list[Device] The devices supported by CuPy. See Also diff --git a/sklearn/externals/array_api_compat/cupy/_typing.py b/sklearn/externals/array_api_compat/cupy/_typing.py index f3d9aab67e52f..d8e49ca773dc5 100644 --- a/sklearn/externals/array_api_compat/cupy/_typing.py +++ b/sklearn/externals/array_api_compat/cupy/_typing.py @@ -1,46 +1,31 @@ from __future__ import annotations -__all__ = [ - "ndarray", - "Device", - "Dtype", -] +__all__ = ["Array", "DType", "Device"] +_all_ignore = ["cp"] -import sys -from typing import ( - Union, - TYPE_CHECKING, -) - -from cupy import ( - ndarray, - dtype, - int8, - int16, - int32, - int64, - uint8, - uint16, - uint32, - uint64, - float32, - float64, -) +from typing import TYPE_CHECKING +import cupy as cp +from cupy import ndarray as Array from cupy.cuda.device import Device -if TYPE_CHECKING or sys.version_info >= (3, 9): - Dtype = dtype[Union[ - int8, - int16, - int32, - int64, - uint8, - uint16, - uint32, - uint64, - float32, - float64, - ]] +if TYPE_CHECKING: + # NumPy 1.x on Python 3.10 fails to parse np.dtype[] + DType = cp.dtype[ + cp.intp + | cp.int8 + | cp.int16 + | cp.int32 + | cp.int64 + | cp.uint8 + | cp.uint16 + | cp.uint32 + | cp.uint64 + | cp.float32 + | cp.float64 + | cp.complex64 + | cp.complex128 + | cp.bool_ + ] else: - Dtype = dtype + DType = cp.dtype diff --git a/sklearn/externals/array_api_compat/dask/array/__init__.py b/sklearn/externals/array_api_compat/dask/array/__init__.py index a6e69ad382e4b..1e47b9606b774 100644 --- a/sklearn/externals/array_api_compat/dask/array/__init__.py +++ b/sklearn/externals/array_api_compat/dask/array/__init__.py @@ -1,9 +1,12 @@ -from dask.array import * # noqa: F403 +from typing import Final + +from dask.array import * # noqa: F403 # These imports may overwrite names from the import * above. -from ._aliases import * # noqa: F403 +from ._aliases import * # noqa: F403 -__array_api_version__ = '2024.12' +__array_api_version__: Final = "2024.12" +# See the comment in the numpy __init__.py __import__(__package__ + '.linalg') __import__(__package__ + '.fft') diff --git a/sklearn/externals/array_api_compat/dask/array/_aliases.py b/sklearn/externals/array_api_compat/dask/array/_aliases.py index 80d66281912ca..d43881ab18f1c 100644 --- a/sklearn/externals/array_api_compat/dask/array/_aliases.py +++ b/sklearn/externals/array_api_compat/dask/array/_aliases.py @@ -1,49 +1,47 @@ -from __future__ import annotations - -from typing import Callable +# pyright: reportPrivateUsage=false +# pyright: reportUnknownArgumentType=false +# pyright: reportUnknownMemberType=false +# pyright: reportUnknownVariableType=false -from ...common import _aliases, array_namespace +from __future__ import annotations -from ..._internal import get_xp +from builtins import bool as py_bool +from collections.abc import Callable +from typing import TYPE_CHECKING, Any -from ._info import __array_namespace_info__ +if TYPE_CHECKING: + from typing_extensions import TypeIs +import dask.array as da import numpy as np +from numpy import bool_ as bool from numpy import ( - # Dtypes - iinfo, - finfo, - bool_ as bool, + can_cast, + complex64, + complex128, float32, float64, int8, int16, int32, int64, + result_type, uint8, uint16, uint32, uint64, - complex64, - complex128, - can_cast, - result_type, ) -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from typing import Optional, Union - - from ...common._typing import ( - Device, - Dtype, - Array, - NestedSequence, - SupportsBufferProtocol, - ) - -import dask.array as da +from ..._internal import get_xp +from ...common import _aliases, _helpers, array_namespace +from ...common._typing import ( + Array, + Device, + DType, + NestedSequence, + SupportsBufferProtocol, +) +from ._info import __array_namespace_info__ isdtype = get_xp(np)(_aliases.isdtype) unstack = get_xp(da)(_aliases.unstack) @@ -52,11 +50,11 @@ # da.astype doesn't respect copy=True def astype( x: Array, - dtype: Dtype, + dtype: DType, /, *, - copy: bool = True, - device: Optional[Device] = None, + copy: py_bool = True, + device: Device | None = None, ) -> Array: """ Array API compatibility wrapper for astype(). @@ -65,6 +63,7 @@ def astype( specification for more details. """ # TODO: respect device keyword? + _helpers._check_device(da, device) if not copy and dtype == x.dtype: return x @@ -79,14 +78,14 @@ def astype( # not pass stop/step as keyword arguments, which will cause # an error with dask def arange( - start: Union[int, float], + start: float, /, - stop: Optional[Union[int, float]] = None, - step: Union[int, float] = 1, + stop: float | None = None, + step: float = 1, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - **kwargs, + dtype: DType | None = None, + device: Device | None = None, + **kwargs: object, ) -> Array: """ Array API compatibility wrapper for arange(). @@ -95,8 +94,9 @@ def arange( specification for more details. """ # TODO: respect device keyword? + _helpers._check_device(da, device) - args = [start] + args: list[Any] = [start] if stop is not None: args.append(stop) else: @@ -140,24 +140,19 @@ def arange( matmul = get_xp(np)(_aliases.matmul) tensordot = get_xp(np)(_aliases.tensordot) sign = get_xp(np)(_aliases.sign) +finfo = get_xp(np)(_aliases.finfo) +iinfo = get_xp(np)(_aliases.iinfo) # asarray also adds the copy keyword, which is not present in numpy 1.0. def asarray( - obj: Union[ - Array, - bool, - int, - float, - NestedSequence[bool | int | float], - SupportsBufferProtocol, - ], + obj: complex | NestedSequence[complex] | Array | SupportsBufferProtocol, /, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - copy: Optional[Union[bool, np._CopyMode]] = None, - **kwargs, + dtype: DType | None = None, + device: Device | None = None, + copy: py_bool | None = None, + **kwargs: object, ) -> Array: """ Array API compatibility wrapper for asarray(). @@ -166,16 +161,17 @@ def asarray( specification for more details. """ # TODO: respect device keyword? + _helpers._check_device(da, device) if isinstance(obj, da.Array): if dtype is not None and dtype != obj.dtype: if copy is False: raise ValueError("Unable to avoid copy when changing dtype") obj = obj.astype(dtype) - return obj.copy() if copy else obj + return obj.copy() if copy else obj # pyright: ignore[reportAttributeAccessIssue] if copy is False: - raise NotImplementedError( + raise ValueError( "Unable to avoid copy when converting a non-dask object to dask" ) @@ -185,22 +181,21 @@ def asarray( return da.from_array(obj) -from dask.array import ( - # Element wise aliases - arccos as acos, - arccosh as acosh, - arcsin as asin, - arcsinh as asinh, - arctan as atan, - arctan2 as atan2, - arctanh as atanh, - left_shift as bitwise_left_shift, - right_shift as bitwise_right_shift, - invert as bitwise_invert, - power as pow, - # Other - concatenate as concat, -) +# Element wise aliases +from dask.array import arccos as acos +from dask.array import arccosh as acosh +from dask.array import arcsin as asin +from dask.array import arcsinh as asinh +from dask.array import arctan as atan +from dask.array import arctan2 as atan2 +from dask.array import arctanh as atanh + +# Other +from dask.array import concatenate as concat +from dask.array import invert as bitwise_invert +from dask.array import left_shift as bitwise_left_shift +from dask.array import power as pow +from dask.array import right_shift as bitwise_right_shift # dask.array.clip does not work unless all three arguments are provided. @@ -210,8 +205,8 @@ def asarray( def clip( x: Array, /, - min: Optional[Union[int, float, Array]] = None, - max: Optional[Union[int, float, Array]] = None, + min: float | Array | None = None, + max: float | Array | None = None, ) -> Array: """ Array API compatibility wrapper for clip(). @@ -220,8 +215,8 @@ def clip( specification for more details. """ - def _isscalar(a): - return isinstance(a, (int, float, type(None))) + def _isscalar(a: float | Array | None, /) -> TypeIs[float | None]: + return a is None or isinstance(a, (int, float)) min_shape = () if _isscalar(min) else min.shape max_shape = () if _isscalar(max) else max.shape @@ -274,7 +269,12 @@ def _ensure_single_chunk(x: Array, axis: int) -> tuple[Array, Callable[[Array], def sort( - x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True + x: Array, + /, + *, + axis: int = -1, + descending: py_bool = False, + stable: py_bool = True, ) -> Array: """ Array API compatibility layer around the lack of sort() in Dask. @@ -304,7 +304,12 @@ def sort( def argsort( - x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True + x: Array, + /, + *, + axis: int = -1, + descending: py_bool = False, + stable: py_bool = True, ) -> Array: """ Array API compatibility layer around the lack of argsort() in Dask. @@ -338,26 +343,34 @@ def argsort( # dask.array.count_nonzero does not have keepdims def count_nonzero( x: Array, - axis=None, - keepdims=False + axis: int | None = None, + keepdims: py_bool = False, ) -> Array: - result = da.count_nonzero(x, axis) - if keepdims: - if axis is None: - return da.reshape(result, [1]*x.ndim) - return da.expand_dims(result, axis) - return result - - - -__all__ = _aliases.__all__ + [ - '__array_namespace_info__', 'asarray', 'astype', 'acos', - 'acosh', 'asin', 'asinh', 'atan', 'atan2', - 'atanh', 'bitwise_left_shift', 'bitwise_invert', - 'bitwise_right_shift', 'concat', 'pow', 'iinfo', 'finfo', 'can_cast', - 'result_type', 'bool', 'float32', 'float64', 'int8', 'int16', 'int32', 'int64', - 'uint8', 'uint16', 'uint32', 'uint64', - 'complex64', 'complex128', 'iinfo', 'finfo', - 'can_cast', 'count_nonzero', 'result_type'] - -_all_ignore = ["Callable", "array_namespace", "get_xp", "da", "np"] + result = da.count_nonzero(x, axis) + if keepdims: + if axis is None: + return da.reshape(result, [1] * x.ndim) + return da.expand_dims(result, axis) + return result + + +__all__ = [ + "__array_namespace_info__", + "count_nonzero", + "bool", + "int8", "int16", "int32", "int64", + "uint8", "uint16", "uint32", "uint64", + "float32", "float64", + "complex64", "complex128", + "asarray", "astype", "can_cast", "result_type", + "pow", + "concat", + "acos", "acosh", "asin", "asinh", "atan", "atan2", "atanh", + "bitwise_left_shift", "bitwise_right_shift", "bitwise_invert", +] # fmt: skip +__all__ += _aliases.__all__ +_all_ignore = ["array_namespace", "get_xp", "da", "np"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/dask/array/_info.py b/sklearn/externals/array_api_compat/dask/array/_info.py index e15a69f4629ab..9e4d736f99657 100644 --- a/sklearn/externals/array_api_compat/dask/array/_info.py +++ b/sklearn/externals/array_api_compat/dask/array/_info.py @@ -7,25 +7,51 @@ more details. """ + +# pyright: reportPrivateUsage=false + +from __future__ import annotations + +from typing import Literal as L +from typing import TypeAlias, overload + +from numpy import bool_ as bool from numpy import ( + complex64, + complex128, dtype, - bool_ as bool, - intp, + float32, + float64, int8, int16, int32, int64, + intp, uint8, uint16, uint32, uint64, - float32, - float64, - complex64, - complex128, ) -from ...common._helpers import _DASK_DEVICE +from ...common._helpers import _DASK_DEVICE, _dask_device +from ...common._typing import ( + Capabilities, + DefaultDTypes, + DType, + DTypeKind, + DTypesAll, + DTypesAny, + DTypesBool, + DTypesComplex, + DTypesIntegral, + DTypesNumeric, + DTypesReal, + DTypesSigned, + DTypesUnsigned, +) + +_Device: TypeAlias = L["cpu"] | _dask_device + class __array_namespace_info__: """ @@ -50,7 +76,7 @@ class __array_namespace_info__: Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_dtypes() {'real floating': dask.float64, 'complex floating': dask.complex128, @@ -59,20 +85,31 @@ class __array_namespace_info__: """ - __module__ = 'dask.array' + __module__ = "dask.array" - def capabilities(self): + def capabilities(self) -> Capabilities: """ Return a dictionary of array API library capabilities. The resulting dictionary has the following keys: - **"boolean indexing"**: boolean indicating whether an array library - supports boolean indexing. Always ``False`` for Dask. + supports boolean indexing. + + Dask support boolean indexing as long as both the index + and the indexed arrays have known shapes. + Note however that the output .shape and .size properties + will contain a non-compliant math.nan instead of None. - **"data-dependent shapes"**: boolean indicating whether an array - library supports data-dependent output shapes. Always ``False`` for - Dask. + library supports data-dependent output shapes. + + Dask implements unique_values et.al. + Note however that the output .shape and .size properties + will contain a non-compliant math.nan instead of None. + + - **"max dimensions"**: integer indicating the maximum number of + dimensions supported by the array library. See https://data-apis.org/array-api/latest/API_specification/generated/array_api.info.capabilities.html @@ -92,20 +129,20 @@ def capabilities(self): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.capabilities() {'boolean indexing': True, - 'data-dependent shapes': True} + 'data-dependent shapes': True, + 'max dimensions': 64} """ return { - "boolean indexing": False, - "data-dependent shapes": False, - # 'max rank' will be part of the 2024.12 standard + "boolean indexing": True, + "data-dependent shapes": True, "max dimensions": 64, } - def default_device(self): + def default_device(self) -> L["cpu"]: """ The default device used for new Dask arrays. @@ -120,19 +157,19 @@ def default_device(self): Returns ------- - device : str + device : Device The default device used for new Dask arrays. Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_device() 'cpu' """ return "cpu" - def default_dtypes(self, *, device=None): + def default_dtypes(self, /, *, device: _Device | None = None) -> DefaultDTypes: """ The default data types used for new Dask arrays. @@ -163,7 +200,7 @@ def default_dtypes(self, *, device=None): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_dtypes() {'real floating': dask.float64, 'complex floating': dask.complex128, @@ -173,8 +210,8 @@ def default_dtypes(self, *, device=None): """ if device not in ["cpu", _DASK_DEVICE, None]: raise ValueError( - 'Device not understood. Only "cpu" or _DASK_DEVICE is allowed, but received:' - f' {device}' + f'Device not understood. Only "cpu" or _DASK_DEVICE is allowed, ' + f"but received: {device!r}" ) return { "real floating": dtype(float64), @@ -183,7 +220,41 @@ def default_dtypes(self, *, device=None): "indexing": dtype(intp), } - def dtypes(self, *, device=None, kind=None): + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: None = None + ) -> DTypesAll: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["bool"] + ) -> DTypesBool: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["signed integer"] + ) -> DTypesSigned: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["unsigned integer"] + ) -> DTypesUnsigned: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["integral"] + ) -> DTypesIntegral: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["real floating"] + ) -> DTypesReal: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["complex floating"] + ) -> DTypesComplex: ... + @overload + def dtypes( + self, /, *, device: _Device | None = None, kind: L["numeric"] + ) -> DTypesNumeric: ... + def dtypes( + self, /, *, device: _Device | None = None, kind: DTypeKind | None = None + ) -> DTypesAny: """ The array API data types supported by Dask. @@ -229,7 +300,7 @@ def dtypes(self, *, device=None, kind=None): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.dtypes(kind='signed integer') {'int8': dask.int8, 'int16': dask.int16, @@ -240,7 +311,7 @@ def dtypes(self, *, device=None, kind=None): if device not in ["cpu", _DASK_DEVICE, None]: raise ValueError( 'Device not understood. Only "cpu" or _DASK_DEVICE is allowed, but received:' - f' {device}' + f" {device}" ) if kind is None: return { @@ -310,14 +381,14 @@ def dtypes(self, *, device=None, kind=None): "complex64": dtype(complex64), "complex128": dtype(complex128), } - if isinstance(kind, tuple): - res = {} + if isinstance(kind, tuple): # type: ignore[reportUnnecessaryIsinstanceCall] + res: dict[str, DType] = {} for k in kind: res.update(self.dtypes(kind=k)) return res raise ValueError(f"unsupported kind: {kind!r}") - def devices(self): + def devices(self) -> list[_Device]: """ The devices supported by Dask. @@ -325,7 +396,7 @@ def devices(self): Returns ------- - devices : list of str + devices : list[Device] The devices supported by Dask. See Also @@ -337,7 +408,7 @@ def devices(self): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.devices() ['cpu', DASK_DEVICE] diff --git a/sklearn/externals/array_api_compat/dask/array/fft.py b/sklearn/externals/array_api_compat/dask/array/fft.py index aebd86f7b201d..3f40dffe7abd5 100644 --- a/sklearn/externals/array_api_compat/dask/array/fft.py +++ b/sklearn/externals/array_api_compat/dask/array/fft.py @@ -4,9 +4,10 @@ # from dask.array.fft import __all__ as linalg_all _n = {} exec('from dask.array.fft import *', _n) -del _n['__builtins__'] +for k in ("__builtins__", "Sequence", "annotations", "warnings"): + _n.pop(k, None) fft_all = list(_n) -del _n +del _n, k from ...common import _fft from ..._internal import get_xp @@ -16,9 +17,5 @@ fftfreq = get_xp(da)(_fft.fftfreq) rfftfreq = get_xp(da)(_fft.rfftfreq) -__all__ = [elem for elem in fft_all if elem != "annotations"] + ["fftfreq", "rfftfreq"] - -del get_xp -del da -del fft_all -del _fft +__all__ = fft_all + ["fftfreq", "rfftfreq"] +_all_ignore = ["da", "fft_all", "get_xp", "warnings"] diff --git a/sklearn/externals/array_api_compat/dask/array/linalg.py b/sklearn/externals/array_api_compat/dask/array/linalg.py index 49c26d8b819f8..0825386ed5dc3 100644 --- a/sklearn/externals/array_api_compat/dask/array/linalg.py +++ b/sklearn/externals/array_api_compat/dask/array/linalg.py @@ -1,33 +1,29 @@ from __future__ import annotations -from ...common import _linalg -from ..._internal import get_xp +from typing import Literal -# Exports -from dask.array.linalg import * # noqa: F403 -from dask.array import outer +import dask.array as da -# These functions are in both the main and linalg namespaces -from dask.array import matmul, tensordot -from ._aliases import matrix_transpose, vecdot +# The `matmul` and `tensordot` functions are in both the main and linalg namespaces +from dask.array import matmul, outer, tensordot -import dask.array as da +# Exports +from dask.array.linalg import * # noqa: F403 -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ...common._typing import Array - from typing import Literal +from ..._internal import get_xp +from ...common import _linalg +from ...common._typing import Array as _Array +from ._aliases import matrix_transpose, vecdot # dask.array.linalg doesn't have __all__. If it is added, replace this with # # from dask.array.linalg import __all__ as linalg_all _n = {} exec('from dask.array.linalg import *', _n) -del _n['__builtins__'] -if 'annotations' in _n: - del _n['annotations'] +for k in ('__builtins__', 'annotations', 'operator', 'warnings', 'Array'): + _n.pop(k, None) linalg_all = list(_n) -del _n +del _n, k EighResult = _linalg.EighResult QRResult = _linalg.QRResult @@ -37,8 +33,11 @@ # supports the mode keyword on QR # https://github.com/dask/dask/issues/10388 #qr = get_xp(da)(_linalg.qr) -def qr(x: Array, mode: Literal['reduced', 'complete'] = 'reduced', - **kwargs) -> QRResult: +def qr( + x: _Array, + mode: Literal["reduced", "complete"] = "reduced", + **kwargs: object, +) -> QRResult: if mode != "reduced": raise ValueError("dask arrays only support using mode='reduced'") return QRResult(*da.linalg.qr(x, **kwargs)) @@ -51,12 +50,12 @@ def qr(x: Array, mode: Literal['reduced', 'complete'] = 'reduced', # Wrap the svd functions to not pass full_matrices to dask # when full_matrices=False (as that is the default behavior for dask), # and dask doesn't have the full_matrices keyword -def svd(x: Array, full_matrices: bool = True, **kwargs) -> SVDResult: +def svd(x: _Array, full_matrices: bool = True, **kwargs) -> SVDResult: if full_matrices: raise ValueError("full_matrics=True is not supported by dask.") return da.linalg.svd(x, coerce_signs=False, **kwargs) -def svdvals(x: Array) -> Array: +def svdvals(x: _Array) -> _Array: # TODO: can't avoid computing U or V for dask _, s, _ = svd(x) return s @@ -70,4 +69,4 @@ def svdvals(x: Array) -> Array: "cholesky", "matrix_rank", "matrix_norm", "svdvals", "vector_norm", "diagonal"] -_all_ignore = ['get_xp', 'da', 'linalg_all'] +_all_ignore = ['get_xp', 'da', 'linalg_all', 'warnings'] diff --git a/sklearn/externals/array_api_compat/numpy/__init__.py b/sklearn/externals/array_api_compat/numpy/__init__.py index 02c55d28a01e8..3e138f53db006 100644 --- a/sklearn/externals/array_api_compat/numpy/__init__.py +++ b/sklearn/externals/array_api_compat/numpy/__init__.py @@ -1,10 +1,16 @@ -from numpy import * # noqa: F403 +# ruff: noqa: PLC0414 +from typing import Final + +from numpy import * # noqa: F403 # pyright: ignore[reportWildcardImportFromLibrary] # from numpy import * doesn't overwrite these builtin names -from numpy import abs, max, min, round # noqa: F401 +from numpy import abs as abs +from numpy import max as max +from numpy import min as min +from numpy import round as round # These imports may overwrite names from the import * above. -from ._aliases import * # noqa: F403 +from ._aliases import * # noqa: F403 # Don't know why, but we have to do an absolute import to import linalg. If we # instead do @@ -13,18 +19,10 @@ # # It doesn't overwrite np.linalg from above. The import is generated # dynamically so that the library can be vendored. -__import__(__package__ + '.linalg') - -__import__(__package__ + '.fft') - -from .linalg import matrix_transpose, vecdot # noqa: F401 +__import__(__package__ + ".linalg") -from ..common._helpers import * # noqa: F403 +__import__(__package__ + ".fft") -try: - # Used in asarray(). Not present in older versions. - from numpy import _CopyMode # noqa: F401 -except ImportError: - pass +from .linalg import matrix_transpose, vecdot # type: ignore[no-redef] # noqa: F401 -__array_api_version__ = '2024.12' +__array_api_version__: Final = "2024.12" diff --git a/sklearn/externals/array_api_compat/numpy/_aliases.py b/sklearn/externals/array_api_compat/numpy/_aliases.py index a47f712146e4a..a1aee5c0df796 100644 --- a/sklearn/externals/array_api_compat/numpy/_aliases.py +++ b/sklearn/externals/array_api_compat/numpy/_aliases.py @@ -1,17 +1,24 @@ +# pyright: reportPrivateUsage=false from __future__ import annotations -from ..common import _aliases +from builtins import bool as py_bool +from typing import TYPE_CHECKING, Any, Literal, TypeAlias, cast -from .._internal import get_xp +import numpy as np +from .._internal import get_xp +from ..common import _aliases, _helpers +from ..common._typing import NestedSequence, SupportsBufferProtocol from ._info import __array_namespace_info__ +from ._typing import Array, Device, DType -from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Optional, Union - from ._typing import ndarray, Device, Dtype, NestedSequence, SupportsBufferProtocol + from typing_extensions import Buffer, TypeIs + +# The values of the `_CopyMode` enum can be either `False`, `True`, or `2`: +# https://github.com/numpy/numpy/blob/5a8a6a79d9c2fff8f07dcab5d41e14f8508d673f/numpy/_globals.pyi#L7-L10 +_Copy: TypeAlias = py_bool | Literal[2] | np._CopyMode -import numpy as np bool = np.bool_ # Basic renames @@ -63,104 +70,121 @@ matrix_transpose = get_xp(np)(_aliases.matrix_transpose) tensordot = get_xp(np)(_aliases.tensordot) sign = get_xp(np)(_aliases.sign) +finfo = get_xp(np)(_aliases.finfo) +iinfo = get_xp(np)(_aliases.iinfo) -def _supports_buffer_protocol(obj): + +def _supports_buffer_protocol(obj: object) -> TypeIs[Buffer]: # pyright: ignore[reportUnusedFunction] try: - memoryview(obj) + memoryview(obj) # pyright: ignore[reportArgumentType] except TypeError: return False return True + # asarray also adds the copy keyword, which is not present in numpy 1.0. # asarray() is different enough between numpy, cupy, and dask, the logic # complicated enough that it's easier to define it separately for each module # rather than trying to combine everything into one function in common/ def asarray( - obj: Union[ - ndarray, - bool, - int, - float, - NestedSequence[bool | int | float], - SupportsBufferProtocol, - ], + obj: Array | complex | NestedSequence[complex] | SupportsBufferProtocol, /, *, - dtype: Optional[Dtype] = None, - device: Optional[Device] = None, - copy: "Optional[Union[bool, np._CopyMode]]" = None, - **kwargs, -) -> ndarray: + dtype: DType | None = None, + device: Device | None = None, + copy: _Copy | None = None, + **kwargs: Any, +) -> Array: """ Array API compatibility wrapper for asarray(). See the corresponding documentation in the array library and/or the array API specification for more details. """ - if device not in ["cpu", None]: - raise ValueError(f"Unsupported device for NumPy: {device!r}") + _helpers._check_device(np, device) - if hasattr(np, '_CopyMode'): - if copy is None: - copy = np._CopyMode.IF_NEEDED - elif copy is False: - copy = np._CopyMode.NEVER - elif copy is True: - copy = np._CopyMode.ALWAYS - else: - # Not present in older NumPys. In this case, we cannot really support - # copy=False. - if copy is False: - raise NotImplementedError("asarray(copy=False) requires a newer version of NumPy.") + if copy is None: + copy = np._CopyMode.IF_NEEDED + elif copy is False: + copy = np._CopyMode.NEVER + elif copy is True: + copy = np._CopyMode.ALWAYS - return np.array(obj, copy=copy, dtype=dtype, **kwargs) + return np.array(obj, copy=copy, dtype=dtype, **kwargs) # pyright: ignore def astype( - x: ndarray, - dtype: Dtype, + x: Array, + dtype: DType, /, *, - copy: bool = True, - device: Optional[Device] = None, -) -> ndarray: + copy: py_bool = True, + device: Device | None = None, +) -> Array: + _helpers._check_device(np, device) return x.astype(dtype=dtype, copy=copy) # count_nonzero returns a python int for axis=None and keepdims=False # https://github.com/numpy/numpy/issues/17562 def count_nonzero( - x : ndarray, - axis=None, - keepdims=False -) -> ndarray: - result = np.count_nonzero(x, axis=axis, keepdims=keepdims) + x: Array, + axis: int | tuple[int, ...] | None = None, + keepdims: py_bool = False, +) -> Array: + # NOTE: this is currently incorrectly typed in numpy, but will be fixed in + # numpy 2.2.5 and 2.3.0: https://github.com/numpy/numpy/pull/28750 + result = cast("Any", np.count_nonzero(x, axis=axis, keepdims=keepdims)) # pyright: ignore[reportArgumentType, reportCallIssue] if axis is None and not keepdims: return np.asarray(result) return result +# take_along_axis: axis defaults to -1 but in numpy axis is a required arg +def take_along_axis(x: Array, indices: Array, /, *, axis: int = -1): + return np.take_along_axis(x, indices, axis=axis) + + # These functions are completely new here. If the library already has them # (i.e., numpy 2.0), use the library version instead of our wrapper. -if hasattr(np, 'vecdot'): +if hasattr(np, "vecdot"): vecdot = np.vecdot else: vecdot = get_xp(np)(_aliases.vecdot) -if hasattr(np, 'isdtype'): +if hasattr(np, "isdtype"): isdtype = np.isdtype else: isdtype = get_xp(np)(_aliases.isdtype) -if hasattr(np, 'unstack'): +if hasattr(np, "unstack"): unstack = np.unstack else: unstack = get_xp(np)(_aliases.unstack) -__all__ = _aliases.__all__ + ['__array_namespace_info__', 'asarray', 'astype', - 'acos', 'acosh', 'asin', 'asinh', 'atan', - 'atan2', 'atanh', 'bitwise_left_shift', - 'bitwise_invert', 'bitwise_right_shift', - 'bool', 'concat', 'count_nonzero', 'pow'] - -_all_ignore = ['np', 'get_xp'] +__all__ = [ + "__array_namespace_info__", + "asarray", + "astype", + "acos", + "acosh", + "asin", + "asinh", + "atan", + "atan2", + "atanh", + "bitwise_left_shift", + "bitwise_invert", + "bitwise_right_shift", + "bool", + "concat", + "count_nonzero", + "pow", + "take_along_axis" +] +__all__ += _aliases.__all__ +_all_ignore = ["np", "get_xp"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/numpy/_info.py b/sklearn/externals/array_api_compat/numpy/_info.py index e706d1188bf14..f307f62c5d5d5 100644 --- a/sklearn/externals/array_api_compat/numpy/_info.py +++ b/sklearn/externals/array_api_compat/numpy/_info.py @@ -7,24 +7,28 @@ more details. """ +from __future__ import annotations + +from numpy import bool_ as bool from numpy import ( + complex64, + complex128, dtype, - bool_ as bool, - intp, + float32, + float64, int8, int16, int32, int64, + intp, uint8, uint16, uint32, uint64, - float32, - float64, - complex64, - complex128, ) +from ._typing import Device, DType + class __array_namespace_info__: """ @@ -94,13 +98,13 @@ def capabilities(self): >>> info = np.__array_namespace_info__() >>> info.capabilities() {'boolean indexing': True, - 'data-dependent shapes': True} + 'data-dependent shapes': True, + 'max dimensions': 64} """ return { "boolean indexing": True, "data-dependent shapes": True, - # 'max rank' will be part of the 2024.12 standard "max dimensions": 64, } @@ -119,7 +123,7 @@ def default_device(self): Returns ------- - device : str + device : Device The default device used for new NumPy arrays. Examples @@ -131,7 +135,11 @@ def default_device(self): """ return "cpu" - def default_dtypes(self, *, device=None): + def default_dtypes( + self, + *, + device: Device | None = None, + ) -> dict[str, dtype[intp | float64 | complex128]]: """ The default data types used for new NumPy arrays. @@ -183,7 +191,12 @@ def default_dtypes(self, *, device=None): "indexing": dtype(intp), } - def dtypes(self, *, device=None, kind=None): + def dtypes( + self, + *, + device: Device | None = None, + kind: str | tuple[str, ...] | None = None, + ) -> dict[str, DType]: """ The array API data types supported by NumPy. @@ -260,7 +273,7 @@ def dtypes(self, *, device=None, kind=None): "complex128": dtype(complex128), } if kind == "bool": - return {"bool": bool} + return {"bool": dtype(bool)} if kind == "signed integer": return { "int8": dtype(int8), @@ -312,13 +325,13 @@ def dtypes(self, *, device=None, kind=None): "complex128": dtype(complex128), } if isinstance(kind, tuple): - res = {} + res: dict[str, DType] = {} for k in kind: res.update(self.dtypes(kind=k)) return res raise ValueError(f"unsupported kind: {kind!r}") - def devices(self): + def devices(self) -> list[Device]: """ The devices supported by NumPy. @@ -326,7 +339,7 @@ def devices(self): Returns ------- - devices : list of str + devices : list[Device] The devices supported by NumPy. See Also @@ -344,3 +357,10 @@ def devices(self): """ return ["cpu"] + + +__all__ = ["__array_namespace_info__"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/numpy/_typing.py b/sklearn/externals/array_api_compat/numpy/_typing.py index c5ebb5abb9875..e771c788bbcab 100644 --- a/sklearn/externals/array_api_compat/numpy/_typing.py +++ b/sklearn/externals/array_api_compat/numpy/_typing.py @@ -1,46 +1,30 @@ from __future__ import annotations -__all__ = [ - "ndarray", - "Device", - "Dtype", -] - -import sys -from typing import ( - Literal, - Union, - TYPE_CHECKING, -) - -from numpy import ( - ndarray, - dtype, - int8, - int16, - int32, - int64, - uint8, - uint16, - uint32, - uint64, - float32, - float64, -) - -Device = Literal["cpu"] -if TYPE_CHECKING or sys.version_info >= (3, 9): - Dtype = dtype[Union[ - int8, - int16, - int32, - int64, - uint8, - uint16, - uint32, - uint64, - float32, - float64, - ]] +from typing import TYPE_CHECKING, Any, Literal, TypeAlias + +import numpy as np + +Device: TypeAlias = Literal["cpu"] + +if TYPE_CHECKING: + + # NumPy 1.x on Python 3.10 fails to parse np.dtype[] + DType: TypeAlias = np.dtype[ + np.bool_ + | np.integer[Any] + | np.float32 + | np.float64 + | np.complex64 + | np.complex128 + ] + Array: TypeAlias = np.ndarray[Any, DType] else: - Dtype = dtype + DType: TypeAlias = np.dtype + Array: TypeAlias = np.ndarray + +__all__ = ["Array", "DType", "Device"] +_all_ignore = ["np"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/numpy/fft.py b/sklearn/externals/array_api_compat/numpy/fft.py index 286675946e0fb..06875f00b4312 100644 --- a/sklearn/externals/array_api_compat/numpy/fft.py +++ b/sklearn/externals/array_api_compat/numpy/fft.py @@ -1,10 +1,9 @@ -from numpy.fft import * # noqa: F403 +import numpy as np from numpy.fft import __all__ as fft_all +from numpy.fft import fft2, ifft2, irfft2, rfft2 -from ..common import _fft from .._internal import get_xp - -import numpy as np +from ..common import _fft fft = get_xp(np)(_fft.fft) ifft = get_xp(np)(_fft.ifft) @@ -21,7 +20,14 @@ fftshift = get_xp(np)(_fft.fftshift) ifftshift = get_xp(np)(_fft.ifftshift) -__all__ = fft_all + _fft.__all__ + +__all__ = ["rfft2", "irfft2", "fft2", "ifft2"] +__all__ += _fft.__all__ + + +def __dir__() -> list[str]: + return __all__ + del get_xp del np diff --git a/sklearn/externals/array_api_compat/numpy/linalg.py b/sklearn/externals/array_api_compat/numpy/linalg.py index 8f01593bd0ae6..2d3e731da3fc0 100644 --- a/sklearn/externals/array_api_compat/numpy/linalg.py +++ b/sklearn/externals/array_api_compat/numpy/linalg.py @@ -1,14 +1,35 @@ -from numpy.linalg import * # noqa: F403 -from numpy.linalg import __all__ as linalg_all -import numpy as _np +# pyright: reportAttributeAccessIssue=false +# pyright: reportUnknownArgumentType=false +# pyright: reportUnknownMemberType=false +# pyright: reportUnknownVariableType=false + +from __future__ import annotations + +import numpy as np + +# intersection of `np.linalg.__all__` on numpy 1.22 and 2.2, minus `_linalg.__all__` +from numpy.linalg import ( + LinAlgError, + cond, + det, + eig, + eigvals, + eigvalsh, + inv, + lstsq, + matrix_power, + multi_dot, + norm, + tensorinv, + tensorsolve, +) -from ..common import _linalg from .._internal import get_xp +from ..common import _linalg # These functions are in both the main and linalg namespaces -from ._aliases import matmul, matrix_transpose, tensordot, vecdot # noqa: F401 - -import numpy as np +from ._aliases import matmul, matrix_transpose, tensordot, vecdot # noqa: F401 +from ._typing import Array cross = get_xp(np)(_linalg.cross) outer = get_xp(np)(_linalg.outer) @@ -38,19 +59,28 @@ # To workaround this, the below is the code from np.linalg.solve except # only calling solve1 in the exactly 1D case. + # This code is here instead of in common because it is numpy specific. Also # note that CuPy's solve() does not currently support broadcasting (see # https://github.com/cupy/cupy/blob/main/cupy/cublas.py#L43). -def solve(x1: _np.ndarray, x2: _np.ndarray, /) -> _np.ndarray: +def solve(x1: Array, x2: Array, /) -> Array: try: from numpy.linalg._linalg import ( - _makearray, _assert_stacked_2d, _assert_stacked_square, - _commonType, isComplexType, _raise_linalgerror_singular + _assert_stacked_2d, + _assert_stacked_square, + _commonType, + _makearray, + _raise_linalgerror_singular, + isComplexType, ) except ImportError: from numpy.linalg.linalg import ( - _makearray, _assert_stacked_2d, _assert_stacked_square, - _commonType, isComplexType, _raise_linalgerror_singular + _assert_stacked_2d, + _assert_stacked_square, + _commonType, + _makearray, + _raise_linalgerror_singular, + isComplexType, ) from numpy.linalg import _umath_linalg @@ -61,6 +91,7 @@ def solve(x1: _np.ndarray, x2: _np.ndarray, /) -> _np.ndarray: t, result_t = _commonType(x1, x2) # This part is different from np.linalg.solve + gufunc: np.ufunc if x2.ndim == 1: gufunc = _umath_linalg.solve1 else: @@ -68,23 +99,45 @@ def solve(x1: _np.ndarray, x2: _np.ndarray, /) -> _np.ndarray: # This does nothing currently but is left in because it will be relevant # when complex dtype support is added to the spec in 2022. - signature = 'DD->D' if isComplexType(t) else 'dd->d' - with _np.errstate(call=_raise_linalgerror_singular, invalid='call', - over='ignore', divide='ignore', under='ignore'): - r = gufunc(x1, x2, signature=signature) + signature = "DD->D" if isComplexType(t) else "dd->d" + with np.errstate( + call=_raise_linalgerror_singular, + invalid="call", + over="ignore", + divide="ignore", + under="ignore", + ): + r: Array = gufunc(x1, x2, signature=signature) return wrap(r.astype(result_t, copy=False)) + # These functions are completely new here. If the library already has them # (i.e., numpy 2.0), use the library version instead of our wrapper. -if hasattr(np.linalg, 'vector_norm'): +if hasattr(np.linalg, "vector_norm"): vector_norm = np.linalg.vector_norm else: vector_norm = get_xp(np)(_linalg.vector_norm) -__all__ = linalg_all + _linalg.__all__ + ['solve'] -del get_xp -del np -del linalg_all -del _linalg +__all__ = [ + "LinAlgError", + "cond", + "det", + "eig", + "eigvals", + "eigvalsh", + "inv", + "lstsq", + "matrix_power", + "multi_dot", + "norm", + "tensorinv", + "tensorsolve", +] +__all__ += _linalg.__all__ +__all__ += ["solve", "vector_norm"] + + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/externals/array_api_compat/py.typed b/sklearn/externals/array_api_compat/py.typed new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/sklearn/externals/array_api_compat/torch/__init__.py b/sklearn/externals/array_api_compat/torch/__init__.py index a985986e649c3..69fd19ce83a56 100644 --- a/sklearn/externals/array_api_compat/torch/__init__.py +++ b/sklearn/externals/array_api_compat/torch/__init__.py @@ -9,16 +9,14 @@ or 'cpu' in n or 'backward' in n): continue - exec(n + ' = torch.' + n) + exec(f"{n} = torch.{n}") +del n # These imports may overwrite names from the import * above. from ._aliases import * # noqa: F403 # See the comment in the numpy __init__.py __import__(__package__ + '.linalg') - __import__(__package__ + '.fft') -from ..common._helpers import * # noqa: F403 - __array_api_version__ = '2024.12' diff --git a/sklearn/externals/array_api_compat/torch/_aliases.py b/sklearn/externals/array_api_compat/torch/_aliases.py index 4b727f1c22ba8..de5d1a5d40eb5 100644 --- a/sklearn/externals/array_api_compat/torch/_aliases.py +++ b/sklearn/externals/array_api_compat/torch/_aliases.py @@ -2,21 +2,15 @@ from functools import reduce as _reduce, wraps as _wraps from builtins import all as _builtin_all, any as _builtin_any - -from ..common import _aliases -from .._internal import get_xp - -from ._info import __array_namespace_info__ +from typing import Any, List, Optional, Sequence, Tuple, Union, Literal import torch -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from typing import List, Optional, Sequence, Tuple, Union - from ..common._typing import Device - from torch import dtype as Dtype - - array = torch.Tensor +from .._internal import get_xp +from ..common import _aliases +from ..common._typing import NestedSequence, SupportsBufferProtocol +from ._info import __array_namespace_info__ +from ._typing import Array, Device, DType _int_dtypes = { torch.uint8, @@ -41,47 +35,23 @@ torch.complex128, } -_promotion_table = { - # bool - (torch.bool, torch.bool): torch.bool, +_promotion_table = { # ints - (torch.int8, torch.int8): torch.int8, (torch.int8, torch.int16): torch.int16, (torch.int8, torch.int32): torch.int32, (torch.int8, torch.int64): torch.int64, - (torch.int16, torch.int8): torch.int16, - (torch.int16, torch.int16): torch.int16, (torch.int16, torch.int32): torch.int32, (torch.int16, torch.int64): torch.int64, - (torch.int32, torch.int8): torch.int32, - (torch.int32, torch.int16): torch.int32, - (torch.int32, torch.int32): torch.int32, (torch.int32, torch.int64): torch.int64, - (torch.int64, torch.int8): torch.int64, - (torch.int64, torch.int16): torch.int64, - (torch.int64, torch.int32): torch.int64, - (torch.int64, torch.int64): torch.int64, - # uints - (torch.uint8, torch.uint8): torch.uint8, # ints and uints (mixed sign) - (torch.int8, torch.uint8): torch.int16, - (torch.int16, torch.uint8): torch.int16, - (torch.int32, torch.uint8): torch.int32, - (torch.int64, torch.uint8): torch.int64, (torch.uint8, torch.int8): torch.int16, (torch.uint8, torch.int16): torch.int16, (torch.uint8, torch.int32): torch.int32, (torch.uint8, torch.int64): torch.int64, # floats - (torch.float32, torch.float32): torch.float32, (torch.float32, torch.float64): torch.float64, - (torch.float64, torch.float32): torch.float64, - (torch.float64, torch.float64): torch.float64, # complexes - (torch.complex64, torch.complex64): torch.complex64, (torch.complex64, torch.complex128): torch.complex128, - (torch.complex128, torch.complex64): torch.complex128, - (torch.complex128, torch.complex128): torch.complex128, # Mixed float and complex (torch.float32, torch.complex64): torch.complex64, (torch.float32, torch.complex128): torch.complex128, @@ -89,6 +59,9 @@ (torch.float64, torch.complex128): torch.complex128, } +_promotion_table.update({(b, a): c for (a, b), c in _promotion_table.items()}) +_promotion_table.update({(a, a): a for a in _array_api_dtypes}) + def _two_arg(f): @_wraps(f) @@ -123,7 +96,9 @@ def _fix_promotion(x1, x2, only_scalar=True): _py_scalars = (bool, int, float, complex) -def result_type(*arrays_and_dtypes: Union[array, Dtype, bool, int, float, complex]) -> Dtype: +def result_type( + *arrays_and_dtypes: Array | DType | bool | int | float | complex +) -> DType: num = len(arrays_and_dtypes) if num == 0: @@ -154,13 +129,18 @@ def result_type(*arrays_and_dtypes: Union[array, Dtype, bool, int, float, comple return _reduce(_result_type, others + scalars) -def _result_type(x, y): +def _result_type( + x: Array | DType | bool | int | float | complex, + y: Array | DType | bool | int | float | complex, +) -> DType: if not (isinstance(x, _py_scalars) or isinstance(y, _py_scalars)): - xdt = x.dtype if not isinstance(x, torch.dtype) else x - ydt = y.dtype if not isinstance(y, torch.dtype) else y + xdt = x if isinstance(x, torch.dtype) else x.dtype + ydt = y if isinstance(y, torch.dtype) else y.dtype - if (xdt, ydt) in _promotion_table: + try: return _promotion_table[xdt, ydt] + except KeyError: + pass # This doesn't result_type(dtype, dtype) for non-array API dtypes # because torch.result_type only accepts tensors. This does however, allow @@ -170,7 +150,7 @@ def _result_type(x, y): return torch.result_type(x, y) -def can_cast(from_: Union[Dtype, array], to: Dtype, /) -> bool: +def can_cast(from_: Union[DType, Array], to: DType, /) -> bool: if not isinstance(from_, torch.dtype): from_ = from_.dtype return torch.can_cast(from_, to) @@ -212,17 +192,39 @@ def can_cast(from_: Union[Dtype, array], to: Dtype, /) -> bool: remainder = _two_arg(torch.remainder) subtract = _two_arg(torch.subtract) + +def asarray( + obj: ( + Array + | bool | int | float | complex + | NestedSequence[bool | int | float | complex] + | SupportsBufferProtocol + ), + /, + *, + dtype: DType | None = None, + device: Device | None = None, + copy: bool | None = None, + **kwargs: Any, +) -> Array: + # torch.asarray does not respect input->output device propagation + # https://github.com/pytorch/pytorch/issues/150199 + if device is None and isinstance(obj, torch.Tensor): + device = obj.device + return torch.asarray(obj, dtype=dtype, device=device, copy=copy, **kwargs) + + # These wrappers are mostly based on the fact that pytorch uses 'dim' instead # of 'axis'. # torch.min and torch.max return a tuple and don't support multiple axes https://github.com/pytorch/pytorch/issues/58745 -def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: # https://github.com/pytorch/pytorch/issues/29137 if axis == (): return torch.clone(x) return torch.amax(x, axis, keepdims=keepdims) -def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: # https://github.com/pytorch/pytorch/issues/29137 if axis == (): return torch.clone(x) @@ -232,10 +234,13 @@ def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep unstack = get_xp(torch)(_aliases.unstack) cumulative_sum = get_xp(torch)(_aliases.cumulative_sum) cumulative_prod = get_xp(torch)(_aliases.cumulative_prod) +finfo = get_xp(torch)(_aliases.finfo) +iinfo = get_xp(torch)(_aliases.iinfo) + # torch.sort also returns a tuple # https://github.com/pytorch/pytorch/issues/70921 -def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True, **kwargs) -> array: +def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True, **kwargs) -> Array: return torch.sort(x, dim=axis, descending=descending, stable=stable, **kwargs).values def _normalize_axes(axis, ndim): @@ -280,28 +285,35 @@ def _reduce_multiple_axes(f, x, axis, keepdims=False, **kwargs): out = torch.unsqueeze(out, a) return out -def prod(x: array, + +def _sum_prod_no_axis(x: Array, dtype: DType | None) -> Array: + """ + Implements `sum(..., axis=())` and `prod(..., axis=())`. + + Works around https://github.com/pytorch/pytorch/issues/29137 + """ + if dtype is not None: + return x.clone() if dtype == x.dtype else x.to(dtype) + + # We can't upcast uint8 according to the spec because there is no + # torch.uint64, so at least upcast to int64 which is what prod does + # when axis=None. + if x.dtype in (torch.uint8, torch.int8, torch.int16, torch.int32): + return x.to(torch.int64) + + return x.clone() + + +def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, keepdims: bool = False, - **kwargs) -> array: - x = torch.asarray(x) - ndim = x.ndim + **kwargs) -> Array: - # https://github.com/pytorch/pytorch/issues/29137. Separate from the logic - # below because it still needs to upcast. if axis == (): - if dtype is None: - # We can't upcast uint8 according to the spec because there is no - # torch.uint64, so at least upcast to int64 which is what sum does - # when axis=None. - if x.dtype in [torch.int8, torch.int16, torch.int32, torch.uint8]: - return x.to(torch.int64) - return x.clone() - return x.to(dtype) - + return _sum_prod_no_axis(x, dtype) # torch.prod doesn't support multiple axes # (https://github.com/pytorch/pytorch/issues/56586). if isinstance(axis, tuple): @@ -310,51 +322,38 @@ def prod(x: array, # torch doesn't support keepdims with axis=None # (https://github.com/pytorch/pytorch/issues/71209) res = torch.prod(x, dtype=dtype, **kwargs) - res = _axis_none_keepdims(res, ndim, keepdims) + res = _axis_none_keepdims(res, x.ndim, keepdims) return res return torch.prod(x, axis, dtype=dtype, keepdims=keepdims, **kwargs) -def sum(x: array, +def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, keepdims: bool = False, - **kwargs) -> array: - x = torch.asarray(x) - ndim = x.ndim + **kwargs) -> Array: - # https://github.com/pytorch/pytorch/issues/29137. - # Make sure it upcasts. if axis == (): - if dtype is None: - # We can't upcast uint8 according to the spec because there is no - # torch.uint64, so at least upcast to int64 which is what sum does - # when axis=None. - if x.dtype in [torch.int8, torch.int16, torch.int32, torch.uint8]: - return x.to(torch.int64) - return x.clone() - return x.to(dtype) - + return _sum_prod_no_axis(x, dtype) if axis is None: # torch doesn't support keepdims with axis=None # (https://github.com/pytorch/pytorch/issues/71209) res = torch.sum(x, dtype=dtype, **kwargs) - res = _axis_none_keepdims(res, ndim, keepdims) + res = _axis_none_keepdims(res, x.ndim, keepdims) return res return torch.sum(x, axis, dtype=dtype, keepdims=keepdims, **kwargs) -def any(x: array, +def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, - **kwargs) -> array: - x = torch.asarray(x) - ndim = x.ndim + **kwargs) -> Array: + if axis == (): return x.to(torch.bool) # torch.any doesn't support multiple axes @@ -366,20 +365,19 @@ def any(x: array, # torch doesn't support keepdims with axis=None # (https://github.com/pytorch/pytorch/issues/71209) res = torch.any(x, **kwargs) - res = _axis_none_keepdims(res, ndim, keepdims) + res = _axis_none_keepdims(res, x.ndim, keepdims) return res.to(torch.bool) # torch.any doesn't return bool for uint8 return torch.any(x, axis, keepdims=keepdims).to(torch.bool) -def all(x: array, +def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, - **kwargs) -> array: - x = torch.asarray(x) - ndim = x.ndim + **kwargs) -> Array: + if axis == (): return x.to(torch.bool) # torch.all doesn't support multiple axes @@ -391,18 +389,18 @@ def all(x: array, # torch doesn't support keepdims with axis=None # (https://github.com/pytorch/pytorch/issues/71209) res = torch.all(x, **kwargs) - res = _axis_none_keepdims(res, ndim, keepdims) + res = _axis_none_keepdims(res, x.ndim, keepdims) return res.to(torch.bool) # torch.all doesn't return bool for uint8 return torch.all(x, axis, keepdims=keepdims).to(torch.bool) -def mean(x: array, +def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, - **kwargs) -> array: + **kwargs) -> Array: # https://github.com/pytorch/pytorch/issues/29137 if axis == (): return torch.clone(x) @@ -414,13 +412,13 @@ def mean(x: array, return res return torch.mean(x, axis, keepdims=keepdims, **kwargs) -def std(x: array, +def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False, - **kwargs) -> array: + **kwargs) -> Array: # Note, float correction is not supported # https://github.com/pytorch/pytorch/issues/61492. We don't try to # implement it here for now. @@ -445,13 +443,13 @@ def std(x: array, return res return torch.std(x, axis, correction=_correction, keepdims=keepdims, **kwargs) -def var(x: array, +def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False, - **kwargs) -> array: + **kwargs) -> Array: # Note, float correction is not supported # https://github.com/pytorch/pytorch/issues/61492. We don't try to # implement it here for now. @@ -474,11 +472,11 @@ def var(x: array, # torch.concat doesn't support dim=None # https://github.com/pytorch/pytorch/issues/70925 -def concat(arrays: Union[Tuple[array, ...], List[array]], +def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0, - **kwargs) -> array: + **kwargs) -> Array: if axis is None: arrays = tuple(ar.flatten() for ar in arrays) axis = 0 @@ -487,7 +485,7 @@ def concat(arrays: Union[Tuple[array, ...], List[array]], # torch.squeeze only accepts int dim and doesn't require it # https://github.com/pytorch/pytorch/issues/70924. Support for tuple dim was # added at https://github.com/pytorch/pytorch/pull/89017. -def squeeze(x: array, /, axis: Union[int, Tuple[int, ...]]) -> array: +def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array: if isinstance(axis, int): axis = (axis,) for a in axis: @@ -501,27 +499,27 @@ def squeeze(x: array, /, axis: Union[int, Tuple[int, ...]]) -> array: return x # torch.broadcast_to uses size instead of shape -def broadcast_to(x: array, /, shape: Tuple[int, ...], **kwargs) -> array: +def broadcast_to(x: Array, /, shape: Tuple[int, ...], **kwargs) -> Array: return torch.broadcast_to(x, shape, **kwargs) # torch.permute uses dims instead of axes -def permute_dims(x: array, /, axes: Tuple[int, ...]) -> array: +def permute_dims(x: Array, /, axes: Tuple[int, ...]) -> Array: return torch.permute(x, axes) # The axis parameter doesn't work for flip() and roll() # https://github.com/pytorch/pytorch/issues/71210. Also torch.flip() doesn't # accept axis=None -def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, **kwargs) -> array: +def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, **kwargs) -> Array: if axis is None: axis = tuple(range(x.ndim)) # torch.flip doesn't accept dim as an int but the method does # https://github.com/pytorch/pytorch/issues/18095 return x.flip(axis, **kwargs) -def roll(x: array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None, **kwargs) -> array: +def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None, **kwargs) -> Array: return torch.roll(x, shift, axis, **kwargs) -def nonzero(x: array, /, **kwargs) -> Tuple[array, ...]: +def nonzero(x: Array, /, **kwargs) -> Tuple[Array, ...]: if x.ndim == 0: raise ValueError("nonzero() does not support zero-dimensional arrays") return torch.nonzero(x, as_tuple=True, **kwargs) @@ -529,45 +527,60 @@ def nonzero(x: array, /, **kwargs) -> Tuple[array, ...]: # torch uses `dim` instead of `axis` def diff( - x: array, + x: Array, /, *, axis: int = -1, n: int = 1, - prepend: Optional[array] = None, - append: Optional[array] = None, -) -> array: + prepend: Optional[Array] = None, + append: Optional[Array] = None, +) -> Array: return torch.diff(x, dim=axis, n=n, prepend=prepend, append=append) # torch uses `dim` instead of `axis`, does not have keepdims def count_nonzero( - x: array, + x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, -) -> array: +) -> Array: result = torch.count_nonzero(x, dim=axis) if keepdims: - if axis is not None: + if isinstance(axis, int): return result.unsqueeze(axis) + elif isinstance(axis, tuple): + n_axis = [x.ndim + ax if ax < 0 else ax for ax in axis] + sh = [1 if i in n_axis else x.shape[i] for i in range(x.ndim)] + return torch.reshape(result, sh) return _axis_none_keepdims(result, x.ndim, keepdims) else: return result +# "repeat" is torch.repeat_interleave; also the dim argument +def repeat(x: Array, repeats: int | Array, /, *, axis: int | None = None) -> Array: + return torch.repeat_interleave(x, repeats, axis) -def where(condition: array, x1: array, x2: array, /) -> array: + +def where( + condition: Array, + x1: Array | bool | int | float | complex, + x2: Array | bool | int | float | complex, + /, +) -> Array: x1, x2 = _fix_promotion(x1, x2) return torch.where(condition, x1, x2) + # torch.reshape doesn't have the copy keyword -def reshape(x: array, +def reshape(x: Array, /, shape: Tuple[int, ...], + *, copy: Optional[bool] = None, - **kwargs) -> array: + **kwargs) -> Array: if copy is not None: raise NotImplementedError("torch.reshape doesn't yet support the copy keyword") return torch.reshape(x, shape, **kwargs) @@ -581,9 +594,9 @@ def arange(start: Union[int, float], stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - **kwargs) -> array: + **kwargs) -> Array: if stop is None: start, stop = 0, start if step > 0 and stop <= start or step < 0 and stop >= start: @@ -602,9 +615,9 @@ def eye(n_rows: int, /, *, k: int = 0, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - **kwargs) -> array: + **kwargs) -> Array: if n_cols is None: n_cols = n_rows z = torch.zeros(n_rows, n_cols, dtype=dtype, device=device, **kwargs) @@ -618,10 +631,10 @@ def linspace(start: Union[int, float], /, num: int, *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, endpoint: bool = True, - **kwargs) -> array: + **kwargs) -> Array: if not endpoint: return torch.linspace(start, stop, num+1, dtype=dtype, device=device, **kwargs)[:-1] return torch.linspace(start, stop, num, dtype=dtype, device=device, **kwargs) @@ -629,11 +642,11 @@ def linspace(start: Union[int, float], # torch.full does not accept an int size # https://github.com/pytorch/pytorch/issues/70906 def full(shape: Union[int, Tuple[int, ...]], - fill_value: Union[bool, int, float, complex], + fill_value: bool | int | float | complex, *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - **kwargs) -> array: + **kwargs) -> Array: if isinstance(shape, int): shape = (shape,) @@ -642,52 +655,52 @@ def full(shape: Union[int, Tuple[int, ...]], # ones, zeros, and empty do not accept shape as a keyword argument def ones(shape: Union[int, Tuple[int, ...]], *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - **kwargs) -> array: + **kwargs) -> Array: return torch.ones(shape, dtype=dtype, device=device, **kwargs) def zeros(shape: Union[int, Tuple[int, ...]], *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - **kwargs) -> array: + **kwargs) -> Array: return torch.zeros(shape, dtype=dtype, device=device, **kwargs) def empty(shape: Union[int, Tuple[int, ...]], *, - dtype: Optional[Dtype] = None, + dtype: Optional[DType] = None, device: Optional[Device] = None, - **kwargs) -> array: + **kwargs) -> Array: return torch.empty(shape, dtype=dtype, device=device, **kwargs) # tril and triu do not call the keyword argument k -def tril(x: array, /, *, k: int = 0) -> array: +def tril(x: Array, /, *, k: int = 0) -> Array: return torch.tril(x, k) -def triu(x: array, /, *, k: int = 0) -> array: +def triu(x: Array, /, *, k: int = 0) -> Array: return torch.triu(x, k) # Functions that aren't in torch https://github.com/pytorch/pytorch/issues/58742 -def expand_dims(x: array, /, *, axis: int = 0) -> array: +def expand_dims(x: Array, /, *, axis: int = 0) -> Array: return torch.unsqueeze(x, axis) def astype( - x: array, - dtype: Dtype, + x: Array, + dtype: DType, /, *, copy: bool = True, device: Optional[Device] = None, -) -> array: +) -> Array: if device is not None: return x.to(device, dtype=dtype, copy=copy) return x.to(dtype=dtype, copy=copy) -def broadcast_arrays(*arrays: array) -> List[array]: +def broadcast_arrays(*arrays: Array) -> List[Array]: shape = torch.broadcast_shapes(*[a.shape for a in arrays]) return [torch.broadcast_to(a, shape) for a in arrays] @@ -697,7 +710,7 @@ def broadcast_arrays(*arrays: array) -> List[array]: UniqueInverseResult) # https://github.com/pytorch/pytorch/issues/70920 -def unique_all(x: array) -> UniqueAllResult: +def unique_all(x: Array) -> UniqueAllResult: # torch.unique doesn't support returning indices. # https://github.com/pytorch/pytorch/issues/36748. The workaround # suggested in that issue doesn't actually function correctly (it relies @@ -710,7 +723,7 @@ def unique_all(x: array) -> UniqueAllResult: # counts[torch.isnan(values)] = 1 # return UniqueAllResult(values, indices, inverse_indices, counts) -def unique_counts(x: array) -> UniqueCountsResult: +def unique_counts(x: Array) -> UniqueCountsResult: values, counts = torch.unique(x, return_counts=True) # torch.unique incorrectly gives a 0 count for nan values. @@ -718,14 +731,14 @@ def unique_counts(x: array) -> UniqueCountsResult: counts[torch.isnan(values)] = 1 return UniqueCountsResult(values, counts) -def unique_inverse(x: array) -> UniqueInverseResult: +def unique_inverse(x: Array) -> UniqueInverseResult: values, inverse = torch.unique(x, return_inverse=True) return UniqueInverseResult(values, inverse) -def unique_values(x: array) -> array: +def unique_values(x: Array) -> Array: return torch.unique(x) -def matmul(x1: array, x2: array, /, **kwargs) -> array: +def matmul(x1: Array, x2: Array, /, **kwargs) -> Array: # torch.matmul doesn't type promote (but differently from _fix_promotion) x1, x2 = _fix_promotion(x1, x2, only_scalar=False) return torch.matmul(x1, x2, **kwargs) @@ -733,12 +746,19 @@ def matmul(x1: array, x2: array, /, **kwargs) -> array: matrix_transpose = get_xp(torch)(_aliases.matrix_transpose) _vecdot = get_xp(torch)(_aliases.vecdot) -def vecdot(x1: array, x2: array, /, *, axis: int = -1) -> array: +def vecdot(x1: Array, x2: Array, /, *, axis: int = -1) -> Array: x1, x2 = _fix_promotion(x1, x2, only_scalar=False) return _vecdot(x1, x2, axis=axis) # torch.tensordot uses dims instead of axes -def tensordot(x1: array, x2: array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, **kwargs) -> array: +def tensordot( + x1: Array, + x2: Array, + /, + *, + axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, + **kwargs, +) -> Array: # Note: torch.tensordot fails with integer dtypes when there is only 1 # element in the axis (https://github.com/pytorch/pytorch/issues/84530). x1, x2 = _fix_promotion(x1, x2, only_scalar=False) @@ -746,7 +766,7 @@ def tensordot(x1: array, x2: array, /, *, axes: Union[int, Tuple[Sequence[int], def isdtype( - dtype: Dtype, kind: Union[Dtype, str, Tuple[Union[Dtype, str], ...]], + dtype: DType, kind: Union[DType, str, Tuple[Union[DType, str], ...]], *, _tuple=True, # Disallow nested tuples ) -> bool: """ @@ -781,7 +801,7 @@ def isdtype( else: return dtype == kind -def take(x: array, indices: array, /, *, axis: Optional[int] = None, **kwargs) -> array: +def take(x: Array, indices: Array, /, *, axis: Optional[int] = None, **kwargs) -> Array: if axis is None: if x.ndim != 1: raise ValueError("axis must be specified when ndim > 1") @@ -789,11 +809,11 @@ def take(x: array, indices: array, /, *, axis: Optional[int] = None, **kwargs) - return torch.index_select(x, axis, indices, **kwargs) -def take_along_axis(x: array, indices: array, /, *, axis: int = -1) -> array: +def take_along_axis(x: Array, indices: Array, /, *, axis: int = -1) -> Array: return torch.take_along_dim(x, indices, dim=axis) -def sign(x: array, /) -> array: +def sign(x: Array, /) -> Array: # torch sign() does not support complex numbers and does not propagate # nans. See https://github.com/data-apis/array-api-compat/issues/136 if x.dtype.is_complex: @@ -808,7 +828,13 @@ def sign(x: array, /) -> array: return out -__all__ = ['__array_namespace_info__', 'result_type', 'can_cast', +def meshgrid(*arrays: Array, indexing: Literal['xy', 'ij'] = 'xy') -> List[Array]: + # enforce the default of 'xy' + # TODO: is the return type a list or a tuple + return list(torch.meshgrid(*arrays, indexing='xy')) + + +__all__ = ['__array_namespace_info__', 'asarray', 'result_type', 'can_cast', 'permute_dims', 'bitwise_invert', 'newaxis', 'conj', 'add', 'atan2', 'bitwise_and', 'bitwise_left_shift', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'copysign', 'count_nonzero', @@ -824,6 +850,6 @@ def sign(x: array, /) -> array: 'UniqueAllResult', 'UniqueCountsResult', 'UniqueInverseResult', 'unique_all', 'unique_counts', 'unique_inverse', 'unique_values', 'matmul', 'matrix_transpose', 'vecdot', 'tensordot', 'isdtype', - 'take', 'take_along_axis', 'sign'] + 'take', 'take_along_axis', 'sign', 'finfo', 'iinfo', 'repeat', 'meshgrid'] _all_ignore = ['torch', 'get_xp'] diff --git a/sklearn/externals/array_api_compat/torch/_info.py b/sklearn/externals/array_api_compat/torch/_info.py index 34fbcb21aa53f..818e5d3702e38 100644 --- a/sklearn/externals/array_api_compat/torch/_info.py +++ b/sklearn/externals/array_api_compat/torch/_info.py @@ -34,7 +34,7 @@ class __array_namespace_info__: Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_dtypes() {'real floating': numpy.float64, 'complex floating': numpy.complex128, @@ -76,16 +76,16 @@ def capabilities(self): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.capabilities() {'boolean indexing': True, - 'data-dependent shapes': True} + 'data-dependent shapes': True, + 'max dimensions': 64} """ return { "boolean indexing": True, "data-dependent shapes": True, - # 'max rank' will be part of the 2024.12 standard "max dimensions": 64, } @@ -102,15 +102,24 @@ def default_device(self): Returns ------- - device : str + device : Device The default device used for new PyTorch arrays. Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_device() - 'cpu' + device(type='cpu') + Notes + ----- + This method returns the static default device when PyTorch is initialized. + However, the *current* device used by creation functions (``empty`` etc.) + can be changed at runtime. + + See Also + -------- + https://github.com/data-apis/array-api/issues/835 """ return torch.device("cpu") @@ -120,9 +129,9 @@ def default_dtypes(self, *, device=None): Parameters ---------- - device : str, optional - The device to get the default data types for. For PyTorch, only - ``'cpu'`` is allowed. + device : Device, optional + The device to get the default data types for. + Unused for PyTorch, as all devices use the same default dtypes. Returns ------- @@ -139,7 +148,7 @@ def default_dtypes(self, *, device=None): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.default_dtypes() {'real floating': torch.float32, 'complex floating': torch.complex64, @@ -250,8 +259,9 @@ def dtypes(self, *, device=None, kind=None): Parameters ---------- - device : str, optional + device : Device, optional The device to get the data types for. + Unused for PyTorch, as all devices use the same dtypes. kind : str or tuple of str, optional The kind of data types to return. If ``None``, all data types are returned. If a string, only data types of that kind are returned. @@ -287,7 +297,7 @@ def dtypes(self, *, device=None, kind=None): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.dtypes(kind='signed integer') {'int8': numpy.int8, 'int16': numpy.int16, @@ -310,7 +320,7 @@ def devices(self): Returns ------- - devices : list of str + devices : list[Device] The devices supported by PyTorch. See Also @@ -322,7 +332,7 @@ def devices(self): Examples -------- - >>> info = np.__array_namespace_info__() + >>> info = xp.__array_namespace_info__() >>> info.devices() [device(type='cpu'), device(type='mps', index=0), device(type='meta')] @@ -333,6 +343,7 @@ def devices(self): # device: try: torch.device('notadevice') + raise AssertionError("unreachable") # pragma: nocover except RuntimeError as e: # The error message is something like: # "Expected one of cpu, cuda, ipu, xpu, mkldnn, opengl, opencl, ideep, hip, ve, fpga, ort, xla, lazy, vulkan, mps, meta, hpu, mtia, privateuseone device type at start of device string: notadevice" diff --git a/sklearn/externals/array_api_compat/torch/_typing.py b/sklearn/externals/array_api_compat/torch/_typing.py new file mode 100644 index 0000000000000..5267087156371 --- /dev/null +++ b/sklearn/externals/array_api_compat/torch/_typing.py @@ -0,0 +1,3 @@ +__all__ = ["Array", "Device", "DType"] + +from torch import device as Device, dtype as DType, Tensor as Array diff --git a/sklearn/externals/array_api_compat/torch/fft.py b/sklearn/externals/array_api_compat/torch/fft.py index 3c9117ee57d35..50e6a0d0a3968 100644 --- a/sklearn/externals/array_api_compat/torch/fft.py +++ b/sklearn/externals/array_api_compat/torch/fft.py @@ -1,76 +1,75 @@ from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - import torch - array = torch.Tensor - from typing import Union, Sequence, Literal +from typing import Union, Sequence, Literal -from torch.fft import * # noqa: F403 +import torch import torch.fft +from torch.fft import * # noqa: F403 + +from ._typing import Array # Several torch fft functions do not map axes to dim def fftn( - x: array, + x: Array, /, *, s: Sequence[int] = None, axes: Sequence[int] = None, norm: Literal["backward", "ortho", "forward"] = "backward", **kwargs, -) -> array: +) -> Array: return torch.fft.fftn(x, s=s, dim=axes, norm=norm, **kwargs) def ifftn( - x: array, + x: Array, /, *, s: Sequence[int] = None, axes: Sequence[int] = None, norm: Literal["backward", "ortho", "forward"] = "backward", **kwargs, -) -> array: +) -> Array: return torch.fft.ifftn(x, s=s, dim=axes, norm=norm, **kwargs) def rfftn( - x: array, + x: Array, /, *, s: Sequence[int] = None, axes: Sequence[int] = None, norm: Literal["backward", "ortho", "forward"] = "backward", **kwargs, -) -> array: +) -> Array: return torch.fft.rfftn(x, s=s, dim=axes, norm=norm, **kwargs) def irfftn( - x: array, + x: Array, /, *, s: Sequence[int] = None, axes: Sequence[int] = None, norm: Literal["backward", "ortho", "forward"] = "backward", **kwargs, -) -> array: +) -> Array: return torch.fft.irfftn(x, s=s, dim=axes, norm=norm, **kwargs) def fftshift( - x: array, + x: Array, /, *, axes: Union[int, Sequence[int]] = None, **kwargs, -) -> array: +) -> Array: return torch.fft.fftshift(x, dim=axes, **kwargs) def ifftshift( - x: array, + x: Array, /, *, axes: Union[int, Sequence[int]] = None, **kwargs, -) -> array: +) -> Array: return torch.fft.ifftshift(x, dim=axes, **kwargs) diff --git a/sklearn/externals/array_api_compat/torch/linalg.py b/sklearn/externals/array_api_compat/torch/linalg.py index e26198b9b562e..70d7240500ce4 100644 --- a/sklearn/externals/array_api_compat/torch/linalg.py +++ b/sklearn/externals/array_api_compat/torch/linalg.py @@ -1,14 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - import torch - array = torch.Tensor - from torch import dtype as Dtype - from typing import Optional, Union, Tuple, Literal - inf = float('inf') - -from ._aliases import _fix_promotion, sum +import torch +from typing import Optional, Union, Tuple from torch.linalg import * # noqa: F403 @@ -19,15 +12,18 @@ # outer is implemented in torch but aren't in the linalg namespace from torch import outer +from ._aliases import _fix_promotion, sum # These functions are in both the main and linalg namespaces from ._aliases import matmul, matrix_transpose, tensordot +from ._typing import Array, DType +from ..common._typing import JustInt, JustFloat # Note: torch.linalg.cross does not default to axis=-1 (it defaults to the # first axis with size 3), see https://github.com/pytorch/pytorch/issues/58743 # torch.cross also does not support broadcasting when it would add new # dimensions https://github.com/pytorch/pytorch/issues/39656 -def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: +def cross(x1: Array, x2: Array, /, *, axis: int = -1) -> Array: x1, x2 = _fix_promotion(x1, x2, only_scalar=False) if not (-min(x1.ndim, x2.ndim) <= axis < max(x1.ndim, x2.ndim)): raise ValueError(f"axis {axis} out of bounds for cross product of arrays with shapes {x1.shape} and {x2.shape}") @@ -36,7 +32,7 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: x1, x2 = torch.broadcast_tensors(x1, x2) return torch_linalg.cross(x1, x2, dim=axis) -def vecdot(x1: array, x2: array, /, *, axis: int = -1, **kwargs) -> array: +def vecdot(x1: Array, x2: Array, /, *, axis: int = -1, **kwargs) -> Array: from ._aliases import isdtype x1, x2 = _fix_promotion(x1, x2, only_scalar=False) @@ -58,7 +54,7 @@ def vecdot(x1: array, x2: array, /, *, axis: int = -1, **kwargs) -> array: return res[..., 0, 0] return torch.linalg.vecdot(x1, x2, dim=axis, **kwargs) -def solve(x1: array, x2: array, /, **kwargs) -> array: +def solve(x1: Array, x2: Array, /, **kwargs) -> Array: x1, x2 = _fix_promotion(x1, x2, only_scalar=False) # Torch tries to emulate NumPy 1 solve behavior by using batched 1-D solve # whenever @@ -79,19 +75,20 @@ def solve(x1: array, x2: array, /, **kwargs) -> array: return torch.linalg.solve(x1, x2, **kwargs) # torch.trace doesn't support the offset argument and doesn't support stacking -def trace(x: array, /, *, offset: int = 0, dtype: Optional[Dtype] = None) -> array: +def trace(x: Array, /, *, offset: int = 0, dtype: Optional[DType] = None) -> Array: # Use our wrapped sum to make sure it does upcasting correctly return sum(torch.diagonal(x, offset=offset, dim1=-2, dim2=-1), axis=-1, dtype=dtype) def vector_norm( - x: array, + x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, - ord: Union[int, float, Literal[inf, -inf]] = 2, + # JustFloat stands for inf | -inf, which are not valid for Literal + ord: JustInt | JustFloat = 2, **kwargs, -) -> array: +) -> Array: # torch.vector_norm incorrectly treats axis=() the same as axis=None if axis == (): out = kwargs.get('out') @@ -119,3 +116,6 @@ def vector_norm( _all_ignore = ['torch_linalg', 'sum'] del linalg_all + +def __dir__() -> list[str]: + return __all__ diff --git a/sklearn/feature_extraction/_hash.py b/sklearn/feature_extraction/_hash.py index ac0bed3110c4e..34756fa06eb4e 100644 --- a/sklearn/feature_extraction/_hash.py +++ b/sklearn/feature_extraction/_hash.py @@ -205,4 +205,5 @@ def __sklearn_tags__(self): tags.input_tags.string = True elif self.input_type == "dict": tags.input_tags.dict = True + tags.requires_fit = False return tags diff --git a/sklearn/feature_extraction/tests/test_feature_hasher.py b/sklearn/feature_extraction/tests/test_feature_hasher.py index 276d0d48b0770..90c51d668f6c0 100644 --- a/sklearn/feature_extraction/tests/test_feature_hasher.py +++ b/sklearn/feature_extraction/tests/test_feature_hasher.py @@ -158,3 +158,18 @@ def test_hash_collisions(): alternate_sign=False, n_features=1, input_type="string" ).fit_transform(X) assert Xt.data[0] == len(X[0]) + + +def test_feature_hasher_requires_fit_tag(): + """Test that FeatureHasher has requires_fit=False tag.""" + hasher = FeatureHasher() + tags = hasher.__sklearn_tags__() + assert not tags.requires_fit + + +def test_feature_hasher_transform_without_fit(): + """Test that FeatureHasher can transform without fitting.""" + hasher = FeatureHasher(n_features=10) + data = [{"dog": 1, "cat": 2}, {"dog": 2, "run": 5}] + result = hasher.transform(data) + assert result.shape == (2, 10) diff --git a/sklearn/feature_extraction/tests/test_image.py b/sklearn/feature_extraction/tests/test_image.py index 2edf1a22d676a..cb490fcd576ee 100644 --- a/sklearn/feature_extraction/tests/test_image.py +++ b/sklearn/feature_extraction/tests/test_image.py @@ -223,13 +223,15 @@ def test_reconstruct_patches_perfect_color(orange_face): np.testing.assert_array_almost_equal(face, face_reconstructed) -def test_patch_extractor_fit(downsampled_face_collection): +def test_patch_extractor_fit(downsampled_face_collection, global_random_seed): faces = downsampled_face_collection - extr = PatchExtractor(patch_size=(8, 8), max_patches=100, random_state=0) + extr = PatchExtractor( + patch_size=(8, 8), max_patches=100, random_state=global_random_seed + ) assert extr == extr.fit(faces) -def test_patch_extractor_max_patches(downsampled_face_collection): +def test_patch_extractor_max_patches(downsampled_face_collection, global_random_seed): faces = downsampled_face_collection i_h, i_w = faces.shape[1:3] p_h, p_w = 8, 8 @@ -237,7 +239,7 @@ def test_patch_extractor_max_patches(downsampled_face_collection): max_patches = 100 expected_n_patches = len(faces) * max_patches extr = PatchExtractor( - patch_size=(p_h, p_w), max_patches=max_patches, random_state=0 + patch_size=(p_h, p_w), max_patches=max_patches, random_state=global_random_seed ) patches = extr.transform(faces) assert patches.shape == (expected_n_patches, p_h, p_w) @@ -247,35 +249,37 @@ def test_patch_extractor_max_patches(downsampled_face_collection): (i_h - p_h + 1) * (i_w - p_w + 1) * max_patches ) extr = PatchExtractor( - patch_size=(p_h, p_w), max_patches=max_patches, random_state=0 + patch_size=(p_h, p_w), max_patches=max_patches, random_state=global_random_seed ) patches = extr.transform(faces) assert patches.shape == (expected_n_patches, p_h, p_w) -def test_patch_extractor_max_patches_default(downsampled_face_collection): +def test_patch_extractor_max_patches_default( + downsampled_face_collection, global_random_seed +): faces = downsampled_face_collection - extr = PatchExtractor(max_patches=100, random_state=0) + extr = PatchExtractor(max_patches=100, random_state=global_random_seed) patches = extr.transform(faces) assert patches.shape == (len(faces) * 100, 19, 25) -def test_patch_extractor_all_patches(downsampled_face_collection): +def test_patch_extractor_all_patches(downsampled_face_collection, global_random_seed): faces = downsampled_face_collection i_h, i_w = faces.shape[1:3] p_h, p_w = 8, 8 expected_n_patches = len(faces) * (i_h - p_h + 1) * (i_w - p_w + 1) - extr = PatchExtractor(patch_size=(p_h, p_w), random_state=0) + extr = PatchExtractor(patch_size=(p_h, p_w), random_state=global_random_seed) patches = extr.transform(faces) assert patches.shape == (expected_n_patches, p_h, p_w) -def test_patch_extractor_color(orange_face): +def test_patch_extractor_color(orange_face, global_random_seed): faces = _make_images(orange_face) i_h, i_w = faces.shape[1:3] p_h, p_w = 8, 8 expected_n_patches = len(faces) * (i_h - p_h + 1) * (i_w - p_w + 1) - extr = PatchExtractor(patch_size=(p_h, p_w), random_state=0) + extr = PatchExtractor(patch_size=(p_h, p_w), random_state=global_random_seed) patches = extr.transform(faces) assert patches.shape == (expected_n_patches, p_h, p_w, 3) diff --git a/sklearn/feature_extraction/tests/test_text.py b/sklearn/feature_extraction/tests/test_text.py index ab3f84668fd2d..00b94831767b5 100644 --- a/sklearn/feature_extraction/tests/test_text.py +++ b/sklearn/feature_extraction/tests/test_text.py @@ -1626,3 +1626,18 @@ def test_tfidf_vectorizer_perserve_dtype_idf(dtype): X = [str(uuid.uuid4()) for i in range(100_000)] vectorizer = TfidfVectorizer(dtype=dtype).fit(X) assert vectorizer.idf_.dtype == dtype + + +def test_hashing_vectorizer_requires_fit_tag(): + """Test that HashingVectorizer has requires_fit=False tag.""" + vectorizer = HashingVectorizer() + tags = vectorizer.__sklearn_tags__() + assert not tags.requires_fit + + +def test_hashing_vectorizer_transform_without_fit(): + """Test that HashingVectorizer can transform without fitting.""" + vectorizer = HashingVectorizer(n_features=10) + corpus = ["This is test", "Another test"] + result = vectorizer.transform(corpus) + assert result.shape == (2, 10) diff --git a/sklearn/feature_extraction/text.py b/sklearn/feature_extraction/text.py index eb3226b01c79e..d32248978a97a 100644 --- a/sklearn/feature_extraction/text.py +++ b/sklearn/feature_extraction/text.py @@ -914,6 +914,7 @@ def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.string = True tags.input_tags.two_d_array = False + tags.requires_fit = False return tags diff --git a/sklearn/feature_selection/_rfe.py b/sklearn/feature_selection/_rfe.py index d2bd78e225a54..d647ad0ca19b1 100644 --- a/sklearn/feature_selection/_rfe.py +++ b/sklearn/feature_selection/_rfe.py @@ -564,6 +564,7 @@ class RFECV(RFE): different numbers of selected features and aggregated together. Finally, the scores are averaged across folds and the number of features selected is set to the number of features that maximize the cross-validation score. + See glossary entry for :term:`cross-validation estimator`. Read more in the :ref:`User Guide `. @@ -755,6 +756,10 @@ class RFECV(RFE): False]) >>> selector.ranking_ array([1, 1, 1, 1, 1, 6, 4, 3, 2, 5]) + + For a detailed example of using RFECV to select features when training a + :class:`~sklearn.linear_model.LogisticRegression`, see + :ref:`sphx_glr_auto_examples_feature_selection_plot_rfe_with_cross_validation.py`. """ _parameter_constraints: dict = { diff --git a/sklearn/impute/_base.py b/sklearn/impute/_base.py index 689ba8aceeaf6..ae74068145678 100644 --- a/sklearn/impute/_base.py +++ b/sklearn/impute/_base.py @@ -38,6 +38,20 @@ def _check_inputs_dtype(X, missing_values): ) +def _safe_min(items): + """Compute the minimum of a list of potentially non-comparable values. + + If values cannot be directly compared due to type incompatibility, the object with + the lowest string representation is returned. + """ + try: + return min(items) + except TypeError as e: + if "'<' not supported between" in str(e): + return min(items, key=lambda x: (str(type(x)), str(x))) + raise # pragma: no cover + + def _most_frequent(array, extra_value, n_repeat): """Compute the most frequent value in a 1d array extended with [extra_value] * n_repeat, where extra_value is assumed to be not part @@ -50,10 +64,12 @@ def _most_frequent(array, extra_value, n_repeat): counter = Counter(array) most_frequent_count = counter.most_common(1)[0][1] # tie breaking similarly to scipy.stats.mode - most_frequent_value = min( - value - for value, count in counter.items() - if count == most_frequent_count + most_frequent_value = _safe_min( + [ + value + for value, count in counter.items() + if count == most_frequent_count + ] ) else: mode = _mode(array) @@ -72,7 +88,7 @@ def _most_frequent(array, extra_value, n_repeat): return most_frequent_value elif most_frequent_count == n_repeat: # tie breaking similarly to scipy.stats.mode - return min(most_frequent_value, extra_value) + return _safe_min([most_frequent_value, extra_value]) class _BaseImputer(TransformerMixin, BaseEstimator): diff --git a/sklearn/impute/tests/test_impute.py b/sklearn/impute/tests/test_impute.py index 16501b0550364..4116964c49a7a 100644 --- a/sklearn/impute/tests/test_impute.py +++ b/sklearn/impute/tests/test_impute.py @@ -1529,6 +1529,26 @@ def test_most_frequent(expected, array, dtype, extra_value, n_repeat): ) +@pytest.mark.parametrize( + "expected,array", + [ + ("a", ["a", "b"]), + (1, [1, 2]), + (None, [None, "a"]), + (None, [None, 1]), + (None, [None, "a", 1]), + (1, [1, "1"]), + (1, ["1", 1]), + ], +) +def test_most_frequent_tie_object(expected, array): + """Check the tie breaking behavior of the most frequent strategy. + + Non-regression test for issue #31717. + """ + assert expected == _most_frequent(np.array(array, dtype=object), None, 0) + + @pytest.mark.parametrize( "initial_strategy", ["mean", "median", "most_frequent", "constant"] ) diff --git a/sklearn/inspection/_plot/decision_boundary.py b/sklearn/inspection/_plot/decision_boundary.py index bc28708d7c488..2ef8538058393 100644 --- a/sklearn/inspection/_plot/decision_boundary.py +++ b/sklearn/inspection/_plot/decision_boundary.py @@ -221,17 +221,22 @@ def plot(self, plot_method="contourf", ax=None, xlabel=None, ylabel=None, **kwar self.surface_ = plot_func(self.xx0, self.xx1, self.response, **kwargs) else: # self.response.ndim == 3 n_responses = self.response.shape[-1] - if ( - isinstance(self.multiclass_colors, str) - or self.multiclass_colors is None + for kwarg in ("cmap", "colors"): + if kwarg in kwargs: + warnings.warn( + f"'{kwarg}' is ignored in favor of 'multiclass_colors' " + "in the multiclass case when the response method is " + "'decision_function' or 'predict_proba'." + ) + del kwargs[kwarg] + + if self.multiclass_colors is None or isinstance( + self.multiclass_colors, str ): - if isinstance(self.multiclass_colors, str): - cmap = self.multiclass_colors + if self.multiclass_colors is None: + cmap = "tab10" if n_responses <= 10 else "gist_rainbow" else: - if n_responses <= 10: - cmap = "tab10" - else: - cmap = "gist_rainbow" + cmap = self.multiclass_colors # Special case for the tab10 and tab20 colormaps that encode a # discrete set of colors that are easily distinguishable @@ -241,40 +246,41 @@ def plot(self, plot_method="contourf", ax=None, xlabel=None, ylabel=None, **kwar elif cmap == "tab20" and n_responses <= 20: colors = plt.get_cmap("tab20", 20).colors[:n_responses] else: - colors = plt.get_cmap(cmap, n_responses).colors - elif isinstance(self.multiclass_colors, str): - colors = colors = plt.get_cmap( - self.multiclass_colors, n_responses - ).colors - else: + cmap = plt.get_cmap(cmap, n_responses) + if not hasattr(cmap, "colors"): + # For LinearSegmentedColormap + colors = cmap(np.linspace(0, 1, n_responses)) + else: + colors = cmap.colors + elif isinstance(self.multiclass_colors, list): colors = [mpl.colors.to_rgba(color) for color in self.multiclass_colors] + else: + raise ValueError("'multiclass_colors' must be a list or a str.") self.multiclass_colors_ = colors - multiclass_cmaps = [ - mpl.colors.LinearSegmentedColormap.from_list( - f"colormap_{class_idx}", [(1.0, 1.0, 1.0, 1.0), (r, g, b, 1.0)] - ) - for class_idx, (r, g, b, _) in enumerate(colors) - ] - - self.surface_ = [] - for class_idx, cmap in enumerate(multiclass_cmaps): - response = np.ma.array( - self.response[:, :, class_idx], - mask=~(self.response.argmax(axis=2) == class_idx), + if plot_method == "contour": + # Plot only argmax map for contour + class_map = self.response.argmax(axis=2) + self.surface_ = plot_func( + self.xx0, self.xx1, class_map, colors=colors, **kwargs ) - # `cmap` should not be in kwargs - safe_kwargs = kwargs.copy() - if "cmap" in safe_kwargs: - del safe_kwargs["cmap"] - warnings.warn( - "Plotting max class of multiclass 'decision_function' or " - "'predict_proba', thus 'multiclass_colors' used and " - "'cmap' kwarg ignored." + else: + multiclass_cmaps = [ + mpl.colors.LinearSegmentedColormap.from_list( + f"colormap_{class_idx}", [(1.0, 1.0, 1.0, 1.0), (r, g, b, 1.0)] + ) + for class_idx, (r, g, b, _) in enumerate(colors) + ] + + self.surface_ = [] + for class_idx, cmap in enumerate(multiclass_cmaps): + response = np.ma.array( + self.response[:, :, class_idx], + mask=~(self.response.argmax(axis=2) == class_idx), + ) + self.surface_.append( + plot_func(self.xx0, self.xx1, response, cmap=cmap, **kwargs) ) - self.surface_.append( - plot_func(self.xx0, self.xx1, response, cmap=cmap, **safe_kwargs) - ) if xlabel is not None or not ax.get_xlabel(): xlabel = self.xlabel if xlabel is None else xlabel diff --git a/sklearn/inspection/_plot/partial_dependence.py b/sklearn/inspection/_plot/partial_dependence.py index bf4975cdfd2d9..b31a5070b236b 100644 --- a/sklearn/inspection/_plot/partial_dependence.py +++ b/sklearn/inspection/_plot/partial_dependence.py @@ -25,20 +25,17 @@ class PartialDependenceDisplay: - """Partial Dependence Plot (PDP). - - This can also display individual partial dependencies which are often - referred to as: Individual Condition Expectation (ICE). + """Partial Dependence Plot (PDP) and Individual Conditional Expectation (ICE). It is recommended to use :func:`~sklearn.inspection.PartialDependenceDisplay.from_estimator` to create a - :class:`~sklearn.inspection.PartialDependenceDisplay`. All parameters are - stored as attributes. + :class:`~sklearn.inspection.PartialDependenceDisplay`. All parameters are stored + as attributes. For general information regarding `scikit-learn` visualization tools, see the :ref:`Visualization Guide `. For guidance on interpreting these plots, refer to the - :ref:`Partial Dependence and ICE plots `. + :ref:`Inspection Guide `. For an example on how to use this class, see the following example: :ref:`sphx_glr_auto_examples_miscellaneous_plot_partial_dependence_visualization_api.py`. @@ -280,17 +277,21 @@ def from_estimator( ): """Partial dependence (PD) and individual conditional expectation (ICE) plots. - Partial dependence plots, individual conditional expectation plots or an - overlay of both of them can be plotted by setting the ``kind`` - parameter. The ``len(features)`` plots are arranged in a grid with - ``n_cols`` columns. Two-way partial dependence plots are plotted as - contour plots. The deciles of the feature values will be shown with tick - marks on the x-axes for one-way plots, and on both axes for two-way - plots. - - Read more in - :ref:`sphx_glr_auto_examples_inspection_plot_partial_dependence.py` - and the :ref:`User Guide `. + Partial dependence plots, individual conditional expectation plots, or an + overlay of both can be plotted by setting the `kind` parameter. + This method generates one plot for each entry in `features`. The plots + are arranged in a grid with `n_cols` columns. For one-way partial + dependence plots, the deciles of the feature values are shown on the + x-axis. For two-way plots, the deciles are shown on both axes and PDPs + are contour plots. + + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Inspection Guide `. + + For an example on how to use this class method, see + :ref:`sphx_glr_auto_examples_inspection_plot_partial_dependence.py`. .. note:: diff --git a/sklearn/inspection/_plot/tests/test_boundary_decision_display.py b/sklearn/inspection/_plot/tests/test_boundary_decision_display.py index 3284f42241fa5..f409a50ab58c0 100644 --- a/sklearn/inspection/_plot/tests/test_boundary_decision_display.py +++ b/sklearn/inspection/_plot/tests/test_boundary_decision_display.py @@ -169,6 +169,10 @@ def test_input_validation_errors(pyplot, kwargs, error_msg, fitted_clf): @pytest.mark.parametrize( "kwargs, error_msg", [ + ( + {"multiclass_colors": {"dict": "not_list"}}, + "'multiclass_colors' must be a list or a str.", + ), ({"multiclass_colors": "not_cmap"}, "it must be a valid Matplotlib colormap"), ({"multiclass_colors": ["red", "green"]}, "it must be of the same length"), ( @@ -617,6 +621,7 @@ def test_multiclass_plot_max_class(pyplot, response_method): "multiclass_colors", [ "plasma", + "Blues", ["red", "green", "blue"], ], ) @@ -642,31 +647,51 @@ def test_multiclass_colors_cmap(pyplot, plot_method, multiclass_colors): if multiclass_colors == "plasma": colors = mpl.pyplot.get_cmap(multiclass_colors, len(clf.classes_)).colors + elif multiclass_colors == "Blues": + cmap = mpl.pyplot.get_cmap(multiclass_colors, len(clf.classes_)) + colors = cmap(np.linspace(0, 1, len(clf.classes_))) else: colors = [mpl.colors.to_rgba(color) for color in multiclass_colors] - cmaps = [ - mpl.colors.LinearSegmentedColormap.from_list( - f"colormap_{class_idx}", [(1.0, 1.0, 1.0, 1.0), (r, g, b, 1.0)] - ) - for class_idx, (r, g, b, _) in enumerate(colors) - ] - - for idx, quad in enumerate(disp.surface_): - assert quad.cmap == cmaps[idx] + if plot_method != "contour": + cmaps = [ + mpl.colors.LinearSegmentedColormap.from_list( + f"colormap_{class_idx}", [(1.0, 1.0, 1.0, 1.0), (r, g, b, 1.0)] + ) + for class_idx, (r, g, b, _) in enumerate(colors) + ] + for idx, quad in enumerate(disp.surface_): + assert quad.cmap == cmaps[idx] + else: + assert_allclose(disp.surface_.colors, colors) -def test_multiclass_plot_max_class_cmap_kwarg(pyplot): - """Check `cmap` kwarg ignored when using plotting max multiclass class.""" +def test_cmap_and_colors_logic(pyplot): + """Check the handling logic for `cmap` and `colors`.""" X, y = load_iris_2d_scaled() clf = LogisticRegression().fit(X, y) - msg = ( - "Plotting max class of multiclass 'decision_function' or 'predict_proba', " - "thus 'multiclass_colors' used and 'cmap' kwarg ignored." - ) - with pytest.warns(UserWarning, match=msg): - DecisionBoundaryDisplay.from_estimator(clf, X, cmap="viridis") + with pytest.warns( + UserWarning, + match="'cmap' is ignored in favor of 'multiclass_colors'", + ): + DecisionBoundaryDisplay.from_estimator( + clf, + X, + multiclass_colors="plasma", + cmap="Blues", + ) + + with pytest.warns( + UserWarning, + match="'colors' is ignored in favor of 'multiclass_colors'", + ): + DecisionBoundaryDisplay.from_estimator( + clf, + X, + multiclass_colors="plasma", + colors="blue", + ) def test_subclass_named_constructors_return_type_is_subclass(pyplot): diff --git a/sklearn/linear_model/_bayes.py b/sklearn/linear_model/_bayes.py index adf515d44d1d9..e519660323d80 100644 --- a/sklearn/linear_model/_bayes.py +++ b/sklearn/linear_model/_bayes.py @@ -568,11 +568,6 @@ class ARDRegression(RegressorMixin, LinearModel): -------- BayesianRidge : Bayesian ridge regression. - Notes - ----- - For an example, see :ref:`examples/linear_model/plot_ard.py - `. - References ---------- D. J. C. MacKay, Bayesian nonlinear modeling for the prediction @@ -594,6 +589,12 @@ class ARDRegression(RegressorMixin, LinearModel): ARDRegression() >>> clf.predict([[1, 1]]) array([1.]) + + - :ref:`sphx_glr_auto_examples_linear_model_plot_ard.py` demonstrates ARD + Regression. + - :ref:`sphx_glr_auto_examples_linear_model_plot_lasso_and_elasticnet.py` + showcases ARD Regression alongside Lasso and Elastic-Net for sparse, + correlated signals, in the presence of noise. """ _parameter_constraints: dict = { diff --git a/sklearn/linear_model/_cd_fast.pyx b/sklearn/linear_model/_cd_fast.pyx index c4c530d907e26..ce598ebb011d2 100644 --- a/sklearn/linear_model/_cd_fast.pyx +++ b/sklearn/linear_model/_cd_fast.pyx @@ -83,6 +83,21 @@ cdef floating diff_abs_max(int n, const floating* a, floating* b) noexcept nogil return m +message_conv = ( + "Objective did not converge. You might want to increase " + "the number of iterations, check the scale of the " + "features or consider increasing regularisation." +) + + +message_ridge = ( + "Linear regression models with a zero l1 penalization " + "strength are more efficiently fitted using one of the " + "solvers implemented in " + "sklearn.linear_model.Ridge/RidgeCV instead." +) + + def enet_coordinate_descent( floating[::1] w, floating alpha, @@ -141,7 +156,7 @@ def enet_coordinate_descent( cdef floating R_norm2 cdef floating w_norm2 cdef floating l1_norm - cdef floating const + cdef floating const_ cdef floating A_norm2 cdef unsigned int ii cdef unsigned int n_iter = 0 @@ -227,19 +242,18 @@ def enet_coordinate_descent( w_norm2 = _dot(n_features, &w[0], 1, &w[0], 1) if (dual_norm_XtA > alpha): - const = alpha / dual_norm_XtA - A_norm2 = R_norm2 * (const ** 2) + const_ = alpha / dual_norm_XtA + A_norm2 = R_norm2 * (const_ ** 2) gap = 0.5 * (R_norm2 + A_norm2) else: - const = 1.0 + const_ = 1.0 gap = R_norm2 l1_norm = _asum(n_features, &w[0], 1) - # np.dot(R.T, y) gap += (alpha * l1_norm - - const * _dot(n_samples, &R[0], 1, &y[0], 1) - + 0.5 * beta * (1 + const ** 2) * (w_norm2)) + - const_ * _dot(n_samples, &R[0], 1, &y[0], 1) # np.dot(R.T, y) + + 0.5 * beta * (1 + const_ ** 2) * (w_norm2)) if gap < tol: # return if we reached desired tolerance @@ -249,18 +263,11 @@ def enet_coordinate_descent( # for/else, runs if for doesn't end with a `break` with gil: message = ( - "Objective did not converge. You might want to increase " - "the number of iterations, check the scale of the " - "features or consider increasing regularisation. " - f"Duality gap: {gap:.3e}, tolerance: {tol:.3e}" + message_conv + + f" Duality gap: {gap:.3e}, tolerance: {tol:.3e}" ) if alpha < np.finfo(np.float64).eps: - message += ( - " Linear regression models with null weight for the " - "l1 regularization term are more efficiently fitted " - "using one of the solvers implemented in " - "sklearn.linear_model.Ridge/RidgeCV instead." - ) + message += "\n" + message_ridge warnings.warn(message, ConvergenceWarning) return np.asarray(w), gap, tol, n_iter + 1 @@ -313,53 +320,50 @@ def sparse_enet_coordinate_descent( # that every calculation results as if we had rescaled y and X (and therefore also # X_mean) by sqrt(sample_weight) without actually calculating the square root. # We work with: - # yw = sample_weight + # yw = sample_weight * y # R = sample_weight * residual # norm_cols_X = np.sum(sample_weight * (X - X_mean)**2, axis=0) + if floating is float: + dtype = np.float32 + else: + dtype = np.float64 + # get the data information into easy vars cdef unsigned int n_samples = y.shape[0] cdef unsigned int n_features = w.shape[0] # compute norms of the columns of X - cdef unsigned int ii - cdef floating[:] norm_cols_X - - cdef unsigned int startptr = X_indptr[0] - cdef unsigned int endptr + cdef floating[:] norm_cols_X = np.zeros(n_features, dtype=dtype) # initial value of the residuals # R = y - Zw, weighted version R = sample_weight * (y - Zw) cdef floating[::1] R - cdef floating[::1] XtA + cdef floating[::1] XtA = np.empty(n_features, dtype=dtype) cdef const floating[::1] yw - if floating is float: - dtype = np.float32 - else: - dtype = np.float64 - - norm_cols_X = np.zeros(n_features, dtype=dtype) - XtA = np.zeros(n_features, dtype=dtype) - cdef floating tmp cdef floating w_ii cdef floating d_w_max cdef floating w_max cdef floating d_w_ii + cdef floating gap = tol + 1.0 + cdef floating d_w_tol = tol + cdef floating dual_norm_XtA cdef floating X_mean_ii cdef floating R_sum = 0.0 cdef floating R_norm2 cdef floating w_norm2 - cdef floating A_norm2 cdef floating l1_norm + cdef floating const_ + cdef floating A_norm2 cdef floating normalize_sum - cdef floating gap = tol + 1.0 - cdef floating d_w_tol = tol - cdef floating dual_norm_XtA + cdef unsigned int ii cdef unsigned int jj cdef unsigned int n_iter = 0 cdef unsigned int f_iter + cdef unsigned int startptr = X_indptr[0] + cdef unsigned int endptr cdef uint32_t rand_r_state_seed = rng.randint(0, RAND_R_MAX) cdef uint32_t* rand_r_state = &rand_r_state_seed cdef bint center = False @@ -380,6 +384,7 @@ def sparse_enet_coordinate_descent( center = True break + # R = y - np.dot(X, w) for ii in range(n_features): X_mean_ii = X_mean[ii] endptr = X_indptr[ii + 1] @@ -395,7 +400,9 @@ def sparse_enet_coordinate_descent( if center: for jj in range(n_samples): R[jj] += X_mean_ii * w_ii + R_sum += R[jj] else: + # R = sw * (y - np.dot(X, w)) for jj in range(startptr, endptr): tmp = sample_weight[X_indices[jj]] # second term will be subtracted by loop over range(n_samples) @@ -406,9 +413,14 @@ def sparse_enet_coordinate_descent( for jj in range(n_samples): normalize_sum += sample_weight[jj] * X_mean_ii ** 2 R[jj] += sample_weight[jj] * X_mean_ii * w_ii + R_sum += R[jj] norm_cols_X[ii] = normalize_sum startptr = endptr + # Note: No need to update R_sum from here on because the update terms cancel + # each other: w_ii * np.sum(X[:,ii] - X_mean[ii]) = 0. R_sum is only ever + # needed and calculated if X_mean is provided. + # tol *= np.dot(y, y) # with sample weights: tol *= y @ (sw * y) tol *= _dot(n_samples, &y[0], 1, &yw[0], 1) @@ -454,9 +466,6 @@ def sparse_enet_coordinate_descent( tmp += R[X_indices[jj]] * X_data[jj] if center: - R_sum = 0.0 - for jj in range(n_samples): - R_sum += R[jj] tmp -= R_sum * X_mean_ii if positive and tmp < 0.0: @@ -492,13 +501,8 @@ def sparse_enet_coordinate_descent( # the tolerance: check the duality gap as ultimate stopping # criterion - # sparse X.T / dense R dot product - if center: - R_sum = 0.0 - for jj in range(n_samples): - R_sum += R[jj] - # XtA = X.T @ R - beta * w + # sparse X.T / dense R dot product for ii in range(n_features): XtA[ii] = 0.0 for kk in range(X_indptr[ii], X_indptr[ii + 1]): @@ -526,21 +530,18 @@ def sparse_enet_coordinate_descent( # w_norm2 = np.dot(w, w) w_norm2 = _dot(n_features, &w[0], 1, &w[0], 1) if (dual_norm_XtA > alpha): - const = alpha / dual_norm_XtA - A_norm2 = R_norm2 * const**2 + const_ = alpha / dual_norm_XtA + A_norm2 = R_norm2 * const_**2 gap = 0.5 * (R_norm2 + A_norm2) else: - const = 1.0 + const_ = 1.0 gap = R_norm2 l1_norm = _asum(n_features, &w[0], 1) - gap += (alpha * l1_norm - const * _dot( - n_samples, - &R[0], 1, - &y[0], 1 - ) - + 0.5 * beta * (1 + const ** 2) * w_norm2) + gap += (alpha * l1_norm + - const_ * _dot(n_samples, &R[0], 1, &y[0], 1) # np.dot(R.T, y) + + 0.5 * beta * (1 + const_ ** 2) * w_norm2) if gap < tol: # return if we reached desired tolerance @@ -549,10 +550,13 @@ def sparse_enet_coordinate_descent( else: # for/else, runs if for doesn't end with a `break` with gil: - warnings.warn("Objective did not converge. You might want to " - "increase the number of iterations. Duality " - "gap: {}, tolerance: {}".format(gap, tol), - ConvergenceWarning) + message = ( + message_conv + + f" Duality gap: {gap:.3e}, tolerance: {tol:.3e}" + ) + if alpha < np.finfo(np.float64).eps: + message += "\n" + message_ridge + warnings.warn(message, ConvergenceWarning) return np.asarray(w), gap, tol, n_iter + 1 @@ -702,19 +706,19 @@ def enet_coordinate_descent_gram( w_norm2 = _dot(n_features, &w[0], 1, &w[0], 1) if (dual_norm_XtA > alpha): - const = alpha / dual_norm_XtA - A_norm2 = R_norm2 * (const ** 2) + const_ = alpha / dual_norm_XtA + A_norm2 = R_norm2 * (const_ ** 2) gap = 0.5 * (R_norm2 + A_norm2) else: - const = 1.0 + const_ = 1.0 gap = R_norm2 # The call to asum is equivalent to the L1 norm of w gap += ( alpha * _asum(n_features, &w[0], 1) - - const * y_norm2 - + const * q_dot_w - + 0.5 * beta * (1 + const ** 2) * w_norm2 + - const_ * y_norm2 + + const_ * q_dot_w + + 0.5 * beta * (1 + const_ ** 2) * w_norm2 ) if gap < tol: @@ -724,10 +728,11 @@ def enet_coordinate_descent_gram( else: # for/else, runs if for doesn't end with a `break` with gil: - warnings.warn("Objective did not converge. You might want to " - "increase the number of iterations. Duality " - "gap: {}, tolerance: {}".format(gap, tol), - ConvergenceWarning) + message = ( + message_conv + + f" Duality gap: {gap:.3e}, tolerance: {tol:.3e}" + ) + warnings.warn(message, ConvergenceWarning) return np.asarray(w), gap, tol, n_iter + 1 @@ -921,11 +926,11 @@ def enet_coordinate_descent_multi_task( R_norm = _nrm2(n_samples * n_tasks, &R[0, 0], 1) w_norm = _nrm2(n_features * n_tasks, &W[0, 0], 1) if (dual_norm_XtA > l1_reg): - const = l1_reg / dual_norm_XtA - A_norm = R_norm * const + const_ = l1_reg / dual_norm_XtA + A_norm = R_norm * const_ gap = 0.5 * (R_norm ** 2 + A_norm ** 2) else: - const = 1.0 + const_ = 1.0 gap = R_norm ** 2 # ry_sum = np.sum(R * y) @@ -938,8 +943,8 @@ def enet_coordinate_descent_multi_task( gap += ( l1_reg * l21_norm - - const * ry_sum - + 0.5 * l2_reg * (1 + const ** 2) * (w_norm ** 2) + - const_ * ry_sum + + 0.5 * l2_reg * (1 + const_ ** 2) * (w_norm ** 2) ) if gap <= tol: @@ -948,9 +953,10 @@ def enet_coordinate_descent_multi_task( else: # for/else, runs if for doesn't end with a `break` with gil: - warnings.warn("Objective did not converge. You might want to " - "increase the number of iterations. Duality " - "gap: {}, tolerance: {}".format(gap, tol), - ConvergenceWarning) + message = ( + message_conv + + f" Duality gap: {gap:.3e}, tolerance: {tol:.3e}" + ) + warnings.warn(message, ConvergenceWarning) return np.asarray(W), gap, tol, n_iter + 1 diff --git a/sklearn/linear_model/_coordinate_descent.py b/sklearn/linear_model/_coordinate_descent.py index 62096133ada2f..940ae6f5e3a30 100644 --- a/sklearn/linear_model/_coordinate_descent.py +++ b/sklearn/linear_model/_coordinate_descent.py @@ -875,6 +875,10 @@ class ElasticNet(MultiOutputMixin, RegressorMixin, LinearModel): 1.451 >>> print(regr.predict([[0, 0]])) [1.451] + + - :ref:`sphx_glr_auto_examples_linear_model_plot_lasso_and_elasticnet.py` + showcases ElasticNet alongside Lasso and ARD Regression for sparse + signal recovery in the presence of noise and feature correlation. """ # "check_input" is used for optimisation and isn't something to be passed @@ -1304,6 +1308,11 @@ class Lasso(ElasticNet): [0.85 0. ] >>> print(clf.intercept_) 0.15 + + - :ref:`sphx_glr_auto_examples_linear_model_plot_lasso_and_elasticnet.py` + compares Lasso with other L1-based regression models (ElasticNet and ARD + Regression) for sparse signal recovery in the presence of noise and + feature correlation. """ _parameter_constraints: dict = { diff --git a/sklearn/linear_model/_glm/_newton_solver.py b/sklearn/linear_model/_glm/_newton_solver.py index d7c8ed8f0943d..cfef023692d68 100644 --- a/sklearn/linear_model/_glm/_newton_solver.py +++ b/sklearn/linear_model/_glm/_newton_solver.py @@ -14,6 +14,7 @@ from ..._loss.loss import HalfSquaredError from ...exceptions import ConvergenceWarning +from ...utils.fixes import _get_additional_lbfgs_options_dict from ...utils.optimize import _check_optimize_result from .._linear_loss import LinearModelLoss @@ -178,21 +179,22 @@ def fallback_lbfgs_solve(self, X, y, sample_weight): - self.coef - self.converged """ + max_iter = self.max_iter - self.iteration opt_res = scipy.optimize.minimize( self.linear_loss.loss_gradient, self.coef, method="L-BFGS-B", jac=True, options={ - "maxiter": self.max_iter - self.iteration, + "maxiter": max_iter, "maxls": 50, # default is 20 - "iprint": self.verbose - 1, "gtol": self.tol, "ftol": 64 * np.finfo(np.float64).eps, + **_get_additional_lbfgs_options_dict("iprint", self.verbose - 1), }, args=(X, y, sample_weight, self.l2_reg_strength, self.n_threads), ) - self.iteration += _check_optimize_result("lbfgs", opt_res) + self.iteration += _check_optimize_result("lbfgs", opt_res, max_iter=max_iter) self.coef = opt_res.x self.converged = opt_res.status == 0 @@ -467,6 +469,19 @@ def setup(self, X, y, sample_weight): self.is_multinomial_no_penalty = ( self.linear_loss.base_loss.is_multiclass and self.l2_reg_strength == 0 ) + if self.is_multinomial_no_penalty: + # See inner_solve. The provided coef might not adhere to the convention + # that the last class is set to zero. + # This is done by the usual freedom of a (overparametrized) multinomial to + # add a constant to all classes which doesn't change predictions. + n_classes = self.linear_loss.base_loss.n_classes + coef = self.coef.reshape(n_classes, -1, order="F") # easier as 2d + coef -= coef[-1, :] # coef -= coef of last class + elif self.is_multinomial_with_intercept: + # See inner_solve. Same as above, but only for the intercept. + n_classes = self.linear_loss.base_loss.n_classes + # intercept -= intercept of last class + self.coef[-n_classes:] -= self.coef[-1] def update_gradient_hessian(self, X, y, sample_weight): _, _, self.hessian_warning = self.linear_loss.gradient_hessian( @@ -516,10 +531,10 @@ def inner_solve(self, X, y, sample_weight): # # We choose the standard approach and set all the coefficients of the last # class to zero, for all features including the intercept. + # Note that coef was already dealt with in setup. n_classes = self.linear_loss.base_loss.n_classes n_dof = self.coef.size // n_classes # degree of freedom per class n = self.coef.size - n_dof # effective size - self.coef[n_classes - 1 :: n_classes] = 0 self.gradient[n_classes - 1 :: n_classes] = 0 self.hessian[n_classes - 1 :: n_classes, :] = 0 self.hessian[:, n_classes - 1 :: n_classes] = 0 @@ -542,7 +557,7 @@ def inner_solve(self, X, y, sample_weight): elif self.is_multinomial_with_intercept: # Here, only intercepts are unpenalized. We again choose the last class and # set its intercept to zero. - self.coef[-1] = 0 + # Note that coef was already dealt with in setup. self.gradient[-1] = 0 self.hessian[-1, :] = 0 self.hessian[:, -1] = 0 diff --git a/sklearn/linear_model/_glm/glm.py b/sklearn/linear_model/_glm/glm.py index c9e10c6378bac..8ba24878b95b2 100644 --- a/sklearn/linear_model/_glm/glm.py +++ b/sklearn/linear_model/_glm/glm.py @@ -21,6 +21,7 @@ from ...utils import check_array from ...utils._openmp_helpers import _openmp_effective_n_threads from ...utils._param_validation import Hidden, Interval, StrOptions +from ...utils.fixes import _get_additional_lbfgs_options_dict from ...utils.optimize import _check_optimize_result from ...utils.validation import _check_sample_weight, check_is_fitted, validate_data from .._linear_loss import LinearModelLoss @@ -273,16 +274,18 @@ def fit(self, X, y, sample_weight=None): options={ "maxiter": self.max_iter, "maxls": 50, # default is 20 - "iprint": self.verbose - 1, "gtol": self.tol, # The constant 64 was found empirically to pass the test suite. # The point is that ftol is very small, but a bit larger than # machine precision for float64, which is the dtype used by lbfgs. "ftol": 64 * np.finfo(float).eps, + **_get_additional_lbfgs_options_dict("iprint", self.verbose - 1), }, args=(X, y, sample_weight, l2_reg_strength, n_threads), ) - self.n_iter_ = _check_optimize_result("lbfgs", opt_res) + self.n_iter_ = _check_optimize_result( + "lbfgs", opt_res, max_iter=self.max_iter + ) coef = opt_res.x elif self.solver == "newton-cholesky": sol = NewtonCholeskySolver( diff --git a/sklearn/linear_model/_glm/tests/test_glm.py b/sklearn/linear_model/_glm/tests/test_glm.py index fbcc4d61a8e1c..535651f3242f5 100644 --- a/sklearn/linear_model/_glm/tests/test_glm.py +++ b/sklearn/linear_model/_glm/tests/test_glm.py @@ -8,7 +8,6 @@ import numpy as np import pytest import scipy -from numpy.testing import assert_allclose from scipy import linalg from scipy.optimize import minimize, root @@ -28,6 +27,7 @@ from sklearn.linear_model._linear_loss import LinearModelLoss from sklearn.metrics import d2_tweedie_score, mean_poisson_deviance from sklearn.model_selection import train_test_split +from sklearn.utils._testing import assert_allclose SOLVERS = ["lbfgs", "newton-cholesky"] @@ -636,11 +636,11 @@ def test_glm_identity_regression(fit_intercept): ) if fit_intercept: glm.fit(X[:, 1:], y) - assert_allclose(glm.coef_, coef[1:], rtol=1e-10) - assert_allclose(glm.intercept_, coef[0], rtol=1e-10) + assert_allclose(glm.coef_, coef[1:]) + assert_allclose(glm.intercept_, coef[0]) else: glm.fit(X, y) - assert_allclose(glm.coef_, coef, rtol=1e-12) + assert_allclose(glm.coef_, coef) @pytest.mark.parametrize("fit_intercept", [False, True]) @@ -663,12 +663,12 @@ def test_glm_sample_weight_consistency(fit_intercept, alpha, GLMEstimator): # sample_weight=np.ones(..) should be equivalent to sample_weight=None sample_weight = np.ones(y.shape) glm.fit(X, y, sample_weight=sample_weight) - assert_allclose(glm.coef_, coef, rtol=1e-12) + assert_allclose(glm.coef_, coef) # sample_weight are normalized to 1 so, scaling them has no effect sample_weight = 2 * np.ones(y.shape) glm.fit(X, y, sample_weight=sample_weight) - assert_allclose(glm.coef_, coef, rtol=1e-12) + assert_allclose(glm.coef_, coef) # setting one element of sample_weight to 0 is equivalent to removing # the corresponding sample @@ -677,7 +677,7 @@ def test_glm_sample_weight_consistency(fit_intercept, alpha, GLMEstimator): glm.fit(X, y, sample_weight=sample_weight) coef1 = glm.coef_.copy() glm.fit(X[:-1], y[:-1]) - assert_allclose(glm.coef_, coef1, rtol=1e-12) + assert_allclose(glm.coef_, coef1) # check that multiplying sample_weight by 2 is equivalent # to repeating corresponding samples twice diff --git a/sklearn/linear_model/_huber.py b/sklearn/linear_model/_huber.py index 51f24035a3c83..87e735ec998db 100644 --- a/sklearn/linear_model/_huber.py +++ b/sklearn/linear_model/_huber.py @@ -10,6 +10,7 @@ from ..utils._mask import axis0_safe_slice from ..utils._param_validation import Interval from ..utils.extmath import safe_sparse_dot +from ..utils.fixes import _get_additional_lbfgs_options_dict from ..utils.optimize import _check_optimize_result from ..utils.validation import _check_sample_weight, validate_data from ._base import LinearModel @@ -329,7 +330,11 @@ def fit(self, X, y, sample_weight=None): method="L-BFGS-B", jac=True, args=(X, y, self.epsilon, self.alpha, sample_weight), - options={"maxiter": self.max_iter, "gtol": self.tol, "iprint": -1}, + options={ + "maxiter": self.max_iter, + "gtol": self.tol, + **_get_additional_lbfgs_options_dict("iprint", -1), + }, bounds=bounds, ) diff --git a/sklearn/linear_model/_logistic.py b/sklearn/linear_model/_logistic.py index 89a17b7fffe0d..35cfcee7ce7d1 100644 --- a/sklearn/linear_model/_logistic.py +++ b/sklearn/linear_model/_logistic.py @@ -30,6 +30,7 @@ ) from ..utils._param_validation import Hidden, Interval, StrOptions from ..utils.extmath import row_norms, softmax +from ..utils.fixes import _get_additional_lbfgs_options_dict from ..utils.metadata_routing import ( MetadataRouter, MethodMapping, @@ -194,17 +195,19 @@ def _logistic_regression_path( only supported by the 'saga' solver. intercept_scaling : float, default=1. - Useful only when the solver 'liblinear' is used - and self.fit_intercept is set to True. In this case, x becomes - [x, self.intercept_scaling], + Useful only when the solver `liblinear` is used + and `self.fit_intercept` is set to `True`. In this case, `x` becomes + `[x, self.intercept_scaling]`, i.e. a "synthetic" feature with constant value equal to - intercept_scaling is appended to the instance vector. - The intercept becomes ``intercept_scaling * synthetic_feature_weight``. + `intercept_scaling` is appended to the instance vector. + The intercept becomes + ``intercept_scaling * synthetic_feature_weight``. - Note! the synthetic feature weight is subject to l1/l2 regularization - as all other features. - To lessen the effect of regularization on synthetic feature weight - (and therefore on the intercept) intercept_scaling has to be increased. + .. note:: + The synthetic feature weight is subject to L1 or L2 + regularization as all other features. + To lessen the effect of regularization on synthetic feature weight + (and therefore on the intercept) `intercept_scaling` has to be increased. multi_class : {'ovr', 'multinomial', 'auto'}, default='auto' If the option chosen is 'ovr', then a binary problem is fit for each @@ -337,7 +340,7 @@ def _logistic_regression_path( else: if solver in ["sag", "saga", "lbfgs", "newton-cg", "newton-cholesky"]: - # SAG, lbfgs, newton-cg and newton-cg multinomial solvers need + # SAG, lbfgs, newton-cg and newton-cholesky multinomial solvers need # LabelEncoder, not LabelBinarizer, i.e. y as a 1d-array of integers. # LabelEncoder also saves memory compared to LabelBinarizer, especially # when n_classes is large. @@ -462,9 +465,9 @@ def _logistic_regression_path( options={ "maxiter": max_iter, "maxls": 50, # default is 20 - "iprint": iprint, "gtol": tol, "ftol": 64 * np.finfo(float).eps, + **_get_additional_lbfgs_options_dict("iprint", iprint), }, ) n_iter_i = _check_optimize_result( @@ -692,16 +695,19 @@ def _log_reg_scoring_path( n_samples > n_features. intercept_scaling : float - Useful only when the solver 'liblinear' is used - and self.fit_intercept is set to True. In this case, x becomes - [x, self.intercept_scaling], - i.e. a "synthetic" feature with constant value equals to - intercept_scaling is appended to the instance vector. - The intercept becomes intercept_scaling * synthetic feature weight - Note! the synthetic feature weight is subject to l1/l2 regularization - as all other features. - To lessen the effect of regularization on synthetic feature weight - (and therefore on the intercept) intercept_scaling has to be increased. + Useful only when the solver `liblinear` is used + and `self.fit_intercept` is set to `True`. In this case, `x` becomes + `[x, self.intercept_scaling]`, + i.e. a "synthetic" feature with constant value equal to + `intercept_scaling` is appended to the instance vector. + The intercept becomes + ``intercept_scaling * synthetic_feature_weight``. + + .. note:: + The synthetic feature weight is subject to L1 or L2 + regularization as all other features. + To lessen the effect of regularization on synthetic feature weight + (and therefore on the intercept) `intercept_scaling` has to be increased. multi_class : {'auto', 'ovr', 'multinomial'} If the option chosen is 'ovr', then a binary problem is fit for each @@ -837,9 +843,9 @@ class LogisticRegression(LinearClassifierMixin, SparseCoefMixin, BaseEstimator): the L2 penalty. The Elastic-Net regularization is only supported by the 'saga' solver. - For :term:`multiclass` problems, only 'newton-cg', 'sag', 'saga' and 'lbfgs' - handle multinomial loss. 'liblinear' and 'newton-cholesky' only handle binary - classification but can be extended to handle multiclass by using + For :term:`multiclass` problems, all solvers but 'liblinear' optimize the + (penalized) multinomial loss. 'liblinear' only handle binary classification but can + be extended to handle multiclass by using :class:`~sklearn.multiclass.OneVsRestClassifier`. Read more in the :ref:`User Guide `. @@ -881,17 +887,19 @@ class LogisticRegression(LinearClassifierMixin, SparseCoefMixin, BaseEstimator): added to the decision function. intercept_scaling : float, default=1 - Useful only when the solver 'liblinear' is used - and self.fit_intercept is set to True. In this case, x becomes - [x, self.intercept_scaling], + Useful only when the solver `liblinear` is used + and `self.fit_intercept` is set to `True`. In this case, `x` becomes + `[x, self.intercept_scaling]`, i.e. a "synthetic" feature with constant value equal to - intercept_scaling is appended to the instance vector. - The intercept becomes ``intercept_scaling * synthetic_feature_weight``. + `intercept_scaling` is appended to the instance vector. + The intercept becomes + ``intercept_scaling * synthetic_feature_weight``. - Note! the synthetic feature weight is subject to l1/l2 regularization - as all other features. - To lessen the effect of regularization on synthetic feature weight - (and therefore on the intercept) intercept_scaling has to be increased. + .. note:: + The synthetic feature weight is subject to L1 or L2 + regularization as all other features. + To lessen the effect of regularization on synthetic feature weight + (and therefore on the intercept) `intercept_scaling` has to be increased. class_weight : dict or 'balanced', default=None Weights associated with classes in the form ``{class_label: weight}``. @@ -957,13 +965,14 @@ class LogisticRegression(LinearClassifierMixin, SparseCoefMixin, BaseEstimator): summarizing solver/penalty supports. .. versionadded:: 0.17 - Stochastic Average Gradient descent solver. + Stochastic Average Gradient (SAG) descent solver. Multinomial support in + version 0.18. .. versionadded:: 0.19 SAGA solver. .. versionchanged:: 0.22 - The default solver changed from 'liblinear' to 'lbfgs' in 0.22. + The default solver changed from 'liblinear' to 'lbfgs' in 0.22. .. versionadded:: 1.2 - newton-cholesky solver. + newton-cholesky solver. Multinomial support in version 1.6. max_iter : int, default=100 Maximum number of iterations taken for the solvers to converge. @@ -981,7 +990,7 @@ class LogisticRegression(LinearClassifierMixin, SparseCoefMixin, BaseEstimator): .. versionchanged:: 0.22 Default changed from 'ovr' to 'auto' in 0.22. .. deprecated:: 1.5 - ``multi_class`` was deprecated in version 1.5 and will be removed in 1.7. + ``multi_class`` was deprecated in version 1.5 and will be removed in 1.8. From then on, the recommended 'multinomial' will always be used for `n_classes >= 3`. Solvers that do not support 'multinomial' will raise an error. @@ -1253,7 +1262,7 @@ def fit(self, X, y, sample_weight=None): warnings.warn( ( "'multi_class' was deprecated in version 1.5 and will be removed in" - " 1.7. From then on, binary problems will be fit as proper binary " + " 1.8. From then on, binary problems will be fit as proper binary " " logistic regression models (as if multi_class='ovr' were set)." " Leave it to its default value to avoid this warning." ), @@ -1263,7 +1272,7 @@ def fit(self, X, y, sample_weight=None): warnings.warn( ( "'multi_class' was deprecated in version 1.5 and will be removed in" - " 1.7. From then on, it will always use 'multinomial'." + " 1.8. From then on, it will always use 'multinomial'." " Leave it to its default value to avoid this warning." ), FutureWarning, @@ -1272,7 +1281,7 @@ def fit(self, X, y, sample_weight=None): warnings.warn( ( "'multi_class' was deprecated in version 1.5 and will be removed in" - " 1.7. Use OneVsRestClassifier(LogisticRegression(..)) instead." + " 1.8. Use OneVsRestClassifier(LogisticRegression(..)) instead." " Leave it to its default value to avoid this warning." ), FutureWarning, @@ -1597,11 +1606,12 @@ class LogisticRegressionCV(LogisticRegression, LinearClassifierMixin, BaseEstima a scaler from :mod:`sklearn.preprocessing`. .. versionadded:: 0.17 - Stochastic Average Gradient descent solver. + Stochastic Average Gradient (SAG) descent solver. Multinomial support in + version 0.18. .. versionadded:: 0.19 SAGA solver. .. versionadded:: 1.2 - newton-cholesky solver. + newton-cholesky solver. Multinomial support in version 1.6. tol : float, default=1e-4 Tolerance for stopping criteria. @@ -1641,17 +1651,19 @@ class LogisticRegressionCV(LogisticRegression, LinearClassifierMixin, BaseEstima best scores across folds are averaged. intercept_scaling : float, default=1 - Useful only when the solver 'liblinear' is used - and self.fit_intercept is set to True. In this case, x becomes - [x, self.intercept_scaling], + Useful only when the solver `liblinear` is used + and `self.fit_intercept` is set to `True`. In this case, `x` becomes + `[x, self.intercept_scaling]`, i.e. a "synthetic" feature with constant value equal to - intercept_scaling is appended to the instance vector. - The intercept becomes ``intercept_scaling * synthetic_feature_weight``. + `intercept_scaling` is appended to the instance vector. + The intercept becomes + ``intercept_scaling * synthetic_feature_weight``. - Note! the synthetic feature weight is subject to l1/l2 regularization - as all other features. - To lessen the effect of regularization on synthetic feature weight - (and therefore on the intercept) intercept_scaling has to be increased. + .. note:: + The synthetic feature weight is subject to L1 or L2 + regularization as all other features. + To lessen the effect of regularization on synthetic feature weight + (and therefore on the intercept) `intercept_scaling` has to be increased. multi_class : {'auto, 'ovr', 'multinomial'}, default='auto' If the option chosen is 'ovr', then a binary problem is fit for each @@ -1666,7 +1678,7 @@ class LogisticRegressionCV(LogisticRegression, LinearClassifierMixin, BaseEstima .. versionchanged:: 0.22 Default changed from 'ovr' to 'auto' in 0.22. .. deprecated:: 1.5 - ``multi_class`` was deprecated in version 1.5 and will be removed in 1.7. + ``multi_class`` was deprecated in version 1.5 and will be removed in 1.8. From then on, the recommended 'multinomial' will always be used for `n_classes >= 3`. Solvers that do not support 'multinomial' will raise an error. @@ -1924,7 +1936,7 @@ def fit(self, X, y, sample_weight=None, **params): warnings.warn( ( "'multi_class' was deprecated in version 1.5 and will be removed in" - " 1.7. From then on, binary problems will be fit as proper binary " + " 1.8. From then on, binary problems will be fit as proper binary " " logistic regression models (as if multi_class='ovr' were set)." " Leave it to its default value to avoid this warning." ), @@ -1934,7 +1946,7 @@ def fit(self, X, y, sample_weight=None, **params): warnings.warn( ( "'multi_class' was deprecated in version 1.5 and will be removed in" - " 1.7. From then on, it will always use 'multinomial'." + " 1.8. From then on, it will always use 'multinomial'." " Leave it to its default value to avoid this warning." ), FutureWarning, @@ -1943,7 +1955,7 @@ def fit(self, X, y, sample_weight=None, **params): warnings.warn( ( "'multi_class' was deprecated in version 1.5 and will be removed in" - " 1.7. Use OneVsRestClassifier(LogisticRegressionCV(..)) instead." + " 1.8. Use OneVsRestClassifier(LogisticRegressionCV(..)) instead." " Leave it to its default value to avoid this warning." ), FutureWarning, diff --git a/sklearn/linear_model/tests/test_logistic.py b/sklearn/linear_model/tests/test_logistic.py index bbb291facdaf9..007c900dd3677 100644 --- a/sklearn/linear_model/tests/test_logistic.py +++ b/sklearn/linear_model/tests/test_logistic.py @@ -444,7 +444,7 @@ def test_logistic_regression_path_convergence_fail(): assert len(record) == 1 warn_msg = record[0].message.args[0] - assert "lbfgs failed to converge" in warn_msg + assert "lbfgs failed to converge after 1 iteration(s)" in warn_msg assert "Increase the number of iterations" in warn_msg assert "scale the data" in warn_msg assert "linear_model.html#logistic-regression" in warn_msg @@ -1402,9 +1402,7 @@ def test_n_iter(solver): assert clf_cv.n_iter_.shape == (1, n_cv_fold, n_Cs) -@pytest.mark.parametrize( - "solver", sorted(set(SOLVERS) - set(["liblinear", "newton-cholesky"])) -) +@pytest.mark.parametrize("solver", sorted(set(SOLVERS) - set(["liblinear"]))) @pytest.mark.parametrize("warm_start", (True, False)) @pytest.mark.parametrize("fit_intercept", (True, False)) def test_warm_start(solver, warm_start, fit_intercept): @@ -1437,6 +1435,40 @@ def test_warm_start(solver, warm_start, fit_intercept): assert cum_diff > 2.0, msg +@pytest.mark.parametrize("solver", ["newton-cholesky", "newton-cg"]) +@pytest.mark.parametrize("fit_intercept", (True, False)) +@pytest.mark.parametrize("penalty", ("l2", None)) +def test_warm_start_newton_solver(global_random_seed, solver, fit_intercept, penalty): + """Test that 2 steps at once are the same as 2 single steps with warm start.""" + X, y = iris.data, iris.target + + clf1 = LogisticRegression( + solver=solver, + max_iter=2, + fit_intercept=fit_intercept, + penalty=penalty, + random_state=global_random_seed, + ) + with ignore_warnings(category=ConvergenceWarning): + clf1.fit(X, y) + + clf2 = LogisticRegression( + solver=solver, + max_iter=1, + warm_start=True, + fit_intercept=fit_intercept, + penalty=penalty, + random_state=global_random_seed, + ) + with ignore_warnings(category=ConvergenceWarning): + clf2.fit(X, y) + clf2.fit(X, y) + + assert_allclose(clf2.coef_, clf1.coef_) + if fit_intercept: + assert_allclose(clf2.intercept_, clf1.intercept_) + + @pytest.mark.parametrize("csr_container", CSR_CONTAINERS) def test_saga_vs_liblinear(csr_container): iris = load_iris() diff --git a/sklearn/linear_model/tests/test_ridge.py b/sklearn/linear_model/tests/test_ridge.py index 60b8a8bb3e144..24515195fb7cc 100644 --- a/sklearn/linear_model/tests/test_ridge.py +++ b/sklearn/linear_model/tests/test_ridge.py @@ -750,9 +750,8 @@ def _make_sparse_offset_regression( "n_samples,dtype,proportion_nonzero", [(20, "float32", 0.1), (40, "float32", 1.0), (20, "float64", 0.2)], ) -@pytest.mark.parametrize("seed", np.arange(3)) def test_solver_consistency( - solver, proportion_nonzero, n_samples, dtype, sparse_container, seed + solver, proportion_nonzero, n_samples, dtype, sparse_container, global_random_seed ): alpha = 1.0 noise = 50.0 if proportion_nonzero > 0.9 else 500.0 @@ -761,10 +760,9 @@ def test_solver_consistency( n_features=30, proportion_nonzero=proportion_nonzero, noise=noise, - random_state=seed, + random_state=global_random_seed, n_samples=n_samples, ) - # Manually scale the data to avoid pathological cases. We use # minmax_scale to deal with the sparse case without breaking # the sparsity pattern. @@ -778,7 +776,21 @@ def test_solver_consistency( if solver == "ridgecv": ridge = RidgeCV(alphas=[alpha]) else: - ridge = Ridge(solver=solver, tol=1e-10, alpha=alpha) + if solver.startswith("sag"): + # Avoid ConvergenceWarning for sag and saga solvers. + tol = 1e-7 + max_iter = 100_000 + else: + tol = 1e-10 + max_iter = None + + ridge = Ridge( + alpha=alpha, + solver=solver, + max_iter=max_iter, + tol=tol, + random_state=global_random_seed, + ) ridge.fit(X, y) assert_allclose(ridge.coef_, svd_ridge.coef_, atol=1e-3, rtol=1e-3) assert_allclose(ridge.intercept_, svd_ridge.intercept_, atol=1e-3, rtol=1e-3) diff --git a/sklearn/manifold/_locally_linear.py b/sklearn/manifold/_locally_linear.py index e6967446274ad..7e3f456f7ca57 100644 --- a/sklearn/manifold/_locally_linear.py +++ b/sklearn/manifold/_locally_linear.py @@ -224,7 +224,7 @@ def _locally_linear_embedding( ) if n_neighbors >= N: raise ValueError( - "Expected n_neighbors <= n_samples, but n_samples = %d, n_neighbors = %d" + "Expected n_neighbors < n_samples, but n_samples = %d, n_neighbors = %d" % (N, n_neighbors) ) diff --git a/sklearn/meson.build b/sklearn/meson.build index 93de0c18d99f9..966da14c1338b 100644 --- a/sklearn/meson.build +++ b/sklearn/meson.build @@ -180,6 +180,8 @@ else: check: true ).stdout().strip() +cython_program = find_program(cython.cmd_array()[0]) + scikit_learn_cython_args = [ '-X language_level=3', '-X boundscheck=' + boundscheck, '-X wraparound=False', '-X initializedcheck=False', '-X nonecheck=False', '-X cdivision=True', @@ -190,7 +192,25 @@ scikit_learn_cython_args = [ ] cython_args += scikit_learn_cython_args -cython_program = find_program(cython.cmd_array()[0]) +if cython.version().version_compare('>=3.1.0') + cython_shared_src = custom_target( + install: false, + output: '_cyutility.c', + command: [ + cython_program, '-3', '--fast-fail', + '--generate-shared=' + meson.current_build_dir()/'_cyutility.c' + ], + ) + + py.extension_module('_cyutility', + cython_shared_src, + subdir: 'sklearn', + cython_args: cython_args, + install: true, + ) + + cython_args += ['--shared=sklearn._cyutility'] +endif cython_gen = generator(cython_program, arguments : cython_args + ['@INPUT@', '--output-file', '@OUTPUT@'], @@ -202,7 +222,6 @@ cython_gen_cpp = generator(cython_program, output : '@BASENAME@.cpp', ) - # Write file in Meson build dir to be able to figure out from Python code # whether scikit-learn was built with Meson. Adapted from pandas # _version_meson.py. @@ -219,7 +238,7 @@ extensions = ['_isotonic'] py.extension_module( '_isotonic', - '_isotonic.pyx', + cython_gen.process('_isotonic.pyx'), cython_args: cython_args, install: true, subdir: 'sklearn', diff --git a/sklearn/metrics/_classification.py b/sklearn/metrics/_classification.py index 65cbfbad6f01f..06503046790be 100644 --- a/sklearn/metrics/_classification.py +++ b/sklearn/metrics/_classification.py @@ -831,8 +831,8 @@ class labels [2]_. List of labels to index the matrix. This may be used to select a subset of labels. If `None`, all labels that appear at least once in ``y1`` or ``y2`` are used. Note that at least one label in `labels` must be - present in `y1`, even though this function is otherwise agnostic to the order - of `y1` and `y2`. + present in `y1`, even though this function is otherwise agnostic to the order + of `y1` and `y2`. weights : {'linear', 'quadratic'}, default=None Weighting type to calculate the score. `None` means not weighted; @@ -1071,9 +1071,10 @@ def jaccard_score( numerator = MCM[:, 1, 1] denominator = MCM[:, 1, 1] + MCM[:, 0, 1] + MCM[:, 1, 0] + xp, _, device_ = get_namespace_and_device(y_true, y_pred) if average == "micro": - numerator = np.array([numerator.sum()]) - denominator = np.array([denominator.sum()]) + numerator = xp.asarray(xp.sum(numerator, keepdims=True), device=device_) + denominator = xp.asarray(xp.sum(denominator, keepdims=True), device=device_) jaccard = _prf_divide( numerator, @@ -1088,14 +1089,14 @@ def jaccard_score( return jaccard if average == "weighted": weights = MCM[:, 1, 0] + MCM[:, 1, 1] - if not np.any(weights): + if not xp.any(weights): # numerator is 0, and warning should have already been issued weights = None elif average == "samples" and sample_weight is not None: weights = sample_weight else: weights = None - return float(np.average(jaccard, weights=weights)) + return float(_average(jaccard, weights=weights, xp=xp)) @validate_params( @@ -1626,6 +1627,11 @@ def fbeta_score( returns 0.0 and raises ``UndefinedMetricWarning``. This behavior can be modified by setting ``zero_division``. + F-beta score is not implemented as a named scorer that can be passed to + the `scoring` parameter of cross-validation tools directly: it requires to be + wrapped with :func:`make_scorer` so as to specify the value of `beta`. See + examples for details. + References ---------- .. [1] R. Baeza-Yates and B. Ribeiro-Neto (2011). @@ -1649,9 +1655,29 @@ def fbeta_score( >>> fbeta_score(y_true, y_pred, average=None, beta=0.5) array([0.71, 0. , 0. ]) >>> y_pred_empty = [0, 0, 0, 0, 0, 0] - >>> fbeta_score(y_true, y_pred_empty, - ... average="macro", zero_division=np.nan, beta=0.5) + >>> fbeta_score( + ... y_true, + ... y_pred_empty, + ... average="macro", + ... zero_division=np.nan, + ... beta=0.5, + ... ) 0.128 + + In order to use :func:`fbeta_scorer` as a scorer, a callable + scorer objects needs to be created first with :func:`make_scorer`, + passing the value for the `beta` parameter. + + >>> from sklearn.metrics import fbeta_score, make_scorer + >>> ftwo_scorer = make_scorer(fbeta_score, beta=2) + >>> from sklearn.model_selection import GridSearchCV + >>> from sklearn.svm import LinearSVC + >>> grid = GridSearchCV( + ... LinearSVC(dual="auto"), + ... param_grid={'C': [1, 10]}, + ... scoring=ftwo_scorer, + ... cv=5 + ... ) """ _, _, f, _ = precision_recall_fscore_support( @@ -3513,7 +3539,7 @@ def brier_score_loss( When True, scale the Brier score by 1/2 to lie in the [0, 1] range instead of the [0, 2] range. The default "auto" option implements the rescaling to [0, 1] only for binary classification (as customary) but keeps the - original [0, 2] range for multiclasss classification. + original [0, 2] range for multiclass classification. .. versionadded:: 1.7 diff --git a/sklearn/metrics/_plot/confusion_matrix.py b/sklearn/metrics/_plot/confusion_matrix.py index 63a5382f3fa2b..cee515bebe08e 100644 --- a/sklearn/metrics/_plot/confusion_matrix.py +++ b/sklearn/metrics/_plot/confusion_matrix.py @@ -15,13 +15,16 @@ class ConfusionMatrixDisplay: """Confusion Matrix visualization. - It is recommend to use + It is recommended to use :func:`~sklearn.metrics.ConfusionMatrixDisplay.from_estimator` or :func:`~sklearn.metrics.ConfusionMatrixDisplay.from_predictions` to create a :class:`ConfusionMatrixDisplay`. All parameters are stored as attributes. - Read more in the :ref:`User Guide `. + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Model Evaluation Guide `. Parameters ---------- @@ -220,7 +223,10 @@ def from_estimator( ): """Plot Confusion Matrix given an estimator and some data. - Read more in the :ref:`User Guide `. + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Model Evaluation Guide `. .. versionadded:: 1.0 @@ -365,7 +371,10 @@ def from_predictions( ): """Plot Confusion Matrix given true and predicted labels. - Read more in the :ref:`User Guide `. + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Model Evaluation Guide `. .. versionadded:: 1.0 diff --git a/sklearn/metrics/_plot/det_curve.py b/sklearn/metrics/_plot/det_curve.py index f15fe0ae9e889..590b908d91723 100644 --- a/sklearn/metrics/_plot/det_curve.py +++ b/sklearn/metrics/_plot/det_curve.py @@ -11,11 +11,14 @@ class DetCurveDisplay(_BinaryClassifierCurveDisplayMixin): """Detection Error Tradeoff (DET) curve visualization. - It is recommend to use :func:`~sklearn.metrics.DetCurveDisplay.from_estimator` + It is recommended to use :func:`~sklearn.metrics.DetCurveDisplay.from_estimator` or :func:`~sklearn.metrics.DetCurveDisplay.from_predictions` to create a visualizer. All parameters are stored as attributes. - Read more in the :ref:`User Guide `. + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Model Evaluation Guide `. .. versionadded:: 0.24 @@ -96,7 +99,10 @@ def from_estimator( ): """Plot DET curve given an estimator and data. - Read more in the :ref:`User Guide `. + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Model Evaluation Guide `. .. versionadded:: 1.0 @@ -207,7 +213,10 @@ def from_predictions( ): """Plot the DET curve given the true and predicted labels. - Read more in the :ref:`User Guide `. + For general information regarding `scikit-learn` visualization tools, see + the :ref:`Visualization Guide `. + For guidance on interpreting these plots, refer to the + :ref:`Model Evaluation Guide `. .. versionadded:: 1.0 diff --git a/sklearn/metrics/_plot/precision_recall_curve.py b/sklearn/metrics/_plot/precision_recall_curve.py index 286fc26d0e208..30dd1fba08761 100644 --- a/sklearn/metrics/_plot/precision_recall_curve.py +++ b/sklearn/metrics/_plot/precision_recall_curve.py @@ -14,7 +14,7 @@ class PrecisionRecallDisplay(_BinaryClassifierCurveDisplayMixin): """Precision Recall visualization. - It is recommend to use + It is recommended to use :func:`~sklearn.metrics.PrecisionRecallDisplay.from_estimator` or :func:`~sklearn.metrics.PrecisionRecallDisplay.from_predictions` to create a :class:`~sklearn.metrics.PrecisionRecallDisplay`. All parameters are diff --git a/sklearn/metrics/_plot/roc_curve.py b/sklearn/metrics/_plot/roc_curve.py index 4a198080e0d0a..383f14e688859 100644 --- a/sklearn/metrics/_plot/roc_curve.py +++ b/sklearn/metrics/_plot/roc_curve.py @@ -1,22 +1,31 @@ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause + import warnings +import numpy as np + +from ...utils import _safe_indexing from ...utils._plotting import ( _BinaryClassifierCurveDisplayMixin, + _check_param_lengths, + _convert_to_list_leaving_none, + _deprecate_estimator_name, _despine, _validate_style_kwargs, ) +from ...utils._response import _get_response_values_binary from .._ranking import auc, roc_curve class RocCurveDisplay(_BinaryClassifierCurveDisplayMixin): """ROC Curve visualization. - It is recommend to use + It is recommended to use :func:`~sklearn.metrics.RocCurveDisplay.from_estimator` or - :func:`~sklearn.metrics.RocCurveDisplay.from_predictions` to create + :func:`~sklearn.metrics.RocCurveDisplay.from_predictions` or + :func:`~sklearn.metrics.RocCurveDisplay.from_cv_results` to create a :class:`~sklearn.metrics.RocCurveDisplay`. All parameters are stored as attributes. @@ -27,17 +36,39 @@ class RocCurveDisplay(_BinaryClassifierCurveDisplayMixin): Parameters ---------- - fpr : ndarray - False positive rate. + fpr : ndarray or list of ndarrays + False positive rates. Each ndarray should contain values for a single curve. + If plotting multiple curves, list should be of same length as `tpr`. - tpr : ndarray - True positive rate. + .. versionchanged:: 1.7 + Now accepts a list for plotting multiple curves. - roc_auc : float, default=None - Area under ROC curve. If None, the roc_auc score is not shown. + tpr : ndarray or list of ndarrays + True positive rates. Each ndarray should contain values for a single curve. + If plotting multiple curves, list should be of same length as `fpr`. - estimator_name : str, default=None - Name of estimator. If None, the estimator name is not shown. + .. versionchanged:: 1.7 + Now accepts a list for plotting multiple curves. + + roc_auc : float or list of floats, default=None + Area under ROC curve, used for labeling each curve in the legend. + If plotting multiple curves, should be a list of the same length as `fpr` + and `tpr`. If `None`, ROC AUC scores are not shown in the legend. + + .. versionchanged:: 1.7 + Now accepts a list for plotting multiple curves. + + name : str or list of str, default=None + Name for labeling legend entries. The number of legend entries is determined + by the `curve_kwargs` passed to `plot`, and is not affected by `name`. + To label each curve, provide a list of strings. To avoid labeling + individual curves that have the same appearance, this cannot be used in + conjunction with `curve_kwargs` being a dictionary or None. If a + string is provided, it will be used to either label the single legend entry + or if there are multiple legend entries, label each individual curve with + the same name. If still `None`, no name is shown in the legend. + + .. versionadded:: 1.7 pos_label : int, float, bool or str, default=None The class considered as the positive class when computing the roc auc @@ -46,10 +77,21 @@ class RocCurveDisplay(_BinaryClassifierCurveDisplayMixin): .. versionadded:: 0.24 + estimator_name : str, default=None + Name of estimator. If None, the estimator name is not shown. + + .. deprecated:: 1.7 + `estimator_name` is deprecated and will be removed in 1.9. Use `name` + instead. + Attributes ---------- - line_ : matplotlib Artist - ROC Curve. + line_ : matplotlib Artist or list of matplotlib Artists + ROC Curves. + + .. versionchanged:: 1.7 + This attribute can now be a list of Artists, for when multiple curves + are plotted. chance_level_ : matplotlib Artist or None The chance level line. It is `None` if the chance level is not plotted. @@ -81,24 +123,52 @@ class RocCurveDisplay(_BinaryClassifierCurveDisplayMixin): >>> fpr, tpr, thresholds = metrics.roc_curve(y_true, y_score) >>> roc_auc = metrics.auc(fpr, tpr) >>> display = metrics.RocCurveDisplay(fpr=fpr, tpr=tpr, roc_auc=roc_auc, - ... estimator_name='example estimator') + ... name='example estimator') >>> display.plot() <...> >>> plt.show() """ - def __init__(self, *, fpr, tpr, roc_auc=None, estimator_name=None, pos_label=None): - self.estimator_name = estimator_name + def __init__( + self, + *, + fpr, + tpr, + roc_auc=None, + name=None, + pos_label=None, + estimator_name="deprecated", + ): self.fpr = fpr self.tpr = tpr self.roc_auc = roc_auc + self.name = _deprecate_estimator_name(estimator_name, name, "1.7") self.pos_label = pos_label + def _validate_plot_params(self, *, ax, name): + self.ax_, self.figure_, name = super()._validate_plot_params(ax=ax, name=name) + + fpr = _convert_to_list_leaving_none(self.fpr) + tpr = _convert_to_list_leaving_none(self.tpr) + roc_auc = _convert_to_list_leaving_none(self.roc_auc) + name = _convert_to_list_leaving_none(name) + + optional = {"self.roc_auc": roc_auc} + if isinstance(name, list) and len(name) != 1: + optional.update({"'name' (or self.name)": name}) + _check_param_lengths( + required={"self.fpr": fpr, "self.tpr": tpr}, + optional=optional, + class_name="RocCurveDisplay", + ) + return fpr, tpr, roc_auc, name + def plot( self, ax=None, *, name=None, + curve_kwargs=None, plot_chance_level=False, chance_level_kw=None, despine=False, @@ -106,17 +176,36 @@ def plot( ): """Plot visualization. - Extra keyword arguments will be passed to matplotlib's ``plot``. - Parameters ---------- ax : matplotlib axes, default=None Axes object to plot on. If `None`, a new figure and axes is created. - name : str, default=None - Name of ROC Curve for labeling. If `None`, use `estimator_name` if - not `None`, otherwise no labeling is shown. + name : str or list of str, default=None + Name for labeling legend entries. The number of legend entries + is determined by `curve_kwargs`, and is not affected by `name`. + To label each curve, provide a list of strings. To avoid labeling + individual curves that have the same appearance, this cannot be used in + conjunction with `curve_kwargs` being a dictionary or None. If a + string is provided, it will be used to either label the single legend entry + or if there are multiple legend entries, label each individual curve with + the same name. If `None`, set to `name` provided at `RocCurveDisplay` + initialization. If still `None`, no name is shown in the legend. + + .. versionadded:: 1.7 + + curve_kwargs : dict or list of dict, default=None + Keywords arguments to be passed to matplotlib's `plot` function + to draw individual ROC curves. For single curve plotting, should be + a dictionary. For multi-curve plotting, if a list is provided the + parameters are applied to the ROC curves of each CV fold + sequentially and a legend entry is added for each curve. + If a single dictionary is provided, the same parameters are applied + to all ROC curves and a single legend entry for all curves is added, + labeled with the mean ROC AUC score. + + .. versionadded:: 1.7 plot_chance_level : bool, default=False Whether to plot the chance level. @@ -137,22 +226,34 @@ def plot( **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. + .. deprecated:: 1.7 + kwargs is deprecated and will be removed in 1.9. Pass matplotlib + arguments to `curve_kwargs` as a dictionary instead. + Returns ------- display : :class:`~sklearn.metrics.RocCurveDisplay` Object that stores computed values. """ - self.ax_, self.figure_, name = self._validate_plot_params(ax=ax, name=name) - - default_line_kwargs = {} - if self.roc_auc is not None and name is not None: - default_line_kwargs["label"] = f"{name} (AUC = {self.roc_auc:0.2f})" - elif self.roc_auc is not None: - default_line_kwargs["label"] = f"AUC = {self.roc_auc:0.2f}" - elif name is not None: - default_line_kwargs["label"] = name - - line_kwargs = _validate_style_kwargs(default_line_kwargs, kwargs) + fpr, tpr, roc_auc, name = self._validate_plot_params(ax=ax, name=name) + n_curves = len(fpr) + if not isinstance(curve_kwargs, list) and n_curves > 1: + if roc_auc: + legend_metric = {"mean": np.mean(roc_auc), "std": np.std(roc_auc)} + else: + legend_metric = {"mean": None, "std": None} + else: + roc_auc = roc_auc if roc_auc is not None else [None] * n_curves + legend_metric = {"metric": roc_auc} + + curve_kwargs = self._validate_curve_kwargs( + n_curves, + name, + legend_metric, + "AUC", + curve_kwargs=curve_kwargs, + **kwargs, + ) default_chance_level_line_kw = { "label": "Chance level (AUC = 0.5)", @@ -167,7 +268,13 @@ def plot( default_chance_level_line_kw, chance_level_kw ) - (self.line_,) = self.ax_.plot(self.fpr, self.tpr, **line_kwargs) + self.line_ = [] + for fpr, tpr, line_kw in zip(fpr, tpr, curve_kwargs): + self.line_.extend(self.ax_.plot(fpr, tpr, **line_kw)) + # Return single artist if only one curve is plotted + if len(self.line_) == 1: + self.line_ = self.line_[0] + info_pos_label = ( f" (Positive label: {self.pos_label})" if self.pos_label is not None else "" ) @@ -190,9 +297,8 @@ def plot( if despine: _despine(self.ax_) - if ( - line_kwargs.get("label") is not None - or chance_level_kw.get("label") is not None + if curve_kwargs[0].get("label") is not None or ( + plot_chance_level and chance_level_kw.get("label") is not None ): self.ax_.legend(loc="lower right") @@ -211,6 +317,7 @@ def from_estimator( pos_label=None, name=None, ax=None, + curve_kwargs=None, plot_chance_level=False, chance_level_kw=None, despine=False, @@ -252,8 +359,8 @@ def from_estimator( :term:`decision_function` is tried next. pos_label : int, float, bool or str, default=None - The class considered as the positive class when computing the roc auc - metrics. By default, `estimators.classes_[1]` is considered + The class considered as the positive class when computing the ROC AUC. + By default, `estimators.classes_[1]` is considered as the positive class. name : str, default=None @@ -263,6 +370,11 @@ def from_estimator( ax : matplotlib axes, default=None Axes object to plot on. If `None`, a new figure and axes is created. + curve_kwargs : dict, default=None + Keywords arguments to be passed to matplotlib's `plot` function. + + .. versionadded:: 1.7 + plot_chance_level : bool, default=False Whether to plot the chance level. @@ -282,6 +394,10 @@ def from_estimator( **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. + .. deprecated:: 1.7 + kwargs is deprecated and will be removed in 1.9. Pass matplotlib + arguments to `curve_kwargs` as a dictionary instead. + Returns ------- display : :class:`~sklearn.metrics.RocCurveDisplay` @@ -324,9 +440,10 @@ def from_estimator( y_score=y_score, sample_weight=sample_weight, drop_intermediate=drop_intermediate, + pos_label=pos_label, name=name, ax=ax, - pos_label=pos_label, + curve_kwargs=curve_kwargs, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, despine=despine, @@ -344,6 +461,7 @@ def from_predictions( pos_label=None, name=None, ax=None, + curve_kwargs=None, plot_chance_level=False, chance_level_kw=None, despine=False, @@ -382,18 +500,23 @@ def from_predictions( points. pos_label : int, float, bool or str, default=None - The label of the positive class. When `pos_label=None`, if `y_true` - is in {-1, 1} or {0, 1}, `pos_label` is set to 1, otherwise an - error will be raised. + The label of the positive class when computing the ROC AUC. + When `pos_label=None`, if `y_true` is in {-1, 1} or {0, 1}, `pos_label` + is set to 1, otherwise an error will be raised. name : str, default=None - Name of ROC curve for labeling. If `None`, name will be set to + Name of ROC curve for legend labeling. If `None`, name will be set to `"Classifier"`. ax : matplotlib axes, default=None Axes object to plot on. If `None`, a new figure and axes is created. + curve_kwargs : dict, default=None + Keywords arguments to be passed to matplotlib's `plot` function. + + .. versionadded:: 1.7 + plot_chance_level : bool, default=False Whether to plot the chance level. @@ -422,6 +545,10 @@ def from_predictions( **kwargs : dict Additional keywords arguments passed to matplotlib `plot` function. + .. deprecated:: 1.7 + kwargs is deprecated and will be removed in 1.9. Pass matplotlib + arguments to `curve_kwargs` as a dictionary instead. + Returns ------- display : :class:`~sklearn.metrics.RocCurveDisplay` @@ -485,15 +612,184 @@ def from_predictions( fpr=fpr, tpr=tpr, roc_auc=roc_auc, - estimator_name=name, + name=name, pos_label=pos_label_validated, ) return viz.plot( ax=ax, - name=name, + curve_kwargs=curve_kwargs, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, despine=despine, **kwargs, ) + + @classmethod + def from_cv_results( + cls, + cv_results, + X, + y, + *, + sample_weight=None, + drop_intermediate=True, + response_method="auto", + pos_label=None, + ax=None, + name=None, + curve_kwargs=None, + plot_chance_level=False, + chance_level_kwargs=None, + despine=False, + ): + """Create a multi-fold ROC curve display given cross-validation results. + + .. versionadded:: 1.7 + + Parameters + ---------- + cv_results : dict + Dictionary as returned by :func:`~sklearn.model_selection.cross_validate` + using `return_estimator=True` and `return_indices=True` (i.e., dictionary + should contain the keys "estimator" and "indices"). + + X : {array-like, sparse matrix} of shape (n_samples, n_features) + Input values. + + y : array-like of shape (n_samples,) + Target values. + + sample_weight : array-like of shape (n_samples,), default=None + Sample weights. + + drop_intermediate : bool, default=True + Whether to drop some suboptimal thresholds which would not appear + on a plotted ROC curve. This is useful in order to create lighter + ROC curves. + + response_method : {'predict_proba', 'decision_function', 'auto'} \ + default='auto' + Specifies whether to use :term:`predict_proba` or + :term:`decision_function` as the target response. If set to 'auto', + :term:`predict_proba` is tried first and if it does not exist + :term:`decision_function` is tried next. + + pos_label : int, float, bool or str, default=None + The class considered as the positive class when computing the ROC AUC + metrics. By default, `estimators.classes_[1]` is considered + as the positive class. + + ax : matplotlib axes, default=None + Axes object to plot on. If `None`, a new figure and axes is + created. + + name : str or list of str, default=None + Name for labeling legend entries. The number of legend entries + is determined by `curve_kwargs`, and is not affected by `name`. + To label each curve, provide a list of strings. To avoid labeling + individual curves that have the same appearance, this cannot be used in + conjunction with `curve_kwargs` being a dictionary or None. If a + string is provided, it will be used to either label the single legend entry + or if there are multiple legend entries, label each individual curve with + the same name. If `None`, no name is shown in the legend. + + curve_kwargs : dict or list of dict, default=None + Keywords arguments to be passed to matplotlib's `plot` function + to draw individual ROC curves. If a list is provided the + parameters are applied to the ROC curves of each CV fold + sequentially and a legend entry is added for each curve. + If a single dictionary is provided, the same parameters are applied + to all ROC curves and a single legend entry for all curves is added, + labeled with the mean ROC AUC score. + + plot_chance_level : bool, default=False + Whether to plot the chance level. + + chance_level_kwargs : dict, default=None + Keyword arguments to be passed to matplotlib's `plot` for rendering + the chance level line. + + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + Returns + ------- + display : :class:`~sklearn.metrics.RocCurveDisplay` + The multi-fold ROC curve display. + + See Also + -------- + roc_curve : Compute Receiver operating characteristic (ROC) curve. + RocCurveDisplay.from_estimator : ROC Curve visualization given an + estimator and some data. + RocCurveDisplay.from_predictions : ROC Curve visualization given the + probabilities of scores of a classifier. + roc_auc_score : Compute the area under the ROC curve. + + Examples + -------- + >>> import matplotlib.pyplot as plt + >>> from sklearn.datasets import make_classification + >>> from sklearn.metrics import RocCurveDisplay + >>> from sklearn.model_selection import cross_validate + >>> from sklearn.svm import SVC + >>> X, y = make_classification(random_state=0) + >>> clf = SVC(random_state=0) + >>> cv_results = cross_validate( + ... clf, X, y, cv=3, return_estimator=True, return_indices=True) + >>> RocCurveDisplay.from_cv_results(cv_results, X, y) + <...> + >>> plt.show() + """ + pos_label_ = cls._validate_from_cv_results_params( + cv_results, + X, + y, + sample_weight=sample_weight, + pos_label=pos_label, + ) + + fpr_folds, tpr_folds, auc_folds = [], [], [] + for estimator, test_indices in zip( + cv_results["estimator"], cv_results["indices"]["test"] + ): + y_true = _safe_indexing(y, test_indices) + y_pred, _ = _get_response_values_binary( + estimator, + _safe_indexing(X, test_indices), + response_method=response_method, + pos_label=pos_label_, + ) + sample_weight_fold = ( + None + if sample_weight is None + else _safe_indexing(sample_weight, test_indices) + ) + fpr, tpr, _ = roc_curve( + y_true, + y_pred, + pos_label=pos_label_, + sample_weight=sample_weight_fold, + drop_intermediate=drop_intermediate, + ) + roc_auc = auc(fpr, tpr) + + fpr_folds.append(fpr) + tpr_folds.append(tpr) + auc_folds.append(roc_auc) + + viz = cls( + fpr=fpr_folds, + tpr=tpr_folds, + roc_auc=auc_folds, + name=name, + pos_label=pos_label_, + ) + return viz.plot( + ax=ax, + curve_kwargs=curve_kwargs, + plot_chance_level=plot_chance_level, + chance_level_kw=chance_level_kwargs, + despine=despine, + ) diff --git a/sklearn/metrics/_plot/tests/test_common_curve_display.py b/sklearn/metrics/_plot/tests/test_common_curve_display.py index 0014a73055e41..753f2a1e7319d 100644 --- a/sklearn/metrics/_plot/tests/test_common_curve_display.py +++ b/sklearn/metrics/_plot/tests/test_common_curve_display.py @@ -132,9 +132,7 @@ def fit(self, X, y): Display.from_estimator(clf, X, y, response_method=response_method) -@pytest.mark.parametrize( - "Display", [DetCurveDisplay, PrecisionRecallDisplay, RocCurveDisplay] -) +@pytest.mark.parametrize("Display", [DetCurveDisplay, PrecisionRecallDisplay]) @pytest.mark.parametrize("constructor_name", ["from_estimator", "from_predictions"]) def test_display_curve_estimator_name_multiple_calls( pyplot, @@ -166,6 +164,8 @@ def test_display_curve_estimator_name_multiple_calls( assert clf_name in disp.line_.get_label() +# TODO: remove this test once classes moved to using `name` instead of +# `estimator_name` @pytest.mark.parametrize( "clf", [ @@ -176,10 +176,8 @@ def test_display_curve_estimator_name_multiple_calls( ), ], ) -@pytest.mark.parametrize( - "Display", [DetCurveDisplay, PrecisionRecallDisplay, RocCurveDisplay] -) -def test_display_curve_not_fitted_errors(pyplot, data_binary, clf, Display): +@pytest.mark.parametrize("Display", [DetCurveDisplay, PrecisionRecallDisplay]) +def test_display_curve_not_fitted_errors_old_name(pyplot, data_binary, clf, Display): """Check that a proper error is raised when the classifier is not fitted.""" X, y = data_binary @@ -194,6 +192,31 @@ def test_display_curve_not_fitted_errors(pyplot, data_binary, clf, Display): assert disp.estimator_name == model.__class__.__name__ +@pytest.mark.parametrize( + "clf", + [ + LogisticRegression(), + make_pipeline(StandardScaler(), LogisticRegression()), + make_pipeline( + make_column_transformer((StandardScaler(), [0, 1])), LogisticRegression() + ), + ], +) +@pytest.mark.parametrize("Display", [RocCurveDisplay]) +def test_display_curve_not_fitted_errors(pyplot, data_binary, clf, Display): + """Check that a proper error is raised when the classifier is not fitted.""" + X, y = data_binary + # clone since we parametrize the test and the classifier will be fitted + # when testing the second and subsequent plotting function + model = clone(clf) + with pytest.raises(NotFittedError): + Display.from_estimator(model, X, y) + model.fit(X, y) + disp = Display.from_estimator(model, X, y) + assert model.__class__.__name__ in disp.line_.get_label() + assert disp.name == model.__class__.__name__ + + @pytest.mark.parametrize( "Display", [DetCurveDisplay, PrecisionRecallDisplay, RocCurveDisplay] ) diff --git a/sklearn/metrics/_plot/tests/test_roc_curve_display.py b/sklearn/metrics/_plot/tests/test_roc_curve_display.py index ca0d7155e7c2c..23fa2f2e3a5e6 100644 --- a/sklearn/metrics/_plot/tests/test_roc_curve_display.py +++ b/sklearn/metrics/_plot/tests/test_roc_curve_display.py @@ -1,3 +1,5 @@ +from collections.abc import Mapping + import numpy as np import pytest from numpy.testing import assert_allclose @@ -9,10 +11,11 @@ from sklearn.exceptions import NotFittedError from sklearn.linear_model import LogisticRegression from sklearn.metrics import RocCurveDisplay, auc, roc_curve -from sklearn.model_selection import train_test_split +from sklearn.model_selection import cross_validate, train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler -from sklearn.utils import shuffle +from sklearn.utils import _safe_indexing, shuffle +from sklearn.utils._response import _get_response_values_binary @pytest.fixture(scope="module") @@ -29,6 +32,24 @@ def data_binary(): return X, y +def _check_figure_axes_and_labels(display, pos_label): + """Check mpl axes and figure defaults are correct.""" + import matplotlib as mpl + + assert isinstance(display.ax_, mpl.axes.Axes) + assert isinstance(display.figure_, mpl.figure.Figure) + assert display.ax_.get_adjustable() == "box" + assert display.ax_.get_aspect() in ("equal", 1.0) + assert display.ax_.get_xlim() == display.ax_.get_ylim() == (-0.01, 1.01) + + expected_pos_label = 1 if pos_label is None else pos_label + expected_ylabel = f"True Positive Rate (Positive label: {expected_pos_label})" + expected_xlabel = f"False Positive Rate (Positive label: {expected_pos_label})" + + assert display.ax_.get_ylabel() == expected_ylabel + assert display.ax_.get_xlabel() == expected_xlabel + + @pytest.mark.parametrize("response_method", ["predict_proba", "decision_function"]) @pytest.mark.parametrize("with_sample_weight", [True, False]) @pytest.mark.parametrize("drop_intermediate", [True, False]) @@ -50,7 +71,7 @@ def test_roc_curve_display_plotting( constructor_name, default_name, ): - """Check the overall plotting behaviour.""" + """Check the overall plotting behaviour for single curve.""" X, y = data_binary pos_label = None @@ -78,7 +99,7 @@ def test_roc_curve_display_plotting( sample_weight=sample_weight, drop_intermediate=drop_intermediate, pos_label=pos_label, - alpha=0.8, + curve_kwargs={"alpha": 0.8}, ) else: display = RocCurveDisplay.from_predictions( @@ -87,7 +108,7 @@ def test_roc_curve_display_plotting( sample_weight=sample_weight, drop_intermediate=drop_intermediate, pos_label=pos_label, - alpha=0.8, + curve_kwargs={"alpha": 0.8}, ) fpr, tpr, _ = roc_curve( @@ -102,27 +123,509 @@ def test_roc_curve_display_plotting( assert_allclose(display.fpr, fpr) assert_allclose(display.tpr, tpr) - assert display.estimator_name == default_name + assert display.name == default_name import matplotlib as mpl + _check_figure_axes_and_labels(display, pos_label) assert isinstance(display.line_, mpl.lines.Line2D) assert display.line_.get_alpha() == 0.8 - assert isinstance(display.ax_, mpl.axes.Axes) - assert isinstance(display.figure_, mpl.figure.Figure) - assert display.ax_.get_adjustable() == "box" - assert display.ax_.get_aspect() in ("equal", 1.0) - assert display.ax_.get_xlim() == display.ax_.get_ylim() == (-0.01, 1.01) expected_label = f"{default_name} (AUC = {display.roc_auc:.2f})" assert display.line_.get_label() == expected_label - expected_pos_label = 1 if pos_label is None else pos_label - expected_ylabel = f"True Positive Rate (Positive label: {expected_pos_label})" - expected_xlabel = f"False Positive Rate (Positive label: {expected_pos_label})" - assert display.ax_.get_ylabel() == expected_ylabel - assert display.ax_.get_xlabel() == expected_xlabel +@pytest.mark.parametrize( + "params, err_msg", + [ + ( + { + "fpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "tpr": [np.array([0, 0.5, 1])], + "roc_auc": None, + "name": None, + }, + "self.fpr and self.tpr from `RocCurveDisplay` initialization,", + ), + ( + { + "fpr": [np.array([0, 0.5, 1])], + "tpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "roc_auc": [0.8, 0.9], + "name": None, + }, + "self.fpr, self.tpr and self.roc_auc from `RocCurveDisplay`", + ), + ( + { + "fpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "tpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "roc_auc": [0.8], + "name": None, + }, + "Got: self.fpr: 2, self.tpr: 2, self.roc_auc: 1", + ), + ( + { + "fpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "tpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "roc_auc": [0.8, 0.9], + "name": ["curve1", "curve2", "curve3"], + }, + r"self.fpr, self.tpr, self.roc_auc and 'name' \(or self.name\)", + ), + ( + { + "fpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "tpr": [np.array([0, 0.5, 1]), np.array([0, 0.5, 1])], + "roc_auc": [0.8, 0.9], + # List of length 1 is always allowed + "name": ["curve1"], + }, + None, + ), + ], +) +def test_roc_curve_plot_parameter_length_validation(pyplot, params, err_msg): + """Check `plot` parameter length validation performed correctly.""" + display = RocCurveDisplay(**params) + if err_msg: + with pytest.raises(ValueError, match=err_msg): + display.plot() + else: + # No error should be raised + display.plot() + + +def test_validate_plot_params(pyplot): + """Check `_validate_plot_params` returns the correct variables.""" + fpr = np.array([0, 0.5, 1]) + tpr = [np.array([0, 0.5, 1])] + roc_auc = None + name = "test_curve" + + # Initialize display with test inputs + display = RocCurveDisplay( + fpr=fpr, + tpr=tpr, + roc_auc=roc_auc, + name=name, + pos_label=None, + ) + fpr_out, tpr_out, roc_auc_out, name_out = display._validate_plot_params( + ax=None, name=None + ) + + assert isinstance(fpr_out, list) + assert isinstance(tpr_out, list) + assert len(fpr_out) == 1 + assert len(tpr_out) == 1 + assert roc_auc_out is None + assert name_out == ["test_curve"] + + +def test_roc_curve_from_cv_results_param_validation(pyplot, data_binary): + """Check parameter validation is correct.""" + X, y = data_binary + + # `cv_results` missing key + cv_results_no_est = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=False + ) + cv_results_no_indices = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=False + ) + for cv_results in (cv_results_no_est, cv_results_no_indices): + with pytest.raises( + ValueError, + match="`cv_results` does not contain one of the following required", + ): + RocCurveDisplay.from_cv_results(cv_results, X, y) + + cv_results = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=True + ) + + # `X` wrong length + with pytest.raises(ValueError, match="`X` does not contain the correct"): + RocCurveDisplay.from_cv_results(cv_results, X[:10, :], y) + + # `y` not binary + y_multi = y.copy() + y_multi[0] = 2 + with pytest.raises(ValueError, match="The target `y` is not binary."): + RocCurveDisplay.from_cv_results(cv_results, X, y_multi) + + # input inconsistent length + with pytest.raises(ValueError, match="Found input variables with inconsistent"): + RocCurveDisplay.from_cv_results(cv_results, X, y[:10]) + with pytest.raises(ValueError, match="Found input variables with inconsistent"): + RocCurveDisplay.from_cv_results(cv_results, X, y, sample_weight=[1, 2]) + + # `pos_label` inconsistency + y_multi[y_multi == 1] = 2 + with pytest.raises(ValueError, match=r"y takes value in \{0, 2\}"): + RocCurveDisplay.from_cv_results(cv_results, X, y_multi) + + # `name` is list while `curve_kwargs` is None or dict + for curve_kwargs in (None, {"alpha": 0.2}): + with pytest.raises(ValueError, match="To avoid labeling individual curves"): + RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + name=["one", "two", "three"], + curve_kwargs=curve_kwargs, + ) + + # `curve_kwargs` incorrect length + with pytest.raises(ValueError, match="`curve_kwargs` must be None, a dictionary"): + RocCurveDisplay.from_cv_results(cv_results, X, y, curve_kwargs=[{"alpha": 1}]) + + # `curve_kwargs` both alias provided + with pytest.raises(TypeError, match="Got both c and"): + RocCurveDisplay.from_cv_results( + cv_results, X, y, curve_kwargs={"c": "blue", "color": "red"} + ) + + +@pytest.mark.parametrize( + "curve_kwargs", + [None, {"alpha": 0.2}, [{"alpha": 0.2}, {"alpha": 0.3}, {"alpha": 0.4}]], +) +def test_roc_curve_display_from_cv_results_curve_kwargs( + pyplot, data_binary, curve_kwargs +): + """Check `curve_kwargs` correctly passed.""" + X, y = data_binary + n_cv = 3 + cv_results = cross_validate( + LogisticRegression(), X, y, cv=n_cv, return_estimator=True, return_indices=True + ) + display = RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + curve_kwargs=curve_kwargs, + ) + if curve_kwargs is None: + # Default `alpha` used + assert all(line.get_alpha() == 0.5 for line in display.line_) + elif isinstance(curve_kwargs, Mapping): + # `alpha` from dict used for all curves + assert all(line.get_alpha() == 0.2 for line in display.line_) + else: + # Different `alpha` used for each curve + assert all( + line.get_alpha() == curve_kwargs[i]["alpha"] + for i, line in enumerate(display.line_) + ) + + +# TODO(1.9): Remove in 1.9 +def test_roc_curve_display_estimator_name_deprecation(pyplot): + """Check deprecation of `estimator_name`.""" + fpr = np.array([0, 0.5, 1]) + tpr = np.array([0, 0.5, 1]) + with pytest.warns(FutureWarning, match="`estimator_name` is deprecated in"): + RocCurveDisplay(fpr=fpr, tpr=tpr, estimator_name="test") + + +# TODO(1.9): Remove in 1.9 +@pytest.mark.parametrize( + "constructor_name", ["from_estimator", "from_predictions", "plot"] +) +def test_roc_curve_display_kwargs_deprecation(pyplot, data_binary, constructor_name): + """Check **kwargs deprecated correctly in favour of `curve_kwargs`.""" + X, y = data_binary + lr = LogisticRegression() + lr.fit(X, y) + fpr = np.array([0, 0.5, 1]) + tpr = np.array([0, 0.5, 1]) + + # Error when both `curve_kwargs` and `**kwargs` provided + with pytest.raises(ValueError, match="Cannot provide both `curve_kwargs`"): + if constructor_name == "from_estimator": + RocCurveDisplay.from_estimator( + lr, X, y, curve_kwargs={"alpha": 1}, label="test" + ) + elif constructor_name == "from_predictions": + RocCurveDisplay.from_predictions( + y, y, curve_kwargs={"alpha": 1}, label="test" + ) + else: + RocCurveDisplay(fpr=fpr, tpr=tpr).plot( + curve_kwargs={"alpha": 1}, label="test" + ) + + # Warning when `**kwargs`` provided + with pytest.warns(FutureWarning, match=r"`\*\*kwargs` is deprecated and will be"): + if constructor_name == "from_estimator": + RocCurveDisplay.from_estimator(lr, X, y, label="test") + elif constructor_name == "from_predictions": + RocCurveDisplay.from_predictions(y, y, label="test") + else: + RocCurveDisplay(fpr=fpr, tpr=tpr).plot(label="test") + + +@pytest.mark.parametrize( + "curve_kwargs", + [ + None, + {"color": "blue"}, + [{"color": "blue"}, {"color": "green"}, {"color": "red"}], + ], +) +@pytest.mark.parametrize("drop_intermediate", [True, False]) +@pytest.mark.parametrize("response_method", ["predict_proba", "decision_function"]) +@pytest.mark.parametrize("with_sample_weight", [True, False]) +@pytest.mark.parametrize("with_strings", [True, False]) +def test_roc_curve_display_plotting_from_cv_results( + pyplot, + data_binary, + with_strings, + with_sample_weight, + response_method, + drop_intermediate, + curve_kwargs, +): + """Check overall plotting of `from_cv_results`.""" + X, y = data_binary + + pos_label = None + if with_strings: + y = np.array(["c", "b"])[y] + pos_label = "c" + + if with_sample_weight: + rng = np.random.RandomState(42) + sample_weight = rng.randint(1, 4, size=(X.shape[0])) + else: + sample_weight = None + + cv_results = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=True + ) + display = RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + sample_weight=sample_weight, + drop_intermediate=drop_intermediate, + response_method=response_method, + pos_label=pos_label, + curve_kwargs=curve_kwargs, + ) + + for idx, (estimator, test_indices) in enumerate( + zip(cv_results["estimator"], cv_results["indices"]["test"]) + ): + y_true = _safe_indexing(y, test_indices) + y_pred = _get_response_values_binary( + estimator, + _safe_indexing(X, test_indices), + response_method=response_method, + pos_label=pos_label, + )[0] + sample_weight_fold = ( + None + if sample_weight is None + else _safe_indexing(sample_weight, test_indices) + ) + fpr, tpr, _ = roc_curve( + y_true, + y_pred, + sample_weight=sample_weight_fold, + drop_intermediate=drop_intermediate, + pos_label=pos_label, + ) + assert_allclose(display.roc_auc[idx], auc(fpr, tpr)) + assert_allclose(display.fpr[idx], fpr) + assert_allclose(display.tpr[idx], tpr) + + assert display.name is None + + import matplotlib as mpl + + _check_figure_axes_and_labels(display, pos_label) + if with_sample_weight: + aggregate_expected_labels = ["AUC = 0.64 +/- 0.04", "_child1", "_child2"] + else: + aggregate_expected_labels = ["AUC = 0.61 +/- 0.05", "_child1", "_child2"] + for idx, line in enumerate(display.line_): + assert isinstance(line, mpl.lines.Line2D) + # Default alpha for `from_cv_results` + line.get_alpha() == 0.5 + if isinstance(curve_kwargs, list): + # Each individual curve labelled + assert line.get_label() == f"AUC = {display.roc_auc[idx]:.2f}" + else: + # Single aggregate label + assert line.get_label() == aggregate_expected_labels[idx] + + +@pytest.mark.parametrize("roc_auc", [[1.0, 1.0, 1.0], None]) +@pytest.mark.parametrize( + "curve_kwargs", + [None, {"color": "red"}, [{"c": "red"}, {"c": "green"}, {"c": "yellow"}]], +) +@pytest.mark.parametrize("name", [None, "single", ["one", "two", "three"]]) +def test_roc_curve_plot_legend_label(pyplot, data_binary, name, curve_kwargs, roc_auc): + """Check legend label correct with all `curve_kwargs`, `name` combinations.""" + fpr = [np.array([0, 0.5, 1]), np.array([0, 0.5, 1]), np.array([0, 0.5, 1])] + tpr = [np.array([0, 0.5, 1]), np.array([0, 0.5, 1]), np.array([0, 0.5, 1])] + if not isinstance(curve_kwargs, list) and isinstance(name, list): + with pytest.raises(ValueError, match="To avoid labeling individual curves"): + RocCurveDisplay(fpr=fpr, tpr=tpr, roc_auc=roc_auc).plot( + name=name, curve_kwargs=curve_kwargs + ) + + else: + display = RocCurveDisplay(fpr=fpr, tpr=tpr, roc_auc=roc_auc).plot( + name=name, curve_kwargs=curve_kwargs + ) + legend = display.ax_.get_legend() + if legend is None: + # No legend is created, exit test early + assert name is None + assert roc_auc is None + return + else: + legend_labels = [text.get_text() for text in legend.get_texts()] + + if isinstance(curve_kwargs, list): + # Multiple labels in legend + assert len(legend_labels) == 3 + for idx, label in enumerate(legend_labels): + if name is None: + expected_label = "AUC = 1.00" if roc_auc else None + assert label == expected_label + elif isinstance(name, str): + expected_label = "single (AUC = 1.00)" if roc_auc else "single" + assert label == expected_label + else: + # `name` is a list of different strings + expected_label = ( + f"{name[idx]} (AUC = 1.00)" if roc_auc else f"{name[idx]}" + ) + assert label == expected_label + else: + # Single label in legend + assert len(legend_labels) == 1 + if name is None: + expected_label = "AUC = 1.00 +/- 0.00" if roc_auc else None + assert legend_labels[0] == expected_label + else: + # name is single string + expected_label = "single (AUC = 1.00 +/- 0.00)" if roc_auc else "single" + assert legend_labels[0] == expected_label + + +@pytest.mark.parametrize( + "curve_kwargs", + [None, {"color": "red"}, [{"c": "red"}, {"c": "green"}, {"c": "yellow"}]], +) +@pytest.mark.parametrize("name", [None, "single", ["one", "two", "three"]]) +def test_roc_curve_from_cv_results_legend_label( + pyplot, data_binary, name, curve_kwargs +): + """Check legend label correct with all `curve_kwargs`, `name` combinations.""" + X, y = data_binary + n_cv = 3 + cv_results = cross_validate( + LogisticRegression(), X, y, cv=n_cv, return_estimator=True, return_indices=True + ) + + if not isinstance(curve_kwargs, list) and isinstance(name, list): + with pytest.raises(ValueError, match="To avoid labeling individual curves"): + RocCurveDisplay.from_cv_results( + cv_results, X, y, name=name, curve_kwargs=curve_kwargs + ) + else: + display = RocCurveDisplay.from_cv_results( + cv_results, X, y, name=name, curve_kwargs=curve_kwargs + ) + + legend = display.ax_.get_legend() + legend_labels = [text.get_text() for text in legend.get_texts()] + if isinstance(curve_kwargs, list): + # Multiple labels in legend + assert len(legend_labels) == 3 + auc = ["0.62", "0.66", "0.55"] + for idx, label in enumerate(legend_labels): + if name is None: + assert label == f"AUC = {auc[idx]}" + elif isinstance(name, str): + assert label == f"single (AUC = {auc[idx]})" + else: + # `name` is a list of different strings + assert label == f"{name[idx]} (AUC = {auc[idx]})" + else: + # Single label in legend + assert len(legend_labels) == 1 + if name is None: + assert legend_labels[0] == "AUC = 0.61 +/- 0.05" + else: + # name is single string + assert legend_labels[0] == "single (AUC = 0.61 +/- 0.05)" + + +@pytest.mark.parametrize( + "curve_kwargs", + [None, {"color": "red"}, [{"c": "red"}, {"c": "green"}, {"c": "yellow"}]], +) +def test_roc_curve_from_cv_results_curve_kwargs(pyplot, data_binary, curve_kwargs): + """Check line kwargs passed correctly in `from_cv_results`.""" + + X, y = data_binary + cv_results = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=True + ) + display = RocCurveDisplay.from_cv_results( + cv_results, X, y, curve_kwargs=curve_kwargs + ) + + for idx, line in enumerate(display.line_): + color = line.get_color() + if curve_kwargs is None: + # Default color + assert color == "blue" + elif isinstance(curve_kwargs, Mapping): + # All curves "red" + assert color == "red" + else: + assert color == curve_kwargs[idx]["c"] + + +def _check_chance_level(plot_chance_level, chance_level_kw, display): + """Check chance level line and line styles correct.""" + import matplotlib as mpl + + if plot_chance_level: + assert isinstance(display.chance_level_, mpl.lines.Line2D) + assert tuple(display.chance_level_.get_xdata()) == (0, 1) + assert tuple(display.chance_level_.get_ydata()) == (0, 1) + else: + assert display.chance_level_ is None + + # Checking for chance level line styles + if plot_chance_level and chance_level_kw is None: + assert display.chance_level_.get_color() == "k" + assert display.chance_level_.get_linestyle() == "--" + assert display.chance_level_.get_label() == "Chance level (AUC = 0.5)" + elif plot_chance_level: + if "c" in chance_level_kw: + assert display.chance_level_.get_color() == chance_level_kw["c"] + else: + assert display.chance_level_.get_color() == chance_level_kw["color"] + if "lw" in chance_level_kw: + assert display.chance_level_.get_linewidth() == chance_level_kw["lw"] + else: + assert display.chance_level_.get_linewidth() == chance_level_kw["linewidth"] + if "ls" in chance_level_kw: + assert display.chance_level_.get_linestyle() == chance_level_kw["ls"] + else: + assert display.chance_level_.get_linestyle() == chance_level_kw["linestyle"] @pytest.mark.parametrize("plot_chance_level", [True, False]) @@ -136,10 +639,7 @@ def test_roc_curve_display_plotting( {"lw": 1, "color": "blue", "ls": "-", "label": None}, ], ) -@pytest.mark.parametrize( - "constructor_name", - ["from_estimator", "from_predictions"], -) +@pytest.mark.parametrize("constructor_name", ["from_estimator", "from_predictions"]) def test_roc_curve_chance_level_line( pyplot, data_binary, @@ -148,7 +648,7 @@ def test_roc_curve_chance_level_line( label, constructor_name, ): - """Check the chance level line plotting behaviour.""" + """Check chance level plotting behavior of `from_predictions`, `from_estimator`.""" X, y = data_binary lr = LogisticRegression() @@ -162,8 +662,7 @@ def test_roc_curve_chance_level_line( lr, X, y, - label=label, - alpha=0.8, + curve_kwargs={"alpha": 0.8, "label": label}, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, ) @@ -171,8 +670,7 @@ def test_roc_curve_chance_level_line( display = RocCurveDisplay.from_predictions( y, y_score, - label=label, - alpha=0.8, + curve_kwargs={"alpha": 0.8, "label": label}, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, ) @@ -184,32 +682,10 @@ def test_roc_curve_chance_level_line( assert isinstance(display.ax_, mpl.axes.Axes) assert isinstance(display.figure_, mpl.figure.Figure) - if plot_chance_level: - assert isinstance(display.chance_level_, mpl.lines.Line2D) - assert tuple(display.chance_level_.get_xdata()) == (0, 1) - assert tuple(display.chance_level_.get_ydata()) == (0, 1) - else: - assert display.chance_level_ is None + _check_chance_level(plot_chance_level, chance_level_kw, display) - # Checking for chance level line styles - if plot_chance_level and chance_level_kw is None: - assert display.chance_level_.get_color() == "k" - assert display.chance_level_.get_linestyle() == "--" - assert display.chance_level_.get_label() == "Chance level (AUC = 0.5)" - elif plot_chance_level: - if "c" in chance_level_kw: - assert display.chance_level_.get_color() == chance_level_kw["c"] - else: - assert display.chance_level_.get_color() == chance_level_kw["color"] - if "lw" in chance_level_kw: - assert display.chance_level_.get_linewidth() == chance_level_kw["lw"] - else: - assert display.chance_level_.get_linewidth() == chance_level_kw["linewidth"] - if "ls" in chance_level_kw: - assert display.chance_level_.get_linestyle() == chance_level_kw["ls"] - else: - assert display.chance_level_.get_linestyle() == chance_level_kw["linestyle"] - # Checking for legend behaviour + # Checking for legend behaviour + if plot_chance_level and chance_level_kw is not None: if label is not None or chance_level_kw.get("label") is not None: legend = display.ax_.get_legend() assert legend is not None # Legend should be present if any label is set @@ -222,6 +698,62 @@ def test_roc_curve_chance_level_line( assert display.ax_.get_legend() is None +@pytest.mark.parametrize("plot_chance_level", [True, False]) +@pytest.mark.parametrize( + "chance_level_kw", + [ + None, + {"linewidth": 1, "color": "red", "linestyle": "-", "label": "DummyEstimator"}, + {"lw": 1, "c": "red", "ls": "-", "label": "DummyEstimator"}, + {"lw": 1, "color": "blue", "ls": "-", "label": None}, + ], +) +@pytest.mark.parametrize("curve_kwargs", [None, {"alpha": 0.8}]) +def test_roc_curve_chance_level_line_from_cv_results( + pyplot, + data_binary, + plot_chance_level, + chance_level_kw, + curve_kwargs, +): + """Check chance level plotting behavior with `from_cv_results`.""" + X, y = data_binary + n_cv = 3 + cv_results = cross_validate( + LogisticRegression(), X, y, cv=n_cv, return_estimator=True, return_indices=True + ) + + display = RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + plot_chance_level=plot_chance_level, + chance_level_kwargs=chance_level_kw, + curve_kwargs=curve_kwargs, + ) + + import matplotlib as mpl + + assert all(isinstance(line, mpl.lines.Line2D) for line in display.line_) + # Ensure both curve line kwargs passed correctly as well + if curve_kwargs: + assert all(line.get_alpha() == 0.8 for line in display.line_) + assert isinstance(display.ax_, mpl.axes.Axes) + assert isinstance(display.figure_, mpl.figure.Figure) + + _check_chance_level(plot_chance_level, chance_level_kw, display) + + legend = display.ax_.get_legend() + # There is always a legend, to indicate each 'Fold' curve + assert legend is not None + legend_labels = [text.get_text() for text in legend.get_texts()] + if plot_chance_level and chance_level_kw is not None: + if chance_level_kw.get("label") is not None: + assert chance_level_kw["label"] in legend_labels + else: + assert len(legend_labels) == 1 + + @pytest.mark.parametrize( "clf", [ @@ -253,31 +785,52 @@ def test_roc_curve_display_complex_pipeline(pyplot, data_binary, clf, constructo name = "Classifier" assert name in display.line_.get_label() - assert display.estimator_name == name + assert display.name == name @pytest.mark.parametrize( - "roc_auc, estimator_name, expected_label", + "roc_auc, name, curve_kwargs, expected_labels", [ - (0.9, None, "AUC = 0.90"), - (None, "my_est", "my_est"), - (0.8, "my_est2", "my_est2 (AUC = 0.80)"), + ([0.9, 0.8], None, None, ["AUC = 0.85 +/- 0.05", "_child1"]), + ([0.9, 0.8], "Est name", None, ["Est name (AUC = 0.85 +/- 0.05)", "_child1"]), + ( + [0.8, 0.7], + ["fold1", "fold2"], + [{"c": "blue"}, {"c": "red"}], + ["fold1 (AUC = 0.80)", "fold2 (AUC = 0.70)"], + ), + (None, ["fold1", "fold2"], [{"c": "blue"}, {"c": "red"}], ["fold1", "fold2"]), ], ) def test_roc_curve_display_default_labels( - pyplot, roc_auc, estimator_name, expected_label + pyplot, roc_auc, name, curve_kwargs, expected_labels ): """Check the default labels used in the display.""" - fpr = np.array([0, 0.5, 1]) - tpr = np.array([0, 0.5, 1]) - disp = RocCurveDisplay( - fpr=fpr, tpr=tpr, roc_auc=roc_auc, estimator_name=estimator_name - ).plot() - assert disp.line_.get_label() == expected_label + fpr = [np.array([0, 0.5, 1]), np.array([0, 0.3, 1])] + tpr = [np.array([0, 0.5, 1]), np.array([0, 0.3, 1])] + disp = RocCurveDisplay(fpr=fpr, tpr=tpr, roc_auc=roc_auc, name=name).plot( + curve_kwargs=curve_kwargs + ) + for idx, expected_label in enumerate(expected_labels): + assert disp.line_[idx].get_label() == expected_label + + +def _check_auc(display, constructor_name): + roc_auc_limit = 0.95679 + roc_auc_limit_multi = [0.97007, 0.985915, 0.980952] + + if constructor_name == "from_cv_results": + for idx, roc_auc in enumerate(display.roc_auc): + assert roc_auc == pytest.approx(roc_auc_limit_multi[idx]) + else: + assert display.roc_auc == pytest.approx(roc_auc_limit) + assert trapezoid(display.tpr, display.fpr) == pytest.approx(roc_auc_limit) @pytest.mark.parametrize("response_method", ["predict_proba", "decision_function"]) -@pytest.mark.parametrize("constructor_name", ["from_estimator", "from_predictions"]) +@pytest.mark.parametrize( + "constructor_name", ["from_estimator", "from_predictions", "from_cv_results"] +) def test_plot_roc_curve_pos_label(pyplot, response_method, constructor_name): # check that we can provide the positive label and display the proper # statistics @@ -300,9 +853,13 @@ def test_plot_roc_curve_pos_label(pyplot, response_method, constructor_name): classifier = LogisticRegression() classifier.fit(X_train, y_train) + cv_results = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=True + ) - # sanity check to be sure the positive class is classes_[0] and that we - # are betrayed by the class imbalance + # Sanity check to be sure the positive class is `classes_[0]` + # Class imbalance ensures a large difference in prediction values between classes, + # allowing us to catch errors when we switch `pos_label` assert classifier.classes_.tolist() == ["cancer", "not cancer"] y_score = getattr(classifier, response_method)(X_test) @@ -311,43 +868,59 @@ def test_plot_roc_curve_pos_label(pyplot, response_method, constructor_name): y_score_cancer = -1 * y_score if y_score.ndim == 1 else y_score[:, 0] y_score_not_cancer = y_score if y_score.ndim == 1 else y_score[:, 1] + pos_label = "cancer" + y_score = y_score_cancer if constructor_name == "from_estimator": display = RocCurveDisplay.from_estimator( classifier, X_test, y_test, - pos_label="cancer", + pos_label=pos_label, response_method=response_method, ) - else: + elif constructor_name == "from_predictions": display = RocCurveDisplay.from_predictions( y_test, - y_score_cancer, - pos_label="cancer", + y_score, + pos_label=pos_label, + ) + else: + display = RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + response_method=response_method, + pos_label=pos_label, ) - roc_auc_limit = 0.95679 - - assert display.roc_auc == pytest.approx(roc_auc_limit) - assert trapezoid(display.tpr, display.fpr) == pytest.approx(roc_auc_limit) + _check_auc(display, constructor_name) + pos_label = "not cancer" + y_score = y_score_not_cancer if constructor_name == "from_estimator": display = RocCurveDisplay.from_estimator( classifier, X_test, y_test, response_method=response_method, - pos_label="not cancer", + pos_label=pos_label, ) - else: + elif constructor_name == "from_predictions": display = RocCurveDisplay.from_predictions( y_test, - y_score_not_cancer, - pos_label="not cancer", + y_score, + pos_label=pos_label, + ) + else: + display = RocCurveDisplay.from_cv_results( + cv_results, + X, + y, + response_method=response_method, + pos_label=pos_label, ) - assert display.roc_auc == pytest.approx(roc_auc_limit) - assert trapezoid(display.tpr, display.fpr) == pytest.approx(roc_auc_limit) + _check_auc(display, constructor_name) # TODO(1.9): remove @@ -381,23 +954,30 @@ def test_y_pred_deprecation_warning(pyplot): @pytest.mark.parametrize("despine", [True, False]) -@pytest.mark.parametrize("constructor_name", ["from_estimator", "from_predictions"]) +@pytest.mark.parametrize( + "constructor_name", ["from_estimator", "from_predictions", "from_cv_results"] +) def test_plot_roc_curve_despine(pyplot, data_binary, despine, constructor_name): # Check that the despine keyword is working correctly X, y = data_binary lr = LogisticRegression().fit(X, y) lr.fit(X, y) + cv_results = cross_validate( + LogisticRegression(), X, y, cv=3, return_estimator=True, return_indices=True + ) y_pred = lr.decision_function(X) - # safe guard for the binary if/else construction - assert constructor_name in ("from_estimator", "from_predictions") + # safe guard for the if/else construction + assert constructor_name in ("from_estimator", "from_predictions", "from_cv_results") if constructor_name == "from_estimator": display = RocCurveDisplay.from_estimator(lr, X, y, despine=despine) - else: + elif constructor_name == "from_predictions": display = RocCurveDisplay.from_predictions(y, y_pred, despine=despine) + else: + display = RocCurveDisplay.from_cv_results(cv_results, X, y, despine=despine) for s in ["top", "right"]: assert display.ax_.spines[s].get_visible() is not despine diff --git a/sklearn/metrics/_ranking.py b/sklearn/metrics/_ranking.py index d4fba69440f13..2d0e5211c236c 100644 --- a/sklearn/metrics/_ranking.py +++ b/sklearn/metrics/_ranking.py @@ -183,6 +183,10 @@ def average_precision_score( roc_auc_score : Compute the area under the ROC curve. precision_recall_curve : Compute precision-recall pairs for different probability thresholds. + PrecisionRecallDisplay.from_estimator : Plot the precision recall curve + using an estimator and data. + PrecisionRecallDisplay.from_predictions : Plot the precision recall curve + using true and predicted labels. Notes ----- diff --git a/sklearn/metrics/_regression.py b/sklearn/metrics/_regression.py index 4c46346d63d92..0731e00ce3a1a 100644 --- a/sklearn/metrics/_regression.py +++ b/sklearn/metrics/_regression.py @@ -57,8 +57,10 @@ ] -def _check_reg_targets(y_true, y_pred, multioutput, dtype="numeric", xp=None): - """Check that y_true and y_pred belong to the same regression task. +def _check_reg_targets( + y_true, y_pred, sample_weight, multioutput, dtype="numeric", xp=None +): + """Check that y_true, y_pred and sample_weight belong to the same regression task. To reduce redundancy when calling `_find_matching_floating_dtype`, please use `_check_reg_targets_with_floating_dtype` instead. @@ -71,6 +73,9 @@ def _check_reg_targets(y_true, y_pred, multioutput, dtype="numeric", xp=None): y_pred : array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated target values. + sample_weight : array-like of shape (n_samples,) or None + Sample weights. + multioutput : array-like or string in ['raw_values', uniform_average', 'variance_weighted'] or None None is accepted due to backward compatibility of r2_score(). @@ -95,6 +100,9 @@ def _check_reg_targets(y_true, y_pred, multioutput, dtype="numeric", xp=None): y_pred : array-like of shape (n_samples, n_outputs) Estimated target values. + sample_weight : array-like of shape (n_samples,) or None + Sample weights. + multioutput : array-like of shape (n_outputs) or string in ['raw_values', uniform_average', 'variance_weighted'] or None Custom output weights if ``multioutput`` is array-like or @@ -103,9 +111,11 @@ def _check_reg_targets(y_true, y_pred, multioutput, dtype="numeric", xp=None): """ xp, _ = get_namespace(y_true, y_pred, multioutput, xp=xp) - check_consistent_length(y_true, y_pred) + check_consistent_length(y_true, y_pred, sample_weight) y_true = check_array(y_true, ensure_2d=False, dtype=dtype) y_pred = check_array(y_pred, ensure_2d=False, dtype=dtype) + if sample_weight is not None: + sample_weight = _check_sample_weight(sample_weight, y_true, dtype=dtype) if y_true.ndim == 1: y_true = xp.reshape(y_true, (-1, 1)) @@ -141,14 +151,13 @@ def _check_reg_targets(y_true, y_pred, multioutput, dtype="numeric", xp=None): ) y_type = "continuous" if n_outputs == 1 else "continuous-multioutput" - return y_type, y_true, y_pred, multioutput + return y_type, y_true, y_pred, sample_weight, multioutput def _check_reg_targets_with_floating_dtype( y_true, y_pred, sample_weight, multioutput, xp=None ): - """Ensures that y_true, y_pred, and sample_weight correspond to the same - regression task. + """Ensures y_true, y_pred, and sample_weight correspond to same regression task. Extends `_check_reg_targets` by automatically selecting a suitable floating-point data type for inputs using `_find_matching_floating_dtype`. @@ -197,15 +206,10 @@ def _check_reg_targets_with_floating_dtype( """ dtype_name = _find_matching_floating_dtype(y_true, y_pred, sample_weight, xp=xp) - y_type, y_true, y_pred, multioutput = _check_reg_targets( - y_true, y_pred, multioutput, dtype=dtype_name, xp=xp + y_type, y_true, y_pred, sample_weight, multioutput = _check_reg_targets( + y_true, y_pred, sample_weight, multioutput, dtype=dtype_name, xp=xp ) - # _check_reg_targets does not accept sample_weight as input. - # Convert sample_weight's data type separately to match dtype_name. - if sample_weight is not None: - sample_weight = xp.asarray(sample_weight, dtype=dtype_name) - return y_type, y_true, y_pred, sample_weight, multioutput @@ -282,8 +286,6 @@ def mean_absolute_error( ) ) - check_consistent_length(y_true, y_pred, sample_weight) - output_errors = _average( xp.abs(y_pred - y_true), weights=sample_weight, axis=0, xp=xp ) @@ -383,7 +385,6 @@ def mean_pinball_loss( ) ) - check_consistent_length(y_true, y_pred, sample_weight) diff = y_true - y_pred sign = xp.astype(diff >= 0, diff.dtype) loss = alpha * sign * diff - (1 - alpha) * (1 - sign) * diff @@ -489,7 +490,6 @@ def mean_absolute_percentage_error( y_true, y_pred, sample_weight, multioutput, xp=xp ) ) - check_consistent_length(y_true, y_pred, sample_weight) epsilon = xp.asarray(xp.finfo(xp.float64).eps, dtype=y_true.dtype, device=device_) y_true_abs = xp.abs(y_true) mape = xp.abs(y_pred - y_true) / xp.maximum(y_true_abs, epsilon) @@ -581,7 +581,6 @@ def mean_squared_error( y_true, y_pred, sample_weight, multioutput, xp=xp ) ) - check_consistent_length(y_true, y_pred, sample_weight) output_errors = _average((y_true - y_pred) ** 2, axis=0, weights=sample_weight) if isinstance(multioutput, str): @@ -753,8 +752,10 @@ def mean_squared_log_error( """ xp, _ = get_namespace(y_true, y_pred) - _, y_true, y_pred, _, _ = _check_reg_targets_with_floating_dtype( - y_true, y_pred, sample_weight, multioutput, xp=xp + _, y_true, y_pred, sample_weight, multioutput = ( + _check_reg_targets_with_floating_dtype( + y_true, y_pred, sample_weight, multioutput, xp=xp + ) ) if xp.any(y_true <= -1) or xp.any(y_pred <= -1): @@ -829,8 +830,10 @@ def root_mean_squared_log_error( """ xp, _ = get_namespace(y_true, y_pred) - _, y_true, y_pred, _, _ = _check_reg_targets_with_floating_dtype( - y_true, y_pred, sample_weight, multioutput, xp=xp + _, y_true, y_pred, sample_weight, multioutput = ( + _check_reg_targets_with_floating_dtype( + y_true, y_pred, sample_weight, multioutput, xp=xp + ) ) if xp.any(y_true <= -1) or xp.any(y_pred <= -1): @@ -912,13 +915,12 @@ def median_absolute_error( >>> median_absolute_error(y_true, y_pred, multioutput=[0.3, 0.7]) 0.85 """ - y_type, y_true, y_pred, multioutput = _check_reg_targets( - y_true, y_pred, multioutput + _, y_true, y_pred, sample_weight, multioutput = _check_reg_targets( + y_true, y_pred, sample_weight, multioutput ) if sample_weight is None: output_errors = np.median(np.abs(y_pred - y_true), axis=0) else: - sample_weight = _check_sample_weight(sample_weight, y_pred) output_errors = _weighted_percentile( np.abs(y_pred - y_true), sample_weight=sample_weight ) @@ -1106,8 +1108,6 @@ def explained_variance_score( ) ) - check_consistent_length(y_true, y_pred, sample_weight) - y_diff_avg = _average(y_true - y_pred, weights=sample_weight, axis=0) numerator = _average( (y_true - y_pred - y_diff_avg) ** 2, weights=sample_weight, axis=0 @@ -1278,8 +1278,6 @@ def r2_score( ) ) - check_consistent_length(y_true, y_pred, sample_weight) - if _num_samples(y_pred) < 2: msg = "R^2 score is not well-defined with less than two samples." warnings.warn(msg, UndefinedMetricWarning) @@ -1343,7 +1341,9 @@ def max_error(y_true, y_pred): 1.0 """ xp, _ = get_namespace(y_true, y_pred) - y_type, y_true, y_pred, _ = _check_reg_targets(y_true, y_pred, None, xp=xp) + y_type, y_true, y_pred, _, _ = _check_reg_targets( + y_true, y_pred, sample_weight=None, multioutput=None, xp=xp + ) if y_type == "continuous-multioutput": raise ValueError("Multioutput not supported in max_error") return float(xp.max(xp.abs(y_true - y_pred))) @@ -1448,7 +1448,6 @@ def mean_tweedie_deviance(y_true, y_pred, *, sample_weight=None, power=0): ) if y_type == "continuous-multioutput": raise ValueError("Multioutput not supported in mean_tweedie_deviance") - check_consistent_length(y_true, y_pred, sample_weight) if sample_weight is not None: sample_weight = column_or_1d(sample_weight) @@ -1773,10 +1772,9 @@ def d2_pinball_score( >>> d2_pinball_score(y_true, y_true, alpha=0.1) 1.0 """ - y_type, y_true, y_pred, multioutput = _check_reg_targets( - y_true, y_pred, multioutput + _, y_true, y_pred, sample_weight, multioutput = _check_reg_targets( + y_true, y_pred, sample_weight, multioutput ) - check_consistent_length(y_true, y_pred, sample_weight) if _num_samples(y_pred) < 2: msg = "D^2 score is not well-defined with less than two samples." @@ -1796,7 +1794,6 @@ def d2_pinball_score( np.percentile(y_true, q=alpha * 100, axis=0), (len(y_true), 1) ) else: - sample_weight = _check_sample_weight(sample_weight, y_true) y_quantile = np.tile( _weighted_percentile( y_true, sample_weight=sample_weight, percentile_rank=alpha * 100 diff --git a/sklearn/metrics/pairwise.py b/sklearn/metrics/pairwise.py index f0e6cee65bc28..050b58866c8ef 100644 --- a/sklearn/metrics/pairwise.py +++ b/sklearn/metrics/pairwise.py @@ -299,7 +299,7 @@ def euclidean_distances( However, this is not the most precise way of doing this computation, because this equation potentially suffers from "catastrophic cancellation". Also, the distance matrix returned by this function may not be exactly - symmetric as required by, e.g., ``scipy.spatial.distance`` functions. + symmetric as required by, e.g., :mod:`scipy.spatial.distance` functions. Read more in the :ref:`User Guide `. @@ -751,7 +751,7 @@ def pairwise_distances_argmin_min( metric : str or callable, default='euclidean' Metric to use for distance computation. Any metric from scikit-learn - or scipy.spatial.distance can be used. + or :mod:`scipy.spatial.distance` can be used. If metric is a callable function, it is called on each pair of instances (rows) and the resulting value recorded. The callable @@ -766,13 +766,13 @@ def pairwise_distances_argmin_min( - from scikit-learn: ['cityblock', 'cosine', 'euclidean', 'l1', 'l2', 'manhattan', 'nan_euclidean'] - - from scipy.spatial.distance: ['braycurtis', 'canberra', 'chebyshev', + - from :mod:`scipy.spatial.distance`: ['braycurtis', 'canberra', 'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski', 'mahalanobis', 'minkowski', 'rogerstanimoto', 'russellrao', 'seuclidean', 'sokalmichener', 'sokalsneath', 'sqeuclidean', 'yule'] - See the documentation for scipy.spatial.distance for details on these + See the documentation for :mod:`scipy.spatial.distance` for details on these metrics. .. note:: @@ -899,7 +899,7 @@ def pairwise_distances_argmin(X, Y, *, axis=1, metric="euclidean", metric_kwargs metric : str or callable, default="euclidean" Metric to use for distance computation. Any metric from scikit-learn - or scipy.spatial.distance can be used. + or :mod:`scipy.spatial.distance` can be used. If metric is a callable function, it is called on each pair of instances (rows) and the resulting value recorded. The callable @@ -914,13 +914,13 @@ def pairwise_distances_argmin(X, Y, *, axis=1, metric="euclidean", metric_kwargs - from scikit-learn: ['cityblock', 'cosine', 'euclidean', 'l1', 'l2', 'manhattan', 'nan_euclidean'] - - from scipy.spatial.distance: ['braycurtis', 'canberra', 'chebyshev', + - from :mod:`scipy.spatial.distance`: ['braycurtis', 'canberra', 'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski', 'mahalanobis', 'minkowski', 'rogerstanimoto', 'russellrao', 'seuclidean', 'sokalmichener', 'sokalsneath', 'sqeuclidean', 'yule'] - See the documentation for scipy.spatial.distance for details on these + See the documentation for :mod:`scipy.spatial.distance` for details on these metrics. .. note:: @@ -2124,7 +2124,7 @@ def pairwise_distances_chunked( metric : str or callable, default='euclidean' The metric to use when calculating distance between instances in a feature array. If metric is a string, it must be one of the options - allowed by scipy.spatial.distance.pdist for its metric parameter, + allowed by :func:`scipy.spatial.distance.pdist` for its metric parameter, or a metric listed in pairwise.PAIRWISE_DISTANCE_FUNCTIONS. If metric is "precomputed", X is assumed to be a distance matrix. Alternatively, if metric is a callable function, it is called on @@ -2148,7 +2148,7 @@ def pairwise_distances_chunked( **kwds : optional keyword parameters Any further parameters are passed directly to the distance function. - If using a scipy.spatial.distance metric, the parameters are still + If using a :mod:`scipy.spatial.distance` metric, the parameters are still metric dependent. See the scipy docs for usage examples. Yields @@ -2304,12 +2304,11 @@ def pairwise_distances( 'manhattan', 'nan_euclidean']. All metrics support sparse matrix inputs except 'nan_euclidean'. - - From scipy.spatial.distance: ['braycurtis', 'canberra', 'chebyshev', + - From :mod:`scipy.spatial.distance`: ['braycurtis', 'canberra', 'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski', 'mahalanobis', 'minkowski', 'rogerstanimoto', 'russellrao', 'seuclidean', - 'sokalmichener', 'sokalsneath', 'sqeuclidean', 'yule'] - See the documentation for scipy.spatial.distance for details on these - metrics. These metrics do not support sparse matrix inputs. + 'sokalmichener', 'sokalsneath', 'sqeuclidean', 'yule']. + These metrics do not support sparse matrix inputs. .. note:: `'kulsinski'` is deprecated from SciPy 1.9 and will be removed in SciPy 1.11. @@ -2318,7 +2317,7 @@ def pairwise_distances( `'matching'` has been removed in SciPy 1.9 (use `'hamming'` instead). Note that in the case of 'cityblock', 'cosine' and 'euclidean' (which are - valid scipy.spatial.distance metrics), the scikit-learn implementation + valid :mod:`scipy.spatial.distance` metrics), the scikit-learn implementation will be used, which is faster and has support for sparse matrices (except for 'cityblock'). For a verbose description of the metrics from scikit-learn, see :func:`sklearn.metrics.pairwise.distance_metrics` @@ -2341,7 +2340,7 @@ def pairwise_distances( metric : str or callable, default='euclidean' The metric to use when calculating distance between instances in a feature array. If metric is a string, it must be one of the options - allowed by scipy.spatial.distance.pdist for its metric parameter, or + allowed by :func:`scipy.spatial.distance.pdist` for its metric parameter, or a metric listed in ``pairwise.PAIRWISE_DISTANCE_FUNCTIONS``. If metric is "precomputed", X is assumed to be a distance matrix. Alternatively, if metric is a callable function, it is called on each diff --git a/sklearn/metrics/tests/test_common.py b/sklearn/metrics/tests/test_common.py index 1000c988abca8..39522876e8f24 100644 --- a/sklearn/metrics/tests/test_common.py +++ b/sklearn/metrics/tests/test_common.py @@ -1588,6 +1588,32 @@ def test_regression_sample_weight_invariance(name): check_sample_weight_invariance(name, metric, y_true, y_pred) +@pytest.mark.parametrize( + "name", + sorted( + set(ALL_METRICS).intersection(set(REGRESSION_METRICS)) + - METRICS_WITHOUT_SAMPLE_WEIGHT + ), +) +def test_regression_with_invalid_sample_weight(name): + # Check that `sample_weight` with incorrect length raises error + n_samples = 50 + random_state = check_random_state(0) + y_true = random_state.random_sample(size=(n_samples,)) + y_pred = random_state.random_sample(size=(n_samples,)) + metric = ALL_METRICS[name] + + sample_weight = random_state.random_sample(size=(n_samples - 1,)) + with pytest.raises(ValueError, match="Found input variables with inconsistent"): + metric(y_true, y_pred, sample_weight=sample_weight) + + sample_weight = random_state.random_sample(size=(n_samples * 2,)).reshape( + (n_samples, 2) + ) + with pytest.raises(ValueError, match="Sample weights must be 1D array or scalar"): + metric(y_true, y_pred, sample_weight=sample_weight) + + @pytest.mark.parametrize( "name", sorted( @@ -1867,10 +1893,11 @@ def check_array_api_metric( np.asarray(a_xp) np.asarray(b_xp) numpy_as_array_works = True - except (TypeError, RuntimeError): + except (TypeError, RuntimeError, ValueError): # PyTorch with CUDA device and CuPy raise TypeError consistently. - # array-api-strict chose to raise RuntimeError instead. Exception type - # may need to be updated in the future for other libraries. + # array-api-strict chose to raise RuntimeError instead. NumPy raises + # a ValueError if the `__array__` dunder does not return an array. + # Exception type may need to be updated in the future for other libraries. numpy_as_array_works = False if numpy_as_array_works: @@ -2147,6 +2174,11 @@ def check_array_api_metric_pairwise(metric, array_namespace, device, dtype_name) check_array_api_multiclass_classification_metric, check_array_api_multilabel_classification_metric, ], + jaccard_score: [ + check_array_api_binary_classification_metric, + check_array_api_multiclass_classification_metric, + check_array_api_multilabel_classification_metric, + ], multilabel_confusion_matrix: [ check_array_api_binary_classification_metric, check_array_api_multiclass_classification_metric, diff --git a/sklearn/metrics/tests/test_regression.py b/sklearn/metrics/tests/test_regression.py index 5e90727583189..396ae5d0ffae1 100644 --- a/sklearn/metrics/tests/test_regression.py +++ b/sklearn/metrics/tests/test_regression.py @@ -330,7 +330,9 @@ def test__check_reg_targets(): for (type1, y1, n_out1), (type2, y2, n_out2) in product(EXAMPLES, repeat=2): if type1 == type2 and n_out1 == n_out2: - y_type, y_check1, y_check2, multioutput = _check_reg_targets(y1, y2, None) + y_type, y_check1, y_check2, _, _ = _check_reg_targets( + y1, y2, sample_weight=None, multioutput=None + ) assert type1 == y_type if type1 == "continuous": assert_array_equal(y_check1, np.reshape(y1, (-1, 1))) @@ -340,7 +342,7 @@ def test__check_reg_targets(): assert_array_equal(y_check2, y2) else: with pytest.raises(ValueError): - _check_reg_targets(y1, y2, None) + _check_reg_targets(y1, y2, sample_weight=None, multioutput=None) def test__check_reg_targets_exception(): @@ -351,7 +353,7 @@ def test__check_reg_targets_exception(): ) ) with pytest.raises(ValueError, match=expected_message): - _check_reg_targets([1, 2, 3], [[1], [2], [3]], invalid_multioutput) + _check_reg_targets([1, 2, 3], [[1], [2], [3]], None, invalid_multioutput) def test_regression_multioutput_array(): diff --git a/sklearn/model_selection/_classification_threshold.py b/sklearn/model_selection/_classification_threshold.py index a5a898abdd1da..c68ed38b8819d 100644 --- a/sklearn/model_selection/_classification_threshold.py +++ b/sklearn/model_selection/_classification_threshold.py @@ -444,13 +444,8 @@ def _fit_and_score_over_thresholds( curve_scorer : scorer instance The scorer taking `classifier` and the validation set as input and outputting decision thresholds and scores as a curve. Note that this is different from - the usual scorer that output a single score value: - - * when `score_method` is one of the four constraint metrics, the curve scorer - will output a curve of two scores parametrized by the decision threshold, e.g. - TPR/TNR or precision/recall curves for each threshold; - * otherwise, the curve scorer will output a single score value for each - threshold. + the usual scorer that outputs a single score value as `curve_scorer` + outputs a single score value for each threshold. score_params : dict Parameters to pass to the `score` method of the underlying scorer. diff --git a/sklearn/model_selection/_search.py b/sklearn/model_selection/_search.py index 61dbd7c1b1d80..5bd3f81195631 100644 --- a/sklearn/model_selection/_search.py +++ b/sklearn/model_selection/_search.py @@ -31,8 +31,8 @@ get_scorer_names, ) from ..utils import Bunch, check_random_state -from ..utils._estimator_html_repr import _VisualBlock from ..utils._param_validation import HasMethods, Interval, StrOptions +from ..utils._repr_html.estimator import _VisualBlock from ..utils._tags import get_tags from ..utils.metadata_routing import ( MetadataRouter, @@ -1328,6 +1328,11 @@ class GridSearchCV(BaseSearchCV): to see how to design a custom selection strategy using a callable via `refit`. + See :ref:`this example + ` + for an example of how to use ``refit=callable`` to balance model + complexity and cross-validated score. + .. versionchanged:: 0.20 Support for callable added. @@ -1704,6 +1709,11 @@ class RandomizedSearchCV(BaseSearchCV): See ``scoring`` parameter to know more about multiple metric evaluation. + See :ref:`this example + ` + for an example of how to use ``refit=callable`` to balance model + complexity and cross-validated score. + .. versionchanged:: 0.20 Support for callable added. @@ -1936,7 +1946,7 @@ class RandomizedSearchCV(BaseSearchCV): >>> clf = RandomizedSearchCV(logistic, distributions, random_state=0) >>> search = clf.fit(iris.data, iris.target) >>> search.best_params_ - {'C': np.float64(2.2), 'penalty': 'l1'} + {'C': np.float64(2.195...), 'penalty': 'l1'} """ _parameter_constraints: dict = { diff --git a/sklearn/model_selection/_search_successive_halving.py b/sklearn/model_selection/_search_successive_halving.py index da608e2bdc6f2..bcd9a83e6dc43 100644 --- a/sklearn/model_selection/_search_successive_halving.py +++ b/sklearn/model_selection/_search_successive_halving.py @@ -487,14 +487,26 @@ class HalvingGridSearchCV(BaseSuccessiveHalving): - `None`: the `estimator`'s :ref:`default evaluation criterion ` is used. - refit : bool, default=True - If True, refit an estimator using the best found parameters on the - whole dataset. + refit : bool or callable, default=True + Refit an estimator using the best found parameters on the whole + dataset. + + Where there are considerations other than maximum score in + choosing a best estimator, ``refit`` can be set to a function which + returns the selected ``best_index_`` given ``cv_results_``. In that + case, the ``best_estimator_`` and ``best_params_`` will be set + according to the returned ``best_index_`` while the ``best_score_`` + attribute will not be available. The refitted estimator is made available at the ``best_estimator_`` attribute and permits using ``predict`` directly on this ``HalvingGridSearchCV`` instance. + See :ref:`this example + ` + for an example of how to use ``refit=callable`` to balance model + complexity and cross-validated score. + error_score : 'raise' or numeric Value to assign to the score if an error occurs in estimator fitting. If set to 'raise', the error is raised. If a numeric value is given, @@ -832,14 +844,26 @@ class HalvingRandomSearchCV(BaseSuccessiveHalving): - `None`: the `estimator`'s :ref:`default evaluation criterion ` is used. - refit : bool, default=True - If True, refit an estimator using the best found parameters on the - whole dataset. + refit : bool or callable, default=True + Refit an estimator using the best found parameters on the whole + dataset. + + Where there are considerations other than maximum score in + choosing a best estimator, ``refit`` can be set to a function which + returns the selected ``best_index_`` given ``cv_results_``. In that + case, the ``best_estimator_`` and ``best_params_`` will be set + according to the returned ``best_index_`` while the ``best_score_`` + attribute will not be available. The refitted estimator is made available at the ``best_estimator_`` attribute and permits using ``predict`` directly on this ``HalvingRandomSearchCV`` instance. + See :ref:`this example + ` + for an example of how to use ``refit=callable`` to balance model + complexity and cross-validated score. + error_score : 'raise' or numeric Value to assign to the score if an error occurs in estimator fitting. If set to 'raise', the error is raised. If a numeric value is given, diff --git a/sklearn/model_selection/_split.py b/sklearn/model_selection/_split.py index ee85af7fe39e6..640b7f6eee2f0 100644 --- a/sklearn/model_selection/_split.py +++ b/sklearn/model_selection/_split.py @@ -1110,10 +1110,10 @@ class TimeSeriesSplit(_BaseKFold): while the train set size accumulates data from previous splits. This cross-validation object is a variation of :class:`KFold`. - In the kth split, it returns first k folds as train set and the - (k+1)th fold as test set. + In the k-th split, it returns the first k folds as the train set and the + (k+1)-th fold as the test set. - Note that unlike standard cross-validation methods, successive + Note that, unlike standard cross-validation methods, successive training sets are supersets of those that come before them. Read more in the :ref:`User Guide `. diff --git a/sklearn/model_selection/_validation.py b/sklearn/model_selection/_validation.py index e9aa7dc77f4c6..c5a1406e6c2a5 100644 --- a/sklearn/model_selection/_validation.py +++ b/sklearn/model_selection/_validation.py @@ -1162,6 +1162,10 @@ def cross_val_predict( >>> y = diabetes.target[:150] >>> lasso = linear_model.Lasso() >>> y_pred = cross_val_predict(lasso, X, y, cv=3) + + For a detailed example of using ``cross_val_predict`` to visualize + prediction errors, please see + :ref:`sphx_glr_auto_examples_model_selection_plot_cv_predict.py`. """ _check_groups_routing_disabled(groups) X, y = indexable(X, y) @@ -1929,6 +1933,11 @@ def learning_curve( Times spent for scoring in seconds. Only present if ``return_times`` is True. + See Also + -------- + LearningCurveDisplay.from_estimator : Plot a learning curve using an + estimator and data. + Examples -------- >>> from sklearn.datasets import make_classification @@ -2391,6 +2400,11 @@ def validation_curve( test_scores : array of shape (n_ticks, n_cv_folds) Scores on test set. + See Also + -------- + ValidationCurveDisplay.from_estimator : Plot the validation curve + given an estimator, the data, and the parameter to vary. + Notes ----- See :ref:`sphx_glr_auto_examples_model_selection_plot_train_error_vs_test_error.py` diff --git a/sklearn/model_selection/tests/test_search.py b/sklearn/model_selection/tests/test_search.py index 393429b29ff92..7888dd2d1766b 100644 --- a/sklearn/model_selection/tests/test_search.py +++ b/sklearn/model_selection/tests/test_search.py @@ -2662,21 +2662,21 @@ def test_search_html_repr(): search_cv = GridSearchCV(pipeline, param_grid=param_grid, refit=False) with config_context(display="diagram"): repr_html = search_cv._repr_html_() - assert "
    DummyClassifier()
    " in repr_html + assert "
    DummyClassifier
    " in repr_html # Fitted with `refit=False` shows the original pipeline search_cv.fit(X, y) with config_context(display="diagram"): repr_html = search_cv._repr_html_() - assert "
    DummyClassifier()
    " in repr_html + assert "
    DummyClassifier
    " in repr_html # Fitted with `refit=True` shows the best estimator search_cv = GridSearchCV(pipeline, param_grid=param_grid, refit=True) search_cv.fit(X, y) with config_context(display="diagram"): repr_html = search_cv._repr_html_() - assert "
    DummyClassifier()
    " not in repr_html - assert "
    LogisticRegression()
    " in repr_html + assert "
    DummyClassifier
    " not in repr_html + assert "
    LogisticRegression
    " in repr_html # Metadata Routing Tests diff --git a/sklearn/naive_bayes.py b/sklearn/naive_bayes.py index e5b03abbb903a..31a1b87af2916 100644 --- a/sklearn/naive_bayes.py +++ b/sklearn/naive_bayes.py @@ -1433,6 +1433,7 @@ def partial_fit(self, X, y, classes=None, sample_weight=None): def __sklearn_tags__(self): tags = super().__sklearn_tags__() + tags.input_tags.categorical = True tags.input_tags.sparse = False tags.input_tags.positive_only = True return tags diff --git a/sklearn/neighbors/_nca.py b/sklearn/neighbors/_nca.py index a4ef3c303b851..8383f95338932 100644 --- a/sklearn/neighbors/_nca.py +++ b/sklearn/neighbors/_nca.py @@ -25,6 +25,7 @@ from ..preprocessing import LabelEncoder from ..utils._param_validation import Interval, StrOptions from ..utils.extmath import softmax +from ..utils.fixes import _get_additional_lbfgs_options_dict from ..utils.multiclass import check_classification_targets from ..utils.random import check_random_state from ..utils.validation import check_array, check_is_fitted, validate_data @@ -312,7 +313,10 @@ def fit(self, X, y): "jac": True, "x0": transformation, "tol": self.tol, - "options": dict(maxiter=self.max_iter, disp=disp), + "options": dict( + maxiter=self.max_iter, + **_get_additional_lbfgs_options_dict("disp", disp), + ), "callback": self._callback, } diff --git a/sklearn/neighbors/meson.build b/sklearn/neighbors/meson.build index df2aab466500c..7993421896218 100644 --- a/sklearn/neighbors/meson.build +++ b/sklearn/neighbors/meson.build @@ -39,7 +39,7 @@ neighbors_extension_metadata = { '_partition_nodes': {'sources': [cython_gen_cpp.process('_partition_nodes.pyx')], 'dependencies': [np_dep]}, - '_quad_tree': {'sources': ['_quad_tree.pyx'], 'dependencies': [np_dep]}, + '_quad_tree': {'sources': [cython_gen.process('_quad_tree.pyx')], 'dependencies': [np_dep]}, } foreach ext_name, ext_dict : neighbors_extension_metadata diff --git a/sklearn/neural_network/_multilayer_perceptron.py b/sklearn/neural_network/_multilayer_perceptron.py index a8a00fe3b4ac5..e8260164202e6 100644 --- a/sklearn/neural_network/_multilayer_perceptron.py +++ b/sklearn/neural_network/_multilayer_perceptron.py @@ -31,6 +31,7 @@ ) from ..utils._param_validation import Interval, Options, StrOptions from ..utils.extmath import safe_sparse_dot +from ..utils.fixes import _get_additional_lbfgs_options_dict from ..utils.metaestimators import available_if from ..utils.multiclass import ( _check_partial_fit_first_call, @@ -585,8 +586,8 @@ def _fit_lbfgs( options={ "maxfun": self.max_fun, "maxiter": self.max_iter, - "iprint": iprint, "gtol": self.tol, + **_get_additional_lbfgs_options_dict("iprint", iprint), }, args=( X, diff --git a/sklearn/pipeline.py b/sklearn/pipeline.py index f3fbf1e3b3299..4b843563bdce3 100644 --- a/sklearn/pipeline.py +++ b/sklearn/pipeline.py @@ -16,9 +16,9 @@ from .exceptions import NotFittedError from .preprocessing import FunctionTransformer from .utils import Bunch -from .utils._estimator_html_repr import _VisualBlock from .utils._metadata_requests import METHODS from .utils._param_validation import HasMethods, Hidden +from .utils._repr_html.estimator import _VisualBlock from .utils._set_output import ( _get_container_adapter, _safe_set_output, @@ -320,6 +320,8 @@ def set_params(self, **kwargs): return self def _validate_steps(self): + if not self.steps: + raise ValueError("The pipeline is empty. Please add steps.") names, estimators = zip(*self.steps) # validate names @@ -1289,7 +1291,6 @@ def __sklearn_is_fitted__(self): An empty pipeline is considered fitted. """ - # First find the last step that is not 'passthrough' last_step = None for _, estimator in reversed(self.steps): @@ -2037,15 +2038,23 @@ def transform(self, X, **params): return self._hstack(Xs) def _hstack(self, Xs): + # Check if Xs dimensions are valid + for X, (name, _) in zip(Xs, self.transformer_list): + if hasattr(X, "shape") and len(X.shape) != 2: + raise ValueError( + f"Transformer '{name}' returned an array or dataframe with " + f"{len(X.shape)} dimensions, but expected 2 dimensions " + "(n_samples, n_features)." + ) + adapter = _get_container_adapter("transform", self) if adapter and all(adapter.is_supported_container(X) for X in Xs): return adapter.hstack(Xs) if any(sparse.issparse(f) for f in Xs): - Xs = sparse.hstack(Xs).tocsr() - else: - Xs = np.hstack(Xs) - return Xs + return sparse.hstack(Xs).tocsr() + + return np.hstack(Xs) def _update_transformer_list(self, transformers): transformers = iter(transformers) diff --git a/sklearn/preprocessing/_function_transformer.py b/sklearn/preprocessing/_function_transformer.py index 3503fead2ba59..f3530f3284dc9 100644 --- a/sklearn/preprocessing/_function_transformer.py +++ b/sklearn/preprocessing/_function_transformer.py @@ -7,8 +7,8 @@ import numpy as np from ..base import BaseEstimator, TransformerMixin, _fit_context -from ..utils._estimator_html_repr import _VisualBlock from ..utils._param_validation import StrOptions +from ..utils._repr_html.estimator import _VisualBlock from ..utils._set_output import ( _get_adapter_from_container, _get_output_config, @@ -200,7 +200,10 @@ def _check_inverse_transform(self, X): # Dataframes can have multiple dtypes dtypes = X.dtypes - if not all(np.issubdtype(d, np.number) for d in dtypes): + # Not all dtypes are numpy dtypes, they can be pandas dtypes as well + if not all( + isinstance(d, np.dtype) and np.issubdtype(d, np.number) for d in dtypes + ): raise ValueError( "'check_inverse' is only supported when all the elements in `X` is" " numerical." @@ -262,7 +265,7 @@ def transform(self, X): if hasattr(out, "columns") and self.feature_names_out is not None: # check the consistency between the column provided by `transform` and - # the the column names provided by `get_feature_names_out`. + # the column names provided by `get_feature_names_out`. feature_names_out = self.get_feature_names_out() if list(out.columns) != list(feature_names_out): # we can override the column names of the output if it is inconsistent diff --git a/sklearn/preprocessing/tests/test_encoders.py b/sklearn/preprocessing/tests/test_encoders.py index dc7bbd2ec03b6..f843a4f16d170 100644 --- a/sklearn/preprocessing/tests/test_encoders.py +++ b/sklearn/preprocessing/tests/test_encoders.py @@ -788,9 +788,9 @@ def test_encoder_dtypes_pandas(): assert_array_equal(enc.transform(X).toarray(), exp) X = pd.DataFrame({"A": [1, 2], "B": ["a", "b"], "C": [3.0, 4.0]}) - X_type = [X["A"].dtype, X["B"].dtype, X["C"].dtype] + expected_cat_type = ["int64", "object", "float64"] enc.fit(X) - assert all([enc.categories_[i].dtype == X_type[i] for i in range(3)]) + assert all([enc.categories_[i].dtype == expected_cat_type[i] for i in range(3)]) assert_array_equal(enc.transform(X).toarray(), exp) diff --git a/sklearn/svm/tests/test_sparse.py b/sklearn/svm/tests/test_sparse.py index 59fede29f359c..4e22c86a66cd8 100644 --- a/sklearn/svm/tests/test_sparse.py +++ b/sklearn/svm/tests/test_sparse.py @@ -125,6 +125,7 @@ def test_unsorted_indices(csr_container): X, y = load_digits(return_X_y=True) X_test = csr_container(X[50:100]) X, y = X[:50], y[:50] + tols = dict(rtol=1e-12, atol=1e-14) X_sparse = csr_container(X) coef_dense = ( @@ -135,7 +136,7 @@ def test_unsorted_indices(csr_container): ) coef_sorted = sparse_svc.coef_ # make sure dense and sparse SVM give the same result - assert_allclose(coef_dense, coef_sorted.toarray()) + assert_allclose(coef_dense, coef_sorted.toarray(), **tols) # reverse each row's indices def scramble_indices(X): @@ -158,9 +159,11 @@ def scramble_indices(X): ) coef_unsorted = unsorted_svc.coef_ # make sure unsorted indices give same result - assert_allclose(coef_unsorted.toarray(), coef_sorted.toarray()) + assert_allclose(coef_unsorted.toarray(), coef_sorted.toarray(), **tols) assert_allclose( - sparse_svc.predict_proba(X_test_unsorted), sparse_svc.predict_proba(X_test) + sparse_svc.predict_proba(X_test_unsorted), + sparse_svc.predict_proba(X_test), + **tols, ) diff --git a/sklearn/tests/test_base.py b/sklearn/tests/test_base.py index b65baa78802bc..0842cf0c82b48 100644 --- a/sklearn/tests/test_base.py +++ b/sklearn/tests/test_base.py @@ -26,7 +26,8 @@ from sklearn.decomposition import PCA from sklearn.ensemble import IsolationForest from sklearn.exceptions import InconsistentVersionWarning -from sklearn.model_selection import GridSearchCV +from sklearn.metrics import get_scorer +from sklearn.model_selection import GridSearchCV, KFold from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC, SVR @@ -992,3 +993,89 @@ def predict(self, X, prop=None): with warnings.catch_warnings(record=True) as record: CustomOutlierDetector().set_predict_request(prop=True).fit_predict([[1]], [1]) assert len(record) == 0 + + +def test_get_params_html(): + """Check the behaviour of the `_get_params_html` method.""" + est = MyEstimator(empty="test") + + assert est._get_params_html() == {"l1": 0, "empty": "test"} + assert est._get_params_html().non_default == ("empty",) + + +def make_estimator_with_param(default_value): + class DynamicEstimator(BaseEstimator): + def __init__(self, param=default_value): + self.param = param + + return DynamicEstimator + + +@pytest.mark.parametrize( + "default_value, test_value", + [ + ((), (1,)), + ((), [1]), + ((), np.array([1])), + ((1, 2), (3, 4)), + ((1, 2), [3, 4]), + ((1, 2), np.array([3, 4])), + (None, 1), + (None, []), + (None, lambda x: x), + (np.nan, 1.0), + (np.nan, np.array([np.nan])), + ("abc", "def"), + ("abc", ["abc"]), + (True, False), + (1, 2), + (1, [1]), + (1, np.array([1])), + (1.0, 2.0), + (1.0, [1.0]), + (1.0, np.array([1.0])), + ([1, 2], [3]), + (np.array([1]), [2, 3]), + (None, KFold()), + (None, get_scorer("accuracy")), + ], +) +def test_param_is_non_default(default_value, test_value): + """Check that we detect non-default parameters with various types. + + Non-regression test for: + https://github.com/scikit-learn/scikit-learn/issues/31525 + """ + estimator = make_estimator_with_param(default_value)(param=test_value) + non_default = estimator._get_params_html().non_default + assert "param" in non_default + + +@pytest.mark.parametrize( + "default_value, test_value", + [ + (None, None), + ((), ()), + ((), []), + ((), np.array([])), + ((1, 2, 3), (1, 2, 3)), + ((1, 2, 3), [1, 2, 3]), + ((1, 2, 3), np.array([1, 2, 3])), + (np.nan, np.nan), + ("abc", "abc"), + (True, True), + (1, 1), + (1.0, 1.0), + (2, 2.0), + ], +) +def test_param_is_default(default_value, test_value): + """Check that we detect the default parameters and values in an array-like will + be reported as default as well. + + Non-regression test for: + https://github.com/scikit-learn/scikit-learn/issues/31525 + """ + estimator = make_estimator_with_param(default_value)(param=test_value) + non_default = estimator._get_params_html().non_default + assert "param" not in non_default diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index de5003687ca95..0ada8c5ef0a30 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -136,6 +136,10 @@ def test_check_estimator_generate_only_deprecation(): "ignore:Since version 1.0, it is not needed to import " "enable_hist_gradient_boosting anymore" ) +# TODO(1.8): remove this filter +@pytest.mark.filterwarnings( + "ignore:Importing from sklearn.utils._estimator_html_repr is deprecated." +) def test_import_all_consistency(): sklearn_path = [os.path.dirname(sklearn.__file__)] # Smoke test to check that any name in a __all__ list is actually defined diff --git a/sklearn/tests/test_metadata_routing.py b/sklearn/tests/test_metadata_routing.py index 46391e9d82bfd..d936fc1c4f3c0 100644 --- a/sklearn/tests/test_metadata_routing.py +++ b/sklearn/tests/test_metadata_routing.py @@ -425,7 +425,7 @@ def test_nested_routing_conflict(): "In WeightedMetaRegressor, there is a conflict on sample_weight between" " what is requested for this estimator and what is requested by its" " children. You can resolve this conflict by using an alias for the" - " child estimator(s) requested metadata." + " child estimators' requested metadata." ) ), ): diff --git a/sklearn/tests/test_min_dependencies_readme.py b/sklearn/tests/test_min_dependencies_readme.py index cc986bd17aeae..6afcd3e57ca04 100644 --- a/sklearn/tests/test_min_dependencies_readme.py +++ b/sklearn/tests/test_min_dependencies_readme.py @@ -32,9 +32,9 @@ def test_min_dependencies_readme(): # sklearn/_min_dependencies.py pattern = re.compile( - r"(\.\. \|)" - r"(([A-Za-z]+\-?)+)" - r"(MinVersion\| replace::)" + r"\.\. \|" + r"([A-Za-z-]+)" + r"MinVersion\| replace::" r"( [0-9]+\.[0-9]+(\.[0-9]+)?)" ) @@ -53,7 +53,7 @@ def test_min_dependencies_readme(): if not matched: continue - package, version = matched.group(2), matched.group(5) + package, version = matched.group(0), matched.group(1) package = package.lower() if package in dependent_packages: diff --git a/sklearn/tests/test_naive_bayes.py b/sklearn/tests/test_naive_bayes.py index 99cfe030a940f..f5638e7384e86 100644 --- a/sklearn/tests/test_naive_bayes.py +++ b/sklearn/tests/test_naive_bayes.py @@ -968,3 +968,12 @@ def test_predict_joint_proba(Estimator, global_random_seed): log_prob_x = logsumexp(jll, axis=1) log_prob_x_y = jll - np.atleast_2d(log_prob_x).T assert_allclose(est.predict_log_proba(X2), log_prob_x_y) + + +@pytest.mark.parametrize("Estimator", ALL_NAIVE_BAYES_CLASSES) +def test_categorical_input_tag(Estimator): + tags = Estimator().__sklearn_tags__() + if Estimator is CategoricalNB: + assert tags.input_tags.categorical + else: + assert not tags.input_tags.categorical diff --git a/sklearn/tests/test_pipeline.py b/sklearn/tests/test_pipeline.py index ad00ffb67a616..96a3052d38b43 100644 --- a/sklearn/tests/test_pipeline.py +++ b/sklearn/tests/test_pipeline.py @@ -282,6 +282,16 @@ def test_pipeline_invalid_parameters(): assert params == params2 +def test_empty_pipeline(): + X = iris.data + y = iris.target + + pipe = Pipeline([]) + msg = "The pipeline is empty. Please add steps." + with pytest.raises(ValueError, match=msg): + pipe.fit(X, y) + + def test_pipeline_init_tuple(): # Pipeline accepts steps as tuple X = np.array([[1, 2]]) @@ -1890,6 +1900,22 @@ def test_feature_union_feature_names_in_(): assert not hasattr(union, "feature_names_in_") +def test_feature_union_1d_output(): + """Test that FeatureUnion raises error for 1D transformer outputs.""" + X = np.arange(6).reshape(3, 2) + + with pytest.raises( + ValueError, + match="Transformer 'b' returned an array or dataframe with 1 dimensions", + ): + FeatureUnion( + [ + ("a", FunctionTransformer(lambda X: X)), + ("b", FunctionTransformer(lambda X: X[:, 1])), + ] + ).fit_transform(X) + + # transform_input tests # ===================== diff --git a/sklearn/utils/__init__.py b/sklearn/utils/__init__.py index 941126c6b083f..8fd8a315a0be2 100644 --- a/sklearn/utils/__init__.py +++ b/sklearn/utils/__init__.py @@ -7,7 +7,6 @@ from . import metadata_routing from ._bunch import Bunch from ._chunking import gen_batches, gen_even_slices -from ._estimator_html_repr import estimator_html_repr # Make _safe_indexing importable from here for backward compat as this particular # helper is considered semi-private and typically very useful for third-party @@ -20,6 +19,8 @@ shuffle, ) from ._mask import safe_mask +from ._repr_html.base import _HTMLDocumentationLinkMixin # noqa: F401 +from ._repr_html.estimator import estimator_html_repr from ._tags import ( ClassifierTags, InputTags, diff --git a/sklearn/utils/_array_api.py b/sklearn/utils/_array_api.py index a9f35516f17b6..6a59249d99bd7 100644 --- a/sklearn/utils/_array_api.py +++ b/sklearn/utils/_array_api.py @@ -638,7 +638,7 @@ def _average(a, axis=None, weights=None, normalize=True, xp=None): # If weights are 1D, add singleton dimensions for broadcasting shape = [1] * a.ndim shape[axis] = a.shape[axis] - weights = xp.reshape(weights, shape) + weights = xp.reshape(weights, tuple(shape)) if xp.isdtype(a.dtype, "complex floating"): raise NotImplementedError( diff --git a/sklearn/utils/_estimator_html_repr.py b/sklearn/utils/_estimator_html_repr.py index 90a700a26ce9c..f7898ae5e76cc 100644 --- a/sklearn/utils/_estimator_html_repr.py +++ b/sklearn/utils/_estimator_html_repr.py @@ -1,549 +1,34 @@ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause -import html -import itertools -from contextlib import closing -from inspect import isclass -from io import StringIO -from pathlib import Path -from string import Template - -from .. import __version__, config_context -from .fixes import parse_version - - -class _IDCounter: - """Generate sequential ids with a prefix.""" - - def __init__(self, prefix): - self.prefix = prefix - self.count = 0 - - def get_id(self): - self.count += 1 - return f"{self.prefix}-{self.count}" - - -def _get_css_style(): - return Path(__file__).with_suffix(".css").read_text(encoding="utf-8") - - -_CONTAINER_ID_COUNTER = _IDCounter("sk-container-id") -_ESTIMATOR_ID_COUNTER = _IDCounter("sk-estimator-id") -_CSS_STYLE = _get_css_style() - - -class _VisualBlock: - """HTML Representation of Estimator - - Parameters - ---------- - kind : {'serial', 'parallel', 'single'} - kind of HTML block - - estimators : list of estimators or `_VisualBlock`s or a single estimator - If kind != 'single', then `estimators` is a list of - estimators. - If kind == 'single', then `estimators` is a single estimator. - - names : list of str, default=None - If kind != 'single', then `names` corresponds to estimators. - If kind == 'single', then `names` is a single string corresponding to - the single estimator. - - name_details : list of str, str, or None, default=None - If kind != 'single', then `name_details` corresponds to `names`. - If kind == 'single', then `name_details` is a single string - corresponding to the single estimator. - - name_caption : str, default=None - The caption below the name. `None` stands for no caption. - Only active when kind == 'single'. - - doc_link_label : str, default=None - The label for the documentation link. If provided, the label would be - "Documentation for {doc_link_label}". Otherwise it will look for `names`. - Only active when kind == 'single'. - - dash_wrapped : bool, default=True - If true, wrapped HTML element will be wrapped with a dashed border. - Only active when kind != 'single'. - """ - - def __init__( - self, - kind, - estimators, - *, - names=None, - name_details=None, - name_caption=None, - doc_link_label=None, - dash_wrapped=True, - ): - self.kind = kind - self.estimators = estimators - self.dash_wrapped = dash_wrapped - self.name_caption = name_caption - self.doc_link_label = doc_link_label - - if self.kind in ("parallel", "serial"): - if names is None: - names = (None,) * len(estimators) - if name_details is None: - name_details = (None,) * len(estimators) - - self.names = names - self.name_details = name_details - - def _sk_visual_block_(self): - return self - - -def _write_label_html( - out, - name, - name_details, - name_caption=None, - doc_link_label=None, - outer_class="sk-label-container", - inner_class="sk-label", - checked=False, - doc_link="", - is_fitted_css_class="", - is_fitted_icon="", -): - """Write labeled html with or without a dropdown with named details. - - Parameters - ---------- - out : file-like object - The file to write the HTML representation to. - name : str - The label for the estimator. It corresponds either to the estimator class name - for a simple estimator or in the case of a `Pipeline` and `ColumnTransformer`, - it corresponds to the name of the step. - name_details : str - The details to show as content in the dropdown part of the toggleable label. It - can contain information such as non-default parameters or column information for - `ColumnTransformer`. - name_caption : str, default=None - The caption below the name. If `None`, no caption will be created. - doc_link_label : str, default=None - The label for the documentation link. If provided, the label would be - "Documentation for {doc_link_label}". Otherwise it will look for `name`. - outer_class : {"sk-label-container", "sk-item"}, default="sk-label-container" - The CSS class for the outer container. - inner_class : {"sk-label", "sk-estimator"}, default="sk-label" - The CSS class for the inner container. - checked : bool, default=False - Whether the dropdown is folded or not. With a single estimator, we intend to - unfold the content. - doc_link : str, default="" - The link to the documentation for the estimator. If an empty string, no link is - added to the diagram. This can be generated for an estimator if it uses the - `_HTMLDocumentationLinkMixin`. - is_fitted_css_class : {"", "fitted"} - The CSS class to indicate whether or not the estimator is fitted. The - empty string means that the estimator is not fitted and "fitted" means that the - estimator is fitted. - is_fitted_icon : str, default="" - The HTML representation to show the fitted information in the diagram. An empty - string means that no information is shown. - """ - out.write( - f'
    ' - ) - name = html.escape(name) - - if name_details is not None: - name_details = html.escape(str(name_details)) - checked_str = "checked" if checked else "" - est_id = _ESTIMATOR_ID_COUNTER.get_id() - - if doc_link: - doc_label = "Online documentation" - if doc_link_label is not None: - doc_label = f"Documentation for {doc_link_label}" - elif name is not None: - doc_label = f"Documentation for {name}" - doc_link = ( - f'?{doc_label}' - ) - - name_caption_div = ( - "" - if name_caption is None - else f'
    {html.escape(name_caption)}
    ' - ) - name_caption_div = f"
    {name}
    {name_caption_div}
    " - links_div = ( - f"
    {doc_link}{is_fitted_icon}
    " - if doc_link or is_fitted_icon - else "" - ) - - label_html = ( - f'' - ) - - fmt_str = ( - f'{label_html}
    {name_details}'
    -            "
    " - ) - out.write(fmt_str) - else: - out.write(f"") - out.write("
    ") # outer_class inner_class - - -def _get_visual_block(estimator): - """Generate information about how to display an estimator.""" - if hasattr(estimator, "_sk_visual_block_"): - try: - return estimator._sk_visual_block_() - except Exception: - return _VisualBlock( - "single", - estimator, - names=estimator.__class__.__name__, - name_details=str(estimator), - ) - - if isinstance(estimator, str): - return _VisualBlock( - "single", estimator, names=estimator, name_details=estimator - ) - elif estimator is None: - return _VisualBlock("single", estimator, names="None", name_details="None") - - # check if estimator looks like a meta estimator (wraps estimators) - if hasattr(estimator, "get_params") and not isclass(estimator): - estimators = [ - (key, est) - for key, est in estimator.get_params(deep=False).items() - if hasattr(est, "get_params") and hasattr(est, "fit") and not isclass(est) - ] - if estimators: - return _VisualBlock( - "parallel", - [est for _, est in estimators], - names=[f"{key}: {est.__class__.__name__}" for key, est in estimators], - name_details=[str(est) for _, est in estimators], - ) - - return _VisualBlock( - "single", - estimator, - names=estimator.__class__.__name__, - name_details=str(estimator), - ) - - -def _write_estimator_html( - out, - estimator, - estimator_label, - estimator_label_details, - is_fitted_css_class, - is_fitted_icon="", - first_call=False, -): - """Write estimator to html in serial, parallel, or by itself (single). - - For multiple estimators, this function is called recursively. - - Parameters - ---------- - out : file-like object - The file to write the HTML representation to. - estimator : estimator object - The estimator to visualize. - estimator_label : str - The label for the estimator. It corresponds either to the estimator class name - for simple estimator or in the case of `Pipeline` and `ColumnTransformer`, it - corresponds to the name of the step. - estimator_label_details : str - The details to show as content in the dropdown part of the toggleable label. - It can contain information as non-default parameters or column information for - `ColumnTransformer`. - is_fitted_css_class : {"", "fitted"} - The CSS class to indicate whether or not the estimator is fitted or not. The - empty string means that the estimator is not fitted and "fitted" means that the - estimator is fitted. - is_fitted_icon : str, default="" - The HTML representation to show the fitted information in the diagram. An empty - string means that no information is shown. If the estimator to be shown is not - the first estimator (i.e. `first_call=False`), `is_fitted_icon` is always an - empty string. - first_call : bool, default=False - Whether this is the first time this function is called. - """ - if first_call: - est_block = _get_visual_block(estimator) - else: - is_fitted_icon = "" - with config_context(print_changed_only=True): - est_block = _get_visual_block(estimator) - # `estimator` can also be an instance of `_VisualBlock` - if hasattr(estimator, "_get_doc_link"): - doc_link = estimator._get_doc_link() - else: - doc_link = "" - if est_block.kind in ("serial", "parallel"): - dashed_wrapped = first_call or est_block.dash_wrapped - dash_cls = " sk-dashed-wrapped" if dashed_wrapped else "" - out.write(f'
    ') - - if estimator_label: - _write_label_html( - out, - estimator_label, - estimator_label_details, - doc_link=doc_link, - is_fitted_css_class=is_fitted_css_class, - is_fitted_icon=is_fitted_icon, - ) - - kind = est_block.kind - out.write(f'
    ') - est_infos = zip(est_block.estimators, est_block.names, est_block.name_details) - - for est, name, name_details in est_infos: - if kind == "serial": - _write_estimator_html( - out, - est, - name, - name_details, - is_fitted_css_class=is_fitted_css_class, - ) - else: # parallel - out.write('
    ') - # wrap element in a serial visualblock - serial_block = _VisualBlock("serial", [est], dash_wrapped=False) - _write_estimator_html( - out, - serial_block, - name, - name_details, - is_fitted_css_class=is_fitted_css_class, - ) - out.write("
    ") # sk-parallel-item - - out.write("
    ") - elif est_block.kind == "single": - _write_label_html( - out, - est_block.names, - est_block.name_details, - est_block.name_caption, - est_block.doc_link_label, - outer_class="sk-item", - inner_class="sk-estimator", - checked=first_call, - doc_link=doc_link, - is_fitted_css_class=is_fitted_css_class, - is_fitted_icon=is_fitted_icon, - ) - - -def estimator_html_repr(estimator): - """Build a HTML representation of an estimator. - - Read more in the :ref:`User Guide `. - - Parameters - ---------- - estimator : estimator object - The estimator to visualize. - - Returns - ------- - html: str - HTML representation of estimator. - - Examples - -------- - >>> from sklearn.utils._estimator_html_repr import estimator_html_repr - >>> from sklearn.linear_model import LogisticRegression - >>> estimator_html_repr(LogisticRegression()) - '" - f'
    ' - '
    ' - f"
    {html.escape(estimator_str)}
    {fallback_msg}" - "
    " - '
    ") - - html_output = out.getvalue() - return html_output - - -class _HTMLDocumentationLinkMixin: - """Mixin class allowing to generate a link to the API documentation. - - This mixin relies on three attributes: - - `_doc_link_module`: it corresponds to the root module (e.g. `sklearn`). Using this - mixin, the default value is `sklearn`. - - `_doc_link_template`: it corresponds to the template used to generate the - link to the API documentation. Using this mixin, the default value is - `"https://scikit-learn.org/{version_url}/modules/generated/ - {estimator_module}.{estimator_name}.html"`. - - `_doc_link_url_param_generator`: it corresponds to a function that generates the - parameters to be used in the template when the estimator module and name are not - sufficient. - - The method :meth:`_get_doc_link` generates the link to the API documentation for a - given estimator. - - This useful provides all the necessary states for - :func:`sklearn.utils.estimator_html_repr` to generate a link to the API - documentation for the estimator HTML diagram. - - Examples - -------- - If the default values for `_doc_link_module`, `_doc_link_template` are not suitable, - then you can override them and provide a method to generate the URL parameters: - >>> from sklearn.base import BaseEstimator - >>> doc_link_template = "https://address.local/{single_param}.html" - >>> def url_param_generator(estimator): - ... return {"single_param": estimator.__class__.__name__} - >>> class MyEstimator(BaseEstimator): - ... # use "builtins" since it is the associated module when declaring - ... # the class in a docstring - ... _doc_link_module = "builtins" - ... _doc_link_template = doc_link_template - ... _doc_link_url_param_generator = url_param_generator - >>> estimator = MyEstimator() - >>> estimator._get_doc_link() - 'https://address.local/MyEstimator.html' - - If instead of overriding the attributes inside the class definition, you want to - override a class instance, you can use `types.MethodType` to bind the method to the - instance: - >>> import types - >>> estimator = BaseEstimator() - >>> estimator._doc_link_template = doc_link_template - >>> estimator._doc_link_url_param_generator = types.MethodType( - ... url_param_generator, estimator) - >>> estimator._get_doc_link() - 'https://address.local/BaseEstimator.html' - """ - - _doc_link_module = "sklearn" - _doc_link_url_param_generator = None - - @property - def _doc_link_template(self): - sklearn_version = parse_version(__version__) - if sklearn_version.dev is None: - version_url = f"{sklearn_version.major}.{sklearn_version.minor}" - else: - version_url = "dev" - return getattr( - self, - "__doc_link_template", - ( - f"https://scikit-learn.org/{version_url}/modules/generated/" - "{estimator_module}.{estimator_name}.html" - ), - ) - - @_doc_link_template.setter - def _doc_link_template(self, value): - setattr(self, "__doc_link_template", value) - - def _get_doc_link(self): - """Generates a link to the API documentation for a given estimator. - - This method generates the link to the estimator's documentation page - by using the template defined by the attribute `_doc_link_template`. - - Returns - ------- - url : str - The URL to the API documentation for this estimator. If the estimator does - not belong to module `_doc_link_module`, the empty string (i.e. `""`) is - returned. - """ - if self.__class__.__module__.split(".")[0] != self._doc_link_module: - return "" - - if self._doc_link_url_param_generator is None: - estimator_name = self.__class__.__name__ - # Construct the estimator's module name, up to the first private submodule. - # This works because in scikit-learn all public estimators are exposed at - # that level, even if they actually live in a private sub-module. - estimator_module = ".".join( - itertools.takewhile( - lambda part: not part.startswith("_"), - self.__class__.__module__.split("."), - ) - ) - return self._doc_link_template.format( - estimator_module=estimator_module, estimator_name=estimator_name - ) - return self._doc_link_template.format(**self._doc_link_url_param_generator()) +import warnings + +from ._repr_html.base import _HTMLDocumentationLinkMixin +from ._repr_html.estimator import ( + _get_visual_block, + _IDCounter, + _VisualBlock, + _write_estimator_html, + _write_label_html, + estimator_html_repr, +) + +__all__ = [ + "_HTMLDocumentationLinkMixin", + "_IDCounter", + "_VisualBlock", + "_get_visual_block", + "_write_estimator_html", + "_write_label_html", + "estimator_html_repr", +] + +# TODO(1.8): Remove the entire module +warnings.warn( + "Importing from sklearn.utils._estimator_html_repr is deprecated. The tools have " + "been moved to sklearn.utils._repr_html. Be aware that this module is private and " + "may be subject to change in the future. The module _estimator_html_repr will be " + "removed in 1.8.0.", + FutureWarning, + stacklevel=2, +) diff --git a/sklearn/utils/_indexing.py b/sklearn/utils/_indexing.py index 09427376a4059..ec83cf6660b25 100644 --- a/sklearn/utils/_indexing.py +++ b/sklearn/utils/_indexing.py @@ -10,6 +10,8 @@ import numpy as np from scipy.sparse import issparse +from sklearn.utils.fixes import PYARROW_VERSION_BELOW_17 + from ._array_api import _is_numpy_namespace, get_namespace from ._param_validation import Interval, validate_params from .extmath import _approximate_mode @@ -131,7 +133,17 @@ def _pyarrow_indexing(X, key, key_dtype, axis): key = np.asarray(key) if key_dtype == "bool": + # TODO(pyarrow): remove version checking and following if-branch when + # pyarrow==17.0.0 is the minimal version, see pyarrow issue + # https://github.com/apache/arrow/issues/42013 for more info + if PYARROW_VERSION_BELOW_17: + import pyarrow + + if not isinstance(key, pyarrow.BooleanArray): + key = pyarrow.array(key, type=pyarrow.bool_()) + X_indexed = X.filter(key) + else: X_indexed = X.take(key) diff --git a/sklearn/utils/_metadata_requests.py b/sklearn/utils/_metadata_requests.py index 2c7e650b133d6..70369c03fce97 100644 --- a/sklearn/utils/_metadata_requests.py +++ b/sklearn/utils/_metadata_requests.py @@ -9,43 +9,51 @@ developers and users who implement custom meta-estimators, need to deal with the objects implemented in this file. -All estimators (should) implement a ``get_metadata_routing`` method, returning -the routing requests set for the estimator. This method is automatically -implemented via ``BaseEstimator`` for all simple estimators, but needs a custom -implementation for meta-estimators. - -In non-routing consumers, i.e. the simplest case, e.g. ``SVM``, -``get_metadata_routing`` returns a ``MetadataRequest`` object. - -In routers, e.g. meta-estimators and a multi metric scorer, -``get_metadata_routing`` returns a ``MetadataRouter`` object. - -An object which is both a router and a consumer, e.g. a meta-estimator which -consumes ``sample_weight`` and routes ``sample_weight`` to its sub-estimators, -routing information includes both information about the object itself (added -via ``MetadataRouter.add_self_request``), as well as the routing information -for its sub-estimators. - -A ``MetadataRequest`` instance includes one ``MethodMetadataRequest`` per -method in ``METHODS``, which includes ``fit``, ``score``, etc. - -Request values are added to the routing mechanism by adding them to -``MethodMetadataRequest`` instances, e.g. -``metadatarequest.fit.add(param="sample_weight", alias="my_weights")``. This is -used in ``set_{method}_request`` which are automatically generated, so users -and developers almost never need to directly call methods on a -``MethodMetadataRequest``. - -The ``alias`` above in the ``add`` method has to be either a string (an alias), -or a {True (requested), False (unrequested), None (error if passed)}``. There +The routing is coordinated by building ``MetadataRequest`` objects +for objects that consume metadata, and ``MetadataRouter`` objects for objects that +can route metadata, which are then aligned during a call to `process_routing()`. This +function returns a Bunch object (dictionary-like) with all the information on the +consumers and which metadata they had requested and the actual metadata values. A +routing method (such as `fit` in a meta-estimator) can now provide the metadata to the +relevant consuming method (such as `fit` in a sub-estimator). + +The ``MetadataRequest`` and ``MetadataRouter`` objects are constructed via a +``get_metadata_routing`` method, which all scikit-learn estimators provide. +This method is automatically implemented via ``BaseEstimator`` for all simple +estimators, but needs a custom implementation for meta-estimators. + +MetadataRequest +~~~~~~~~~~~~~~~ + +In non-routing consumers, the simplest case, e.g. ``SVM``, ``get_metadata_routing`` +returns a ``MetadataRequest`` object which is assigned to the consumer's +`_metadata_request` attribute. It stores which metadata is required by each method of +the consumer by including one ``MethodMetadataRequest`` per method in ``METHODS`` +(e. g. ``fit``, ``score``, etc). + +Users and developers almost never need to directly add a new ``MethodMetadataRequest``, +to the consumer's `_metadata_request` attribute, since these are generated +automatically. This attribute is modified while running `set_{method}_request` methods +(such as `set_fit_request()`), which adds the request via +`method_metadata_request.add_request(param=prop, alias=alias)`. + +The ``alias`` in the ``add_request`` method has to be either a string (an alias), +or one of ``[True (requested), False (unrequested), None (error if passed)]``. There are some other special values such as ``UNUSED`` and ``WARN`` which are used for purposes such as warning of removing a metadata in a child class, but not used by the end users. -``MetadataRouter`` includes information about sub-objects' routing and how -methods are mapped together. For instance, the information about which methods -of a sub-estimator are called in which methods of the meta-estimator are all -stored here. Conceptually, this information looks like: +MetadataRouter +~~~~~~~~~~~~~~ + +In routers (such as meta-estimators or multi metric scorers), ``get_metadata_routing`` +returns a ``MetadataRouter`` object. It provides information about which method, from +the router object, calls which method in a consumer's object, and also, which metadata +had been requested by the consumer's methods, thus specifying how metadata is to be +passed. If a sub-estimator is a router as well, their routing information is also stored +in the meta-estimators router. + +Conceptually, this information looks like: ``` { @@ -57,17 +65,30 @@ } ``` +The `MetadataRouter` objects are never stored and are always recreated anew whenever +the object's `get_metadata_routing` method is called. + +An object that is both a router and a consumer, e.g. a meta-estimator which +consumes ``sample_weight`` and routes ``sample_weight`` to its sub-estimators +also returns a ``MetadataRouter`` object. Its routing information includes both +information about what metadata is required by the object itself (added via +``MetadataRouter.add_self_request``), as well as the routing information for its +sub-estimators (added via ``MetadataRouter.add``). + +Implementation Details +~~~~~~~~~~~~~~~~~~~~~~ + To give the above representation some structure, we use the following objects: -- ``(caller=..., callee=...)`` is a namedtuple called ``MethodPair`` +- ``(caller=..., callee=...)`` is a namedtuple called ``MethodPair``. - The list of ``MethodPair`` stored in the ``mapping`` field of a `RouterMappingPair` is - a ``MethodMapping`` object + a ``MethodMapping`` object. -- ``(mapping=..., router=...)`` is a namedtuple called ``RouterMappingPair`` +- ``(mapping=..., router=...)`` is a namedtuple called ``RouterMappingPair``. The ``set_{method}_request`` methods are dynamically generated for estimators -which inherit from the ``BaseEstimator``. This is done by attaching instances +which inherit from ``BaseEstimator``. This is done by attaching instances of the ``RequestMethod`` descriptor to classes, which is done in the ``_MetadataRequester`` class, and ``BaseEstimator`` inherits from this mixin. This mixin also implements the ``get_metadata_routing``, which meta-estimators @@ -242,7 +263,7 @@ def get_metadata_routing(self): def request_is_alias(item): - """Check if an item is a valid alias. + """Check if an item is a valid string alias for a metadata. Values in ``VALID_REQUEST_VALUES`` are not considered aliases in this context. Only a string which is a valid identifier is. @@ -250,7 +271,7 @@ def request_is_alias(item): Parameters ---------- item : object - The given item to be checked if it can be an alias. + The given item to be checked if it can be an alias for the metadata. Returns ------- @@ -287,9 +308,10 @@ def request_is_valid(item): class MethodMetadataRequest: - """A prescription of how metadata is to be passed to a single method. + """Container for metadata requests associated with a single method. - Refer to :class:`MetadataRequest` for how this class is used. + Instances of this class get used within a :class:`MetadataRequest` - one per each + public method (`fit`, `transform`, ...) that its owning consumer has. .. versionadded:: 1.3 @@ -326,13 +348,14 @@ def add_request( Parameters ---------- param : str - The property for which a request is set. + The metadata for which a request is set. alias : str, or {True, False, None} - Specifies which metadata should be routed to `param` + Specifies which metadata should be routed to the method that owns this + `MethodMetadataRequest`. - str: the name (or alias) of metadata given to a meta-estimator that - should be routed to this parameter. + should be routed to the method that owns this `MethodMetadataRequest`. - True: requested @@ -378,7 +401,7 @@ def _get_param_names(self, return_alias): Returns ------- names : set of str - A set of strings with the names of all parameters. + A set of strings with the names of all metadata. """ return set( alias if return_alias and not request_is_valid(alias) else prop @@ -413,10 +436,10 @@ def _check_warnings(self, *, params): ) def _route_params(self, params, parent, caller): - """Prepare the given parameters to be passed to the method. + """Prepare the given metadata to be passed to the method. The output of this method can be used directly as the input to the - corresponding method as extra props. + corresponding method as **kwargs. Parameters ---------- @@ -432,8 +455,8 @@ def _route_params(self, params, parent, caller): Returns ------- params : Bunch - A :class:`~sklearn.utils.Bunch` of {prop: value} which can be given to the - corresponding method. + A :class:`~sklearn.utils.Bunch` of {metadata: value} which can be + passed to the corresponding method. """ self._check_warnings(params=params) unrequested = dict() @@ -478,7 +501,7 @@ def _route_params(self, params, parent, caller): return res def _consumes(self, params): - """Check whether the given parameters are consumed by this method. + """Check whether the given metadata are consumed by this method. Parameters ---------- @@ -548,7 +571,7 @@ def __init__(self, owner): ) def consumes(self, method, params): - """Check whether the given parameters are consumed by the given method. + """Check whether the given metadata are consumed by the given method. .. versionadded:: 1.4 @@ -619,7 +642,7 @@ def _get_param_names(self, method, return_alias, ignore_self_request=None): Returns ------- names : set of str - A set of strings with the names of all parameters. + A set of strings with the names of all metadata. """ return getattr(self, method)._get_param_names(return_alias=return_alias) @@ -647,8 +670,8 @@ def _route_params(self, *, params, method, parent, caller): Returns ------- params : Bunch - A :class:`~sklearn.utils.Bunch` of {prop: value} which can be given to the - corresponding method. + A :class:`~sklearn.utils.Bunch` of {metadata: value} which can be given to + the corresponding method. """ return getattr(self, method)._route_params( params=params, parent=parent, caller=caller @@ -708,7 +731,7 @@ def __str__(self): class MethodMapping: - """Stores the mapping between caller and callee methods for a router. + """Stores the mapping between caller and callee methods for a :term:`router`. This class is primarily used in a ``get_metadata_routing()`` of a router object when defining the mapping between the router's methods and a sub-object (a @@ -777,14 +800,15 @@ def __str__(self): class MetadataRouter: - """Stores and handles metadata routing for a router object. + """Coordinates metadata routing for a :term:`router` object. - This class is used by router objects to store and handle metadata routing. - Routing information is stored as a dictionary of the form ``{"object_name": - RouteMappingPair(method_mapping, routing_info)}``, where ``method_mapping`` + This class is used by :term:`meta-estimators` or functions that can route metadata, + to handle their metadata routing. Routing information is stored in a + dictionary-like structure of the form ``{"object_name": + RouterMappingPair(mapping, router)}``, where ``mapping`` is an instance of :class:`~sklearn.utils.metadata_routing.MethodMapping` and - ``routing_info`` is either a - :class:`~sklearn.utils.metadata_routing.MetadataRequest` or a + ``router`` is either a + :class:`~sklearn.utils.metadata_routing.MetadataRequest` or another :class:`~sklearn.utils.metadata_routing.MetadataRouter` instance. .. versionadded:: 1.3 @@ -804,21 +828,21 @@ def __init__(self, owner): self._route_mappings = dict() # `_self_request` is used if the router is also a consumer. # _self_request, (added using `add_self_request()`) is treated - # differently from the other objects which are stored in + # differently from the other consumer objects which are stored in # _route_mappings. self._self_request = None self.owner = owner def add_self_request(self, obj): - """Add `self` (as a consumer) to the routing. + """Add `self` (as a :term:`consumer`) to the `MetadataRouter`. - This method is used if the router is also a consumer, and hence the - router itself needs to be included in the routing. The passed object + This method is used if the :term:`router` is also a :term:`consumer`, and hence + the router itself needs to be included in the routing. The passed object can be an estimator or a :class:`~sklearn.utils.metadata_routing.MetadataRequest`. A router should add itself using this method instead of `add` since it - should be treated differently than the other objects to which metadata + should be treated differently than the other consumer objects to which metadata is routed by the router. Parameters @@ -846,15 +870,20 @@ def add_self_request(self, obj): return self def add(self, *, method_mapping, **objs): - """Add named objects with their corresponding method mapping. + """Add :term:`consumers ` to the `MetadataRouter`. + + The estimators that consume metadata are passed as named objects along with a + method mapping, that defines how their methods relate to those of the + :term:`router`. Parameters ---------- method_mapping : MethodMapping - The mapping between the child and the parent's methods. + The mapping between the child (:term:`consumer`) and the parent's + (:term:`router`'s) methods. **objs : dict - A dictionary of objects from which metadata is extracted by calling + A dictionary of objects, whose requests are extracted by calling :func:`~sklearn.utils.metadata_routing.get_routing_for_object` on them. Returns @@ -871,7 +900,7 @@ def add(self, *, method_mapping, **objs): return self def consumes(self, method, params): - """Check whether the given parameters are consumed by the given method. + """Check whether the given metadata is consumed by the given method. .. versionadded:: 1.4 @@ -925,7 +954,7 @@ def _get_param_names(self, *, method, return_alias, ignore_self_request): Returns ------- names : set of str - A set of strings with the names of all parameters. + A set of strings with the names of all metadata. """ res = set() if self._self_request and not ignore_self_request: @@ -946,14 +975,14 @@ def _get_param_names(self, *, method, return_alias, ignore_self_request): return res def _route_params(self, *, params, method, parent, caller): - """Prepare the given parameters to be passed to the method. + """Prepare the given metadata to be passed to the method. This is used when a router is used as a child object of another router. The parent router then passes all parameters understood by the child object to it and delegates their validation to the child. The output of this method can be used directly as the input to the - corresponding method as extra props. + corresponding method as **kwargs. Parameters ---------- @@ -961,8 +990,7 @@ def _route_params(self, *, params, method, parent, caller): A dictionary of provided metadata. method : str - The name of the method for which the parameters are requested and - routed. + The name of the method for which the metadata is requested and routed. parent : object Parent class object, that routes the metadata. @@ -973,8 +1001,8 @@ def _route_params(self, *, params, method, parent, caller): Returns ------- params : Bunch - A :class:`~sklearn.utils.Bunch` of {prop: value} which can be given to the - corresponding method. + A :class:`~sklearn.utils.Bunch` of {metadata: value} which can be given to + the corresponding method. """ res = Bunch() if self._self_request: @@ -1001,28 +1029,28 @@ def _route_params(self, *, params, method, parent, caller): f"In {self.owner}, there is a conflict on {key} between what is" " requested for this estimator and what is requested by its" " children. You can resolve this conflict by using an alias for" - " the child estimator(s) requested metadata." + " the child estimators' requested metadata." ) res.update(child_params) return res def route_params(self, *, caller, params): - """Return the input parameters requested by child objects. + """Get the values of metadata requested by :term:`consumers `. - The output of this method is a :class:`~sklearn.utils.Bunch`, which includes the - metadata for all methods of each child object that is used in the router's - `caller` method. + Returns a :class:`~sklearn.utils.Bunch` containing the metadata that this + :term:`router`'s `caller` method needs to route, organized by each + :term:`consumer` and their corresponding methods. - If the router is also a consumer, it also checks for warnings of - `self`'s/consumer's requested metadata. + This can be used to pass the required metadata to corresponding methods in + consumers. Parameters ---------- caller : str - The name of the method for which the parameters are requested and - routed. If called inside the :term:`fit` method of a router, it - would be `"fit"`. + The name of the :term:`router`'s method through which the metadata is + routed. For example, if called inside the :term:`fit` method of a router, + this would be `"fit"`. params : dict A dictionary of provided metadata. @@ -1031,9 +1059,7 @@ def route_params(self, *, caller, params): ------- params : Bunch A :class:`~sklearn.utils.Bunch` of the form - ``{"object_name": {"method_name": {params: value}}}`` which can be - used to pass the required metadata to corresponding methods or - corresponding child objects. + ``{"object_name": {"method_name": {metadata: value}}}``. """ if self._self_request: self._self_request._check_warnings(params=params, method=caller) @@ -1062,9 +1088,9 @@ def validate_metadata(self, *, method, params): Parameters ---------- method : str - The name of the method for which the parameters are requested and - routed. If called inside the :term:`fit` method of a router, it - would be `"fit"`. + The name of the :term:`router`'s method through which the metadata is + routed. For example, if called inside the :term:`fit` method of a router, + this would be `"fit"`. params : dict A dictionary of provided metadata. @@ -1149,8 +1175,8 @@ def get_routing_for_object(obj=None): Returns ------- - obj : MetadataRequest or MetadataRouting - A ``MetadataRequest`` or a ``MetadataRouting`` taken or created from + obj : MetadataRequest or MetadataRouter + A ``MetadataRequest`` or a ``MetadataRouter`` taken or created from the given object. """ # doing this instead of a try/except since an AttributeError could be raised @@ -1166,16 +1192,18 @@ def get_routing_for_object(obj=None): # Request method # ============== -# This section includes what's needed for the request method descriptor and -# their dynamic generation in a meta class. - -# These strings are used to dynamically generate the docstrings for -# set_{method}_request methods. -REQUESTER_DOC = """ Request metadata passed to the ``{method}`` method. - - Note that this method is only relevant if - ``enable_metadata_routing=True`` (see :func:`sklearn.set_config`). - Please see :ref:`User Guide ` on how the routing +# This section includes what's needed for the `RequestMethod` descriptor and +# the dynamic generation of `set_{method}_request` methods in the `_MetadataRequester` +# mixin class. + +# These strings are used to dynamically generate the docstrings for the methods. +REQUESTER_DOC = """ Configure whether metadata should be requested to be \ +passed to the ``{method}`` method. + + Note that this method is only relevant when this estimator is used as a + sub-estimator within a :term:`meta-estimator` and metadata routing is enabled + with ``enable_metadata_routing=True`` (see :func:`sklearn.set_config`). + Please check the :ref:`User Guide ` on how the routing mechanism works. The options for each parameter are: @@ -1199,11 +1227,6 @@ def get_routing_for_object(obj=None): .. versionadded:: 1.3 - .. note:: - This method is only relevant if this estimator is used as a - sub-estimator of a meta-estimator, e.g. used inside a - :class:`~sklearn.pipeline.Pipeline`. Otherwise it has no effect. - Parameters ---------- """ @@ -1221,7 +1244,7 @@ def get_routing_for_object(obj=None): class RequestMethod: """ - A descriptor for request methods. + Descriptor for defining `set_{method}_request` methods in estimators. .. versionadded:: 1.3 @@ -1260,10 +1283,11 @@ def __init__(self, name, keys, validate_keys=True): def __get__(self, instance, owner): # we would want to have a method which accepts only the expected args def func(*args, **kw): - """Updates the request for provided parameters + """Updates the `_metadata_request` attribute of the consumer (`instance`) + for the parameters provided as `**kw`. This docstring is overwritten below. - See REQUESTER_DOC for expected functionality + See REQUESTER_DOC for expected functionality. """ if not _routing_enabled(): raise RuntimeError( @@ -1488,7 +1512,7 @@ class attributes, as well as determining request keys from method return requests def _get_metadata_request(self): - """Get requested data properties. + """Get requested metadata for the instance. Please check :ref:`User Guide ` on how the routing mechanism works. @@ -1531,9 +1555,9 @@ def get_metadata_routing(self): # prefix to reduce the chances of name collisions with the passed metadata, and # since they're positional only, users will never type those underscores. def process_routing(_obj, _method, /, **kwargs): - """Validate and route input parameters. + """Validate and route metadata. - This function is used inside a router's method, e.g. :term:`fit`, + This function is used inside a :term:`router`'s method, e.g. :term:`fit`, to validate the metadata and handle the routing. Assuming this signature of a router's fit method: @@ -1551,7 +1575,7 @@ def process_routing(_obj, _method, /, **kwargs): ---------- _obj : object An object implementing ``get_metadata_routing``. Typically a - meta-estimator. + :term:`meta-estimator`. _method : str The name of the router's method in which this function is called. @@ -1563,9 +1587,9 @@ def process_routing(_obj, _method, /, **kwargs): ------- routed_params : Bunch A :class:`~utils.Bunch` of the form ``{"object_name": {"method_name": - {params: value}}}`` which can be used to pass the required metadata to + {metadata: value}}}`` which can be used to pass the required metadata to A :class:`~sklearn.utils.Bunch` of the form ``{"object_name": {"method_name": - {params: value}}}`` which can be used to pass the required metadata to + {metadata: value}}}`` which can be used to pass the required metadata to corresponding methods or corresponding child objects. The object names are those defined in `obj.get_metadata_routing()`. """ diff --git a/sklearn/utils/_plotting.py b/sklearn/utils/_plotting.py index 946c95186374b..1a3883b7db7f5 100644 --- a/sklearn/utils/_plotting.py +++ b/sklearn/utils/_plotting.py @@ -1,13 +1,16 @@ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause +import warnings +from collections.abc import Mapping import numpy as np from . import check_consistent_length from ._optional_dependencies import check_matplotlib_support from ._response import _get_response_values_binary +from .fixes import parse_version from .multiclass import type_of_target -from .validation import _check_pos_label_consistency +from .validation import _check_pos_label_consistency, _num_samples class _BinaryClassifierCurveDisplayMixin: @@ -24,7 +27,10 @@ def _validate_plot_params(self, *, ax=None, name=None): if ax is None: _, ax = plt.subplots() - name = self.estimator_name if name is None else name + # Display classes are in process of changing from `estimator_name` to `name`. + # Try old attr name: `estimator_name` first. + if name is None: + name = getattr(self, "estimator_name", getattr(self, "name", None)) return ax, ax.figure, name @classmethod @@ -63,6 +69,186 @@ def _validate_from_predictions_params( return pos_label, name + @classmethod + def _validate_from_cv_results_params( + cls, + cv_results, + X, + y, + *, + sample_weight, + pos_label, + ): + check_matplotlib_support(f"{cls.__name__}.from_cv_results") + + required_keys = {"estimator", "indices"} + if not all(key in cv_results for key in required_keys): + raise ValueError( + "`cv_results` does not contain one of the following required keys: " + f"{required_keys}. Set explicitly the parameters " + "`return_estimator=True` and `return_indices=True` to the function" + "`cross_validate`." + ) + + train_size, test_size = ( + len(cv_results["indices"]["train"][0]), + len(cv_results["indices"]["test"][0]), + ) + + if _num_samples(X) != train_size + test_size: + raise ValueError( + "`X` does not contain the correct number of samples. " + f"Expected {train_size + test_size}, got {_num_samples(X)}." + ) + + if type_of_target(y) != "binary": + raise ValueError( + f"The target `y` is not binary. Got {type_of_target(y)} type of target." + ) + check_consistent_length(X, y, sample_weight) + + try: + pos_label = _check_pos_label_consistency(pos_label, y) + except ValueError as e: + # Adapt error message + raise ValueError(str(e).replace("y_true", "y")) + + return pos_label + + @staticmethod + def _get_legend_label(curve_legend_metric, curve_name, legend_metric_name): + """Helper to get legend label using `name` and `legend_metric`""" + if curve_legend_metric is not None and curve_name is not None: + label = f"{curve_name} ({legend_metric_name} = {curve_legend_metric:0.2f})" + elif curve_legend_metric is not None: + label = f"{legend_metric_name} = {curve_legend_metric:0.2f}" + elif curve_name is not None: + label = curve_name + else: + label = None + return label + + @staticmethod + def _validate_curve_kwargs( + n_curves, + name, + legend_metric, + legend_metric_name, + curve_kwargs, + **kwargs, + ): + """Get validated line kwargs for each curve. + + Parameters + ---------- + n_curves : int + Number of curves. + + name : list of str or None + Name for labeling legend entries. + + legend_metric : dict + Dictionary with "mean" and "std" keys, or "metric" key of metric + values for each curve. If None, "label" will not contain metric values. + + legend_metric_name : str + Name of the summary value provided in `legend_metrics`. + + curve_kwargs : dict or list of dict or None + Dictionary with keywords passed to the matplotlib's `plot` function + to draw the individual curves. If a list is provided, the + parameters are applied to the curves sequentially. If a single + dictionary is provided, the same parameters are applied to all + curves. + + **kwargs : dict + Deprecated. Keyword arguments to be passed to matplotlib's `plot`. + """ + # TODO(1.9): Remove deprecated **kwargs + if curve_kwargs and kwargs: + raise ValueError( + "Cannot provide both `curve_kwargs` and `kwargs`. `**kwargs` is " + "deprecated in 1.7 and will be removed in 1.9. Pass all matplotlib " + "arguments to `curve_kwargs` as a dictionary." + ) + if kwargs: + warnings.warn( + "`**kwargs` is deprecated and will be removed in 1.9. Pass all " + "matplotlib arguments to `curve_kwargs` as a dictionary instead.", + FutureWarning, + ) + curve_kwargs = kwargs + + if isinstance(curve_kwargs, list) and len(curve_kwargs) != n_curves: + raise ValueError( + f"`curve_kwargs` must be None, a dictionary or a list of length " + f"{n_curves}. Got: {curve_kwargs}." + ) + + # Ensure valid `name` and `curve_kwargs` combination. + if ( + isinstance(name, list) + and len(name) != 1 + and not isinstance(curve_kwargs, list) + ): + raise ValueError( + "To avoid labeling individual curves that have the same appearance, " + f"`curve_kwargs` should be a list of {n_curves} dictionaries. " + "Alternatively, set `name` to `None` or a single string to label " + "a single legend entry with mean ROC AUC score of all curves." + ) + + # Ensure `name` is of the correct length + if isinstance(name, str): + name = [name] + if isinstance(name, list) and len(name) == 1: + name = name * n_curves + name = [None] * n_curves if name is None else name + + # Ensure `curve_kwargs` is of correct length + if isinstance(curve_kwargs, Mapping): + curve_kwargs = [curve_kwargs] * n_curves + + default_multi_curve_kwargs = {"alpha": 0.5, "linestyle": "--", "color": "blue"} + if curve_kwargs is None: + if n_curves > 1: + curve_kwargs = [default_multi_curve_kwargs] * n_curves + else: + curve_kwargs = [{}] + + labels = [] + if "mean" in legend_metric: + label_aggregate = _BinaryClassifierCurveDisplayMixin._get_legend_label( + legend_metric["mean"], name[0], legend_metric_name + ) + # Note: "std" always `None` when "mean" is `None` - no metric value added + # to label in this case + if legend_metric["std"] is not None: + # Add the "+/- std" to the end (in brackets if name provided) + if name[0] is not None: + label_aggregate = ( + label_aggregate[:-1] + f" +/- {legend_metric['std']:0.2f})" + ) + else: + label_aggregate = ( + label_aggregate + f" +/- {legend_metric['std']:0.2f}" + ) + # Add `label` for first curve only, set to `None` for remaining curves + labels.extend([label_aggregate] + [None] * (n_curves - 1)) + else: + for curve_legend_metric, curve_name in zip(legend_metric["metric"], name): + labels.append( + _BinaryClassifierCurveDisplayMixin._get_legend_label( + curve_legend_metric, curve_name, legend_metric_name + ) + ) + + curve_kwargs_ = [ + _validate_style_kwargs({"label": label}, curve_kwargs[fold_idx]) + for fold_idx, label in enumerate(labels) + ] + return curve_kwargs_ + def _validate_score_name(score_name, scoring, negate_score): """Validate the `score_name` parameter. @@ -177,3 +363,57 @@ def _despine(ax): ax.spines[s].set_visible(False) for s in ["bottom", "left"]: ax.spines[s].set_bounds(0, 1) + + +def _deprecate_estimator_name(estimator_name, name, version): + """Deprecate `estimator_name` in favour of `name`.""" + version = parse_version(version) + version_remove = f"{version.major}.{version.minor + 2}" + if estimator_name != "deprecated": + if name: + raise ValueError( + "Cannot provide both `estimator_name` and `name`. `estimator_name` " + f"is deprecated in {version} and will be removed in {version_remove}. " + "Use `name` only." + ) + warnings.warn( + f"`estimator_name` is deprecated in {version} and will be removed in " + f"{version_remove}. Use `name` instead.", + FutureWarning, + ) + return estimator_name + return name + + +def _convert_to_list_leaving_none(param): + """Convert parameters to a list, leaving `None` as is.""" + if param is None: + return None + if isinstance(param, list): + return param + return [param] + + +def _check_param_lengths(required, optional, class_name): + """Check required and optional parameters are of the same length.""" + optional_provided = {} + for name, param in optional.items(): + if isinstance(param, list): + optional_provided[name] = param + + all_params = {**required, **optional_provided} + if len({len(param) for param in all_params.values()}) > 1: + param_keys = [key for key in all_params.keys()] + # Note: below code requires `len(param_keys) >= 2`, which is the case for all + # display classes + params_formatted = " and ".join([", ".join(param_keys[:-1]), param_keys[-1]]) + or_plot = "" + if "'name' (or self.name)" in param_keys: + or_plot = " (or `plot`)" + lengths_formatted = ", ".join( + f"{key}: {len(value)}" for key, value in all_params.items() + ) + raise ValueError( + f"{params_formatted} from `{class_name}` initialization{or_plot}, " + f"should all be lists of the same length. Got: {lengths_formatted}" + ) diff --git a/sklearn/utils/_repr_html/__init__.py b/sklearn/utils/_repr_html/__init__.py new file mode 100644 index 0000000000000..67dd18fb94b59 --- /dev/null +++ b/sklearn/utils/_repr_html/__init__.py @@ -0,0 +1,2 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause diff --git a/sklearn/utils/_repr_html/base.py b/sklearn/utils/_repr_html/base.py new file mode 100644 index 0000000000000..28020a2a74698 --- /dev/null +++ b/sklearn/utils/_repr_html/base.py @@ -0,0 +1,152 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +import itertools + +from ... import __version__ +from ..._config import get_config +from ..fixes import parse_version + + +class _HTMLDocumentationLinkMixin: + """Mixin class allowing to generate a link to the API documentation. + + This mixin relies on three attributes: + - `_doc_link_module`: it corresponds to the root module (e.g. `sklearn`). Using this + mixin, the default value is `sklearn`. + - `_doc_link_template`: it corresponds to the template used to generate the + link to the API documentation. Using this mixin, the default value is + `"https://scikit-learn.org/{version_url}/modules/generated/ + {estimator_module}.{estimator_name}.html"`. + - `_doc_link_url_param_generator`: it corresponds to a function that generates the + parameters to be used in the template when the estimator module and name are not + sufficient. + + The method :meth:`_get_doc_link` generates the link to the API documentation for a + given estimator. + + This useful provides all the necessary states for + :func:`sklearn.utils.estimator_html_repr` to generate a link to the API + documentation for the estimator HTML diagram. + + Examples + -------- + If the default values for `_doc_link_module`, `_doc_link_template` are not suitable, + then you can override them and provide a method to generate the URL parameters: + >>> from sklearn.base import BaseEstimator + >>> doc_link_template = "https://address.local/{single_param}.html" + >>> def url_param_generator(estimator): + ... return {"single_param": estimator.__class__.__name__} + >>> class MyEstimator(BaseEstimator): + ... # use "builtins" since it is the associated module when declaring + ... # the class in a docstring + ... _doc_link_module = "builtins" + ... _doc_link_template = doc_link_template + ... _doc_link_url_param_generator = url_param_generator + >>> estimator = MyEstimator() + >>> estimator._get_doc_link() + 'https://address.local/MyEstimator.html' + + If instead of overriding the attributes inside the class definition, you want to + override a class instance, you can use `types.MethodType` to bind the method to the + instance: + >>> import types + >>> estimator = BaseEstimator() + >>> estimator._doc_link_template = doc_link_template + >>> estimator._doc_link_url_param_generator = types.MethodType( + ... url_param_generator, estimator) + >>> estimator._get_doc_link() + 'https://address.local/BaseEstimator.html' + """ + + _doc_link_module = "sklearn" + _doc_link_url_param_generator = None + + @property + def _doc_link_template(self): + sklearn_version = parse_version(__version__) + if sklearn_version.dev is None: + version_url = f"{sklearn_version.major}.{sklearn_version.minor}" + else: + version_url = "dev" + return getattr( + self, + "__doc_link_template", + ( + f"https://scikit-learn.org/{version_url}/modules/generated/" + "{estimator_module}.{estimator_name}.html" + ), + ) + + @_doc_link_template.setter + def _doc_link_template(self, value): + setattr(self, "__doc_link_template", value) + + def _get_doc_link(self): + """Generates a link to the API documentation for a given estimator. + + This method generates the link to the estimator's documentation page + by using the template defined by the attribute `_doc_link_template`. + + Returns + ------- + url : str + The URL to the API documentation for this estimator. If the estimator does + not belong to module `_doc_link_module`, the empty string (i.e. `""`) is + returned. + """ + if self.__class__.__module__.split(".")[0] != self._doc_link_module: + return "" + + if self._doc_link_url_param_generator is None: + estimator_name = self.__class__.__name__ + # Construct the estimator's module name, up to the first private submodule. + # This works because in scikit-learn all public estimators are exposed at + # that level, even if they actually live in a private sub-module. + estimator_module = ".".join( + itertools.takewhile( + lambda part: not part.startswith("_"), + self.__class__.__module__.split("."), + ) + ) + return self._doc_link_template.format( + estimator_module=estimator_module, estimator_name=estimator_name + ) + return self._doc_link_template.format(**self._doc_link_url_param_generator()) + + +class ReprHTMLMixin: + """Mixin to handle consistently the HTML representation. + + When inheriting from this class, you need to define an attribute `_html_repr` + which is a callable that returns the HTML representation to be shown. + """ + + @property + def _repr_html_(self): + """HTML representation of estimator. + This is redundant with the logic of `_repr_mimebundle_`. The latter + should be favored in the long term, `_repr_html_` is only + implemented for consumers who do not interpret `_repr_mimbundle_`. + """ + if get_config()["display"] != "diagram": + raise AttributeError( + "_repr_html_ is only defined when the " + "'display' configuration option is set to " + "'diagram'" + ) + return self._repr_html_inner + + def _repr_html_inner(self): + """This function is returned by the @property `_repr_html_` to make + `hasattr(estimator, "_repr_html_") return `True` or `False` depending + on `get_config()["display"]`. + """ + return self._html_repr() + + def _repr_mimebundle_(self, **kwargs): + """Mime bundle used by jupyter kernels to display estimator""" + output = {"text/plain": repr(self)} + if get_config()["display"] == "diagram": + output["text/html"] = self._html_repr() + return output diff --git a/sklearn/utils/_estimator_html_repr.css b/sklearn/utils/_repr_html/estimator.css similarity index 99% rename from sklearn/utils/_estimator_html_repr.css rename to sklearn/utils/_repr_html/estimator.css index 0a8c277845cb1..ece8781c6bd76 100644 --- a/sklearn/utils/_estimator_html_repr.css +++ b/sklearn/utils/_repr_html/estimator.css @@ -178,9 +178,7 @@ clickable and can be expanded/collapsed. /* Toggleable content - dropdown */ #$id div.sk-toggleable__content { - max-height: 0; - max-width: 0; - overflow: hidden; + display: none; text-align: left; /* unfitted */ background-color: var(--sklearn-color-unfitted-level-0); @@ -206,9 +204,9 @@ clickable and can be expanded/collapsed. #$id input.sk-toggleable__control:checked~div.sk-toggleable__content { /* Expand drop-down */ - max-height: 200px; - max-width: 100%; - overflow: auto; + display: block; + width: 100%; + overflow: visible; } #$id input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before { diff --git a/sklearn/utils/_repr_html/estimator.js b/sklearn/utils/_repr_html/estimator.js new file mode 100644 index 0000000000000..5de0a021c63bb --- /dev/null +++ b/sklearn/utils/_repr_html/estimator.js @@ -0,0 +1,42 @@ +function copyToClipboard(text, element) { + // Get the parameter prefix from the closest toggleable content + const toggleableContent = element.closest('.sk-toggleable__content'); + const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : ''; + const fullParamName = paramPrefix ? `${paramPrefix}${text}` : text; + + const originalStyle = element.style; + const computedStyle = window.getComputedStyle(element); + const originalWidth = computedStyle.width; + const originalHTML = element.innerHTML.replace('Copied!', ''); + + navigator.clipboard.writeText(fullParamName) + .then(() => { + element.style.width = originalWidth; + element.style.color = 'green'; + element.innerHTML = "Copied!"; + + setTimeout(() => { + element.innerHTML = originalHTML; + element.style = originalStyle; + }, 2000); + }) + .catch(err => { + console.error('Failed to copy:', err); + element.style.color = 'red'; + element.innerHTML = "Failed!"; + setTimeout(() => { + element.innerHTML = originalHTML; + element.style = originalStyle; + }, 2000); + }); + return false; +} + +document.querySelectorAll('.fa-regular.fa-copy').forEach(function(element) { + const toggleableContent = element.closest('.sk-toggleable__content'); + const paramPrefix = toggleableContent ? toggleableContent.dataset.paramPrefix : ''; + const paramName = element.parentElement.nextElementSibling.textContent.trim(); + const fullParamName = paramPrefix ? `${paramPrefix}${paramName}` : paramName; + + element.setAttribute('title', fullParamName); +}); diff --git a/sklearn/utils/_repr_html/estimator.py b/sklearn/utils/_repr_html/estimator.py new file mode 100644 index 0000000000000..7d101dde58d74 --- /dev/null +++ b/sklearn/utils/_repr_html/estimator.py @@ -0,0 +1,497 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +import html +from contextlib import closing +from inspect import isclass +from io import StringIO +from pathlib import Path +from string import Template + +from ... import config_context + + +class _IDCounter: + """Generate sequential ids with a prefix.""" + + def __init__(self, prefix): + self.prefix = prefix + self.count = 0 + + def get_id(self): + self.count += 1 + return f"{self.prefix}-{self.count}" + + +def _get_css_style(): + estimator_css_file = Path(__file__).parent / "estimator.css" + params_css_file = Path(__file__).parent / "params.css" + + estimator_css = estimator_css_file.read_text(encoding="utf-8") + params_css = params_css_file.read_text(encoding="utf-8") + + return f"{estimator_css}\n{params_css}" + + +_CONTAINER_ID_COUNTER = _IDCounter("sk-container-id") +_ESTIMATOR_ID_COUNTER = _IDCounter("sk-estimator-id") +_CSS_STYLE = _get_css_style() + + +class _VisualBlock: + """HTML Representation of Estimator + + Parameters + ---------- + kind : {'serial', 'parallel', 'single'} + kind of HTML block + + estimators : list of estimators or `_VisualBlock`s or a single estimator + If kind != 'single', then `estimators` is a list of + estimators. + If kind == 'single', then `estimators` is a single estimator. + + names : list of str, default=None + If kind != 'single', then `names` corresponds to estimators. + If kind == 'single', then `names` is a single string corresponding to + the single estimator. + + name_details : list of str, str, or None, default=None + If kind != 'single', then `name_details` corresponds to `names`. + If kind == 'single', then `name_details` is a single string + corresponding to the single estimator. + + name_caption : str, default=None + The caption below the name. `None` stands for no caption. + Only active when kind == 'single'. + + doc_link_label : str, default=None + The label for the documentation link. If provided, the label would be + "Documentation for {doc_link_label}". Otherwise it will look for `names`. + Only active when kind == 'single'. + + dash_wrapped : bool, default=True + If true, wrapped HTML element will be wrapped with a dashed border. + Only active when kind != 'single'. + """ + + def __init__( + self, + kind, + estimators, + *, + names=None, + name_details=None, + name_caption=None, + doc_link_label=None, + dash_wrapped=True, + ): + self.kind = kind + self.estimators = estimators + self.dash_wrapped = dash_wrapped + self.name_caption = name_caption + self.doc_link_label = doc_link_label + + if self.kind in ("parallel", "serial"): + if names is None: + names = (None,) * len(estimators) + if name_details is None: + name_details = (None,) * len(estimators) + + self.names = names + self.name_details = name_details + + def _sk_visual_block_(self): + return self + + +def _write_label_html( + out, + params, + name, + name_details, + name_caption=None, + doc_link_label=None, + outer_class="sk-label-container", + inner_class="sk-label", + checked=False, + doc_link="", + is_fitted_css_class="", + is_fitted_icon="", + param_prefix="", +): + """Write labeled html with or without a dropdown with named details. + + Parameters + ---------- + out : file-like object + The file to write the HTML representation to. + params: str + If estimator has `get_params` method, this is the HTML representation + of the estimator's parameters and their values. When the estimator + does not have `get_params`, it is an empty string. + name : str + The label for the estimator. It corresponds either to the estimator class name + for a simple estimator or in the case of a `Pipeline` and `ColumnTransformer`, + it corresponds to the name of the step. + name_details : str + The details to show as content in the dropdown part of the toggleable label. It + can contain information such as non-default parameters or column information for + `ColumnTransformer`. + name_caption : str, default=None + The caption below the name. If `None`, no caption will be created. + doc_link_label : str, default=None + The label for the documentation link. If provided, the label would be + "Documentation for {doc_link_label}". Otherwise it will look for `name`. + outer_class : {"sk-label-container", "sk-item"}, default="sk-label-container" + The CSS class for the outer container. + inner_class : {"sk-label", "sk-estimator"}, default="sk-label" + The CSS class for the inner container. + checked : bool, default=False + Whether the dropdown is folded or not. With a single estimator, we intend to + unfold the content. + doc_link : str, default="" + The link to the documentation for the estimator. If an empty string, no link is + added to the diagram. This can be generated for an estimator if it uses the + `_HTMLDocumentationLinkMixin`. + is_fitted_css_class : {"", "fitted"} + The CSS class to indicate whether or not the estimator is fitted. The + empty string means that the estimator is not fitted and "fitted" means that the + estimator is fitted. + is_fitted_icon : str, default="" + The HTML representation to show the fitted information in the diagram. An empty + string means that no information is shown. + param_prefix : str, default="" + The prefix to prepend to parameter names for nested estimators. + """ + out.write( + f'
    ' + ) + name = html.escape(name) + if name_details is not None: + name_details = html.escape(str(name_details)) + checked_str = "checked" if checked else "" + est_id = _ESTIMATOR_ID_COUNTER.get_id() + + if doc_link: + doc_label = "Online documentation" + if doc_link_label is not None: + doc_label = f"Documentation for {doc_link_label}" + elif name is not None: + doc_label = f"Documentation for {name}" + doc_link = ( + f'?{doc_label}' + ) + + name_caption_div = ( + "" + if name_caption is None + else f'
    {html.escape(name_caption)}
    ' + ) + name_caption_div = f"
    {name}
    {name_caption_div}
    " + links_div = ( + f"
    {doc_link}{is_fitted_icon}
    " + if doc_link or is_fitted_icon + else "" + ) + + label_html = ( + f'' + ) + + fmt_str = ( + f'{label_html}
    ' + ) + + if params: + fmt_str = "".join([fmt_str, f"{params}
    "]) + elif name_details and ("Pipeline" not in name): + fmt_str = "".join([fmt_str, f"
    {name_details}
    "]) + + out.write(fmt_str) + else: + out.write(f"") + out.write("") # outer_class inner_class + + +def _get_visual_block(estimator): + """Generate information about how to display an estimator.""" + if hasattr(estimator, "_sk_visual_block_"): + try: + return estimator._sk_visual_block_() + except Exception: + return _VisualBlock( + "single", + estimator, + names=estimator.__class__.__name__, + name_details=str(estimator), + ) + + if isinstance(estimator, str): + return _VisualBlock( + "single", estimator, names=estimator, name_details=estimator + ) + elif estimator is None: + return _VisualBlock("single", estimator, names="None", name_details="None") + + # check if estimator looks like a meta estimator (wraps estimators) + if hasattr(estimator, "get_params") and not isclass(estimator): + estimators = [ + (key, est) + for key, est in estimator.get_params(deep=False).items() + if hasattr(est, "get_params") and hasattr(est, "fit") and not isclass(est) + ] + if estimators: + return _VisualBlock( + "parallel", + [est for _, est in estimators], + names=[f"{key}: {est.__class__.__name__}" for key, est in estimators], + name_details=[str(est) for _, est in estimators], + ) + + return _VisualBlock( + "single", + estimator, + names=estimator.__class__.__name__, + name_details=str(estimator), + ) + + +def _write_estimator_html( + out, + estimator, + estimator_label, + estimator_label_details, + is_fitted_css_class, + is_fitted_icon="", + first_call=False, + param_prefix="", +): + """Write estimator to html in serial, parallel, or by itself (single). + + For multiple estimators, this function is called recursively. + + Parameters + ---------- + out : file-like object + The file to write the HTML representation to. + estimator : estimator object + The estimator to visualize. + estimator_label : str + The label for the estimator. It corresponds either to the estimator class name + for simple estimator or in the case of `Pipeline` and `ColumnTransformer`, it + corresponds to the name of the step. + estimator_label_details : str + The details to show as content in the dropdown part of the toggleable label. + It can contain information as non-default parameters or column information for + `ColumnTransformer`. + is_fitted_css_class : {"", "fitted"} + The CSS class to indicate whether or not the estimator is fitted or not. The + empty string means that the estimator is not fitted and "fitted" means that the + estimator is fitted. + is_fitted_icon : str, default="" + The HTML representation to show the fitted information in the diagram. An empty + string means that no information is shown. If the estimator to be shown is not + the first estimator (i.e. `first_call=False`), `is_fitted_icon` is always an + empty string. + first_call : bool, default=False + Whether this is the first time this function is called. + param_prefix : str, default="" + The prefix to prepend to parameter names for nested estimators. + For example, in a pipeline this might be "pipeline__stepname__". + """ + if first_call: + est_block = _get_visual_block(estimator) + else: + is_fitted_icon = "" + with config_context(print_changed_only=True): + est_block = _get_visual_block(estimator) + # `estimator` can also be an instance of `_VisualBlock` + if hasattr(estimator, "_get_doc_link"): + doc_link = estimator._get_doc_link() + else: + doc_link = "" + if est_block.kind in ("serial", "parallel"): + dashed_wrapped = first_call or est_block.dash_wrapped + dash_cls = " sk-dashed-wrapped" if dashed_wrapped else "" + out.write(f'
    ') + + if estimator_label: + if hasattr(estimator, "get_params") and hasattr( + estimator, "_get_params_html" + ): + params = estimator._get_params_html(deep=False)._repr_html_inner() + else: + params = "" + + _write_label_html( + out, + params, + estimator_label, + estimator_label_details, + doc_link=doc_link, + is_fitted_css_class=is_fitted_css_class, + is_fitted_icon=is_fitted_icon, + param_prefix=param_prefix, + ) + + kind = est_block.kind + out.write(f'
    ') + est_infos = zip(est_block.estimators, est_block.names, est_block.name_details) + + for est, name, name_details in est_infos: + # Build the parameter prefix for nested estimators + + if param_prefix and hasattr(name, "split"): + # If we already have a prefix, append the new component + new_prefix = f"{param_prefix}{name.split(':')[0]}__" + elif hasattr(name, "split"): + # If this is the first level, start the prefix + new_prefix = f"{name.split(':')[0]}__" if name else "" + else: + new_prefix = param_prefix + + if kind == "serial": + _write_estimator_html( + out, + est, + name, + name_details, + is_fitted_css_class=is_fitted_css_class, + param_prefix=new_prefix, + ) + else: # parallel + out.write('
    ') + # wrap element in a serial visualblock + serial_block = _VisualBlock("serial", [est], dash_wrapped=False) + _write_estimator_html( + out, + serial_block, + name, + name_details, + is_fitted_css_class=is_fitted_css_class, + param_prefix=new_prefix, + ) + out.write("
    ") # sk-parallel-item + + out.write("
    ") + elif est_block.kind == "single": + if hasattr(estimator, "_get_params_html"): + params = estimator._get_params_html()._repr_html_inner() + else: + params = "" + + _write_label_html( + out, + params, + est_block.names, + est_block.name_details, + est_block.name_caption, + est_block.doc_link_label, + outer_class="sk-item", + inner_class="sk-estimator", + checked=first_call, + doc_link=doc_link, + is_fitted_css_class=is_fitted_css_class, + is_fitted_icon=is_fitted_icon, + param_prefix=param_prefix, + ) + + +def estimator_html_repr(estimator): + """Build a HTML representation of an estimator. + + Read more in the :ref:`User Guide `. + + Parameters + ---------- + estimator : estimator object + The estimator to visualize. + + Returns + ------- + html: str + HTML representation of estimator. + + Examples + -------- + >>> from sklearn.utils._repr_html.estimator import estimator_html_repr + >>> from sklearn.linear_model import LogisticRegression + >>> estimator_html_repr(LogisticRegression()) + '" + f"" + f'
    ' + '
    ' + f"
    {html.escape(estimator_str)}
    {fallback_msg}" + "
    " + '
    " + + out.write(html_end) + + html_output = out.getvalue() + return html_output diff --git a/sklearn/utils/_repr_html/params.css b/sklearn/utils/_repr_html/params.css new file mode 100644 index 0000000000000..df815f966ffcf --- /dev/null +++ b/sklearn/utils/_repr_html/params.css @@ -0,0 +1,63 @@ +.estimator-table summary { + padding: .5rem; + font-family: monospace; + cursor: pointer; +} + +.estimator-table details[open] { + padding-left: 0.1rem; + padding-right: 0.1rem; + padding-bottom: 0.3rem; +} + +.estimator-table .parameters-table { + margin-left: auto !important; + margin-right: auto !important; +} + +.estimator-table .parameters-table tr:nth-child(odd) { + background-color: #fff; +} + +.estimator-table .parameters-table tr:nth-child(even) { + background-color: #f6f6f6; +} + +.estimator-table .parameters-table tr:hover { + background-color: #e0e0e0; +} + +.estimator-table table td { + border: 1px solid rgba(106, 105, 104, 0.232); +} + +.user-set td { + color:rgb(255, 94, 0); + text-align: left; +} + +.user-set td.value pre { + color:rgb(255, 94, 0) !important; + background-color: transparent !important; +} + +.default td { + color: black; + text-align: left; +} + +.user-set td i, +.default td i { + color: black; +} + +.copy-paste-icon { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDggNTEyIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIDYuNy4yIGJ5IEBmb250YXdlc29tZSAtIGh0dHBzOi8vZm9udGF3ZXNvbWUuY29tIExpY2Vuc2UgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbS9saWNlbnNlL2ZyZWUgQ29weXJpZ2h0IDIwMjUgRm9udGljb25zLCBJbmMuLS0+PHBhdGggZD0iTTIwOCAwTDMzMi4xIDBjMTIuNyAwIDI0LjkgNS4xIDMzLjkgMTQuMWw2Ny45IDY3LjljOSA5IDE0LjEgMjEuMiAxNC4xIDMzLjlMNDQ4IDMzNmMwIDI2LjUtMjEuNSA0OC00OCA0OGwtMTkyIDBjLTI2LjUgMC00OC0yMS41LTQ4LTQ4bDAtMjg4YzAtMjYuNSAyMS41LTQ4IDQ4LTQ4ek00OCAxMjhsODAgMCAwIDY0LTY0IDAgMCAyNTYgMTkyIDAgMC0zMiA2NCAwIDAgNDhjMCAyNi41LTIxLjUgNDgtNDggNDhMNDggNTEyYy0yNi41IDAtNDgtMjEuNS00OC00OEwwIDE3NmMwLTI2LjUgMjEuNS00OCA0OC00OHoiLz48L3N2Zz4=); + background-repeat: no-repeat; + background-size: 14px 14px; + background-position: 0; + display: inline-block; + width: 14px; + height: 14px; + cursor: pointer; +} diff --git a/sklearn/utils/_repr_html/params.py b/sklearn/utils/_repr_html/params.py new file mode 100644 index 0000000000000..d85bf1280a8fc --- /dev/null +++ b/sklearn/utils/_repr_html/params.py @@ -0,0 +1,83 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +import html +import reprlib +from collections import UserDict + +from sklearn.utils._repr_html.base import ReprHTMLMixin + + +def _read_params(name, value, non_default_params): + """Categorizes parameters as 'default' or 'user-set' and formats their values. + Escapes or truncates parameter values for display safety and readability. + """ + r = reprlib.Repr() + r.maxlist = 2 # Show only first 2 items of lists + r.maxtuple = 1 # Show only first item of tuples + r.maxstring = 50 # Limit string length + cleaned_value = html.escape(r.repr(value)) + + param_type = "user-set" if name in non_default_params else "default" + + return {"param_type": param_type, "param_name": name, "param_value": cleaned_value} + + +def _params_html_repr(params): + """Generate HTML representation of estimator parameters. + + Creates an HTML table with parameter names and values, wrapped in a + collapsible details element. Parameters are styled differently based + on whether they are default or user-set values. + """ + HTML_TEMPLATE = """ +
    +
    + Parameters + + + {rows} + +
    +
    +
    + """ + ROW_TEMPLATE = """ + + + {param_name}  + {param_value} + + """ + + rows = [ + ROW_TEMPLATE.format(**_read_params(name, value, params.non_default)) + for name, value in params.items() + ] + + return HTML_TEMPLATE.format(rows="\n".join(rows)) + + +class ParamsDict(ReprHTMLMixin, UserDict): + """Dictionary-like class to store and provide an HTML representation. + + It builds an HTML structure to be used with Jupyter notebooks or similar + environments. It allows storing metadata to track non-default parameters. + + Parameters + ---------- + params : dict, default=None + The original dictionary of parameters and their values. + + non_default : tuple + The list of non-default parameters. + """ + + _html_repr = _params_html_repr + + def __init__(self, params=None, non_default=tuple()): + super().__init__(params or {}) + self.non_default = non_default diff --git a/sklearn/utils/_repr_html/tests/__init__.py b/sklearn/utils/_repr_html/tests/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/sklearn/utils/_repr_html/tests/test_estimator.py b/sklearn/utils/_repr_html/tests/test_estimator.py new file mode 100644 index 0000000000000..cc975d854ed8f --- /dev/null +++ b/sklearn/utils/_repr_html/tests/test_estimator.py @@ -0,0 +1,616 @@ +import html +import locale +import re +import types +from contextlib import closing +from functools import partial +from io import StringIO +from unittest.mock import patch + +import numpy as np +import pytest + +from sklearn import config_context +from sklearn.base import BaseEstimator +from sklearn.cluster import AgglomerativeClustering, Birch +from sklearn.compose import ColumnTransformer, make_column_transformer +from sklearn.datasets import load_iris +from sklearn.decomposition import PCA, TruncatedSVD +from sklearn.ensemble import StackingClassifier, StackingRegressor, VotingClassifier +from sklearn.feature_selection import SelectPercentile +from sklearn.gaussian_process.kernels import ExpSineSquared +from sklearn.impute import SimpleImputer +from sklearn.kernel_ridge import KernelRidge +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import RandomizedSearchCV +from sklearn.multiclass import OneVsOneClassifier +from sklearn.neural_network import MLPClassifier +from sklearn.pipeline import FeatureUnion, Pipeline, make_pipeline +from sklearn.preprocessing import FunctionTransformer, OneHotEncoder, StandardScaler +from sklearn.svm import LinearSVC, LinearSVR +from sklearn.tree import DecisionTreeClassifier +from sklearn.utils._repr_html.base import _HTMLDocumentationLinkMixin +from sklearn.utils._repr_html.estimator import ( + _get_css_style, + _get_visual_block, + _write_label_html, + estimator_html_repr, +) +from sklearn.utils.fixes import parse_version + + +def dummy_function(x, y): + return x + y # pragma: nocover + + +@pytest.mark.parametrize("checked", [True, False]) +def test_write_label_html(checked): + # Test checking logic and labeling + name = "LogisticRegression" + params = "" + tool_tip = "hello-world" + + with closing(StringIO()) as out: + _write_label_html(out, params, name, tool_tip, checked=checked) + html_label = out.getvalue() + + p = ( + r'