diff --git a/.github/workflows/cuda-ci.yml b/.github/workflows/cuda-ci.yml index 2b2bbd5fe2657..80bebf1437ffc 100644 --- a/.github/workflows/cuda-ci.yml +++ b/.github/workflows/cuda-ci.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - name: Build wheels - uses: pypa/cibuildwheel@v2.21.1 + uses: pypa/cibuildwheel@v2.21.3 env: CIBW_BUILD: cp312-manylinux_x86_64 CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 diff --git a/.github/workflows/publish_pypi.yml b/.github/workflows/publish_pypi.yml index b2d402c6a55a9..584a3dabf9886 100644 --- a/.github/workflows/publish_pypi.yml +++ b/.github/workflows/publish_pypi.yml @@ -39,13 +39,13 @@ jobs: run: | python build_tools/github/check_wheels.py - name: Publish package to TestPyPI - uses: pypa/gh-action-pypi-publish@897895f1e160c830e369f9779632ebc134688e1b # v1.10.2 + uses: pypa/gh-action-pypi-publish@fb13cb306901256ace3dab689990e13a5550ffaa # v1.11.0 with: repository-url: https://test.pypi.org/legacy/ print-hash: true if: ${{ github.event.inputs.pypi_repo == 'testpypi' }} - name: Publish package to PyPI - uses: pypa/gh-action-pypi-publish@897895f1e160c830e369f9779632ebc134688e1b # v1.10.2 + uses: pypa/gh-action-pypi-publish@fb13cb306901256ace3dab689990e13a5550ffaa # v1.11.0 if: ${{ github.event.inputs.pypi_repo == 'pypi' }} with: print-hash: true diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index d4522dbce6004..c3bda80d2ca0c 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -73,8 +73,6 @@ jobs: - os: windows-latest python: 313 platform_id: win_amd64 - # TODO: remove next line when Python 3.13 is released - prerelease_pythons: True # Linux 64 bit manylinux2014 - os: ubuntu-latest diff --git a/.gitignore b/.gitignore index be5be706d40ff..7e00b8802bd01 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ doc/developers/maintainer.rst doc/index.rst doc/min_dependency_table.rst doc/min_dependency_substitutions.rst +# release notes generated by towncrier +doc/whats_new/notes-towncrier.rst + *.pdf pip-log.txt scikit_learn.egg-info/ diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 92b26c44488ee..fc4010e95176e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -253,7 +253,7 @@ jobs: - template: build_tools/azure/posix.yml parameters: name: macOS - vmImage: macOS-12 + vmImage: macOS-13 dependsOn: [linting, git_commit, Ubuntu_Jammy_Jellyfish] # Runs when dependencies succeeded or skipped condition: | diff --git a/build_tools/azure/debian_32bit_lock.txt b/build_tools/azure/debian_32bit_lock.txt index cd1ced3f3fe30..3a0185eead5d3 100644 --- a/build_tools/azure/debian_32bit_lock.txt +++ b/build_tools/azure/debian_32bit_lock.txt @@ -4,7 +4,7 @@ # # pip-compile --output-file=build_tools/azure/debian_32bit_lock.txt build_tools/azure/debian_32bit_requirements.txt # -coverage[toml]==7.6.3 +coverage[toml]==7.6.4 # via pytest-cov cython==3.0.11 # via -r build_tools/azure/debian_32bit_requirements.txt @@ -12,9 +12,9 @@ iniconfig==2.0.0 # via pytest joblib==1.4.2 # via -r build_tools/azure/debian_32bit_requirements.txt -meson==1.5.2 +meson==1.6.0 # via meson-python -meson-python==0.16.0 +meson-python==0.17.1 # via -r build_tools/azure/debian_32bit_requirements.txt ninja==1.11.1.1 # via -r build_tools/azure/debian_32bit_requirements.txt @@ -25,13 +25,13 @@ packaging==24.1 # pytest pluggy==1.5.0 # via pytest -pyproject-metadata==0.8.1 +pyproject-metadata==0.9.0 # via meson-python pytest==8.3.3 # via # -r build_tools/azure/debian_32bit_requirements.txt # pytest-cov -pytest-cov==5.0.0 +pytest-cov==6.0.0 # via -r build_tools/azure/debian_32bit_requirements.txt threadpoolctl==3.5.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 5d6bd9ca38999..315c9a4e9d4a1 100755 --- a/build_tools/azure/install.sh +++ b/build_tools/azure/install.sh @@ -120,9 +120,11 @@ scikit_learn_install() { # brings in openmp so that you end up having the omp.h include inside # the conda environment. find $CONDA_PREFIX -name omp.h -delete -print - # meson 1.5 detects OpenMP installed with brew and OpenMP is installed - # with brew in CI runner - brew uninstall --ignore-dependencies libomp + # meson >= 1.5 detects OpenMP installed with brew and OpenMP may be installed + # with brew in CI runner. OpenMP was installed with brew in macOS-12 CI + # runners which doesn't seem to be the case in macOS-13 runners anymore, + # but we keep the next line just to be safe ... + brew uninstall --ignore-dependencies --force libomp fi if [[ "$UNAMESTR" == "Linux" ]]; then diff --git a/build_tools/azure/install_setup_conda.sh b/build_tools/azure/install_setup_conda.sh new file mode 100755 index 0000000000000..d09a02cda5a9f --- /dev/null +++ b/build_tools/azure/install_setup_conda.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +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" +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 +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" +set -x diff --git a/build_tools/azure/posix.yml b/build_tools/azure/posix.yml index 35e5165d22c83..5468a6e629c42 100644 --- a/build_tools/azure/posix.yml +++ b/build_tools/azure/posix.yml @@ -36,11 +36,8 @@ jobs: - bash: $(pyTools.pythonLocation)/bin/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/bin" - displayName: Add conda to PATH - condition: startsWith(variables['DISTRIB'], 'conda') - - bash: sudo chown -R $USER $CONDA - displayName: Take ownership of conda installation + - bash: build_tools/azure/install_setup_conda.sh + displayName: Install conda if necessary and set it up condition: startsWith(variables['DISTRIB'], 'conda') - task: Cache@2 inputs: 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 a33312db2387a..fdd6ef65da174 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 @@ -12,23 +12,23 @@ https://conda.anaconda.org/conda-forge/linux-64/mkl-include-2023.2.0-h84fe81f_50 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.12-5_cp312.conda#0424ae29b104430108f5218a66db7260 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 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_1.conda#83e1364586ceb8d0739fbc85b5c95837 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda#048b02e3962f066da18efe3a21b77672 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_1.conda#1ece2ccb1dc8c68639712b05e0fae070 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.1-h024ca30_0.conda#f1fe1a838fecddbcee97c9d4afe24af5 +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.3-h024ca30_0.conda#d36687dc90337917a84a96a45111ad59 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#562b26ba2e19059551a811e72ab7f793 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_1.conda#38a5cd3be5fb620b48069e27285f1a44 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_1.conda#e12057a66af8f2a38a839754ca4481e9 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.28-hb9d3cd8_0.conda#1b53af320b24547ce0fb8196d2604542 -https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.1-heb4867d_0.conda#db792eada25e970c46642f624b029fd7 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda#3cb76c3f10d3bc7f1105b2fc9db984df +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.31-hb9d3cd8_0.conda#75f7776e1c9af78287f055ca34797517 +https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.2-heb4867d_0.conda#2b780c0338fc0ffa678ac82c54af51fd 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.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.3-h5888daf_0.conda#59f4c43bb1b5ef1c71946ff2cbf59524 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.1.0-hc5f4f2c_1.conda#10a0cef64b784d6ab6da50ebca4e984d -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 -https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.1-hb9d3cd8_0.conda#52849ca4b3be33ac3f01c77da737e068 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda#e39480b9ca41323497b05492a63bc35b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda#9822b874ea29af082e5d36098d25427d +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda#234a5554c53625688d51062645337328 +https://conda.anaconda.org/conda-forge/linux-64/libuv-1.49.2-hb9d3cd8_0.conda#070e3c9ddab77e38799d5c30b109c633 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e @@ -37,42 +37,43 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.11-hb9d3cd8_1.co 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-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.12-h4ab18f5_0.conda#7ed427f0871fd41cb1d9c17727c17589 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.7.4-hfd43aa1_1.conda#f301eb944d297fc879c441fffe461d8a -https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.19-h756ea98_1.conda#5e08c385a1b8a79b52012b74653bbb99 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.19-h756ea98_3.conda#bfe6623096906d2502c78ccdbfc3bc7a -https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.20-h756ea98_0.conda#ff7dbb319545f4bd1e5e0f8555cf9e7f +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.0-hd3f4568_0.conda#0902512e7a2de9722697fb011db07a54 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-hf20e7d7_0.conda#84412135f9c1dd8985741e9c351f499a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.0-hf20e7d7_0.conda#ff265c3736cdac819c8adb844e0557d8 +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.0-hf20e7d7_0.conda#e54103489d34bd5a106b9298dc28c848 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.6.3-h5888daf_0.conda#6595440079bed734b113de44ffd3cd0a https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 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/libabseil-20240722.0-cxx17_h5888daf_1.conda#e1f604644fe8d78e22660e2fec6756bc 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/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/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.1.0-h69a702a_1.conda#591e631bc1ae62c64f2ab4f66178c097 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda#f1fd30127802683586f768875127a987 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda#d66573916ffcf376178462f1b61c941e https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.4-h7f98852_1002.tar.bz2#e728e874159b042d92b90238a3cb0dc2 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.44-hadc24fc_0.conda#f4cc49d7aa68316213e4b12be35308d1 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.0-hadc24fc_1.conda#b6f02b52a174e612e89548f4663ce56a https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda#8371ac6457591af2cf6159439c1fd051 https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-h166bdaf_0.tar.bz2#ede4266dc02e875fe1ea77b25dd43747 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda#b26e8aa824079e1be0294e7152ca4559 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/mysql-common-9.0.1-h266115a_1.conda#e97f73d51b5acdf1340a15b195738f16 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_2.conda#85c0dc0bcd110c998b01856975486ee7 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe -https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda#334dba9982ab9f5d62033c61698a8683 +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.6-h0e56266_0.conda#54752411d7559a8bbd4c0204a8f1cf35 https://conda.anaconda.org/conda-forge/linux-64/sleef-3.7-h1b44611_0.conda#f8b9a3928def0a7f4e37c67045542584 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/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.18-h2af50b2_12.conda#700f1883f5a0a28c30fd98c43d4d946f +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.0-h17eb868_2.conda#bb03f4ce96deea2175fc3ec17b2c1c04 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/double-conversion-3.3.0-h59595ed_0.conda#c2f83a5ddadadcdb08fe05863295ee97 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-h267a509_2.conda#9ae35c3d96db2c94ce0cef86efdfa2cb @@ -81,13 +82,14 @@ https://conda.anaconda.org/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda#c94a5 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/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f -https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda#c48fc56ec03229f294176923c3265c05 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.123-hb9d3cd8_0.conda#ee605e794bdc14e2b7f84c4faa0d8c2c https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.1.0-h69a702a_1.conda#16cec94c5992d7f42ae3f9fa8b25df8d -https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda#700ac6ea6d53d5510591c4344d5c989a -https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.20.0-h0e7cc3e_1.conda#d0ed81c4591775b70384f4cc78e05cd1 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_1.conda#0a7f4cd238267c88e5d69f7826a407eb +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.27.5-h5b01275_2.conda#66ed3107adbdfc25ba70454ba11e6d1e +https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda#2124de47357b7a516c0a3efd8f88c143 +https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda#dcb95c0a98ba9ff737f7ae482aef7833 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/ninja-1.12.1-h297d8ca_0.conda#3aa1c7e292afeff25a0091ddd7c69b72 https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda#df359c09c41cd186fffb93a2d87aa6f5 @@ -103,29 +105,29 @@ https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.con https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-he73a12e_1.conda#05a8ea5f446de33006171a7afe6ae857 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.10-h4f16b4b_0.conda#0b666058a179b744a622d0a4a0c56353 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.4.3-h29ce20c_2.conda#d533baa7e43239591d5cc0233849c475 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.8.10-h5e77a74_0.conda#947cd303444ea92a382a10e43bad1a3f +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h68c3b0c_2.conda#a08831d82df7546a599095b33f3cae2a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.0-hfad4ed3_3.conda#01bc29be557b8c7c1963f7ad7185529a https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.1-h2ff4ddf_0.conda#47a2209fa0df11797df0b767d1de1275 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda#13e8e54035ddd2b91875ba399f0f7c04 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_1.conda#80a57756c545ad11f9847835aa21e6b2 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/libprotobuf-4.25.3-hd5b35b9_1.conda#06def97690ef90781a91b786cb48a0a9 -https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda#41c69fba59d495e8cf5ffda48a607e35 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda#63872517c98aa305da58a757c443698e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda#08a9265c637230c37cb1be4a6cad4536 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.4-hb346dea_2.conda#69b90b70c434b916abf5a1d5ee5d55fb https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda#2eeb50cab6652538eee8fc0bc3340c81 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_1.conda#274f367df5d56f152a49ed3203c3b1c1 +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_2.conda#57a9e7ee3c0840d3c8c9012473978629 +https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h690cf93_1.conda#0044701dd48af57d3d5467a704ef9ebd https://conda.anaconda.org/conda-forge/linux-64/python-3.12.7-hc5c86c4_0_cpython.conda#0515111a9cdf69f83278f7c197db9807 +https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda#01093ff37c1b5e6bf9f17c0116747d11 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.43-hb9d3cd8_0.conda#f725c7425d6d7c15e31f3b99a88ea02f 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.11-hb9d3cd8_1.conda#a7a49a8b85122b49214798321e2e96b4 -https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.9-pyhd8ed1ab_0.conda#967029d5f1687e4d9a38e4fc21184151 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.31-h57bd9a3_0.conda#83be3b5e072d88b76841cc02c6dd458e -https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.7-h02abb05_0.conda#b442b985952afe5820da96bb976ee006 +https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.9.1-pyhd8ed1ab_0.conda#f2328337441baa8f669d2a830cfd0097 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-h56a2c13_4.conda#44a599a9c2c7e5d75e062457ddc6666a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h407ecb8_2.conda#f9fcf88ac9d34b2bfe70429064d7744c https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-hebfffa5_3.conda#fceaedf1cdbcb02df9699a0d9b005292 https://conda.anaconda.org/conda-forge/linux-64/ccache-4.10.1-h065aff2_0.conda#d6b48c138e0c8170a6fe9c136e063540 https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 @@ -138,37 +140,37 @@ https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#e https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0 https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda#15dda3cdbf330abfe9f555d22f66db46 https://conda.anaconda.org/conda-forge/noarch/filelock-3.16.1-pyhd8ed1ab_0.conda#916f8ec5dd4128cd5f207a3c4c07b2c6 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.9.0-pyhff2d567_0.conda#ace4329fbff4c69ab0309db6da182987 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhff2d567_0.conda#816dbc4679a64e4417cd1385d661bb31 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py312h68727a3_0.conda#444266743652a4f1538145e9362f6d3b https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda#51bb7010fc86f70eee639b4bb7a894f5 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.10.1-hbbe4b11_0.conda#6e801c50a40301f6978c53976917b277 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_1.conda#204892bce2e44252b5cf272712f10bdd +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.65.5-hf5c653b_0.conda#3b0048cabc6815a4d8874a0240519d32 https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.1-default_hecaa2ac_1000.conda#f54aeebefb5c5ff84eca4fb05ca8aa3a -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.1-ha7bfdaf_0.conda#000cd5fc23967c97284b720cc6049c1e +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.3-ha7bfdaf_0.conda#8bd654307c455162668cd66e36494000 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.7.0-h2c5496b_1.conda#e2eaefa4de2b7237af7c907b8bbc760a https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.1-py312h178313f_1.conda#490afd4d3627a7f999b9d633c4b6c229 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_0.conda#a755704ea0e2503f8c227d84829a8e81 https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda#aa14b9a5196a6d8dd364164b7ce56acf https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_0.conda#dbf6e2d89137da32fa6670f3bffc024e 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.1-pyhd8ed1ab_0.conda#4994669899eb2e84ab855edcb71efc58 +https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyhd8ed1ab_1.conda#1d4c088869f206413c59acdd309908b7 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.2-h488ebb8_0.conda#7f2e286780f072ed750df46dc2631138 -https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h669347b_0.conda#1e6c10f7d749a490612404efeb179eb8 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda#035c17fbf099f50ff60bf2eb303b0a83 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_0.conda#986287f89929b2d629bd6ef6497dc307 https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad -https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda#8f70e36268dea8eb666ef14c29bd3cda -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda#df68d78237980a159bd7149f33c0e8fd https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py312h66e93f0_1.conda#af648b62462794649066366af4ecd5b0 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py312h66e93f0_1.conda#588486a61153f94c7c13816f7069e440 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 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 @@ -176,66 +178,65 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.2-hb9d3cd8_0 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.5-hb9d3cd8_3.conda#2159fc3619590b4f62473b6b9631549f -https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.6.6-h834ce55_0.conda#dbf33f245023697941d4ff6b996d2b2c -https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.13.0-h935415a_0.conda#debd1677c2fea41eb2233a260f48a298 -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.6.2-py312h66e93f0_0.conda#fa85b4b778217fbeb88425985f001497 -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py312h66e93f0_0.conda#e311030d9322f6f77e71e013490c83b2 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.5-hb9d3cd8_4.conda#7da9007c0582712c4bad4131f89c8372 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.0-hadeddc1_5.conda#429e7497e7f08bc470d2872147d8ef6d +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/coverage-7.6.4-py312h178313f_0.conda#a32fbd2322865ac80c7db74c553f5306 +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py312h178313f_1.conda#bbbf5fa5cab622c33907bc8d7eeea9f7 https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.1.5-py312h7201bc8_2.conda#af9faf103fb57241246416dc70b466f7 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-9.0.0-hda332d3_1.conda#76b32dcf243444aea9c6b804bcfa40b8 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2 https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.1-default_hb5137d0_0.conda#a5feadc4a296e2d31ab5a642498ff85e -https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.1-default_h9c6a7e4_0.conda#2e8992c584c2525a5b8ec7485cbe360c -https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda#8dabe607748cb3d7002ad73cd06f1325 -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.3-default_hb5137d0_0.conda#311e6a1d041db3d6a8a8437750d4234f +https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.3-default_h9c6a7e4_0.conda#b8a8cd77810b20754f358f2327812552 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.30.0-h438788a_0.conda#ab8466a39822527f7786b0d0b2aac223 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.8-hedd0468_0.conda#dcd0ed5147d8876b0848a552b416ce76 -https://conda.anaconda.org/conda-forge/linux-64/pillow-10.4.0-py312h56024de_1.conda#4bd6077376c7f9c1ce33fd8319069e5b -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda#385f46a4df6f97892503a841121a9acf +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.13.0-h84d6215_0.conda#ee6f7fd1e76061ef1fa307d41fa86a96 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.28.3-h3e6eb3e_6.conda#a12a25457b517277e15228889e568daa -https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.8.0-hd126650_2.conda#36df3cf05459de5d0a41c77c4329634b -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.7.0-h10ac4d7_1.conda#ab6d507ad16dbe2157920451d662e4a1 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.29.0-h435de7b_0.conda#5d95d9040c4319997644f68e9aefbe70 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_2.conda#c00807c15530f0cb373a89fd5ead6599 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.0-h73f0fd4_6.conda#19f6d559f3be939046d2ac5c7b2ded7a +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/libgoogle-cloud-storage-2.30.0-h0121fbd_0.conda#ad86b6c98964772688298a727cb20ef8 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_4.conda#392cae2a58fbcb9db8c2147c6d6d1620 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 https://conda.anaconda.org/conda-forge/linux-64/mkl-2023.2.0-h84fe81f_50496.conda#81d4a1a57d618adf0152db973d93b2ad -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda#c54c0107057d67ddf077751339ec2c63 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.0.0-pyhd8ed1ab_0.conda#cb8a11b6d209e3d85e5094bdbd9ebd9c https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 https://conda.anaconda.org/conda-forge/noarch/sympy-1.13.3-pyh2585a3b_104.conda#68085d736d2b2f54498832b65059875d -https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.407-h9f1560d_0.conda#5c3dd49b04db05e0e884de48ff77ae24 -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.12.0-hd2e3451_0.conda#61f1c193452f0daa582f39634627ea33 +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.407-h6a6dca0_6.conda#3c25988c0b0a2085b4df578b7160d963 +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/libblas-3.9.0-20_linux64_mkl.conda#8bf521f6007b0b0eb91515a1165b5d85 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.29.0-h0121fbd_0.conda#06dfd5208170b56eee943d9ac674a533 https://conda.anaconda.org/conda-forge/linux-64/mkl-devel-2023.2.0-ha770c72_50496.conda#3b4c50e31ff098b18a450e4f5f860adf -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.7.3-h6e8976b_1.conda#f3234422a977b5d400ccf503ad55c5d1 -https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.11.0-h325d260_1.conda#11d926d1f4a75a1b03d1c053ca20424b +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.0-h6e8976b_0.conda#6d1c5d2d904d24c17cbb538a95855a4e +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/libcblas-3.9.0-20_linux64_mkl.conda#7a2972758a03adc92d856072c71c9170 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-20_linux64_mkl.conda#4db0cd03efcdab535f6f066aca4cddbb -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.7.3-py312h91f0f75_1.conda#64a74d686fd29fa04c4c313a688e2421 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-17.0.0-had3b6fe_16_cpu.conda#c899e532e16be21570d32bc74ea3d34f +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.0.2-py312h91f0f75_0.conda#ec3da81d5f9d3612b227e09a650f7bf2 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.0.0-ha5db6c2_0_cpu.conda#55f4011e75175bfbbc10f8e5998345d4 https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-20_linux64_mkl.conda#3dea5e9be386b963d7f4368966e238b3 -https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.4.1-cpu_mkl_he3c781b_100.conda#1ff7dab0e018bc6030e424779fba070b -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.2-py312h58c1407_0.conda#b7e9a46277a1ee0afc6311e7760df0c3 -https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.0.1-pyhd8ed1ab_0.conda#2c00d29e0e276f2d32dfe20e698b8eeb +https://conda.anaconda.org/conda-forge/linux-64/libtorch-2.4.1-cpu_mkl_hc74595f_102.conda#1845dcc1389ff54a038027b96fbce318 +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.3-py312h58c1407_0.conda#dfdbc12e6d81889ba4c494a23f23eba8 +https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.1-pyhd8ed1ab_0.conda#15cc819ed82470249cbf1337791bc5ff https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-20_linux64_mkl.conda#079d50df2338a3d47522d7e84c3dfbf6 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.0-py312h68727a3_2.conda#ff28f374b31937c048107521c814791e -https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-17.0.0-h5888daf_16_cpu.conda#18f796aae018a26a20ac51d19de69115 -https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h39682fd_16_cpu.conda#dd1fee2da0659103080fdd74004656df +https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.0.0-h5888daf_0_cpu.conda#8771a1fcc6d8bf2fd18cc57d778f90a3 +https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.0.0-h6bd9018_0_cpu.conda#f9efb8ef19962dc9d87b29e667a13287 https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda#8bce4f6caaf8c5448c7ac86d87e26b4b -https://conda.anaconda.org/conda-forge/linux-64/polars-1.9.0-py312hfe7c9be_0.conda#bc31e545080b876de204326cbac5b44d -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-17.0.0-py312h9cafe31_1_cpu.conda#235827b9c93850cafdd2d5ab359893f9 -https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.4.1-cpu_mkl_py312hf535c18_100.conda#04b64211581334528fa8ce33e0762aef -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.14.1-py312h7d485d2_0.conda#7418a22e73008356d9aba99d93dfeeee +https://conda.anaconda.org/conda-forge/linux-64/polars-1.12.0-py312hfe7c9be_0.conda#47b6df7e8b629d1257a6f971b88c15de +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.0.0-py312h01725c0_0_cpu.conda#9100ae6cdd482666b38fa20e7819b385 +https://conda.anaconda.org/conda-forge/linux-64/pytorch-2.4.1-cpu_mkl_py312ha1f5ba4_102.conda#186d21edb5f86bcd6516e3d10edc38c0 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.14.1-py312h62794b6_1.conda#b43233a9e2f62fb94affe5607ea79473 https://conda.anaconda.org/conda-forge/linux-64/blas-2.120-mkl.conda#9444330235a4828878cbe9c897ba0aa3 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-17.0.0-h5888daf_16_cpu.conda#5400efd6bf101674e0ce170906a0f7cb +https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.0.0-h5888daf_0_cpu.conda#5c121a2d50b068076ff4f2b6d68dbca5 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.9.2-py312hd3ec401_1.conda#2f4f3854f23be30de29e9e4d39758349 https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py312hc39e661_1.conda#372efc32220f0dfb603e5b31ffaefa23 -https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.4.1-cpu_mkl_py312h09a6fac_100.conda#cc6d18496be926320ddeadd23a9c8d49 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-hf54134d_16_cpu.conda#1cbc3fb1ee28c99e5f8c52920a7717a3 +https://conda.anaconda.org/conda-forge/linux-64/pytorch-cpu-2.4.1-cpu_mkl_py312h09a6fac_102.conda#3579b30b19e9aa1ed58bd971203c8590 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.0.0-he882d9a_0_cpu.conda#1d73c2c8cabb70f9bf1dd36222ef7b25 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.9.2-py312h7900ff3_1.conda#07d5646ea9f22f4b1c46c2947d1b2f58 -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_1.conda#7e8ddbd44fb99ba376b09c4e9e61e509 +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.0.0-py312h9cebb41_0.conda#e110b1f861e749bc1dd48ad5467adab8 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 d7b1ccb16203d..9dbaa15306088 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 @@ -15,12 +15,12 @@ https://conda.anaconda.org/conda-forge/osx-64/xz-5.2.6-h775f41a_0.tar.bz2#a72f9d https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-hfdf4475_7.conda#7ed4301d437b59045be7e051a0308211 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-19.1.1-hf95d169_0.conda#2b09d0f92cae6df4b1670adcaca9c38c +https://conda.anaconda.org/conda-forge/osx-64/libcxx-19.1.3-hf95d169_0.conda#86801fc56d4641e3ef7a63f5d996b960 https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.22-h00291cd_0.conda#a15785ccc62ae2a8febd299424081efb https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.6.3-hac325c4_0.conda#c1db99b0a94a2f23bd6ce39e2d314e07 https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hfdf4475_0.conda#ed625b2e59dff82859c23dd24774156b 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-19.1.1-h545e0da_0.conda#3f3e4a599dd2638a945fc5821090db07 +https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-19.1.3-hf78d878_0.conda#18a8498d57d871da066beaa09263a638 https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-hf036a51_1.conda#e102bbf8a6ceeaf429deab8032fc8977 https://conda.anaconda.org/conda-forge/osx-64/openssl-3.3.2-hd23fc13_0.conda#2ff47134c8e292868a4609519b1ea3b6 https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda#8bcf980d2c6b17094961198284b8e862 @@ -34,9 +34,9 @@ https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.1.0-h00291cd_2.cond https://conda.anaconda.org/conda-forge/osx-64/libcxx-devel-17.0.6-h8f8a49f_6.conda#faa013d493ffd2d5f2d2fc6df5f98f2e https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-13.2.0-h2873a65_3.conda#e4fb4d23ec2870ff3c40d10afe305aec https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.44-h4b8f8c9_0.conda#f32ac2c8dd390dbf169f550887ed09d9 -https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.46.1-h4b8f8c9_0.conda#84de0078b58f899fc164303b0603ff0e +https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.47.0-h2f8c449_1.conda#af445c495253a871c3d809e1199bb12b 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.12.7-heaf3512_4.conda#ea1be6ecfe814da889e882c8b6ead79d +https://conda.anaconda.org/conda-forge/osx-64/libxml2-2.13.4-h12808cf_2.conda#0649b977d9e3d2fd579148643884535e https://conda.anaconda.org/conda-forge/osx-64/ninja-1.12.1-h3c5361c_0.conda#a0ebabd021c8191aeb82793fe43cfdcb 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-h9e318b2_1.conda#f17f77f2acf4d344734bda76829ce14e @@ -52,7 +52,7 @@ https://conda.anaconda.org/conda-forge/osx-64/libhwloc-2.11.1-default_h456cccd_1 https://conda.anaconda.org/conda-forge/osx-64/libllvm17-17.0.6-hbedff68_1.conda#fcd38f0553a99fa279fb66a5bfc2fb28 https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.0-h583c2ba_1.conda#4b78bcdcc8780cede8b3d090deba874d 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.0-h8a9149f_100_cp313.conda#1aa71d8a76ca8c86ca151e977660624c +https://conda.anaconda.org/conda-forge/osx-64/python-3.13.0-h0608dab_100_cp313.conda#9603103619775a3f99fe4b58d278775e https://conda.anaconda.org/conda-forge/osx-64/brotli-1.1.0-h00291cd_2.conda#2db0c38a7f2321c5bdaf32b181e832c7 https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99 @@ -71,12 +71,12 @@ https://conda.anaconda.org/conda-forge/osx-64/mpc-1.3.1-h9d8efa1_1.conda#0520855 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.2-h7310d3a_0.conda#05a14cc9d725dd74995927968d6547e3 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh145f28c_1.conda#6a9bb1b135a8c58e8bfb178d3f8dc28c +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh145f28c_0.conda#ca3afe2d7b893a8c8cdf489d30a2b1a3 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda#035c17fbf099f50ff60bf2eb303b0a83 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_0.conda#986287f89929b2d629bd6ef6497dc307 https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/osx-64/tbb-2021.13.0-h37c8870_0.conda#89742f5ac7aeb5c44ec2b4c3c6692c3c https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda#df68d78237980a159bd7149f33c0e8fd @@ -86,46 +86,46 @@ https://conda.anaconda.org/conda-forge/osx-64/tornado-6.4.1-py313ha37c0e0_1.cond https://conda.anaconda.org/conda-forge/osx-64/ccache-4.10.1-hee5fd93_0.conda#09898bb80e196695cea9e07402cff215 https://conda.anaconda.org/conda-forge/osx-64/cctools_osx-64-1010.6-h98e843e_1.conda#ed757b98aaa22a9e38c5a76191fb477c https://conda.anaconda.org/conda-forge/osx-64/clang-17-17.0.6-default_hb173f14_7.conda#809e36447b1bfb87ed1b7fb46339561a -https://conda.anaconda.org/conda-forge/osx-64/coverage-7.6.3-py313hb558fbc_0.conda#5c01cf639e64f9c275b88db1661cc51a -https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.54.1-py313ha37c0e0_0.conda#a870d7d30159f6ff847352f64ece1f7e +https://conda.anaconda.org/conda-forge/osx-64/coverage-7.6.4-py313h25ec13a_0.conda#3b51dc2570f11e20bb75fa3bd7d88c46 +https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.54.1-py313h25ec13a_1.conda#86e0b9a91e6d6f97f8dbe7591ad22c76 https://conda.anaconda.org/conda-forge/osx-64/gfortran_impl_osx-64-13.2.0-h2bc304d_3.conda#57aa4cb95277a27aa0a1834ed97be45b https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f https://conda.anaconda.org/conda-forge/osx-64/ld64-951.9-h0a3eb4e_1.conda#8b8e1a4bd8384bf4b884c9e41636038f -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa 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-10.4.0-py313hcd5872a_1.conda#7f2a83576cab0a1398fb1bf1719e0536 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/osx-64/pillow-11.0.0-py313h4d44d4f_0.conda#d5a3e556600840a77c61394c48ee52d9 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/osx-64/cctools-1010.6-h5b2de21_1.conda#5a08ae55869b0b1eb7fbee910aa30d19 https://conda.anaconda.org/conda-forge/osx-64/clang-17.0.6-default_he371ed4_7.conda#fd6888f26c44ddb10c9954a2df5765c7 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.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 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-cov-5.0.0-pyhd8ed1ab_0.conda#c54c0107057d67ddf077751339ec2c63 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.0.0-pyhd8ed1ab_0.conda#cb8a11b6d209e3d85e5094bdbd9ebd9c https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 https://conda.anaconda.org/conda-forge/osx-64/clangxx-17.0.6-default_he371ed4_7.conda#4f110486af1272f0d4dee6adc5041fbf 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/compiler-rt_osx-64-17.0.6-hf2b8a54_2.conda#98e6d83e484e42f6beebba4276e38145 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.1.2-py313hd1f2bdd_0.conda#6b6950575916f90c82ad76e13a8a58f4 +https://conda.anaconda.org/conda-forge/osx-64/numpy-2.1.3-py313h7ca3f3b_0.conda#b827b0af2098c63435b27b7f4e4d50dd 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/compiler-rt-17.0.6-h1020d70_2.conda#be4cb4531d4cee9df94bf752455d68de -https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.0-py313h0c4e38b_2.conda#8c9d40dbfc3bac12357cd787aec53c57 +https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.0-py313hc99daa9_2.conda#572ff94936f32a90610cb9943f8f9d4f https://conda.anaconda.org/conda-forge/osx-64/pandas-2.2.3-py313h38cdd20_1.conda#ab61fb255c951a0514616e92dd2e18b2 -https://conda.anaconda.org/conda-forge/osx-64/scipy-1.14.1-py313h6711662_0.conda#c2b88aea865b2faac84c6f06714259ab +https://conda.anaconda.org/conda-forge/osx-64/scipy-1.14.1-py313hbd2dc07_1.conda#63098e1999a8f08b82ae921440e6ed0a https://conda.anaconda.org/conda-forge/osx-64/blas-2.120-mkl.conda#b041a7677a412f3d925d8208936cb1e2 https://conda.anaconda.org/conda-forge/osx-64/clang_impl_osx-64-17.0.6-h1af8efd_21.conda#6ef491cbc462aae64eaa0213e7ae6222 https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.9.2-py313h04f2f9a_1.conda#e0355aa34089010cce072986cfb9c989 https://conda.anaconda.org/conda-forge/osx-64/pyamg-5.2.1-py313h0322a6a_1.conda#4bda5182eeaef3d2017a2ec625802e1a https://conda.anaconda.org/conda-forge/osx-64/clang_osx-64-17.0.6-hb91bd55_21.conda#d94a0f2c03e7a50203d2b78d7dd9fa25 https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.9.2-py313habf4b1d_1.conda#5323d57b4ec77c8cdd7475cbdd85072b -https://conda.anaconda.org/conda-forge/osx-64/c-compiler-1.8.0-hb714fc7_0.conda#71b4b830facf1fe50f7a3c753a7b99bb +https://conda.anaconda.org/conda-forge/osx-64/c-compiler-1.8.0-hfc4bf79_1.conda#d6e3cf55128335736c8d4bb86e73c191 https://conda.anaconda.org/conda-forge/osx-64/clangxx_impl_osx-64-17.0.6-hc3430b7_21.conda#9dbdec57445cac0f0c39aefe3d3900bc https://conda.anaconda.org/conda-forge/osx-64/gfortran_osx-64-13.2.0-h18f7dce_1.conda#71d59c1ae3fea7a97154ff0e20b38df3 https://conda.anaconda.org/conda-forge/osx-64/clangxx_osx-64-17.0.6-hb91bd55_21.conda#cfcbb6790123280b5be7992d392e8194 https://conda.anaconda.org/conda-forge/osx-64/gfortran-13.2.0-h2c809b3_1.conda#b5ad3b799b9ae996fcc8aab3a60fb48e -https://conda.anaconda.org/conda-forge/osx-64/cxx-compiler-1.8.0-h6a1c779_0.conda#a61a75c445dba355b9bf7006332fea7b -https://conda.anaconda.org/conda-forge/osx-64/fortran-compiler-1.8.0-h33d1f46_0.conda#c69efb52bf1eea6570a816278d64d683 -https://conda.anaconda.org/conda-forge/osx-64/compilers-1.8.0-h694c41f_0.conda#3d9fa39371da870a987fc83a24366c1c +https://conda.anaconda.org/conda-forge/osx-64/cxx-compiler-1.8.0-h385f146_1.conda#b72f72f89de328cc907bcdf88b85447d +https://conda.anaconda.org/conda-forge/osx-64/fortran-compiler-1.8.0-h33d1f46_1.conda#f3f15da7cbc7be80ea112ecd5dd73b22 +https://conda.anaconda.org/conda-forge/osx-64/compilers-1.8.0-h694c41f_1.conda#d9d0a18b6b3e96409c4a5cf76d513a05 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 index 6727ba5fdf284..e4ac139fba46c 100644 --- 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 @@ -10,7 +10,7 @@ https://repo.anaconda.com/pkgs/main/osx-64/libbrotlicommon-1.0.9-h6c40b1e_8.cond 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.17-hb664fd8_1.conda#b6116b8db33ea6a5b5287dae70d4a913 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-h6c40b1e_0.conda#d8fd9f599dd4e012694e69d119016442 +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-2024b-h04d1e81_0.conda#9be694715c6a65f9631bb1b242125e9d @@ -80,7 +80,7 @@ https://repo.anaconda.com/pkgs/main/osx-64/scipy-1.11.4-py312h81688c2_0.conda#7d https://repo.anaconda.com/pkgs/main/osx-64/pandas-2.2.2-py312h77d3abe_0.conda#463868c40d8ff98bec263f1fd57a8d97 https://repo.anaconda.com/pkgs/main/osx-64/pyamg-4.2.3-py312h44cbcf4_0.conda#3bdc7be74087b3a5a83c520a74e1e8eb # pip cython @ https://files.pythonhosted.org/packages/58/50/fbb23239efe2183e4eaf76689270d6f5b3bbcf9be9ad1eb97cc34349e6fc/Cython-3.0.11-cp312-cp312-macosx_10_9_x86_64.whl#sha256=11996c40c32abf843ba652a6d53cb15944c88d91f91fc4e6f0028f5df8a8f8a1 -# pip meson @ https://files.pythonhosted.org/packages/55/a6/47b9353c331318a13eb050887eacfd61eb075746285f9baf7ef7de6ae235/meson-1.5.2-py3-none-any.whl#sha256=77706e2368a00d789c097632ccf4fc39251fba56d03e1e1b262559a3c7a08f5b +# pip meson @ https://files.pythonhosted.org/packages/76/73/3dc4edc855c9988ff05ea5590f5c7bda72b6e0d138b2ddc1fab92a1f242f/meson-1.6.0-py3-none-any.whl#sha256=234a45f9206c6ee33b473ec1baaef359d20c0b89a71871d58c65a6db6d98fe74 # pip threadpoolctl @ https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl#sha256=56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467 -# pip pyproject-metadata @ https://files.pythonhosted.org/packages/22/81/42aaafbff27ca340eef777a4e3e8a509941e75fc0eeb9da2be5ee4159041/pyproject_metadata-0.8.1-py3-none-any.whl#sha256=adf593fa478b787c90cc77fcea4114f19a3a1335532bdcba2851be9459a6c39e -# pip meson-python @ https://files.pythonhosted.org/packages/91/c0/104cb6244c83fe6bc3886f144cc433db0c0c78efac5dc00e409a5a08c87d/meson_python-0.16.0-py3-none-any.whl#sha256=842dc9f5dc29e55fc769ff1b6fe328412fe6c870220fc321060a1d2d395e69e8 +# pip pyproject-metadata @ https://files.pythonhosted.org/packages/e8/61/9dd3e68d2b6aa40a5fc678662919be3c3a7bf22cba5a6b4437619b77e156/pyproject_metadata-0.9.0-py3-none-any.whl#sha256=fc862aab066a2e87734333293b0af5845fe8ac6cb69c451a41551001e923be0b +# 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_pip_openblas_pandas_linux-64_conda.lock b/build_tools/azure/pylatest_pip_openblas_pandas_linux-64_conda.lock index 43a6b0352e220..d3a9418c90019 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 @@ -26,11 +26,11 @@ https://repo.anaconda.com/pkgs/main/linux-64/setuptools-75.1.0-py311h06a4308_0.c https://repo.anaconda.com/pkgs/main/linux-64/wheel-0.44.0-py311h06a4308_0.conda#1fb091aa98b4fc5ca036b2086dac1db5 https://repo.anaconda.com/pkgs/main/linux-64/pip-24.2-py311h06a4308_0.conda#eff3ec695130b6912d64997edbc0db16 # pip alabaster @ https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl#sha256=fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b -# pip array-api-compat @ https://files.pythonhosted.org/packages/45/78/17985eac75d04c30f8cc375e4400e20b0787dc4a1c853a8fe9fad7932f55/array_api_compat-1.9-py3-none-any.whl#sha256=76db63c2d2461ba0e86b920c8b087f0a1617eb14de3ec29fe6811eeecad9c5e8 +# pip array-api-compat @ https://files.pythonhosted.org/packages/13/1d/2b2d33635de5dbf5e703114c11f1129394e68be16cc4dc5ccc2021a17f7b/array_api_compat-1.9.1-py3-none-any.whl#sha256=41a2703a662832d21619359ddddc5c0449876871f6c01e108c335f2a9432df94 # pip babel @ https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl#sha256=368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b # pip certifi @ https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl#sha256=922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8 # pip charset-normalizer @ https://files.pythonhosted.org/packages/eb/5b/6f10bad0f6461fa272bfbbdf5d0023b5fb9bc6217c92bf068fa5a99820f5/charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc -# pip coverage @ https://files.pythonhosted.org/packages/09/ec/c3c4dd9cdcd97f127141dfa348c737912d32130e6129e61645736106c341/coverage-7.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=0c6c0f4d53ef603397fc894a895b960ecd7d44c727df42a8d500031716d4e8d2 +# pip coverage @ https://files.pythonhosted.org/packages/cc/57/cb08f0eda0389a9a8aaa4fc1f9fec7ac361c3e2d68efd5890d7042c18aa3/coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e # 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/93/03/e330b241ad8aa12bb9d98b58fb76d4eb7dcbe747479aab5c29fce937b9e7/Cython-3.0.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=3999fb52d3328a6a5e8c63122b0a8bd110dfcdb98dda585a3def1426b991cba7 # pip docutils @ https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl#sha256=dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2 @@ -41,13 +41,13 @@ https://repo.anaconda.com/pkgs/main/linux-64/pip-24.2-py311h06a4308_0.conda#eff3 # pip iniconfig @ https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl#sha256=b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 # pip joblib @ https://files.pythonhosted.org/packages/91/29/df4b9b42f2be0b623cbd5e2140cafcaa2bef0759a00b7b70104dcfe2fb51/joblib-1.4.2-py3-none-any.whl#sha256=06d478d5674cbc267e7496a410ee875abd68e4340feff4490bcb7afb88060ae6 # pip kiwisolver @ https://files.pythonhosted.org/packages/a7/4b/2db7af3ed3af7c35f388d5f53c28e155cd402a55432d800c543dc6deb731/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18 -# pip markupsafe @ https://files.pythonhosted.org/packages/ae/1d/7d5ec8bcfd9c2db235d720fa51d818b7e2abc45250ce5f53dd6cb60409ca/MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b -# pip meson @ https://files.pythonhosted.org/packages/55/a6/47b9353c331318a13eb050887eacfd61eb075746285f9baf7ef7de6ae235/meson-1.5.2-py3-none-any.whl#sha256=77706e2368a00d789c097632ccf4fc39251fba56d03e1e1b262559a3c7a08f5b -# pip networkx @ https://files.pythonhosted.org/packages/8b/4e/bf7a4ccc11ded738efd0bda39296c7cee3617e800f890f919de5c0fe00c8/networkx-3.4.1-py3-none-any.whl#sha256=e30a87b48c9a6a7cc220e732bffefaee585bdb166d13377734446ce1a0620eed +# pip markupsafe @ https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84 +# pip meson @ https://files.pythonhosted.org/packages/76/73/3dc4edc855c9988ff05ea5590f5c7bda72b6e0d138b2ddc1fab92a1f242f/meson-1.6.0-py3-none-any.whl#sha256=234a45f9206c6ee33b473ec1baaef359d20c0b89a71871d58c65a6db6d98fe74 +# pip networkx @ https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl#sha256=df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f # pip ninja @ https://files.pythonhosted.org/packages/6d/92/8d7aebd4430ab5ff65df2bfee6d5745f95c004284db2d8ca76dcbfd9de47/ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl#sha256=84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b -# pip numpy @ https://files.pythonhosted.org/packages/23/69/538317f0d925095537745f12aced33be1570bbdc4acde49b33748669af96/numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1 +# pip numpy @ https://files.pythonhosted.org/packages/7a/f0/80811e836484262b236c684a75dfc4ba0424bc670e765afaa911468d9f39/numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b # pip packaging @ https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl#sha256=5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124 -# pip pillow @ https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl#sha256=76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319 +# pip pillow @ https://files.pythonhosted.org/packages/39/63/b3fc299528d7df1f678b0666002b37affe6b8751225c3d9c12cf530e73ed/pillow-11.0.0-cp311-cp311-manylinux_2_28_x86_64.whl#sha256=45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa # 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/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl#sha256=b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a # pip pyparsing @ https://files.pythonhosted.org/packages/be/ec/2eb3cd785efd67806c46c13a17339708ddc346cbb684eade7a6e6f79536a/pyparsing-3.2.0-py3-none-any.whl#sha256=93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84 @@ -64,12 +64,12 @@ https://repo.anaconda.com/pkgs/main/linux-64/pip-24.2-py311h06a4308_0.conda#eff3 # pip threadpoolctl @ https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl#sha256=56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467 # pip tzdata @ https://files.pythonhosted.org/packages/a6/ab/7e5f53c3b9d14972843a647d8d7a853969a58aecc7559cb3267302c94774/tzdata-2024.2-py2.py3-none-any.whl#sha256=a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd # pip urllib3 @ https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl#sha256=ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac -# pip array-api-strict @ https://files.pythonhosted.org/packages/08/06/aba69bce257fd1cda0d1db616c12728af0f46878a5cc1923fcbb94201947/array_api_strict-2.0.1-py3-none-any.whl#sha256=f74cbf0d0c182fcb45c5ee7f28f9c7b77e6281610dfbbdd63be60b1a5a7872b3 +# pip array-api-strict @ https://files.pythonhosted.org/packages/2d/bc/e7f5e40d85744e59cb7692f8098f828e63610d3b850957bba5bbf569a90a/array_api_strict-2.1-py3-none-any.whl#sha256=322740ba4422e7ca758290d00edfe75491f1783ad1ab44325007c44162aa938a # pip contourpy @ https://files.pythonhosted.org/packages/03/33/003065374f38894cdf1040cef474ad0546368eea7e3a51d48b8a423961f8/contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d # pip imageio @ https://files.pythonhosted.org/packages/4e/e7/26045404a30c8a200e960fb54fbaf4b73d12e58cd28e03b306b084253f4f/imageio-2.36.0-py3-none-any.whl#sha256=471f1eda55618ee44a3c9960911c35e647d9284c68f077e868df633398f137f0 # pip jinja2 @ https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl#sha256=bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d # 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/22/81/42aaafbff27ca340eef777a4e3e8a509941e75fc0eeb9da2be5ee4159041/pyproject_metadata-0.8.1-py3-none-any.whl#sha256=adf593fa478b787c90cc77fcea4114f19a3a1335532bdcba2851be9459a6c39e +# pip pyproject-metadata @ https://files.pythonhosted.org/packages/e8/61/9dd3e68d2b6aa40a5fc678662919be3c3a7bf22cba5a6b4437619b77e156/pyproject_metadata-0.9.0-py3-none-any.whl#sha256=fc862aab066a2e87734333293b0af5845fe8ac6cb69c451a41551001e923be0b # pip pytest @ https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl#sha256=a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2 # 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 @@ -77,10 +77,10 @@ https://repo.anaconda.com/pkgs/main/linux-64/pip-24.2-py311h06a4308_0.conda#eff3 # pip tifffile @ https://files.pythonhosted.org/packages/50/0a/435d5d7ec64d1c8b422ac9ebe42d2f3b2ac0b3f8a56f5c04dd0f3b7ba83c/tifffile-2024.9.20-py3-none-any.whl#sha256=c54dc85bc1065d972cb8a6ffb3181389d597876aa80177933459733e4ed243dd # pip lightgbm @ https://files.pythonhosted.org/packages/4e/19/1b928cad70a4e1a3e2c37d5417ca2182510f2451eaadb6c91cd9ec692cae/lightgbm-4.5.0-py3-none-manylinux_2_28_x86_64.whl#sha256=960a0e7c077de0ca3053f1325d3edfc92ea815acf5176adcacdea0f635aeef9b # pip matplotlib @ https://files.pythonhosted.org/packages/01/75/6c7ce560e95714a10fcbb3367d1304975a1a3e620f72af28921b796403f3/matplotlib-3.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447 -# pip meson-python @ https://files.pythonhosted.org/packages/91/c0/104cb6244c83fe6bc3886f144cc433db0c0c78efac5dc00e409a5a08c87d/meson_python-0.16.0-py3-none-any.whl#sha256=842dc9f5dc29e55fc769ff1b6fe328412fe6c870220fc321060a1d2d395e69e8 +# 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/cd/5f/4dba1d39bb9c38d574a9a22548c540177f78ea47b32f99c0ff2ec499fac5/pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc # pip pyamg @ https://files.pythonhosted.org/packages/d3/e8/6898b3b791f369605012e896ed903b6626f3bd1208c6a647d7219c070209/pyamg-5.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=679a5904eac3a4880288c8c0e6a29f110a2627ea15a443a4e9d5997c7dc5fab6 -# pip pytest-cov @ https://files.pythonhosted.org/packages/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl#sha256=4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652 +# pip pytest-cov @ https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl#sha256=eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35 # pip pytest-xdist @ https://files.pythonhosted.org/packages/6d/82/1d96bf03ee4c0fdc3c0cbe61470070e659ca78dc0086fb88b66c185e2449/pytest_xdist-3.6.1-py3-none-any.whl#sha256=9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7 # pip scikit-image @ https://files.pythonhosted.org/packages/ad/96/138484302b8ec9a69cdf65e8d4ab47a640a3b1a8ea3c437e1da3e1a5a6b8/scikit_image-0.24.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=fa27b3a0dbad807b966b8db2d78da734cb812ca4787f7fbb143764800ce2fa9c # pip sphinx @ https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl#sha256=09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2 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 bf0c60c710644..2f704dfeadddd 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 @@ -57,13 +57,13 @@ https://repo.anaconda.com/pkgs/main/linux-64/pip-24.2-py313h06a4308_0.conda#59f8 # pip threadpoolctl @ https://files.pythonhosted.org/packages/4b/2c/ffbf7a134b9ab11a67b0cf0726453cedd9c5043a4fe7a35d1cefa9a1bcfb/threadpoolctl-3.5.0-py3-none-any.whl#sha256=56c1e26c150397e58c4926da8eeee87533b1e32bef131bd4bf6a2f45f3185467 # pip urllib3 @ https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl#sha256=ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac # pip jinja2 @ https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl#sha256=bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d -# pip pyproject-metadata @ https://files.pythonhosted.org/packages/22/81/42aaafbff27ca340eef777a4e3e8a509941e75fc0eeb9da2be5ee4159041/pyproject_metadata-0.8.1-py3-none-any.whl#sha256=adf593fa478b787c90cc77fcea4114f19a3a1335532bdcba2851be9459a6c39e +# pip pyproject-metadata @ https://files.pythonhosted.org/packages/e8/61/9dd3e68d2b6aa40a5fc678662919be3c3a7bf22cba5a6b4437619b77e156/pyproject_metadata-0.9.0-py3-none-any.whl#sha256=fc862aab066a2e87734333293b0af5845fe8ac6cb69c451a41551001e923be0b # pip pytest @ https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl#sha256=a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2 # 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/91/c0/104cb6244c83fe6bc3886f144cc433db0c0c78efac5dc00e409a5a08c87d/meson_python-0.16.0-py3-none-any.whl#sha256=842dc9f5dc29e55fc769ff1b6fe328412fe6c870220fc321060a1d2d395e69e8 +# pip meson-python @ https://files.pythonhosted.org/packages/7d/ec/40c0ddd29ef4daa6689a2b9c5ced47d5b58fa54ae149b19e9a97f4979c8c/meson_python-0.17.1-py3-none-any.whl#sha256=30a75c52578ef14aff8392677b09c39346e0a24d2b2c6204b8ed30583c11269c # 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/78/3a/af5b4fa5961d9a1e6237b530eb87dd04aea6eb83da09d2a4073d81b54ccf/pytest_cov-5.0.0-py3-none-any.whl#sha256=4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652 +# pip pytest-cov @ https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl#sha256=eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35 # 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/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl#sha256=09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2 # 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 index 38d5002e819b6..147126a809ec6 100644 --- a/build_tools/azure/pymin_conda_forge_mkl_win-64_conda.lock +++ b/build_tools/azure/pymin_conda_forge_mkl_win-64_conda.lock @@ -8,7 +8,7 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed3 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.1.0-h66d3029_694.conda#1f80971a50e69c1f7af15707619df49e +https://conda.anaconda.org/conda-forge/win-64/mkl-include-2024.2.2-h66d3029_14.conda#19e51a50ba5fc6f7421f12fba6d0b775 https://conda.anaconda.org/conda-forge/win-64/python_abi-3.9-5_cp39.conda#86ba1bbcf9b259d1592201f3c345c810 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.22621.0-h57928b3_1.conda#6797b005cd0f439c4c5c9ac565783700 @@ -16,7 +16,7 @@ https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766 https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_8.conda#03cccbba200ee0523bde1f3dad60b1f3 https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.40.33810-hcc2c482_22.conda#ce23a4b980ee0556a118ed96550ff3f3 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.1.0-h1383e82_1.conda#f8aa80643cd3ff1767ea4e6008ed52d1 +https://conda.anaconda.org/conda-forge/win-64/libgomp-14.2.0-h1383e82_1.conda#9e2d4d1214df6f21cba12f6eff4972f9 https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h8a93ad2_22.conda#a47cd756e88d8a80dfae678842d4acc9 https://conda.anaconda.org/conda-forge/win-64/vs2015_runtime-14.40.33810-h3bf8584_22.conda#8c6b061d44cafdfc8e8c6eb5f100caf0 https://conda.anaconda.org/conda-forge/win-64/_openmp_mutex-4.5-2_gnu.conda#37e16618af5c4851a3f3d66dd0e11141 @@ -31,7 +31,7 @@ https://conda.anaconda.org/conda-forge/win-64/libexpat-2.6.3-he0c23c2_0.conda#21 https://conda.anaconda.org/conda-forge/win-64/libffi-3.4.2-h8ffe710_5.tar.bz2#2c96d1b6915b408893f9472569dee135 https://conda.anaconda.org/conda-forge/win-64/libiconv-1.17-hcfcfb64_2.conda#e1eb10b1cca179f2baa3601e4efc8712 https://conda.anaconda.org/conda-forge/win-64/libjpeg-turbo-3.0.0-hcfcfb64_1.conda#3f1b948619c45b1ca714d60c7389092c -https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.46.1-h2466b09_0.conda#8a7c1ad01f58623bfbae8d601db7cf3b +https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.47.0-h2466b09_1.conda#5b1f36012cc3d09c4eb9f24ad0e2c379 https://conda.anaconda.org/conda-forge/win-64/libwebp-base-1.4.0-hcfcfb64_0.conda#abd61d0ab127ec5cd68f62c2969e6f34 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_0.conda#a557dde55343e03c68cd7e29e7f87279 @@ -41,15 +41,14 @@ https://conda.anaconda.org/conda-forge/win-64/qhull-2020.2-hc790b64_5.conda#854f https://conda.anaconda.org/conda-forge/win-64/tbb-2021.7.0-h91493d7_0.tar.bz2#f57be598137919e4f7e7d159960d66a1 https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h5226925_1.conda#fc048363eb8f03cd1737600a5d08aafe https://conda.anaconda.org/conda-forge/win-64/xz-5.2.6-h8d14728_0.tar.bz2#515d77642eaa3639413c6b1bc3f94219 -https://conda.anaconda.org/conda-forge/win-64/expat-2.6.3-he0c23c2_0.conda#a85588222941f75577eb39711058e1de 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.1.0-h1383e82_1.conda#5464b6bb50d593b8f529d1fbcd58f3b2 +https://conda.anaconda.org/conda-forge/win-64/libgcc-14.2.0-h1383e82_1.conda#75fdd34824997a0f9950a703b15d8ac5 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.44-h3ca93ac_0.conda#639ac6b55a40aa5de7b8c1b4d78f9e81 -https://conda.anaconda.org/conda-forge/win-64/libxml2-2.12.7-h0f24e4e_4.conda#ed4d301f0d2149b34deb9c4fecafd836 -https://conda.anaconda.org/conda-forge/win-64/mkl-2024.1.0-h66d3029_694.conda#a17423859d3fb912c8f2e9797603ddb6 +https://conda.anaconda.org/conda-forge/win-64/libxml2-2.13.4-h442d1da_2.conda#46c233e5c137a2de2d1d95ca35ad8d6a +https://conda.anaconda.org/conda-forge/win-64/mkl-2024.2.2-h66d3029_14.conda#f011e7cc21918dc9d1efe0209e27fa16 https://conda.anaconda.org/conda-forge/win-64/pcre2-10.44-h3d7b363_2.conda#a3a3baddcfb8c80db84bec3cb7746fb8 https://conda.anaconda.org/conda-forge/win-64/python-3.9.20-hfaddaf0_1_cpython.conda#445389d1d311435a90def248c814ddd6 https://conda.anaconda.org/conda-forge/win-64/zlib-1.3.1-h2466b09_2.conda#be60c4e8efa55fddc17b4131aa47acbd @@ -64,60 +63,60 @@ https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda#1 https://conda.anaconda.org/conda-forge/win-64/freetype-2.12.1-hdaf720e_2.conda#3761b23693f768dc75a8fd0a73ca053f https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/win-64/kiwisolver-1.4.7-py39h2b77a98_0.conda#c116c25e2e36f770f065559ad2a1da73 -https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-24_win64_mkl.conda#ea127210707251a33116b437c22b8dad -https://conda.anaconda.org/conda-forge/win-64/libclang13-19.1.1-default_ha5278ca_0.conda#72f980e3852ad8f490485868bd391851 -https://conda.anaconda.org/conda-forge/win-64/libgfortran5-14.1.0-hf275ef4_1.conda#a3c498d4f17c0604a11c46e3e6c067ed -https://conda.anaconda.org/conda-forge/win-64/libglib-2.82.1-h7025463_0.conda#f784035a6fcb34f0583ca3bd0dcc6c3b +https://conda.anaconda.org/conda-forge/win-64/libblas-3.9.0-25_win64_mkl.conda#499208e81242efb6e5abc7366c91c816 +https://conda.anaconda.org/conda-forge/win-64/libclang13-19.1.3-default_ha5278ca_0.conda#fe6aa50eeb307558f8974f115305388f +https://conda.anaconda.org/conda-forge/win-64/libgfortran5-14.2.0-hf020157_1.conda#294a5033b744648a2ba816b34ffd810a +https://conda.anaconda.org/conda-forge/win-64/libglib-2.82.2-h7025463_0.conda#3e379c1b908a7101ecbc503def24613f https://conda.anaconda.org/conda-forge/win-64/libtiff-4.7.0-hfc51747_1.conda#eac317ed1cc6b9c0af0c27297e364665 https://conda.anaconda.org/conda-forge/win-64/libxslt-1.1.39-h3df6e99_0.conda#279ee338c9b34871d578cb3c7aa68f70 -https://conda.anaconda.org/conda-forge/win-64/mkl-devel-2024.1.0-h57928b3_694.conda#cb1406a70154cdef203167c6a95f6351 +https://conda.anaconda.org/conda-forge/win-64/mkl-devel-2024.2.2-h57928b3_14.conda#ecc2c244eff5cb6289b6db5e0401c0aa https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf 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.0-pyhd8ed1ab_1.conda#035c17fbf099f50ff60bf2eb303b0a83 -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda#df68d78237980a159bd7149f33c0e8fd https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727 https://conda.anaconda.org/conda-forge/win-64/tornado-6.4.1-py39ha55e580_1.conda#4a93d22ed5b2cede80fbee7f7f775a9d -https://conda.anaconda.org/conda-forge/win-64/unicodedata2-15.1.0-py39ha55989b_0.conda#20ec896e8d97f2ff8be1124e624dc8f2 +https://conda.anaconda.org/conda-forge/win-64/unicodedata2-15.1.0-py39ha55e580_1.conda#7b7e5732092b9a635440ec939e45651d https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 https://conda.anaconda.org/conda-forge/win-64/xorg-libxau-1.0.11-h0e40799_1.conda#ca66d6f8fe86dd53664e8de5087ef6b1 https://conda.anaconda.org/conda-forge/win-64/xorg-libxdmcp-1.1.5-h0e40799_0.conda#8393c0f7e7870b4eb45553326f81f0ff https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda#4daaed111c05672ae669f7036ee5bba3 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.6.2-py39ha55e580_0.conda#ca127b25cef0da2707570d50493ca4ac -https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.14.2-hbde0cde_0.conda#08767992f1a4f1336a257af1241034bd +https://conda.anaconda.org/conda-forge/win-64/coverage-7.6.4-py39hf73967f_0.conda#7f2ad67ee529ce63fbb4e69949ee56a0 +https://conda.anaconda.org/conda-forge/win-64/fontconfig-2.15.0-h765892d_1.conda#9bb0026a2131b09404c59c4290c697cd https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_0.conda#c808991d29b9838fb4d96ce8267ec9ec https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f https://conda.anaconda.org/conda-forge/win-64/lcms2-2.16-h67d730c_0.conda#d3592435917b62a8becff3a60db674f6 -https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-24_win64_mkl.conda#a42c7390d3249698c0ffb6040e9396e7 -https://conda.anaconda.org/conda-forge/win-64/libgfortran-14.1.0-h719f0c7_1.conda#568db0df166b9f7c2018f6883c7e6eee -https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-24_win64_mkl.conda#c69b7b6756a8d58cc8cf17081fffdc5c +https://conda.anaconda.org/conda-forge/win-64/libcblas-3.9.0-25_win64_mkl.conda#3ed189ba03a9888a8013aaee0d67c49d +https://conda.anaconda.org/conda-forge/win-64/libgfortran-14.2.0-h719f0c7_1.conda#bd709ec903eeb030208c78e4c35691d6 +https://conda.anaconda.org/conda-forge/win-64/liblapack-3.9.0-25_win64_mkl.conda#f716ef84564c574e8e74ae725f5d5f93 https://conda.anaconda.org/conda-forge/win-64/libxcb-1.17.0-h0e4246c_0.conda#a69bbf778a462da324489976c84cfc8c -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/win-64/openjpeg-2.5.2-h3d672ee_0.conda#7e7099ad94ac3b599808950cec30ad4e -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/win-64/cairo-1.18.0-h32b962e_3.conda#8f43723a4925c51e55c2d81725a97db4 -https://conda.anaconda.org/conda-forge/win-64/fonttools-4.54.1-py39ha55e580_0.conda#89999d310ad13d9982db606fb1d71035 +https://conda.anaconda.org/conda-forge/win-64/fonttools-4.54.1-py39hf73967f_1.conda#c0fcffbde1793dfd8067cf29fb22bc5f https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.4.5-pyhd8ed1ab_0.conda#67f4772681cf86652f3e2261794cf045 -https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-24_win64_mkl.conda#783f608f74e98ded5a45f767e5022d3a -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/win-64/liblapacke-3.9.0-25_win64_mkl.conda#d59fc46f1e1c2f3cf38a08a0a76ffee5 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 https://conda.anaconda.org/conda-forge/win-64/numpy-2.0.2-py39h60232e0_0.conda#13c59f25f5d4ad7d1c677667555f6547 -https://conda.anaconda.org/conda-forge/win-64/pillow-10.4.0-py39hfa8c767_1.conda#e2ecab76054dece3dc94c80ab1cdca0d -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda#c54c0107057d67ddf077751339ec2c63 +https://conda.anaconda.org/conda-forge/win-64/pillow-11.0.0-py39h5ee314c_0.conda#0c57206c5215a7e56414ce0332805226 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.0.0-pyhd8ed1ab_0.conda#cb8a11b6d209e3d85e5094bdbd9ebd9c https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 -https://conda.anaconda.org/conda-forge/win-64/blas-devel-3.9.0-24_win64_mkl.conda#cd958fc0b59b299e23540ec99fd9b48b +https://conda.anaconda.org/conda-forge/win-64/blas-devel-3.9.0-25_win64_mkl.conda#b3c40599e865dac087085b596fbbf4ad https://conda.anaconda.org/conda-forge/win-64/contourpy-1.3.0-py39h2b77a98_2.conda#37f8619ee96710220ead6bb386b9b24b https://conda.anaconda.org/conda-forge/win-64/harfbuzz-9.0.0-h2bedf89_1.conda#254f119aaed2c0be271c1114ae18d09b https://conda.anaconda.org/conda-forge/win-64/scipy-1.13.1-py39h1a10956_0.conda#9f8e571406af04d2f5fdcbecec704505 -https://conda.anaconda.org/conda-forge/win-64/blas-2.124-mkl.conda#fb905a873725572b705e903dd687ab41 +https://conda.anaconda.org/conda-forge/win-64/blas-2.125-mkl.conda#186eeb4e8ba0a5944775e04f241fc02a https://conda.anaconda.org/conda-forge/win-64/matplotlib-base-3.9.2-py39h5376392_1.conda#6538e11505db6f3e1ee15a8207839f34 -https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.7.3-hfb098fa_1.conda#3dd4b78a610e48def640c3c9acd0c7e7 -https://conda.anaconda.org/conda-forge/win-64/pyside6-6.7.3-py39h0285922_1.conda#74a9e0a5e28a45dd0f8c550c4a7f2af0 +https://conda.anaconda.org/conda-forge/win-64/qt6-main-6.8.0-hfb098fa_0.conda#053046ca73b71bbcc81c6dc114264d24 +https://conda.anaconda.org/conda-forge/win-64/pyside6-6.8.0.2-py39h0285922_0.conda#07b75557409b6bdbaf723b1bc020afb5 https://conda.anaconda.org/conda-forge/win-64/matplotlib-3.9.2-py39hcbf5309_1.conda#d14badfe4135e9bb2bec118bd3cff611 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 5fbc0ff8e818e..16de8b3604fe8 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 @@ -11,25 +11,25 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.co https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-5_cp39.conda#40363a30db350596b5f225d0d5a33328 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 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_1.conda#83e1364586ceb8d0739fbc85b5c95837 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda#048b02e3962f066da18efe3a21b77672 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_1.conda#1ece2ccb1dc8c68639712b05e0fae070 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.1-h024ca30_0.conda#f1fe1a838fecddbcee97c9d4afe24af5 +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.3-h024ca30_0.conda#d36687dc90337917a84a96a45111ad59 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#562b26ba2e19059551a811e72ab7f793 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_1.conda#38a5cd3be5fb620b48069e27285f1a44 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda#3cb76c3f10d3bc7f1105b2fc9db984df https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.3-h5888daf_0.conda#59f4c43bb1b5ef1c71946ff2cbf59524 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.1.0-hc5f4f2c_1.conda#10a0cef64b784d6ab6da50ebca4e984d -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda#e39480b9ca41323497b05492a63bc35b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda#9822b874ea29af082e5d36098d25427d +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda#234a5554c53625688d51062645337328 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e 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.1-hb9d3cd8_1.conda#19608a9656912805b2b9a2f6bd257b04 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.11-hb9d3cd8_1.conda#77cbc488235ebbaab2b6e912d3934bae 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-xf86vidmodeproto-2.3.1-hb9d3cd8_1003.conda#139e6c4010a04f20897b5d655470bfec +https://conda.anaconda.org/conda-forge/linux-64/xorg-xf86vidmodeproto-2.3.1-hb9d3cd8_1004.conda#24831329718daa6cbe35fcd071b778d4 https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.12-h4ab18f5_0.conda#7ed427f0871fd41cb1d9c17727c17589 https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00 @@ -41,7 +41,7 @@ https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.22.5-he02047a_3.conda#efab66b82ec976930b96d62a976de8e7 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.1.0-h69a702a_1.conda#591e631bc1ae62c64f2ab4f66178c097 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda#f1fd30127802683586f768875127a987 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda#d66573916ffcf376178462f1b61c941e https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 @@ -49,14 +49,16 @@ https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.5-h4ab18f5_0.conda#60 https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f 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.44-hadc24fc_0.conda#f4cc49d7aa68316213e4b12be35308d1 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.0-hadc24fc_1.conda#b6f02b52a174e612e89548f4663ce56a +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda#8371ac6457591af2cf6159439c1fd051 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda#b26e8aa824079e1be0294e7152ca4559 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/mysql-common-9.0.1-h266115a_1.conda#e97f73d51b5acdf1340a15b195738f16 +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_2.conda#85c0dc0bcd110c998b01856975486ee7 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.36-h5888daf_0.conda#de9cd5bca9e4918527b9b72b6e2e1409 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/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 @@ -69,12 +71,11 @@ https://conda.anaconda.org/conda-forge/linux-64/libcap-2.69-h0f662aa_0.conda#25c https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.123-hb9d3cd8_0.conda#ee605e794bdc14e2b7f84c4faa0d8c2c https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.22.5-he02047a_3.conda#9aba7960731e6b4547b3a52f812ed801 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.1.0-h69a702a_1.conda#16cec94c5992d7f42ae3f9fa8b25df8d +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_1.conda#0a7f4cd238267c88e5d69f7826a407eb 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/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 -https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.6-h59595ed_0.conda#9160cdeb523a1b20cf8d2a0bf821f45d https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-h297d8ca_0.conda#3aa1c7e292afeff25a0091ddd7c69b72 -https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.106-hdf54f9c_0.conda#efe735c7dc47dddbb14b3433d11c6feb https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda#df359c09c41cd186fffb93a2d87aa6f5 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.43.2-h59595ed_0.conda#71004cbf7924e19c02746ccde9fd7123 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 @@ -85,21 +86,21 @@ https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.con https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-he73a12e_1.conda#05a8ea5f446de33006171a7afe6ae857 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.10-h4f16b4b_0.conda#0b666058a179b744a622d0a4a0c56353 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee 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.22.5-he8f35ee_3.conda#1091193789bb830127ed067a9e01ac57 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.1-h2ff4ddf_0.conda#47a2209fa0df11797df0b767d1de1275 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda#13e8e54035ddd2b91875ba399f0f7c04 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_1.conda#80a57756c545ad11f9847835aa21e6b2 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/libtiff-4.7.0-he137b08_1.conda#63872517c98aa305da58a757c443698e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda#08a9265c637230c37cb1be4a6cad4536 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_1.conda#274f367df5d56f152a49ed3203c3b1c1 -https://conda.anaconda.org/conda-forge/linux-64/nss-3.105-hd34e28f_0.conda#28d7602527b76052422aaf5d6fd7ad81 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.4-hb346dea_2.conda#69b90b70c434b916abf5a1d5ee5d55fb +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_2.conda#57a9e7ee3c0840d3c8c9012473978629 https://conda.anaconda.org/conda-forge/linux-64/python-3.9.20-h13acc7a_1_cpython.conda#951cff166a5f170e27908811917165f8 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.43-hb9d3cd8_0.conda#f725c7425d6d7c15e31f3b99a88ea02f 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.11-hb9d3cd8_1.conda#a7a49a8b85122b49214798321e2e96b4 https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-hebfffa5_3.conda#fceaedf1cdbcb02df9699a0d9b005292 https://conda.anaconda.org/conda-forge/linux-64/ccache-4.10.1-h065aff2_0.conda#d6b48c138e0c8170a6fe9c136e063540 @@ -111,16 +112,15 @@ https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#e https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0 https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda#15dda3cdbf330abfe9f555d22f66db46 https://conda.anaconda.org/conda-forge/linux-64/gettext-0.22.5-he02047a_3.conda#c7f243bbaea97cd6ea1edd693270100e -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.82.1-h4833e2c_0.conda#0677a6c26a59b568599846b829ec5c8c +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.82.2-h4833e2c_0.conda#12859f91830f58b1803e32846651c6f6 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py39h74842e3_0.conda#1bf77976372ff6de02af7b75cf034ce5 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda#51bb7010fc86f70eee639b4bb7a894f5 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/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_1.conda#204892bce2e44252b5cf272712f10bdd -https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.7-hb3ce162_4.conda#8a35df3cbc0c8b12cc8af9473ae75eef -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.1-ha7bfdaf_0.conda#000cd5fc23967c97284b720cc6049c1e -https://conda.anaconda.org/conda-forge/linux-64/libpq-16.4-h2d7952a_2.conda#76c891962472b55544b51c52bae15587 +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.3-ha7bfdaf_0.conda#8bd654307c455162668cd66e36494000 +https://conda.anaconda.org/conda-forge/linux-64/libpq-16.4-h2d7952a_3.conda#50e2dddb3417a419cbc2388d0b1c06f7 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.7.0-h2c5496b_1.conda#e2eaefa4de2b7237af7c907b8bbc760a 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/openjpeg-2.5.2-h488ebb8_0.conda#7f2e286780f072ed750df46dc2631138 @@ -137,21 +137,22 @@ https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e97 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py39h8cd3c5a_1.conda#48d269953fcddbbcde078429d4b27afe https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.5-hb9d3cd8_3.conda#2159fc3619590b4f62473b6b9631549f -https://conda.anaconda.org/conda-forge/linux-64/coverage-7.6.2-py39h8cd3c5a_0.conda#3c83710c33f63f14118640a102b6d571 -https://conda.anaconda.org/conda-forge/linux-64/glib-2.82.1-h2ff4ddf_0.conda#12e1763ee9bf6685025981239ffaf482 +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.5-hb9d3cd8_4.conda#7da9007c0582712c4bad4131f89c8372 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.6.4-py39h9399b63_0.conda#997fc2d288ec458e692dfd784c173704 +https://conda.anaconda.org/conda-forge/linux-64/glib-2.82.2-h44428e9_0.conda#f19f985ab043e8843045410f3b99de8a https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-9.0.0-hda332d3_1.conda#76b32dcf243444aea9c6b804bcfa40b8 https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2#7583652522d71ad78ba536bba06940eb 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-cpp15-15.0.7-default_h127d8a8_5.conda#d0a9633b53cdc319b8a1a532ae7822b8 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.1-default_h9c6a7e4_0.conda#2e8992c584c2525a5b8ec7485cbe360c +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.3-default_hb5137d0_0.conda#311e6a1d041db3d6a8a8437750d4234f +https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.3-default_h9c6a7e4_0.conda#b8a8cd77810b20754f358f2327812552 https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869 https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.50-h4f305b6_0.conda#0d7ff1a8e69565ca3add6925e18e708f https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-20_linux64_openblas.conda#6fabc51f5e647d09cc010c40061557e0 -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 -https://conda.anaconda.org/conda-forge/linux-64/pillow-10.4.0-py39h648eaa6_1.conda#d633f654c8f6ddc94a55473ba5361003 -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py39h538c539_0.conda#a2bafdf8ae51c9eb6e5be684cfcedd60 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.12-py39h3d6467e_0.conda#e667a3ab0df62c54e60e1843d2e6defb @@ -162,11 +163,11 @@ https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.2.2-hc60ed4a_1.cond https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 https://conda.anaconda.org/conda-forge/linux-64/numpy-1.19.5-py39hd249d9e_3.tar.bz2#0cf333996ebdeeba8d1c8c1c0ee9eff9 https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py39h3d6467e_5.conda#93aff412f3e49fdb43361c0215cbd72d -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda#c54c0107057d67ddf077751339ec2c63 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.0.0-pyhd8ed1ab_0.conda#cb8a11b6d209e3d85e5094bdbd9ebd9c https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 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.7-h0a52356_0.conda#d368425fbd031a2f8e801a40c3415c72 -https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.7-h2774228_0.conda#cdf7c26c2f9cc1e4ac01d57b03a85323 +https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.7-h2774228_1.conda#ad328c530a12a8798776e5f03942090f https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.3.4-py39h2fa2bec_0.tar.bz2#9ec0b2186fab9121c54f4844f93ee5b7 https://conda.anaconda.org/conda-forge/linux-64/pandas-1.1.5-py39hde0f152_0.tar.bz2#79fc4b5b3a865b90dd3701cecf1ad33c https://conda.anaconda.org/conda-forge/linux-64/polars-0.20.30-py39ha963410_0.conda#322084e8890afc27fcca6df7a528df25 @@ -174,6 +175,6 @@ https://conda.anaconda.org/conda-forge/linux-64/scipy-1.6.0-py39hee8e79c_0.tar.b https://conda.anaconda.org/conda-forge/linux-64/blas-2.120-openblas.conda#c8f6916a81a340650078171b1d852574 https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hb77b528_0.conda#07f45f1be1c25345faddb8db0de8039b https://conda.anaconda.org/conda-forge/linux-64/pyamg-4.2.3-py39hac2352c_1.tar.bz2#6fb0628d6195d8b6caa2422d09296399 -https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.8-h3155989_26.conda#0b133022b9d6317733bfee559b6433c9 +https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.15-h374914d_0.conda#26e8b00e73c114c9b787d36edcbf4424 https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py39h52134e7_5.conda#e1f148e57d071b09187719df86f513c1 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.3.4-py39hf3d152e_0.tar.bz2#cbaec993375a908bbe506dc7328d747c 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 b40144dc9a5aa..2924533aadbde 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 @@ -11,20 +11,20 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.co https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-5_cp39.conda#40363a30db350596b5f225d0d5a33328 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 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_1.conda#83e1364586ceb8d0739fbc85b5c95837 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda#048b02e3962f066da18efe3a21b77672 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_1.conda#1ece2ccb1dc8c68639712b05e0fae070 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.1-h024ca30_0.conda#f1fe1a838fecddbcee97c9d4afe24af5 +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.3-h024ca30_0.conda#d36687dc90337917a84a96a45111ad59 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#562b26ba2e19059551a811e72ab7f793 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_1.conda#38a5cd3be5fb620b48069e27285f1a44 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_1.conda#e12057a66af8f2a38a839754ca4481e9 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda#3cb76c3f10d3bc7f1105b2fc9db984df 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.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.3-h5888daf_0.conda#59f4c43bb1b5ef1c71946ff2cbf59524 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.1.0-hc5f4f2c_1.conda#10a0cef64b784d6ab6da50ebca4e984d -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda#e39480b9ca41323497b05492a63bc35b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda#9822b874ea29af082e5d36098d25427d +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda#234a5554c53625688d51062645337328 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e @@ -39,20 +39,20 @@ https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz 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/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.1.0-h69a702a_1.conda#591e631bc1ae62c64f2ab4f66178c097 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda#f1fd30127802683586f768875127a987 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda#d66573916ffcf376178462f1b61c941e https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.4-h7f98852_1002.tar.bz2#e728e874159b042d92b90238a3cb0dc2 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.44-hadc24fc_0.conda#f4cc49d7aa68316213e4b12be35308d1 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.0-hadc24fc_1.conda#b6f02b52a174e612e89548f4663ce56a +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda#8371ac6457591af2cf6159439c1fd051 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda#b26e8aa824079e1be0294e7152ca4559 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/mysql-common-9.0.1-h266115a_1.conda#e97f73d51b5acdf1340a15b195738f16 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_2.conda#85c0dc0bcd110c998b01856975486ee7 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe 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/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 @@ -65,7 +65,7 @@ https://conda.anaconda.org/conda-forge/linux-64/icu-75.1-he02047a_0.conda#8b1893 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.123-hb9d3cd8_0.conda#ee605e794bdc14e2b7f84c4faa0d8c2c https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.1.0-h69a702a_1.conda#16cec94c5992d7f42ae3f9fa8b25df8d +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_1.conda#0a7f4cd238267c88e5d69f7826a407eb https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-h297d8ca_0.conda#3aa1c7e292afeff25a0091ddd7c69b72 https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda#df359c09c41cd186fffb93a2d87aa6f5 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.43.2-h59595ed_0.conda#71004cbf7924e19c02746ccde9fd7123 @@ -80,15 +80,15 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-he73a12e_1.cond https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.10-h4f16b4b_0.conda#0b666058a179b744a622d0a4a0c56353 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45 https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.1-h2ff4ddf_0.conda#47a2209fa0df11797df0b767d1de1275 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda#13e8e54035ddd2b91875ba399f0f7c04 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_1.conda#80a57756c545ad11f9847835aa21e6b2 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.27-pthreads_hac2b453_1.conda#ae05ece66d3924ac3d48b4aa3fa96cec +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_0.conda#9ebc9aedafaa2515ab247ff6bb509458 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda#63872517c98aa305da58a757c443698e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda#08a9265c637230c37cb1be4a6cad4536 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_1.conda#274f367df5d56f152a49ed3203c3b1c1 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.4-hb346dea_2.conda#69b90b70c434b916abf5a1d5ee5d55fb +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_2.conda#57a9e7ee3c0840d3c8c9012473978629 https://conda.anaconda.org/conda-forge/linux-64/python-3.9.20-h13acc7a_1_cpython.conda#951cff166a5f170e27908811917165f8 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.43-hb9d3cd8_0.conda#f725c7425d6d7c15e31f3b99a88ea02f @@ -116,15 +116,15 @@ https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.b https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py39h74842e3_0.conda#1bf77976372ff6de02af7b75cf034ce5 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda#51bb7010fc86f70eee639b4bb7a894f5 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-24_linux64_openblas.conda#80aea6603a6813b16ec119d00382b772 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-25_linux64_openblas.conda#8ea26d42ca88ec5258802715fe1ee10b https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_1.conda#204892bce2e44252b5cf272712f10bdd -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.1-ha7bfdaf_0.conda#000cd5fc23967c97284b720cc6049c1e +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.3-ha7bfdaf_0.conda#8bd654307c455162668cd66e36494000 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.7.0-h2c5496b_1.conda#e2eaefa4de2b7237af7c907b8bbc760a https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.1-py39h9399b63_1.conda#0782842622e8dc374909a8c39bafe9f3 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py39h9399b63_0.conda#d38773fed557834d3211e019b7cf7c2f https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.27-pthreads_h9eca1d5_1.conda#5633a1616bda33f8b815841eba4dbfb8 +https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.28-pthreads_hbcdf1e8_0.conda#edb742fa7cd3fc5fbcbfc24135dcadd8 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.2-h488ebb8_0.conda#7f2e286780f072ed750df46dc2631138 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf @@ -134,7 +134,7 @@ https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_0.conda#986287f89929b2d629bd6ef6497dc307 https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_0.conda#da1d979339e2714c30a8e806a33ec087 @@ -142,7 +142,7 @@ https://conda.anaconda.org/conda-forge/noarch/tabulate-0.9.0-pyhd8ed1ab_1.tar.bz https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda#df68d78237980a159bd7149f33c0e8fd https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py39h8cd3c5a_1.conda#48d269953fcddbbcde078429d4b27afe -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py39hd1e30aa_0.conda#1da984bbb6e765743e13388ba7b7b2c8 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py39h8cd3c5a_1.conda#6346898044e4387631c614290789a434 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 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 @@ -150,46 +150,46 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.2-hb9d3cd8_0 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.5-hb9d3cd8_3.conda#2159fc3619590b4f62473b6b9631549f +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.5-hb9d3cd8_4.conda#7da9007c0582712c4bad4131f89c8372 https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda#4daaed111c05672ae669f7036ee5bba3 https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py39h15c3d72_0.conda#7e61b8777f42e00b08ff059f9e8ebc44 -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py39h8cd3c5a_0.conda#a0987ca1f47be10976b5b53bc1f2db74 +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py39h9399b63_1.conda#1a4772f78ffa4675c84a4219db3934fd https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-9.0.0-hda332d3_1.conda#76b32dcf243444aea9c6b804bcfa40b8 https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda#54198435fce4d64d8a89af22573012a8 https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_0.conda#c808991d29b9838fb4d96ce8267ec9ec https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2 https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-24_linux64_openblas.conda#f5b8822297c9c790cec0795ca1fc9be6 -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.1-default_hb5137d0_0.conda#a5feadc4a296e2d31ab5a642498ff85e -https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.1-default_h9c6a7e4_0.conda#2e8992c584c2525a5b8ec7485cbe360c -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-24_linux64_openblas.conda#fd540578678aefe025705f4b58b36b2e -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-25_linux64_openblas.conda#5dbd1b0fc0d01ec5e0e1fbe667281a11 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.3-default_hb5137d0_0.conda#311e6a1d041db3d6a8a8437750d4234f +https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.3-default_h9c6a7e4_0.conda#b8a8cd77810b20754f358f2327812552 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-25_linux64_openblas.conda#4dc03a53fc69371a6158d0ed37214cd3 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.8-hedd0468_0.conda#dcd0ed5147d8876b0848a552b416ce76 -https://conda.anaconda.org/conda-forge/linux-64/pillow-10.4.0-py39h648eaa6_1.conda#d633f654c8f6ddc94a55473ba5361003 -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py39h538c539_0.conda#a2bafdf8ae51c9eb6e5be684cfcedd60 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda#7bbe9a0cc0df0ac5f5a8ad6d6a11af2f https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.4.5-pyhd8ed1ab_0.conda#67f4772681cf86652f3e2261794cf045 -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-24_linux64_openblas.conda#6db5d87ee60d6c7b5e64d18862a233d5 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_2.conda#c00807c15530f0cb373a89fd5ead6599 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-25_linux64_openblas.conda#8f5ead31b3a168aedd488b8a87736c41 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_4.conda#392cae2a58fbcb9db8c2147c6d6d1620 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 https://conda.anaconda.org/conda-forge/linux-64/numpy-2.0.2-py39h9cb892a_0.conda#ed28982e8b085c5d47361fc4af0902ac https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py39h08a7858_1.conda#cd9fa334e11886738f17254f52210bc3 -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-24_linux64_openblas.conda#4485873878da20ee1ce0f21d248b33d9 +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-25_linux64_openblas.conda#02c516384c77f5a7b4d03ed6c0412c57 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.0-py39h74842e3_2.conda#5645190ef7f6d3aebee71e298dc9677b https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py39h3b40f6f_1.conda#d07f482720066758dad87cf90b3de111 -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.7.3-h6e8976b_1.conda#f3234422a977b5d400ccf503ad55c5d1 +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.0-h6e8976b_0.conda#6d1c5d2d904d24c17cbb538a95855a4e https://conda.anaconda.org/conda-forge/linux-64/scipy-1.13.1-py39haf93ffa_0.conda#492a2cd65862d16a4aaf535ae9ccb761 https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_0.conda#6b55867f385dd762ed99ea687af32a69 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.124-openblas.conda#fec523f5e113812b956ec1adaec1212e +https://conda.anaconda.org/conda-forge/linux-64/blas-2.125-openblas.conda#0c46b8a31a587738befc587dd8e52558 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.9.2-py39h16632d1_1.conda#83d48ae12dfd01615013e2e8ace6ff86 https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py39hf59e57a_1.conda#720dbce3188cecd95fc26525394d1e65 -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.7.3-py39h0383914_1.conda#7177da0d3d26abfa3d11583ae89bf2a1 +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.0.2-py39h0383914_0.conda#b93573a620eb5396f0196e6267490738 https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.9.2-py39hf3d152e_1.conda#18df8fd10aeee04b1721c2efbf95c8cd https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.8.0-pyhd8ed1ab_0.conda#0a5522bdd3983c52102e75d1307ad8c4 diff --git a/build_tools/azure/ubuntu_atlas_lock.txt b/build_tools/azure/ubuntu_atlas_lock.txt index e2fe198c30915..6a79490ba6c66 100644 --- a/build_tools/azure/ubuntu_atlas_lock.txt +++ b/build_tools/azure/ubuntu_atlas_lock.txt @@ -14,9 +14,9 @@ iniconfig==2.0.0 # via pytest joblib==1.2.0 # via -r build_tools/azure/ubuntu_atlas_requirements.txt -meson==1.5.2 +meson==1.6.0 # via meson-python -meson-python==0.16.0 +meson-python==0.17.1 # via -r build_tools/azure/ubuntu_atlas_requirements.txt ninja==1.11.1.1 # via -r build_tools/azure/ubuntu_atlas_requirements.txt @@ -27,7 +27,7 @@ packaging==24.1 # pytest pluggy==1.5.0 # via pytest -pyproject-metadata==0.8.1 +pyproject-metadata==0.9.0 # via meson-python pytest==8.3.3 # via diff --git a/build_tools/circle/doc_linux-64_conda.lock b/build_tools/circle/doc_linux-64_conda.lock index 86dbeda548a6e..2484e59659927 100644 --- a/build_tools/circle/doc_linux-64_conda.lock +++ b/build_tools/circle/doc_linux-64_conda.lock @@ -8,31 +8,31 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab 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_17.conda#285931bd28b3b8f176d46dd9fd627a09 +https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda#ad8527bf134a90e1c9ed35fa0b64318c https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-5_cp39.conda#40363a30db350596b5f225d0d5a33328 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 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_1.conda#83e1364586ceb8d0739fbc85b5c95837 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda#048b02e3962f066da18efe3a21b77672 https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-13.3.0-h84ea5a7_101.conda#0ce69d40c142915ac9734bc6134e514a https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_1.conda#1ece2ccb1dc8c68639712b05e0fae070 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda#cc3573974587f12dda90d96e3e55a702 https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-13.3.0-h84ea5a7_101.conda#29b5a4ed4613fa81a07c21045e3f5bf6 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.1-h024ca30_0.conda#f1fe1a838fecddbcee97c9d4afe24af5 -https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h4a8ded7_17.conda#f58cb23983633068700a756f0b5f165a +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.3-h024ca30_0.conda#d36687dc90337917a84a96a45111ad59 +https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h4a8ded7_18.conda#0ea96f90a10838f58412aa84fdd9df09 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#562b26ba2e19059551a811e72ab7f793 -https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_1.conda#5f354010f194e85dc681dec92405ef9e +https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_2.conda#cf0c5521ac2a20dfa6c662a4009eeef6 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_1.conda#38a5cd3be5fb620b48069e27285f1a44 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_1.conda#e12057a66af8f2a38a839754ca4481e9 -https://conda.anaconda.org/conda-forge/linux-64/binutils-2.43-h4852527_1.conda#900e000d42b28bf0ac35b9451ec92bd9 -https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.43-h4852527_1.conda#8d70caec6e4c8754066ea278f0a282dd -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 +https://conda.anaconda.org/conda-forge/linux-64/binutils-2.43-h4852527_2.conda#348619f90eee04901f4a70615efff35b +https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.43-h4852527_2.conda#18aba879ddf1f8f28145ca6fcb873d8c +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda#3cb76c3f10d3bc7f1105b2fc9db984df 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.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.3-h5888daf_0.conda#59f4c43bb1b5ef1c71946ff2cbf59524 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.1.0-hc5f4f2c_1.conda#10a0cef64b784d6ab6da50ebca4e984d -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda#e39480b9ca41323497b05492a63bc35b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda#9822b874ea29af082e5d36098d25427d +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda#234a5554c53625688d51062645337328 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda#b3c17d95b5a10c6e64a21fa17573e70e @@ -50,7 +50,7 @@ https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz 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/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.1.0-h69a702a_1.conda#591e631bc1ae62c64f2ab4f66178c097 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda#f1fd30127802683586f768875127a987 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda#d66573916ffcf376178462f1b61c941e https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 @@ -58,15 +58,16 @@ https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.4-h7f98852_1002.tar.bz 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.44-hadc24fc_0.conda#f4cc49d7aa68316213e4b12be35308d1 https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-13.3.0-heb74ff8_1.conda#c4cb22f270f501f5c59a122dc2adf20a -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.0-hadc24fc_1.conda#b6f02b52a174e612e89548f4663ce56a +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda#8371ac6457591af2cf6159439c1fd051 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda#b26e8aa824079e1be0294e7152ca4559 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/mysql-common-9.0.1-h266115a_1.conda#e97f73d51b5acdf1340a15b195738f16 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_2.conda#85c0dc0bcd110c998b01856975486ee7 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.6.6-he8a937b_2.conda#77d9955b4abddb811cb8ab1aa7d743e4 +https://conda.anaconda.org/conda-forge/linux-64/svt-av1-2.3.0-h5888daf_0.conda#355898d24394b2af353eb96358db9fdd 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/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h5888daf_2.conda#e0409515c467b87176b070bff5d9442e @@ -84,7 +85,7 @@ https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76 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.123-hb9d3cd8_0.conda#ee605e794bdc14e2b7f84c4faa0d8c2c https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.1.0-h69a702a_1.conda#16cec94c5992d7f42ae3f9fa8b25df8d +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_1.conda#0a7f4cd238267c88e5d69f7826a407eb https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.1.0-h00ab1b0_0.conda#88928158ccfe797eac29ef5e03f7d23d 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/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 @@ -94,7 +95,6 @@ https://conda.anaconda.org/conda-forge/linux-64/pixman-0.43.2-h59595ed_0.conda#7 https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda#353823361b1d27eb3960efb076dfcaf6 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-ha2e4443_0.conda#6b7dcc7349efd123d493d2dbe85a045f -https://conda.anaconda.org/conda-forge/linux-64/svt-av1-2.2.1-h5888daf_0.conda#0d9c441855be3d8dfdb2e800fe755059 https://conda.anaconda.org/conda-forge/linux-64/wayland-1.23.1-h3e06ad9_0.conda#0a732427643ae5e0486a727927791da1 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-keysyms-0.4.1-hb711507_0.conda#ad748ccca349aec3e91743e08b5e2b50 @@ -106,20 +106,20 @@ https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d05 https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-hef167b5_0.conda#54fe76ab3d0189acaef95156874db7f9 https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.15.1-hc57e6cf_0.conda#5f84961d86d0ef78851cb34f9d5e31fe -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/gcc-13.3.0-h9576a4e_1.conda#606924335b5bcdf90e9aed9a2f5d22ed -https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-hc28eda2_4.conda#134bce313fe195be28a74597224441f1 +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-hc28eda2_5.conda#ffbadbbc3345d9a315ba31c8a9188d4c https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-13.3.0-h10434e7_1.conda#6709e113709b6ba67cc0f4b0de58ef7f https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hdbfa832_1.conda#806367e23a0a6ad21e51875b34c57d7e https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 -https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.1.1-h104a339_1.conda#9ef052c2eee74c792833ac2e820e481e -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.1-h2ff4ddf_0.conda#47a2209fa0df11797df0b767d1de1275 +https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.1.1-h1909e37_2.conda#21e468ed3786ebcb2124b123aa2484b7 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda#13e8e54035ddd2b91875ba399f0f7c04 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_1.conda#80a57756c545ad11f9847835aa21e6b2 -https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.0-hdb8da77_1.conda#2909a4fe277c73a515fa4b37f3633074 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.27-pthreads_hac2b453_1.conda#ae05ece66d3924ac3d48b4aa3fa96cec +https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.0-hdb8da77_2.conda#9c4554fafc94db681543804037e65de2 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.28-pthreads_h94d23a6_0.conda#9ebc9aedafaa2515ab247ff6bb509458 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda#63872517c98aa305da58a757c443698e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda#08a9265c637230c37cb1be4a6cad4536 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_1.conda#274f367df5d56f152a49ed3203c3b1c1 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.4-hb346dea_2.conda#69b90b70c434b916abf5a1d5ee5d55fb +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_2.conda#57a9e7ee3c0840d3c8c9012473978629 https://conda.anaconda.org/conda-forge/linux-64/python-3.9.20-h13acc7a_1_cpython.conda#951cff166a5f170e27908811917165f8 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.43-hb9d3cd8_0.conda#f725c7425d6d7c15e31f3b99a88ea02f @@ -129,7 +129,7 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.11-hb9d3cd8_ https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.16-pyhd8ed1ab_0.conda#def531a3ac77b7fb8c21d17bb5d0badb https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py39hf88036b_2.conda#8ea5af6ac902f1a4429190970d9099ce 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.8.0-h2b85faf_0.conda#1e7d93b16ce10cdc68228dde0844980b +https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.8.0-h2b85faf_1.conda#fa7b3bf2965b9d74a81a0702d9bb49ee https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-hebfffa5_3.conda#fceaedf1cdbcb02df9699a0d9b005292 https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_0.conda#a374efa97290b8799046df7c5ca17164 @@ -142,9 +142,9 @@ https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_0.conda https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0 https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda#15dda3cdbf330abfe9f555d22f66db46 https://conda.anaconda.org/conda-forge/linux-64/gfortran-13.3.0-h9576a4e_1.conda#5e5e3b592d5174eb49607a973c77825b -https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-hb919d3a_4.conda#403476a87e07f0973aa5a6d13253cd32 +https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-hb919d3a_5.conda#67dbd742855cc95233eb04c43004a29a https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_1.conda#209182ca6b20aeff62f442e843961d81 -https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_4.conda#9101274fec035244a7fb08d03b50c458 +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_5.conda#81ddb2db98fbe3031aa7ebbbf8bb3ffd https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71 https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4 https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda#7ba2ede0e7c795ff95088daf0dc59753 @@ -152,28 +152,28 @@ https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.b https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.7-py39h74842e3_0.conda#1bf77976372ff6de02af7b75cf034ce5 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda#51bb7010fc86f70eee639b4bb7a894f5 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-24_linux64_openblas.conda#80aea6603a6813b16ec119d00382b772 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-25_linux64_openblas.conda#8ea26d42ca88ec5258802715fe1ee10b https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_1.conda#204892bce2e44252b5cf272712f10bdd -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.1-ha7bfdaf_0.conda#000cd5fc23967c97284b720cc6049c1e +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.3-ha7bfdaf_0.conda#8bd654307c455162668cd66e36494000 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.7.0-h2c5496b_1.conda#e2eaefa4de2b7237af7c907b8bbc760a https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.1-py39h9399b63_1.conda#0782842622e8dc374909a8c39bafe9f3 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py39h9399b63_0.conda#d38773fed557834d3211e019b7cf7c2f 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.2.1-pyhd8ed1ab_0.conda#425fce3b531bed6ec3c74fab3e5f0a1c -https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.27-pthreads_h9eca1d5_1.conda#5633a1616bda33f8b815841eba4dbfb8 +https://conda.anaconda.org/conda-forge/linux-64/openblas-0.3.28-pthreads_hbcdf1e8_0.conda#edb742fa7cd3fc5fbcbfc24135dcadd8 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.2-h488ebb8_0.conda#7f2e286780f072ed750df46dc2631138 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.3.6-pyhd8ed1ab_0.conda#fd8f2b18b65bbf62e8f653100690c8d2 https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf -https://conda.anaconda.org/conda-forge/linux-64/psutil-6.0.0-py39h8cd3c5a_1.conda#45a3a1bbc95b90e35af5976c3d957c9f +https://conda.anaconda.org/conda-forge/linux-64/psutil-6.1.0-py39h8cd3c5a_0.conda#ef257b7ce1e1cb152639ced6bc653475 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088 https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda#035c17fbf099f50ff60bf2eb303b0a83 https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025 https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_0.conda#986287f89929b2d629bd6ef6497dc307 https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 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 @@ -184,7 +184,7 @@ https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.c https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py39h8cd3c5a_1.conda#48d269953fcddbbcde078429d4b27afe https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py39hd1e30aa_0.conda#1da984bbb6e765743e13388ba7b7b2c8 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py39h8cd3c5a_1.conda#6346898044e4387631c614290789a434 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 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 @@ -192,70 +192,70 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.2-hb9d3cd8_0 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.5-hb9d3cd8_3.conda#2159fc3619590b4f62473b6b9631549f +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.5-hb9d3cd8_4.conda#7da9007c0582712c4bad4131f89c8372 https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda#4daaed111c05672ae669f7036ee5bba3 https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_0.conda#1bb1ef9806a9a20872434f58b3e7fc1a https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py39h15c3d72_0.conda#7e61b8777f42e00b08ff059f9e8ebc44 -https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.8.0-h1a2810e_0.conda#36848c05490b8cb46221517ca12aa4bf -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py39h8cd3c5a_0.conda#a0987ca1f47be10976b5b53bc1f2db74 -https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.8.0-h36df796_0.conda#53932a433fcb479d509fc5eeff3c6d5d +https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.8.0-h1a2810e_1.conda#3bb4907086d7187bf01c8bec397ffa5e +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py39h9399b63_1.conda#1a4772f78ffa4675c84a4219db3934fd +https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.8.0-h36df796_1.conda#6b57750841d53ade8d3b47eafe53dd9f https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-9.0.0-hda332d3_1.conda#76b32dcf243444aea9c6b804bcfa40b8 https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda#54198435fce4d64d8a89af22573012a8 https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_0.conda#c808991d29b9838fb4d96ce8267ec9ec https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2 https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-24_linux64_openblas.conda#f5b8822297c9c790cec0795ca1fc9be6 -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.1-default_hb5137d0_0.conda#a5feadc4a296e2d31ab5a642498ff85e -https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.1-default_h9c6a7e4_0.conda#2e8992c584c2525a5b8ec7485cbe360c -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-24_linux64_openblas.conda#fd540578678aefe025705f4b58b36b2e +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-25_linux64_openblas.conda#5dbd1b0fc0d01ec5e0e1fbe667281a11 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.3-default_hb5137d0_0.conda#311e6a1d041db3d6a8a8437750d4234f +https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.3-default_h9c6a7e4_0.conda#b8a8cd77810b20754f358f2327812552 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-25_linux64_openblas.conda#4dc03a53fc69371a6158d0ed37214cd3 https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhd8ed1ab_0.tar.bz2#8b45f9f2b2f7a98b0ec179c8991a4a9b -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.8-hedd0468_0.conda#dcd0ed5147d8876b0848a552b416ce76 -https://conda.anaconda.org/conda-forge/linux-64/pillow-10.4.0-py39h648eaa6_1.conda#d633f654c8f6ddc94a55473ba5361003 -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py39h538c539_0.conda#a2bafdf8ae51c9eb6e5be684cfcedd60 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 https://conda.anaconda.org/conda-forge/noarch/plotly-5.24.1-pyhd8ed1ab_0.conda#81bb643d6c3ab4cbeaf724e9d68d0a6a -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c 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/compilers-1.8.0-ha770c72_0.conda#e08e569c1b7e923654d1fe9e76dadb3d +https://conda.anaconda.org/conda-forge/linux-64/compilers-1.8.0-ha770c72_1.conda#061e111d02f33a99548f0de07169d9fb https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.4.5-pyhd8ed1ab_0.conda#67f4772681cf86652f3e2261794cf045 https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.4-pyhd8ed1ab_1.conda#4809b9f4c6ce106d443c3f90b8e10db2 -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-24_linux64_openblas.conda#6db5d87ee60d6c7b5e64d18862a233d5 -https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_2.conda#c00807c15530f0cb373a89fd5ead6599 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-25_linux64_openblas.conda#8f5ead31b3a168aedd488b8a87736c41 +https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_4.conda#392cae2a58fbcb9db8c2147c6d6d1620 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 https://conda.anaconda.org/conda-forge/linux-64/numpy-2.0.2-py39h9cb892a_0.conda#ed28982e8b085c5d47361fc4af0902ac https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py39h08a7858_1.conda#cd9fa334e11886738f17254f52210bc3 -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-24_linux64_openblas.conda#4485873878da20ee1ce0f21d248b33d9 +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-25_linux64_openblas.conda#02c516384c77f5a7b4d03ed6c0412c57 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.0-py39h74842e3_2.conda#5645190ef7f6d3aebee71e298dc9677b -https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2024.6.1-py39h1a335b5_5.conda#a9ea5cdabda4124f6b45d43750a0c8fb -https://conda.anaconda.org/conda-forge/noarch/imageio-2.35.1-pyh12aca89_0.conda#b03ff3631329c8ef17bae35d2bb216f7 +https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2024.9.22-py39h1aa77c4_0.conda#6001ae3f85403137d61e3ef7e96dd940 +https://conda.anaconda.org/conda-forge/noarch/imageio-2.36.0-pyh12aca89_1.conda#36349844ff73fcd0140ee7f30745f0bf https://conda.anaconda.org/conda-forge/noarch/lazy_loader-0.4-pyhd8ed1ab_1.conda#ec6f70b8a5242936567d4f886726a372 https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py39h3b40f6f_1.conda#d07f482720066758dad87cf90b3de111 https://conda.anaconda.org/conda-forge/noarch/patsy-0.5.6-pyhd8ed1ab_0.conda#a5b55d1cb110cdcedc748b5c3e16e687 -https://conda.anaconda.org/conda-forge/linux-64/polars-1.9.0-py39h74f158a_0.conda#e507477c6f586b2c1aec3cffbd5d5765 +https://conda.anaconda.org/conda-forge/linux-64/polars-1.12.0-py39h74f158a_0.conda#698f8f845bcb227d52695b4ab6f7c381 https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.6.0-py39hd92a3bb_0.conda#32e26e16f60c568b17a82e3033a4d309 -https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.7.3-h6e8976b_1.conda#f3234422a977b5d400ccf503ad55c5d1 +https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.0-h6e8976b_0.conda#6d1c5d2d904d24c17cbb538a95855a4e https://conda.anaconda.org/conda-forge/linux-64/scipy-1.13.1-py39haf93ffa_0.conda#492a2cd65862d16a4aaf535ae9ccb761 https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_0.conda#6b55867f385dd762ed99ea687af32a69 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.124-openblas.conda#fec523f5e113812b956ec1adaec1212e +https://conda.anaconda.org/conda-forge/linux-64/blas-2.125-openblas.conda#0c46b8a31a587738befc587dd8e52558 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.9.2-py39h16632d1_1.conda#83d48ae12dfd01615013e2e8ace6ff86 https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py39hf59e57a_1.conda#720dbce3188cecd95fc26525394d1e65 -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.7.3-py39h0383914_1.conda#7177da0d3d26abfa3d11583ae89bf2a1 +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.0.2-py39h0383914_0.conda#b93573a620eb5396f0196e6267490738 https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52 https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.4-py39hf3d9206_0.conda#f633ed7c19e120b9e6c0efb79f20a53f https://conda.anaconda.org/conda-forge/noarch/tifffile-2024.6.18-pyhd8ed1ab_0.conda#7c3077529bfe3b86f9425d526d73bd24 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.9.2-py39hf3d152e_1.conda#18df8fd10aeee04b1721c2efbf95c8cd https://conda.anaconda.org/conda-forge/noarch/pooch-1.8.2-pyhd8ed1ab_0.conda#8dab97d8a9616e07d779782995710aed -https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.24.0-py39h5114956_2.conda#3d33123e655e3279a7aa39ec974612f5 +https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.24.0-py39h3b40f6f_3.conda#63666cfacc4dc32c8b2ff49705988f92 https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_2.conda#b713b116feaf98acdba93ad4d7f90ca1 https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_2.conda#a79d8797f62715255308d92d3a91ef2e https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.8.0-pyhd8ed1ab_0.conda#0a5522bdd3983c52102e75d1307ad8c4 -https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.15.4-pyhd8ed1ab_0.conda#c7c50dd5192caa58a05e6a4248a27acb +https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.16.0-pyhd8ed1ab_0.conda#344261b0e77f5d2faaffb4eac225eeb7 https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_0.conda#ac832cc43adc79118cf6e23f1f9b8995 https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.6.1-pyhd8ed1ab_1.conda#db0f1eb28b6df3a11e89437597309009 https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.18.0-pyhd8ed1ab_0.conda#dc78276cbf5ec23e4b959d1bbd9caadb @@ -286,7 +286,7 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxext-opengraph-0.9.1-pyhd8ed1 # pip python-json-logger @ https://files.pythonhosted.org/packages/35/a6/145655273568ee78a581e734cf35beb9e33a370b29c5d3c8fee3744de29f/python_json_logger-2.0.7-py3-none-any.whl#sha256=f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd # pip pyyaml @ https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 # 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/04/d8/e73d56b1908a6c0e3e5982365eb293170cd458cc25a19363f69c76e00fd2/rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda +# pip rpds-py @ https://files.pythonhosted.org/packages/d4/62/c9bd294c4b5f84d9cc2c387b548ae53096ad7e71ac5b02b6310e9dc85aa4/rpds_py-0.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl#sha256=e75ba609dba23f2c95b776efb9dd3f0b78a76a151e96f96cc5b6b1b0004de66f # 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 @@ -295,10 +295,10 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxext-opengraph-0.9.1-pyhd8ed1 # pip webcolors @ https://files.pythonhosted.org/packages/f0/33/12020ba99beaff91682b28dc0bbf0345bbc3244a4afbae7644e4fa348f23/webcolors-24.8.0-py3-none-any.whl#sha256=fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a # 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/3e/dc/a27d58194ddcbeb295500cc6bf233d4dfb34a95a10ca5dbe4ff8454399e4/anyio-4.6.2-py3-none-any.whl#sha256=6caec6b1391f6f6d7b2ef2258d2902d36753149f67478f7df4be8e54d03a8f54 +# pip anyio @ https://files.pythonhosted.org/packages/e4/f5/f2b75d2fc6f1a260f340f0e7c6a060f4dd2961cc16884ed851b0d18da06a/anyio-4.6.2.post1-py3-none-any.whl#sha256=6d170c36fba3bdd840c73d3868c1e777e33676a69c3a72cf0a0d5d6d8009b61d # 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 bleach @ https://files.pythonhosted.org/packages/ea/63/da7237f805089ecc28a3f36bca6a21c31fcbc2eb380f3b8f1be3312abd14/bleach-6.1.0-py3-none-any.whl#sha256=3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6 +# pip bleach @ https://files.pythonhosted.org/packages/fc/55/96142937f66150805c25c4d0f31ee4132fd33497753400734f9dfdcbdc66/bleach-6.2.0-py3-none-any.whl#sha256=117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e # 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 pyzmq @ https://files.pythonhosted.org/packages/6e/bd/3ff3e1172f12f55769793a3a334e956ec2886805ebfb2f64756b6b5c6a1a/pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl#sha256=05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9 @@ -306,15 +306,15 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxext-opengraph-0.9.1-pyhd8ed1 # 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/2e/87/7c2eb08e3ca1d6baae32c0a5e005330fe1cec93a36aa085e714c3b3a3c7d/sphinxcontrib_sass-0.3.4-py2.py3-none-any.whl#sha256=a0c79a44ae8b8935c02dc340ebe40c9e002c839331201c899dc93708970c355a # 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/2c/4d/0db5b8a613d2a59bbc29bc5bb44a2f8070eb9ceab11c50d477502a8a0092/tinycss2-1.3.0-py3-none-any.whl#sha256=54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7 +# 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 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/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl#sha256=a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf # 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/0e/ba/f7157aed7341d01121ddbce0d4f8d3875aeb688d6cc5514af6f24f981aca/jupyterlite_core-0.4.2-py3-none-any.whl#sha256=1f2e147f4c6d87de823573a9e13e45de259149f40d039b2a4b9699c7454e3c04 +# pip jupyterlite-core @ https://files.pythonhosted.org/packages/3a/d9/ca90f3136565863ae3ddc445a38c965124655010b0102c409cbd31151161/jupyterlite_core-0.4.3-py3-none-any.whl#sha256=1922530b04196c985b69cfdf94654c64ca55598cd69b4214442579fef51c9877 # 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/9a/38/8d94eb15014a8c1107128b8bfb88101f28b39628eee5cdc2daacbe92b82e/jupyterlite_pyodide_kernel-0.4.2-py3-none-any.whl#sha256=d78fd12f1ac08eb98c55b476275b53e7d011fb46a01c631ed182da3f00d5895a +# pip jupyterlite-pyodide-kernel @ https://files.pythonhosted.org/packages/ea/f1/bd65f1fe3b9535f5aa00d89ed2b2bf3cf4cff39273a3e7dac97e890141cd/jupyterlite_pyodide_kernel-0.4.3-py3-none-any.whl#sha256=88ddfddb2c17d71db0180c1a5b335213bd2fd1d8a964b84c3b69dda1f949dfad # pip jupyter-events @ https://files.pythonhosted.org/packages/a5/94/059180ea70a9a326e1815176b2370da56376da347a796f8c4f0b830208ef/jupyter_events-0.10.0-py3-none-any.whl#sha256=4b72130875e59d57716d327ea70d3ebc3af1944d3717e5a498b8a06c6c159960 # pip nbformat @ https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl#sha256=3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b # pip nbclient @ https://files.pythonhosted.org/packages/66/e8/00517a23d3eeaed0513e718fbc94aab26eaa1758f5690fc8578839791c79/nbclient-0.10.0-py3-none-any.whl#sha256=f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f 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 4e550b3e09683..0e0a5f9a82f19 100644 --- a/build_tools/circle/doc_min_dependencies_linux-64_conda.lock +++ b/build_tools/circle/doc_min_dependencies_linux-64_conda.lock @@ -8,38 +8,38 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab 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_17.conda#285931bd28b3b8f176d46dd9fd627a09 -https://conda.anaconda.org/conda-forge/linux-64/mkl-include-2024.1.0-ha957f24_693.conda#249c91c2186d236c6d180342241db2ec +https://conda.anaconda.org/conda-forge/noarch/kernel-headers_linux-64-3.10.0-he073ed8_18.conda#ad8527bf134a90e1c9ed35fa0b64318c +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/python_abi-3.9-5_cp39.conda#40363a30db350596b5f225d0d5a33328 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 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_1.conda#83e1364586ceb8d0739fbc85b5c95837 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda#048b02e3962f066da18efe3a21b77672 https://conda.anaconda.org/conda-forge/noarch/libgcc-devel_linux-64-13.3.0-h84ea5a7_101.conda#0ce69d40c142915ac9734bc6134e514a https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_1.conda#1ece2ccb1dc8c68639712b05e0fae070 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.1.0-h77fa898_1.conda#23c255b008c4f2ae008f81edcabaca89 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-14.2.0-h77fa898_1.conda#cc3573974587f12dda90d96e3e55a702 https://conda.anaconda.org/conda-forge/noarch/libstdcxx-devel_linux-64-13.3.0-h84ea5a7_101.conda#29b5a4ed4613fa81a07c21045e3f5bf6 -https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.1-h024ca30_0.conda#f1fe1a838fecddbcee97c9d4afe24af5 -https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h4a8ded7_17.conda#f58cb23983633068700a756f0b5f165a +https://conda.anaconda.org/conda-forge/linux-64/llvm-openmp-19.1.3-h024ca30_0.conda#d36687dc90337917a84a96a45111ad59 +https://conda.anaconda.org/conda-forge/noarch/sysroot_linux-64-2.17-h4a8ded7_18.conda#0ea96f90a10838f58412aa84fdd9df09 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#562b26ba2e19059551a811e72ab7f793 -https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_1.conda#5f354010f194e85dc681dec92405ef9e +https://conda.anaconda.org/conda-forge/linux-64/binutils_impl_linux-64-2.43-h4bf12b8_2.conda#cf0c5521ac2a20dfa6c662a4009eeef6 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_1.conda#38a5cd3be5fb620b48069e27285f1a44 -https://conda.anaconda.org/conda-forge/linux-64/binutils-2.43-h4852527_1.conda#900e000d42b28bf0ac35b9451ec92bd9 -https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.43-h4852527_1.conda#8d70caec6e4c8754066ea278f0a282dd -https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.1.0-h77fa898_1.conda#002ef4463dd1e2b44a94a4ace468f5d2 +https://conda.anaconda.org/conda-forge/linux-64/binutils-2.43-h4852527_2.conda#348619f90eee04901f4a70615efff35b +https://conda.anaconda.org/conda-forge/linux-64/binutils_linux-64-2.43-h4852527_2.conda#18aba879ddf1f8f28145ca6fcb873d8c +https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda#3cb76c3f10d3bc7f1105b2fc9db984df 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.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.6.3-h5888daf_0.conda#59f4c43bb1b5ef1c71946ff2cbf59524 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.1.0-h69a702a_1.conda#1efc0ad219877a73ef977af7dbb51f17 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.1.0-hc5f4f2c_1.conda#10a0cef64b784d6ab6da50ebca4e984d -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.1.0-hc0a3c3a_1.conda#9dbb9699ea467983ba8a4ba89b08b066 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-14.2.0-h69a702a_1.conda#e39480b9ca41323497b05492a63bc35b +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-14.2.0-hd5240d6_1.conda#9822b874ea29af082e5d36098d25427d +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-14.2.0-hc0a3c3a_1.conda#234a5554c53625688d51062645337328 https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda#edb0dca6bc32e4f4789199455a1dbeb8 https://conda.anaconda.org/conda-forge/linux-64/openssl-3.3.2-hb9d3cd8_0.conda#4d638782050ab6faa27275bed57e9b4e 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.1-hb9d3cd8_1.conda#19608a9656912805b2b9a2f6bd257b04 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.11-hb9d3cd8_1.conda#77cbc488235ebbaab2b6e912d3934bae 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-xf86vidmodeproto-2.3.1-hb9d3cd8_1003.conda#139e6c4010a04f20897b5d655470bfec +https://conda.anaconda.org/conda-forge/linux-64/xorg-xf86vidmodeproto-2.3.1-hb9d3cd8_1004.conda#24831329718daa6cbe35fcd071b778d4 https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.12-h4ab18f5_0.conda#7ed427f0871fd41cb1d9c17727c17589 https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00 @@ -56,7 +56,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.1.0-hb9d3cd8_2.co https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda#a1cfcc585f0c42bf8d5546bb1dfb668d https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-0.22.5-he02047a_3.conda#efab66b82ec976930b96d62a976de8e7 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.1.0-h69a702a_1.conda#591e631bc1ae62c64f2ab4f66178c097 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-14.2.0-h69a702a_1.conda#f1fd30127802683586f768875127a987 https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-hd590300_2.conda#d66573916ffcf376178462f1b61c941e https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.0.0-hd590300_1.conda#ea25936bb4080d843790b586850f82b8 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30fd6e37fe21f86f4bd26d6ee73eeec7 @@ -65,15 +65,18 @@ https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2 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.44-hadc24fc_0.conda#f4cc49d7aa68316213e4b12be35308d1 https://conda.anaconda.org/conda-forge/linux-64/libsanitizer-13.3.0-heb74ff8_1.conda#c4cb22f270f501f5c59a122dc2adf20a -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.1.0-h4852527_1.conda#bd2598399a70bb86d8218e95548d735e +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.0-hadc24fc_1.conda#b6f02b52a174e612e89548f4663ce56a +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda#8371ac6457591af2cf6159439c1fd051 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.38.1-h0b41bf4_0.conda#40b61aab5c7ba9ff276c41cfffe6b80b https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.conda#b26e8aa824079e1be0294e7152ca4559 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/mysql-common-9.0.1-h266115a_1.conda#e97f73d51b5acdf1340a15b195738f16 +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_2.conda#85c0dc0bcd110c998b01856975486ee7 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.36-h5888daf_0.conda#de9cd5bca9e4918527b9b72b6e2e1409 https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.6.6-he8a937b_2.conda#77d9955b4abddb811cb8ab1aa7d743e4 +https://conda.anaconda.org/conda-forge/linux-64/svt-av1-2.3.0-h5888daf_0.conda#355898d24394b2af353eb96358db9fdd 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/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae @@ -94,19 +97,17 @@ https://conda.anaconda.org/conda-forge/linux-64/libcap-2.69-h0f662aa_0.conda#25c https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.123-hb9d3cd8_0.conda#ee605e794bdc14e2b7f84c4faa0d8c2c https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 https://conda.anaconda.org/conda-forge/linux-64/libgettextpo-devel-0.22.5-he02047a_3.conda#9aba7960731e6b4547b3a52f812ed801 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.1.0-h69a702a_1.conda#16cec94c5992d7f42ae3f9fa8b25df8d +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_1.conda#0a7f4cd238267c88e5d69f7826a407eb https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.1.0-h00ab1b0_0.conda#88928158ccfe797eac29ef5e03f7d23d 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/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 -https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.32.6-h59595ed_0.conda#9160cdeb523a1b20cf8d2a0bf821f45d https://conda.anaconda.org/conda-forge/linux-64/ninja-1.12.1-h297d8ca_0.conda#3aa1c7e292afeff25a0091ddd7c69b72 -https://conda.anaconda.org/conda-forge/linux-64/nspr-4.35-h27087fc_0.conda#da0ec11a6454ae19bff5b02ed881a2b1 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.106-hdf54f9c_0.conda#efe735c7dc47dddbb14b3433d11c6feb https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.44-hba22ea6_2.conda#df359c09c41cd186fffb93a2d87aa6f5 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.43.2-h59595ed_0.conda#71004cbf7924e19c02746ccde9fd7123 https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8228510_1.conda#47d31b792659ce70f470b5c82fdfb7a4 https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.1-ha2e4443_0.conda#6b7dcc7349efd123d493d2dbe85a045f -https://conda.anaconda.org/conda-forge/linux-64/svt-av1-2.2.1-h5888daf_0.conda#0d9c441855be3d8dfdb2e800fe755059 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-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 @@ -117,36 +118,36 @@ https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d05 https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-hef167b5_0.conda#54fe76ab3d0189acaef95156874db7f9 https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.15.1-hc57e6cf_0.conda#5f84961d86d0ef78851cb34f9d5e31fe -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/gcc-13.3.0-h9576a4e_1.conda#606924335b5bcdf90e9aed9a2f5d22ed -https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-hc28eda2_4.conda#134bce313fe195be28a74597224441f1 +https://conda.anaconda.org/conda-forge/linux-64/gcc_linux-64-13.3.0-hc28eda2_5.conda#ffbadbbc3345d9a315ba31c8a9188d4c https://conda.anaconda.org/conda-forge/linux-64/gfortran_impl_linux-64-13.3.0-h10434e7_1.conda#6709e113709b6ba67cc0f4b0de58ef7f https://conda.anaconda.org/conda-forge/linux-64/gxx_impl_linux-64-13.3.0-hdbfa832_1.conda#806367e23a0a6ad21e51875b34c57d7e 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.22.5-he8f35ee_3.conda#1091193789bb830127ed067a9e01ac57 -https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.1.1-h104a339_1.conda#9ef052c2eee74c792833ac2e820e481e -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.1-h2ff4ddf_0.conda#47a2209fa0df11797df0b767d1de1275 +https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.1.1-h1909e37_2.conda#21e468ed3786ebcb2124b123aa2484b7 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda#13e8e54035ddd2b91875ba399f0f7c04 https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_1.conda#80a57756c545ad11f9847835aa21e6b2 -https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.0-hdb8da77_1.conda#2909a4fe277c73a515fa4b37f3633074 +https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.0-hdb8da77_2.conda#9c4554fafc94db681543804037e65de2 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.0-he137b08_1.conda#63872517c98aa305da58a757c443698e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda#08a9265c637230c37cb1be4a6cad4536 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_1.conda#274f367df5d56f152a49ed3203c3b1c1 -https://conda.anaconda.org/conda-forge/linux-64/nss-3.105-hd34e28f_0.conda#28d7602527b76052422aaf5d6fd7ad81 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.4-hb346dea_2.conda#69b90b70c434b916abf5a1d5ee5d55fb +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_2.conda#57a9e7ee3c0840d3c8c9012473978629 https://conda.anaconda.org/conda-forge/linux-64/python-3.9.20-h13acc7a_1_cpython.conda#951cff166a5f170e27908811917165f8 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.43-hb9d3cd8_0.conda#f725c7425d6d7c15e31f3b99a88ea02f 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.11-hb9d3cd8_1.conda#a7a49a8b85122b49214798321e2e96b4 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-pyh9f0ad1d_0.tar.bz2#5f095bc6454094e96f146491fd03633b https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.1.0-py39hf88036b_2.conda#8ea5af6ac902f1a4429190970d9099ce 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.8.0-h2b85faf_0.conda#1e7d93b16ce10cdc68228dde0844980b +https://conda.anaconda.org/conda-forge/linux-64/c-compiler-1.8.0-h2b85faf_1.conda#fa7b3bf2965b9d74a81a0702d9bb49ee https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-hebfffa5_3.conda#fceaedf1cdbcb02df9699a0d9b005292 https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.0-pyhd8ed1ab_0.conda#a374efa97290b8799046df7c5ca17164 https://conda.anaconda.org/conda-forge/noarch/click-8.1.7-unix_pyh707e725_0.conda#f3ad426304898027fc619827ff428eca -https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.0.0-pyhd8ed1ab_0.conda#753d29fe41bb881e4b9c004f0abf973f +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-3.1.0-pyhd8ed1ab_1.conda#c88ca2bb7099167912e3b26463fff079 https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99 https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhd8ed1ab_0.conda#5cd86562580f274031ede6aa6aa24441 https://conda.anaconda.org/conda-forge/linux-64/cython-3.0.10-py39h3d6467e_0.conda#76b5d215fb735a6dc43010ffbe78040e @@ -154,13 +155,13 @@ https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#e https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_0.conda#e8cd5d629f65bdf0f3bb312cde14659e https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.2.2-pyhd8ed1ab_0.conda#d02ae936e42063ca46af6cdad2dbd1e0 https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda#15dda3cdbf330abfe9f555d22f66db46 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.9.0-pyhff2d567_0.conda#ace4329fbff4c69ab0309db6da182987 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2024.10.0-pyhff2d567_0.conda#816dbc4679a64e4417cd1385d661bb31 https://conda.anaconda.org/conda-forge/linux-64/gettext-0.22.5-he02047a_3.conda#c7f243bbaea97cd6ea1edd693270100e https://conda.anaconda.org/conda-forge/linux-64/gfortran-13.3.0-h9576a4e_1.conda#5e5e3b592d5174eb49607a973c77825b -https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-hb919d3a_4.conda#403476a87e07f0973aa5a6d13253cd32 -https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.82.1-h4833e2c_0.conda#0677a6c26a59b568599846b829ec5c8c +https://conda.anaconda.org/conda-forge/linux-64/gfortran_linux-64-13.3.0-hb919d3a_5.conda#67dbd742855cc95233eb04c43004a29a +https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.82.2-h4833e2c_0.conda#12859f91830f58b1803e32846651c6f6 https://conda.anaconda.org/conda-forge/linux-64/gxx-13.3.0-h9576a4e_1.conda#209182ca6b20aeff62f442e843961d81 -https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_4.conda#9101274fec035244a7fb08d03b50c458 +https://conda.anaconda.org/conda-forge/linux-64/gxx_linux-64-13.3.0-h6834431_5.conda#81ddb2db98fbe3031aa7ebbbf8bb3ffd https://conda.anaconda.org/conda-forge/noarch/hpack-4.0.0-pyh9f0ad1d_0.tar.bz2#914d6646c4dbb1fd3ff539830a12fd71 https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.0.1-pyhd8ed1ab_0.tar.bz2#9f765cbfab6870c8435b9eefecd7a1f4 https://conda.anaconda.org/conda-forge/noarch/idna-3.10-pyhd8ed1ab_0.conda#7ba2ede0e7c795ff95088daf0dc59753 @@ -171,18 +172,17 @@ https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.16-hb7c19ff_0.conda#51bb https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d4529f4dff3057982a7617c7ac58fde3 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_1.conda#204892bce2e44252b5cf272712f10bdd https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.1-default_hecaa2ac_1000.conda#f54aeebefb5c5ff84eca4fb05ca8aa3a -https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.7-hb3ce162_4.conda#8a35df3cbc0c8b12cc8af9473ae75eef -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.1-ha7bfdaf_0.conda#000cd5fc23967c97284b720cc6049c1e -https://conda.anaconda.org/conda-forge/linux-64/libpq-16.4-h2d7952a_2.conda#76c891962472b55544b51c52bae15587 +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.3-ha7bfdaf_0.conda#8bd654307c455162668cd66e36494000 +https://conda.anaconda.org/conda-forge/linux-64/libpq-16.4-h2d7952a_3.conda#50e2dddb3417a419cbc2388d0b1c06f7 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.7.0-h2c5496b_1.conda#e2eaefa4de2b7237af7c907b8bbc760a 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.1-py39h9399b63_1.conda#0782842622e8dc374909a8c39bafe9f3 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py39h9399b63_0.conda#d38773fed557834d3211e019b7cf7c2f 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.2-h488ebb8_0.conda#7f2e286780f072ed750df46dc2631138 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf https://conda.anaconda.org/conda-forge/noarch/ply-3.11-pyhd8ed1ab_2.conda#18c6deb6f9602e32446398203c8f0e91 -https://conda.anaconda.org/conda-forge/linux-64/psutil-6.0.0-py39h8cd3c5a_1.conda#45a3a1bbc95b90e35af5976c3d957c9f +https://conda.anaconda.org/conda-forge/linux-64/psutil-6.1.0-py39h8cd3c5a_0.conda#ef257b7ce1e1cb152639ced6bc653475 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyhd8ed1ab_0.conda#844d9eb3b43095b031874477f7d70088 https://conda.anaconda.org/conda-forge/noarch/pygments-2.18.0-pyhd8ed1ab_0.conda#b7f5c092b8f9800150d998a71b76d5a1 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda#035c17fbf099f50ff60bf2eb303b0a83 @@ -202,63 +202,64 @@ https://conda.anaconda.org/conda-forge/noarch/toolz-1.0.0-pyhd8ed1ab_0.conda#34f https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py39h8cd3c5a_1.conda#48d269953fcddbbcde078429d4b27afe https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.5-hb9d3cd8_3.conda#2159fc3619590b4f62473b6b9631549f +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.5-hb9d3cd8_4.conda#7da9007c0582712c4bad4131f89c8372 https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda#4daaed111c05672ae669f7036ee5bba3 https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_0.conda#1bb1ef9806a9a20872434f58b3e7fc1a https://conda.anaconda.org/conda-forge/noarch/babel-2.14.0-pyhd8ed1ab_0.conda#9669586875baeced8fc30c0826c3270e https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.12.3-pyha770c72_0.conda#332493000404d8411859539a5a630865 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.17.1-py39h15c3d72_0.conda#7e61b8777f42e00b08ff059f9e8ebc44 -https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.8.0-h1a2810e_0.conda#36848c05490b8cb46221517ca12aa4bf +https://conda.anaconda.org/conda-forge/linux-64/cxx-compiler-1.8.0-h1a2810e_1.conda#3bb4907086d7187bf01c8bec397ffa5e https://conda.anaconda.org/conda-forge/linux-64/cytoolz-1.0.0-py39h8cd3c5a_1.conda#7a98e8be85fb0ce5531cac253ca95497 -https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.8.0-h36df796_0.conda#53932a433fcb479d509fc5eeff3c6d5d -https://conda.anaconda.org/conda-forge/linux-64/glib-2.82.1-h2ff4ddf_0.conda#12e1763ee9bf6685025981239ffaf482 +https://conda.anaconda.org/conda-forge/linux-64/fortran-compiler-1.8.0-h36df796_1.conda#6b57750841d53ade8d3b47eafe53dd9f +https://conda.anaconda.org/conda-forge/linux-64/glib-2.82.2-h44428e9_0.conda#f19f985ab043e8843045410f3b99de8a https://conda.anaconda.org/conda-forge/noarch/h2-4.1.0-pyhd8ed1ab_0.tar.bz2#b748fbf7060927a6e82df7cb5ee8f097 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-9.0.0-hda332d3_1.conda#76b32dcf243444aea9c6b804bcfa40b8 https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.5.0-pyha770c72_0.conda#54198435fce4d64d8a89af22573012a8 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2 https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp15-15.0.7-default_h127d8a8_5.conda#d0a9633b53cdc319b8a1a532ae7822b8 -https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.1-default_h9c6a7e4_0.conda#2e8992c584c2525a5b8ec7485cbe360c +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.3-default_hb5137d0_0.conda#311e6a1d041db3d6a8a8437750d4234f +https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.3-default_h9c6a7e4_0.conda#b8a8cd77810b20754f358f2327812552 https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.3-h59595ed_0.conda#ee48bf17cc83a00f59ca1494d5646869 https://conda.anaconda.org/conda-forge/linux-64/libgpg-error-1.50-h4f305b6_0.conda#0d7ff1a8e69565ca3add6925e18e708f https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhd8ed1ab_0.tar.bz2#8b45f9f2b2f7a98b0ec179c8991a4a9b -https://conda.anaconda.org/conda-forge/noarch/meson-1.5.2-pyhd8ed1ab_0.conda#9e677e9cfb20529c3db797105cca1cf9 +https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda#0badf9c54e24cecfb0ad2f99d680c163 -https://conda.anaconda.org/conda-forge/linux-64/pillow-10.4.0-py39h648eaa6_1.conda#d633f654c8f6ddc94a55473ba5361003 -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 +https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py39h538c539_0.conda#a2bafdf8ae51c9eb6e5be684cfcedd60 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 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.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.12-py39h3d6467e_0.conda#e667a3ab0df62c54e60e1843d2e6defb https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.13.0-h84d6215_0.conda#ee6f7fd1e76061ef1fa307d41fa86a96 -https://conda.anaconda.org/conda-forge/linux-64/compilers-1.8.0-ha770c72_0.conda#e08e569c1b7e923654d1fe9e76dadb3d +https://conda.anaconda.org/conda-forge/linux-64/compilers-1.8.0-ha770c72_1.conda#061e111d02f33a99548f0de07169d9fb https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.24.7-hf3bb09a_0.conda#c78bc4ef0afb3cd2365d9973c71fc876 https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-8.5.0-hd8ed1ab_0.conda#2a92e152208121afadf85a5e1f3a5f4d https://conda.anaconda.org/conda-forge/linux-64/libgcrypt-1.11.0-h4ab18f5_1.conda#14858a47d4cc995892e79f2b340682d7 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/mkl-2024.1.0-ha957f24_693.conda#ff0f4abf6f94e36a918f1ef4dbeb9769 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 +https://conda.anaconda.org/conda-forge/linux-64/mkl-2024.2.2-ha957f24_16.conda#1459379c79dda834673426504d52b319 https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.12.2-py39h3d6467e_5.conda#93aff412f3e49fdb43361c0215cbd72d https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 https://conda.anaconda.org/conda-forge/linux-64/zstandard-0.23.0-py39h08a7858_1.conda#cd9fa334e11886738f17254f52210bc3 https://conda.anaconda.org/conda-forge/noarch/dask-core-2024.8.0-pyhd8ed1ab_0.conda#bf68bf9ff9a18f1b17aa8c817225aee0 https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.24.7-h0a52356_0.conda#d368425fbd031a2f8e801a40c3415c72 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-24_linux64_mkl.conda#e8b11b7c5880eabf24e009e67cbe2b13 -https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.7-h2774228_0.conda#cdf7c26c2f9cc1e4ac01d57b03a85323 -https://conda.anaconda.org/conda-forge/linux-64/mkl-devel-2024.1.0-ha770c72_693.conda#7f422e2cf549a3fb920c95288393870d +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-25_linux64_mkl.conda#b77ebfb548eae4d91639e2ca003662c8 +https://conda.anaconda.org/conda-forge/linux-64/libsystemd0-256.7-h2774228_1.conda#ad328c530a12a8798776e5f03942090f +https://conda.anaconda.org/conda-forge/linux-64/mkl-devel-2024.2.2-ha770c72_16.conda#140891ea14285fc634353b31e9e40a95 https://conda.anaconda.org/conda-forge/noarch/urllib3-2.2.3-pyhd8ed1ab_0.conda#6b55867f385dd762ed99ea687af32a69 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-24_linux64_mkl.conda#30dc2e4d803e321e03a08883fa5211c3 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-24_linux64_mkl.conda#e582054f04bc172d79a44555263e4ee4 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-25_linux64_mkl.conda#e48aeb4ab1a293f621fe995959f1d32f +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-25_linux64_mkl.conda#d5afbe3777c594434e4de6481254e99c https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-client-17.0-hb77b528_0.conda#07f45f1be1c25345faddb8db0de8039b https://conda.anaconda.org/conda-forge/noarch/requests-2.32.3-pyhd8ed1ab_0.conda#5ede4753180c7a550a443c430dc8ab52 -https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-24_linux64_mkl.conda#1b9c5f340c4f56c77257fd59b88acb51 +https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-25_linux64_mkl.conda#cbddb4169d3d24b13b308403b45f401e https://conda.anaconda.org/conda-forge/linux-64/numpy-1.19.5-py39hd249d9e_3.tar.bz2#0cf333996ebdeeba8d1c8c1c0ee9eff9 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.8-h3155989_26.conda#0b133022b9d6317733bfee559b6433c9 -https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-24_linux64_mkl.conda#e19a2c62b6aa1f88365a9d9400bf07ce -https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2024.6.1-py39h1a335b5_5.conda#a9ea5cdabda4124f6b45d43750a0c8fb -https://conda.anaconda.org/conda-forge/noarch/imageio-2.35.1-pyh12aca89_0.conda#b03ff3631329c8ef17bae35d2bb216f7 +https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.15-h374914d_0.conda#26e8b00e73c114c9b787d36edcbf4424 +https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-25_linux64_mkl.conda#cb60caae3cb30988431d7107691bd587 +https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2024.9.22-py39h1aa77c4_0.conda#6001ae3f85403137d61e3ef7e96dd940 +https://conda.anaconda.org/conda-forge/noarch/imageio-2.36.0-pyh12aca89_1.conda#36349844ff73fcd0140ee7f30745f0bf https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.3.4-py39h2fa2bec_0.tar.bz2#9ec0b2186fab9121c54f4844f93ee5b7 https://conda.anaconda.org/conda-forge/linux-64/pandas-1.1.5-py39hde0f152_0.tar.bz2#79fc4b5b3a865b90dd3701cecf1ad33c https://conda.anaconda.org/conda-forge/noarch/patsy-0.5.6-pyhd8ed1ab_0.conda#a5b55d1cb110cdcedc748b5c3e16e687 @@ -266,7 +267,7 @@ https://conda.anaconda.org/conda-forge/linux-64/polars-0.20.30-py39ha963410_0.co https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.9-py39h52134e7_5.conda#e1f148e57d071b09187719df86f513c1 https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.6.0-py39hd92a3bb_0.conda#32e26e16f60c568b17a82e3033a4d309 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.6.0-py39hee8e79c_0.tar.bz2#3afcb78281836e61351a2924f3230060 -https://conda.anaconda.org/conda-forge/linux-64/blas-2.124-mkl.conda#00acde830223e8d6e7d4d2e8e6e94cac +https://conda.anaconda.org/conda-forge/linux-64/blas-2.125-mkl.conda#8a0ffaaae2bccf691cffdde83cb0f1a5 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.3.4-py39hf3d152e_0.tar.bz2#cbaec993375a908bbe506dc7328d747c https://conda.anaconda.org/conda-forge/linux-64/pyamg-4.2.3-py39hac2352c_1.tar.bz2#6fb0628d6195d8b6caa2422d09296399 https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.12.2-pyhd8ed1ab_0.conda#cf88f3a1c11536bc3c10c14ad00ccc42 diff --git a/build_tools/cirrus/pymin_conda_forge_linux-aarch64_conda.lock b/build_tools/cirrus/pymin_conda_forge_linux-aarch64_conda.lock index 91e4f080e25e1..9b2b12078f0a5 100644 --- a/build_tools/cirrus/pymin_conda_forge_linux-aarch64_conda.lock +++ b/build_tools/cirrus/pymin_conda_forge_linux-aarch64_conda.lock @@ -7,9 +7,9 @@ https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab 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_1.conda#5019b8e4dd2433395270cc0838ad4065 +https://conda.anaconda.org/conda-forge/linux-aarch64/ld_impl_linux-aarch64-2.43-h80caac9_2.conda#fcbde5ea19d55468953bf588770c0501 https://conda.anaconda.org/conda-forge/linux-aarch64/libglvnd-1.7.0-hd24410f_1.conda#32763e24bc6e5ed4de4a4a1598448d5b -https://conda.anaconda.org/conda-forge/linux-aarch64/llvm-openmp-19.1.2-h013ceaa_0.conda#d51a2e037784c2604ba616b4fd9508e3 +https://conda.anaconda.org/conda-forge/linux-aarch64/llvm-openmp-19.1.3-h013ceaa_0.conda#41689b81ad3f991ac539fd00b37af432 https://conda.anaconda.org/conda-forge/linux-aarch64/python_abi-3.9-5_cp39.conda#2d2843f11ec622f556137d72d9c72d89 https://conda.anaconda.org/conda-forge/noarch/tzdata-2024b-hc8b5060_0.conda#8ac3367aafb1cc0a068483c580af8015 https://conda.anaconda.org/conda-forge/linux-aarch64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#98a1185182fec3c434069fa74e6473d6 @@ -45,13 +45,13 @@ https://conda.anaconda.org/conda-forge/linux-aarch64/libnsl-2.0.1-h31becfc_0.con 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.44-hc4a20ef_0.conda#5d25802b25fcc7419fa13e21affaeb3a -https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.46.1-hc4a20ef_0.conda#cd559337c1bd9545ecbeaad017e7d878 +https://conda.anaconda.org/conda-forge/linux-aarch64/libsqlite-3.47.0-hc4a20ef_1.conda#a6b185aac10d08028340858f77231b23 https://conda.anaconda.org/conda-forge/linux-aarch64/libstdcxx-ng-14.2.0-hf1166c9_1.conda#0e75771b8a03afae5a2c6ce71bc733f5 https://conda.anaconda.org/conda-forge/linux-aarch64/libuuid-2.38.1-hb4cce97_0.conda#000e30b09db0b7c775b21695dff30969 https://conda.anaconda.org/conda-forge/linux-aarch64/libwebp-base-1.4.0-h31becfc_0.conda#5fd7ab3e5f382c70607fbac6335e6e19 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.0.1-h3f5c77f_1.conda#63a410df608cb32500240c090090a0c7 +https://conda.anaconda.org/conda-forge/linux-aarch64/mysql-common-9.0.1-h3f5c77f_2.conda#cc7bc11893dd1aee492dae85f317769e https://conda.anaconda.org/conda-forge/linux-aarch64/ncurses-6.5-hcccb83c_1.conda#91d49c85cacd92caa40cf375ef72a25d https://conda.anaconda.org/conda-forge/linux-aarch64/tk-8.6.13-h194ca79_0.conda#f75105e0585851f818e0009dd1dde4dc https://conda.anaconda.org/conda-forge/linux-aarch64/xz-5.2.6-h9cdd2b7_0.tar.bz2#83baad393a31d59c20b63ba4da6592df @@ -79,15 +79,15 @@ https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libsm-1.2.4-hbac51e1_1 https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libx11-1.8.9-he755bbd_2.conda#7acc45f80415e6ec352b729105dc0375 https://conda.anaconda.org/conda-forge/linux-aarch64/zstd-1.5.6-h02f22dd_0.conda#be8d5f8cf21aed237b8b182ea86b3dd6 https://conda.anaconda.org/conda-forge/linux-aarch64/brotli-1.1.0-h86ecc28_2.conda#5094acc34eb173f74205c0b55f0dd4a4 -https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.14.2-ha9a116f_0.conda#6d2d19ea85f9d41534cd28fdefd59a25 +https://conda.anaconda.org/conda-forge/linux-aarch64/fontconfig-2.15.0-h8dda3cd_1.conda#112b71b6af28b47c624bcbeefeea685b https://conda.anaconda.org/conda-forge/linux-aarch64/krb5-1.21.3-h50a48e9_0.conda#29c10432a2ca1472b53f299ffb2ffa37 https://conda.anaconda.org/conda-forge/linux-aarch64/libglib-2.82.2-hc486b8e_0.conda#47f6d85fe47b865e56c539f2ba5f4dad https://conda.anaconda.org/conda-forge/linux-aarch64/libglx-1.7.0-hd24410f_1.conda#b4e4c7703e944564b512dabbcc1130d0 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/libopenblas-0.3.27-pthreads_h076ed1e_1.conda#cc0a15e3a6f92f454b6132ca6aca8e8d +https://conda.anaconda.org/conda-forge/linux-aarch64/libopenblas-0.3.28-pthreads_h9d3fd7e_0.conda#554edd2031035f21b042fdbc74429774 https://conda.anaconda.org/conda-forge/linux-aarch64/libtiff-4.7.0-hec21d91_1.conda#1f80061f5ba6956fcdc381f34618cd8d -https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.12.7-h00a45b3_4.conda#d25c3e16ee77cd25342e4e235424c758 -https://conda.anaconda.org/conda-forge/linux-aarch64/mysql-libs-9.0.1-h11569fd_1.conda#3b80bd7cf3198769f7fe9ed3e76f616e +https://conda.anaconda.org/conda-forge/linux-aarch64/libxml2-2.13.4-hf4efe5d_2.conda#0e28ab30d29c5a566d05bf73dfc5c184 +https://conda.anaconda.org/conda-forge/linux-aarch64/mysql-libs-9.0.1-h11569fd_2.conda#94c70f21e0a1f8558941d901027215a4 https://conda.anaconda.org/conda-forge/linux-aarch64/python-3.9.20-h4a649e4_1_cpython.conda#c2833e3d5a6d210ffb433cbd4a1cf174 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.43-h86ecc28_0.conda#a809b8e3776fbc05696c82f8cf6f5a92 @@ -107,24 +107,24 @@ https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.1-pyhd8ed1ab_0.conda#1 https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.0.0-pyhd8ed1ab_0.conda#f800d2da156d08e289b14e87e43c1ae5 https://conda.anaconda.org/conda-forge/linux-aarch64/kiwisolver-1.4.7-py39h78c8b8d_0.conda#8dc5516dd121089f14c1a557ecec3224 https://conda.anaconda.org/conda-forge/linux-aarch64/lcms2-2.16-h922389a_0.conda#ffdd8267a04c515e7ce69c727b051414 -https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-24_linuxaarch64_openblas.conda#f763daad76fe32da91acfdf3e476ec0d +https://conda.anaconda.org/conda-forge/linux-aarch64/libblas-3.9.0-25_linuxaarch64_openblas.conda#f9b8a4a955ed2d0b68b1f453abcc1c9e https://conda.anaconda.org/conda-forge/linux-aarch64/libcups-2.3.3-h405e4a8_4.conda#d42c670b0c96c1795fd859d5e0275a55 https://conda.anaconda.org/conda-forge/linux-aarch64/libgl-1.7.0-hd24410f_1.conda#06cf88e73c69957c56318c6a1ccc5306 -https://conda.anaconda.org/conda-forge/linux-aarch64/libllvm19-19.1.2-h2edbd07_0.conda#e0c251e0b6815995e2f19532ab604f9b +https://conda.anaconda.org/conda-forge/linux-aarch64/libllvm19-19.1.3-h2edbd07_0.conda#4f335bb2183b2a9a062518cbc079dc8b https://conda.anaconda.org/conda-forge/linux-aarch64/libxkbcommon-1.7.0-h46f2afe_1.conda#78a24e611ab9c09c518f519be49c2e46 https://conda.anaconda.org/conda-forge/linux-aarch64/libxslt-1.1.39-h1cc9640_0.conda#13e1d3f9188e85c6d59a98651aced002 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.27-pthreads_hd33deab_1.conda#70c0aa7d1dd049fffae952bfe8f2c4e9 +https://conda.anaconda.org/conda-forge/linux-aarch64/openblas-0.3.28-pthreads_h395f137_0.conda#ee90d0aeb560772351c2bb5628dea2fd https://conda.anaconda.org/conda-forge/linux-aarch64/openjpeg-2.5.2-h0d9d63b_0.conda#fd2898519e839d5ceb778343f39a3176 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda#035c17fbf099f50ff60bf2eb303b0a83 -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda#df68d78237980a159bd7149f33c0e8fd https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727 https://conda.anaconda.org/conda-forge/linux-aarch64/tornado-6.4.1-py39h3e3acee_1.conda#a4d4b0a58bf2fadfa1285f4710b72f99 -https://conda.anaconda.org/conda-forge/linux-aarch64/unicodedata2-15.1.0-py39h898b7ef_0.conda#8c072c9329aeea97a46005625267a851 +https://conda.anaconda.org/conda-forge/linux-aarch64/unicodedata2-15.1.0-py39h060674a_1.conda#22a119d3f80e6d91b28fbc49a3cc08b2 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 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 @@ -134,33 +134,33 @@ 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.5-h57736b2_4.conda#82fa1f5642ef7ac7172e295327ce20e2 https://conda.anaconda.org/conda-forge/noarch/zipp-3.20.2-pyhd8ed1ab_0.conda#4daaed111c05672ae669f7036ee5bba3 -https://conda.anaconda.org/conda-forge/linux-aarch64/fonttools-4.54.1-py39h060674a_0.conda#d5615bf26c4f003722dada444370efe0 +https://conda.anaconda.org/conda-forge/linux-aarch64/fonttools-4.54.1-py39hbebea31_1.conda#48e4d4179d70359d8d1fa6716467ef62 https://conda.anaconda.org/conda-forge/linux-aarch64/harfbuzz-9.0.0-hbf49d6b_1.conda#ceb458f664cab8550fcd74fff26451db https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.4.5-pyhd8ed1ab_0.conda#c808991d29b9838fb4d96ce8267ec9ec https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f -https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-24_linuxaarch64_openblas.conda#fe7560187584eaae4f115d471b62c09c -https://conda.anaconda.org/conda-forge/linux-aarch64/libclang-cpp19.1-19.1.2-default_he324ac1_1.conda#6d4b791f9aa0523de4a8f46cd8b2e3ea -https://conda.anaconda.org/conda-forge/linux-aarch64/libclang13-19.1.2-default_h4390ef5_1.conda#0aed30adc7dd7e5929596bde6659785d -https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-24_linuxaarch64_openblas.conda#a5ed3c9636f97ac4078cc96e7d79614c +https://conda.anaconda.org/conda-forge/linux-aarch64/libcblas-3.9.0-25_linuxaarch64_openblas.conda#db6af51123c67814572a8c25542cb368 +https://conda.anaconda.org/conda-forge/linux-aarch64/libclang-cpp19.1-19.1.3-default_he324ac1_0.conda#9ac4956d6676bdb251279d8c27406954 +https://conda.anaconda.org/conda-forge/linux-aarch64/libclang13-19.1.3-default_h4390ef5_0.conda#d23cae404c2763d07fee33a9299f2d63 +https://conda.anaconda.org/conda-forge/linux-aarch64/liblapack-3.9.0-25_linuxaarch64_openblas.conda#0eb74e81de46454960bde9e44e7ee378 https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/linux-aarch64/openldap-2.6.8-h50f9a67_0.conda#6f6627099ae614fe176e162e6eeae240 https://conda.anaconda.org/conda-forge/linux-aarch64/pillow-11.0.0-py39hb20fde8_0.conda#78cdfe29a452feee8c5bd689c2c871bd -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/linux-aarch64/xorg-libxtst-1.2.5-h57736b2_3.conda#c05698071b5c8e0da82a282085845860 https://conda.anaconda.org/conda-forge/noarch/importlib-resources-6.4.5-pyhd8ed1ab_0.conda#67f4772681cf86652f3e2261794cf045 -https://conda.anaconda.org/conda-forge/linux-aarch64/liblapacke-3.9.0-24_linuxaarch64_openblas.conda#71432f1b3c394a610ed990635004e04e +https://conda.anaconda.org/conda-forge/linux-aarch64/liblapacke-3.9.0-25_linuxaarch64_openblas.conda#1e68063075954830f707b41dab6c7fd8 https://conda.anaconda.org/conda-forge/linux-aarch64/libpq-17.0-h081282e_4.conda#4627c6a062463cf4191aafca4d6c748c -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 https://conda.anaconda.org/conda-forge/linux-aarch64/numpy-2.0.2-py39h4a34e27_0.conda#4d6edcc002364ced01e4fc947832eee6 https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 -https://conda.anaconda.org/conda-forge/linux-aarch64/blas-devel-3.9.0-24_linuxaarch64_openblas.conda#ca38db346a73514b2d813fa69a11a509 +https://conda.anaconda.org/conda-forge/linux-aarch64/blas-devel-3.9.0-25_linuxaarch64_openblas.conda#32539a9b9e09140a83e987edf3c09926 https://conda.anaconda.org/conda-forge/linux-aarch64/contourpy-1.3.0-py39hbd2ca3f_2.conda#57fa6811a7a80c5641e373408389bc5a https://conda.anaconda.org/conda-forge/linux-aarch64/qt6-main-6.8.0-h666f7c6_0.conda#1c50a44d681075eff85d0332624c927e https://conda.anaconda.org/conda-forge/linux-aarch64/scipy-1.13.1-py39hb921187_0.conda#1aac9080de661e03d286f18fb71e5240 -https://conda.anaconda.org/conda-forge/linux-aarch64/blas-2.124-openblas.conda#f5436b450f17f50944a3e1b49548d576 +https://conda.anaconda.org/conda-forge/linux-aarch64/blas-2.125-openblas.conda#dfbaf914827bc38dda840c90231c91df https://conda.anaconda.org/conda-forge/linux-aarch64/matplotlib-base-3.9.2-py39hd333c8e_1.conda#b1a6b946d3b38515ecaf10f1ee5aa6c6 -https://conda.anaconda.org/conda-forge/linux-aarch64/pyside6-6.8.0-py39h51c6ee1_1.conda#5829dbb24b1bddb12a58a8fe9d54578e +https://conda.anaconda.org/conda-forge/linux-aarch64/pyside6-6.8.0.2-py39h51c6ee1_0.conda#c130c84c26696485a720d85bd530e992 https://conda.anaconda.org/conda-forge/linux-aarch64/matplotlib-3.9.2-py39ha65689a_1.conda#10358b436f2d5adcaa436a018ffc7d97 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 24235d09c1f3a..260cf1a598ee0 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 @@ -17,7 +17,7 @@ https://conda.anaconda.org/conda-forge/noarch/cuda-cccl_linux-64-12.4.127-ha770c https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-static_linux-64-12.4.127-h85509e4_2.conda#0b0522d8685968f25370d5c36bb9fba3 https://conda.anaconda.org/conda-forge/noarch/cuda-cudart_linux-64-12.4.127-h85509e4_2.conda#329163110a96514802e9e64d971edf43 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_1.conda#83e1364586ceb8d0739fbc85b5c95837 +https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.43-h712a8e2_2.conda#048b02e3962f066da18efe3a21b77672 https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_1.conda#1ece2ccb1dc8c68639712b05e0fae070 https://conda.anaconda.org/conda-forge/noarch/cuda-cudart-dev_linux-64-12.4.127-h85509e4_2.conda#12039deb2a3f103f5756831702bf29fc https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab @@ -25,7 +25,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_1.conda#38 https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_1.conda#e12057a66af8f2a38a839754ca4481e9 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_kmp_llvm.tar.bz2#562b26ba2e19059551a811e72ab7f793 https://conda.anaconda.org/conda-forge/linux-64/libgcc-14.2.0-h77fa898_1.conda#3cb76c3f10d3bc7f1105b2fc9db984df -https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.29-hb9d3cd8_0.conda#acc51b49fd7467c8dfe4343001b812b4 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.9.31-hb9d3cd8_0.conda#75f7776e1c9af78287f055ca34797517 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.2-heb4867d_0.conda#2b780c0338fc0ffa678ac82c54af51fd 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.22-hb9d3cd8_0.conda#b422943d5d772b7cc858b36ad2a92db5 @@ -41,10 +41,10 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.11-hb9d3cd8_1.co 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-xorgproto-2024.1-hb9d3cd8_1.conda#7c21106b851ec72c037b162c216d8f05 https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.12-h4ab18f5_0.conda#7ed427f0871fd41cb1d9c17727c17589 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.7.4-hae4d56a_2.conda#cdc628e4ffb4ffcd476e3847267e1689 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.2.19-h2bff981_2.conda#87a059d4d2ab89409496416119dd7152 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.1.19-h2bff981_4.conda#5a8afd37e2dfe464d68e63d1c38b08c5 -https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.1.20-h2bff981_1.conda#8b424cf6b3cfc5cffe98bf4d16c032fb +https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.8.0-hd3f4568_0.conda#0902512e7a2de9722697fb011db07a54 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.0-hf20e7d7_0.conda#84412135f9c1dd8985741e9c351f499a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.0-hf20e7d7_0.conda#ff265c3736cdac819c8adb844e0557d8 +https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.0-hf20e7d7_0.conda#e54103489d34bd5a106b9298dc28c848 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.6.3-h5888daf_0.conda#6595440079bed734b113de44ffd3cd0a https://conda.anaconda.org/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda#d411fc29e338efb48c5fd4576d71d881 @@ -62,7 +62,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hd590300_0.conda#30 https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.4-h7f98852_1002.tar.bz2#e728e874159b042d92b90238a3cb0dc2 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.44-hadc24fc_0.conda#f4cc49d7aa68316213e4b12be35308d1 -https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.46.1-hadc24fc_0.conda#36f79405ab16bf271edb55b213836dac +https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.47.0-hadc24fc_1.conda#b6f02b52a174e612e89548f4663ce56a https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.0-h0841786_0.conda#1f5a58e686b13bcfde88b93f547d23fe https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-14.2.0-h4852527_1.conda#8371ac6457591af2cf6159439c1fd051 https://conda.anaconda.org/conda-forge/linux-64/libutf8proc-2.8.0-h166bdaf_0.tar.bz2#ede4266dc02e875fe1ea77b25dd43747 @@ -71,15 +71,15 @@ https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.4.0-hd590300_0.co 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/llvm-openmp-15.0.7-h0cdce71_0.conda#589c9a3575a050b583241c3d688ad9aa -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_1.conda#e97f73d51b5acdf1340a15b195738f16 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-9.0.1-h266115a_2.conda#85c0dc0bcd110c998b01856975486ee7 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-he02047a_1.conda#70caf8bb6cf39a0b6b7efc885f51c0fe https://conda.anaconda.org/conda-forge/linux-64/ocl-icd-2.3.2-hd590300_1.conda#c66f837ac65e4d1cdeb80e2a1d5fcc3d -https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.5-h3931f03_0.conda#334dba9982ab9f5d62033c61698a8683 +https://conda.anaconda.org/conda-forge/linux-64/s2n-1.5.6-h0e56266_0.conda#54752411d7559a8bbd4c0204a8f1cf35 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/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0 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/zlib-1.3.1-hb9d3cd8_2.conda#c9f075ab2f33b3bbee9e62d4ad0a6cd8 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.14.19-hc9e6898_1.conda#ec84785f7ae14ed43156a54aec33bb14 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.15.0-h17eb868_2.conda#bb03f4ce96deea2175fc3ec17b2c1c04 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/cuda-cudart-12.4.127-he02047a_2.conda#a748faa52331983fc3adcc3b116fe0e4 https://conda.anaconda.org/conda-forge/linux-64/cuda-cupti-12.4.127-he02047a_2.conda#46422ef1b1161fb180027e50c598ecd0 @@ -100,12 +100,12 @@ https://conda.anaconda.org/conda-forge/linux-64/libcurand-10.3.5.147-he02047a_2. https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.123-hb9d3cd8_0.conda#ee605e794bdc14e2b7f84c4faa0d8c2c https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-14.2.0-h69a702a_1.conda#0a7f4cd238267c88e5d69f7826a407eb -https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.58.0-h47da74e_1.conda#700ac6ea6d53d5510591c4344d5c989a +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.64.0-h161d5f1_0.conda#19e57602824042dfd0446292ef90488b https://conda.anaconda.org/conda-forge/linux-64/libnpp-12.2.5.30-he02047a_2.conda#a96a1edd18bee676cf2dcca251d3d6a4 https://conda.anaconda.org/conda-forge/linux-64/libnvfatbin-12.4.127-he02047a_2.conda#d746b76642b4ac6e40f1219405672beb https://conda.anaconda.org/conda-forge/linux-64/libnvjitlink-12.4.127-he02047a_2.conda#303845d6c48bf4185dc4138634650468 https://conda.anaconda.org/conda-forge/linux-64/libnvjpeg-12.3.1.117-he02047a_2.conda#8f3ed0e41a4b505de40b4f96f4bfb0fa -https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.27.5-h5b01275_2.conda#66ed3107adbdfc25ba70454ba11e6d1e +https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-5.28.2-h5b01275_0.conda#ab0bff36363bec94720275a681af8b83 https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2024.07.02-hbbce691_1.conda#2124de47357b7a516c0a3efd8f88c143 https://conda.anaconda.org/conda-forge/linux-64/libthrift-0.21.0-h0e7cc3e_0.conda#dcb95c0a98ba9ff737f7ae482aef7833 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.4-hcb278e6_0.conda#318b08df404f9c9be5712aaa5a6f0bb0 @@ -123,10 +123,10 @@ https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.con https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.4-he73a12e_1.conda#05a8ea5f446de33006171a7afe6ae857 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.10-h4f16b4b_0.conda#0b666058a179b744a622d0a4a0c56353 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.6-ha6fb4c9_0.conda#4d056880988120e29d75bfff282e0f45 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.4.3-h19b0707_4.conda#df38f56123f30d61de24474e600e7d41 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.8.10-h14a7884_2.conda#6147c6b6cef67adcb85516f5cf775be7 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.0-h68c3b0c_2.conda#a08831d82df7546a599095b33f3cae2a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.9.0-hfad4ed3_3.conda#01bc29be557b8c7c1963f7ad7185529a https://conda.anaconda.org/conda-forge/linux-64/brotli-1.1.0-hb9d3cd8_2.conda#98514fe74548d768907ce7a13f680e8f -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.2-h14ed4e7_0.conda#0f69b688f52ff6da70bccb7ff7001d1d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.15.0-h7e30c49_1.conda#8f5b0b297b59e1ac160ad4beec99dbee https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda#3f43953b7d3fb3aaa1d0d0723d91e368 https://conda.anaconda.org/conda-forge/linux-64/libcublas-12.4.5.8-he02047a_2.conda#d446adae085aa1ff37c44b69988a6f06 https://conda.anaconda.org/conda-forge/linux-64/libcusparse-12.3.1.170-he02047a_2.conda#1c4c7ff54dc5b947f2ab8f5ff8a28dae @@ -134,10 +134,10 @@ https://conda.anaconda.org/conda-forge/linux-64/libglib-2.82.2-h2ff4ddf_0.conda# https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_1.conda#80a57756c545ad11f9847835aa21e6b2 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/libtiff-4.7.0-he137b08_1.conda#63872517c98aa305da58a757c443698e -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.12.7-he7c6b58_4.conda#08a9265c637230c37cb1be4a6cad4536 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.13.4-hb346dea_2.conda#69b90b70c434b916abf5a1d5ee5d55fb https://conda.anaconda.org/conda-forge/linux-64/mpfr-4.2.1-h90cbb55_3.conda#2eeb50cab6652538eee8fc0bc3340c81 -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_1.conda#274f367df5d56f152a49ed3203c3b1c1 -https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-h690cf93_1.conda#0044701dd48af57d3d5467a704ef9ebd +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-9.0.1-he0572af_2.conda#57a9e7ee3c0840d3c8c9012473978629 +https://conda.anaconda.org/conda-forge/linux-64/orc-2.0.2-he039a57_2.conda#5e7bb9779cc5c200e63475eb2538d382 https://conda.anaconda.org/conda-forge/linux-64/python-3.12.7-hc5c86c4_0_cpython.conda#0515111a9cdf69f83278f7c197db9807 https://conda.anaconda.org/conda-forge/linux-64/re2-2024.07.02-h77b4e00_1.conda#01093ff37c1b5e6bf9f17c0116747d11 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda#a0901183f08b6c7107aab109733a3c91 @@ -145,9 +145,9 @@ https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.43-hb9d3cd8_0 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.11-hb9d3cd8_1.conda#a7a49a8b85122b49214798321e2e96b4 -https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.9-pyhd8ed1ab_0.conda#967029d5f1687e4d9a38e4fc21184151 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.7.31-he1a10d6_2.conda#76550a294cc78aaccfca7824bb4814ce -https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.10.7-hb8d5873_2.conda#8dc25ca24c1a50b8295a848c384ede99 +https://conda.anaconda.org/conda-forge/noarch/array-api-compat-1.9.1-pyhd8ed1ab_0.conda#f2328337441baa8f669d2a830cfd0097 +https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.8.0-h56a2c13_4.conda#44a599a9c2c7e5d75e062457ddc6666a +https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.11.0-h407ecb8_2.conda#f9fcf88ac9d34b2bfe70429064d7744c https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.0-hebfffa5_3.conda#fceaedf1cdbcb02df9699a0d9b005292 https://conda.anaconda.org/conda-forge/linux-64/ccache-4.10.1-h065aff2_0.conda#d6b48c138e0c8170a6fe9c136e063540 https://conda.anaconda.org/conda-forge/noarch/certifi-2024.8.30-pyhd8ed1ab_0.conda#12f7d00853807b0531775e9be891cb11 @@ -168,16 +168,16 @@ https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h4637d8d_4.conda#d https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.10.1-hbbe4b11_0.conda#6e801c50a40301f6978c53976917b277 https://conda.anaconda.org/conda-forge/linux-64/libcusolver-11.6.1.9-he02047a_2.conda#9f6877f8936be962f598db5e9b8efc51 https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_1.conda#204892bce2e44252b5cf272712f10bdd -https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.65.5-hf5c653b_0.conda#3b0048cabc6815a4d8874a0240519d32 +https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.67.1-hc2c308b_0.conda#4606a4647bfe857e3cfe21ca12ac3afb https://conda.anaconda.org/conda-forge/linux-64/libhwloc-2.11.1-default_hecaa2ac_1000.conda#f54aeebefb5c5ff84eca4fb05ca8aa3a -https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.2-ha7bfdaf_0.conda#128e74a4f8f4fef4dc5130a8bbccc15d +https://conda.anaconda.org/conda-forge/linux-64/libllvm19-19.1.3-ha7bfdaf_0.conda#8bd654307c455162668cd66e36494000 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.7.0-h2c5496b_1.conda#e2eaefa4de2b7237af7c907b8bbc760a https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.39-h76b75d6_0.conda#e71f31f8cfb0a91439f2086fc8aa0461 https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.2-py312h178313f_0.conda#a755704ea0e2503f8c227d84829a8e81 https://conda.anaconda.org/conda-forge/linux-64/mpc-1.3.1-h24ddda3_1.conda#aa14b9a5196a6d8dd364164b7ce56acf https://conda.anaconda.org/conda-forge/noarch/mpmath-1.3.0-pyhd8ed1ab_0.conda#dbf6e2d89137da32fa6670f3bffc024e 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.1-pyhd8ed1ab_0.conda#4994669899eb2e84ab855edcb71efc58 +https://conda.anaconda.org/conda-forge/noarch/networkx-3.4.2-pyhd8ed1ab_1.conda#1d4c088869f206413c59acdd309908b7 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.2-h488ebb8_0.conda#7f2e286780f072ed750df46dc2631138 https://conda.anaconda.org/conda-forge/noarch/packaging-24.1-pyhd8ed1ab_0.conda#cbe1bb1f21567018ce595d9c2be0f0db https://conda.anaconda.org/conda-forge/noarch/pluggy-1.5.0-pyhd8ed1ab_0.conda#d3483c8fc2dc2cc3f5cf43e26d60cabf @@ -185,13 +185,14 @@ https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.0-pyhd8ed1ab_1.conda https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2024.2-pyhd8ed1ab_0.conda#986287f89929b2d629bd6ef6497dc307 https://conda.anaconda.org/conda-forge/noarch/pytz-2024.1-pyhd8ed1ab_0.conda#3eeeeb9e4827ace8c0c1419c85d590ad https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.2-py312h66e93f0_1.conda#549e5930e768548a89c23f595dac5a95 -https://conda.anaconda.org/conda-forge/noarch/setuptools-75.1.0-pyhd8ed1ab_0.conda#d5cd48392c67fb6849ba459c2c2b671f +https://conda.anaconda.org/conda-forge/noarch/setuptools-75.3.0-pyhd8ed1ab_0.conda#2ce9825396daf72baabaade36cee16da https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.5.0-pyhc1e730c_0.conda#df68d78237980a159bd7149f33c0e8fd https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.2-pyhd8ed1ab_0.conda#e977934e00b355ff55ed154904044727 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.4.1-py312h66e93f0_1.conda#af648b62462794649066366af4ecd5b0 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.12.2-pyha770c72_0.conda#ebe6952715e1d5eb567eeebf25250fa7 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.1.0-py312h66e93f0_1.conda#588486a61153f94c7c13816f7069e440 https://conda.anaconda.org/conda-forge/noarch/wheel-0.44.0-pyhd8ed1ab_0.conda#d44e3b085abcaef02983c6305b84b584 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 @@ -200,39 +201,39 @@ 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.5-hb9d3cd8_4.conda#7da9007c0582712c4bad4131f89c8372 -https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.6.7-h666547d_0.conda#7f59dcbbd4eab14ca9256f20b43849eb +https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.7.0-hadeddc1_5.conda#429e7497e7f08bc470d2872147d8ef6d 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/coverage-7.6.3-py312h178313f_1.conda#2621104ac246594948615017c1254c66 +https://conda.anaconda.org/conda-forge/linux-64/coverage-7.6.4-py312h178313f_0.conda#a32fbd2322865ac80c7db74c553f5306 https://conda.anaconda.org/conda-forge/linux-64/cuda-libraries-12.4.1-ha770c72_1.conda#6bb3f998485d4344a7539e0b218b3fc1 -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py312h66e93f0_0.conda#e311030d9322f6f77e71e013490c83b2 +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.54.1-py312h178313f_1.conda#bbbf5fa5cab622c33907bc8d7eeea9f7 https://conda.anaconda.org/conda-forge/linux-64/gmpy2-2.1.5-py312h7201bc8_2.conda#af9faf103fb57241246416dc70b466f7 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-9.0.0-hda332d3_1.conda#76b32dcf243444aea9c6b804bcfa40b8 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.4-pyhd8ed1ab_0.conda#7b86ecb7d3557821c649b3c31e3eb9f2 https://conda.anaconda.org/conda-forge/noarch/joblib-1.4.2-pyhd8ed1ab_0.conda#25df261d4523d9f9783bcdb7208d872f -https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.2-default_hb5137d0_1.conda#7e574c7499bc41f92537634a23fed79a -https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.2-default_h9c6a7e4_1.conda#cb5c5ff12b37aded00d9aaa7b9a86a78 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.30.0-h438788a_0.conda#ab8466a39822527f7786b0d0b2aac223 +https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp19.1-19.1.3-default_hb5137d0_0.conda#311e6a1d041db3d6a8a8437750d4234f +https://conda.anaconda.org/conda-forge/linux-64/libclang13-19.1.3-default_h9c6a7e4_0.conda#b8a8cd77810b20754f358f2327812552 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.30.0-h804f50b_1.conda#0a1c61bdbf27a966bbb0c8bf9df37b02 https://conda.anaconda.org/conda-forge/noarch/meson-1.6.0-pyhd8ed1ab_0.conda#380ba6a3eddd8e7649bfe8e6812611aa https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.8-hedd0468_0.conda#dcd0ed5147d8876b0848a552b416ce76 https://conda.anaconda.org/conda-forge/linux-64/pillow-11.0.0-py312h7b63e92_0.conda#385f46a4df6f97892503a841121a9acf -https://conda.anaconda.org/conda-forge/noarch/pip-24.2-pyh8b19718_1.conda#6c78fbb8ddfd64bcb55b5cbafd2d2c43 -https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.8.1-pyh2cfa8aa_0.conda#c503dd01a15639101d4e38c0f0da6249 +https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh8b19718_0.conda#5dd546fe99b44fda83963d15f84263b7 +https://conda.anaconda.org/conda-forge/noarch/pyproject-metadata-0.9.0-pyh2cfa8aa_0.conda#10906a130eeb4a68645bf97c28333141 https://conda.anaconda.org/conda-forge/noarch/pytest-8.3.3-pyhd8ed1ab_0.conda#c03d61f31f38fdb9facf70c29958bf7a https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0-pyhd8ed1ab_0.conda#2cf4264fffb9e6eff6031c5b6884d61c https://conda.anaconda.org/conda-forge/linux-64/tbb-2021.13.0-h84d6215_0.conda#ee6f7fd1e76061ef1fa307d41fa86a96 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.28.3-hbe26082_8.conda#80d5fac04be0e6c2774f57eb7529f145 +https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.29.0-h73f0fd4_6.conda#19f6d559f3be939046d2ac5c7b2ded7a 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/cuda-runtime-12.4.1-ha804496_0.conda#48829f4ef6005ae8d4867b99168ff2b8 -https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.30.0-h0121fbd_0.conda#ad86b6c98964772688298a727cb20ef8 +https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.30.0-h0121fbd_1.conda#afbbf507f8c96faa95a2efa376ad484c https://conda.anaconda.org/conda-forge/linux-64/libpq-17.0-h04577a9_4.conda#392cae2a58fbcb9db8c2147c6d6d1620 -https://conda.anaconda.org/conda-forge/noarch/meson-python-0.16.0-pyh0c530f3_0.conda#e16f0dbf502da873be9f9adb0dc52547 +https://conda.anaconda.org/conda-forge/noarch/meson-python-0.17.1-pyh70fd9c4_0.conda#722b649da38842068d83b6e6770f11a1 https://conda.anaconda.org/conda-forge/linux-64/mkl-2022.1.0-h84fe81f_915.tar.bz2#b9c8f925797a93dbff45e1626b025a6b -https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda#c54c0107057d67ddf077751339ec2c63 +https://conda.anaconda.org/conda-forge/noarch/pytest-cov-6.0.0-pyhd8ed1ab_0.conda#cb8a11b6d209e3d85e5094bdbd9ebd9c https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.6.1-pyhd8ed1ab_0.conda#b39568655c127a9c4a44d178ac99b6d0 https://conda.anaconda.org/conda-forge/noarch/sympy-1.13.3-pyh2585a3b_104.conda#68085d736d2b2f54498832b65059875d -https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.407-h25d6d5c_1.conda#0f2bd0128d59a45c9fd56151eab0b37e +https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.407-h6a6dca0_6.conda#3c25988c0b0a2085b4df578b7160d963 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/libblas-3.9.0-16_linux64_mkl.tar.bz2#85f61af03fd291dae33150ffe89dc09a https://conda.anaconda.org/conda-forge/linux-64/mkl-devel-2022.1.0-ha770c72_916.tar.bz2#69ba49e445f87aea2cba343a71a35ca2 @@ -241,27 +242,27 @@ https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.8.0-h6e8976b_0.conda# 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/libcblas-3.9.0-16_linux64_mkl.tar.bz2#361bf757b95488de76c4f123805742d3 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-16_linux64_mkl.tar.bz2#a2f166748917d6d6e4707841ca1f519e -https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.0-py312h91f0f75_1.conda#81abe3bd7285eec2fe288045043fe419 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-17.0.0-ha07344c_22_cpu.conda#041e55887514cd414ec7df03d68210fb +https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.8.0.2-py312h91f0f75_0.conda#ec3da81d5f9d3612b227e09a650f7bf2 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-18.0.0-ha5db6c2_1_cpu.conda#412e50a89595c2ed2d0d49e1c3665be6 https://conda.anaconda.org/conda-forge/linux-64/liblapacke-3.9.0-16_linux64_mkl.tar.bz2#44ccc4d4dca6a8d57fa17442bc64b5a1 -https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.2-py312h58c1407_0.conda#b7e9a46277a1ee0afc6311e7760df0c3 -https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.0.1-pyhd8ed1ab_0.conda#2c00d29e0e276f2d32dfe20e698b8eeb +https://conda.anaconda.org/conda-forge/linux-64/numpy-2.1.3-py312h58c1407_0.conda#dfdbc12e6d81889ba4c494a23f23eba8 +https://conda.anaconda.org/conda-forge/noarch/array-api-strict-2.1-pyhd8ed1ab_0.conda#15cc819ed82470249cbf1337791bc5ff https://conda.anaconda.org/conda-forge/linux-64/blas-devel-3.9.0-16_linux64_mkl.tar.bz2#3f92c1c9e1c0e183462c5071aa02cae1 https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.0-py312h68727a3_2.conda#ff28f374b31937c048107521c814791e https://conda.anaconda.org/conda-forge/linux-64/cupy-core-13.3.0-py312h1acd1a8_2.conda#15e9530e87664584a6b409ecdf5c9264 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-17.0.0-h5888daf_22_cpu.conda#1675812f0edd6e6edede105de6218ff8 -https://conda.anaconda.org/conda-forge/linux-64/libparquet-17.0.0-h6bd9018_22_cpu.conda#48c058a044a8d1bfd38153d054c2a911 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-acero-18.0.0-h5888daf_1_cpu.conda#5eea7d1aad1772bb0b7629ba0bf9ba38 +https://conda.anaconda.org/conda-forge/linux-64/libparquet-18.0.0-h6bd9018_1_cpu.conda#13ac6045ae32e0fd87e51003570ba372 https://conda.anaconda.org/conda-forge/linux-64/pandas-2.2.3-py312hf9745cd_1.conda#8bce4f6caaf8c5448c7ac86d87e26b4b -https://conda.anaconda.org/conda-forge/linux-64/polars-1.10.0-py312hfe7c9be_0.conda#d4399ea96bf38f71e5539dd87f89c436 -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-17.0.0-py312h9cafe31_1_cpu.conda#235827b9c93850cafdd2d5ab359893f9 +https://conda.anaconda.org/conda-forge/linux-64/polars-1.12.0-py312hfe7c9be_0.conda#47b6df7e8b629d1257a6f971b88c15de +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-core-18.0.0-py312h01725c0_0_cpu.conda#9100ae6cdd482666b38fa20e7819b385 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.14.1-py312h62794b6_1.conda#b43233a9e2f62fb94affe5607ea79473 https://conda.anaconda.org/conda-forge/linux-64/blas-2.116-mkl.tar.bz2#c196a26abf6b4f132c88828ab7c2231c https://conda.anaconda.org/conda-forge/linux-64/cupy-13.3.0-py312h7d319b9_2.conda#009ef049020fef7d1541183d52fab5a9 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-17.0.0-h5888daf_22_cpu.conda#fa5b60076d370bd032f5a97024c86405 +https://conda.anaconda.org/conda-forge/linux-64/libarrow-dataset-18.0.0-h5888daf_1_cpu.conda#ba0a7a916ce6145f484e46c32e89ba97 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.9.2-py312hd3ec401_1.conda#2f4f3854f23be30de29e9e4d39758349 https://conda.anaconda.org/conda-forge/linux-64/pyamg-5.2.1-py312hc39e661_1.conda#372efc32220f0dfb603e5b31ffaefa23 -https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-17.0.0-he882d9a_22_cpu.conda#764d9eaef6da3e22f3d988e93ec7896b +https://conda.anaconda.org/conda-forge/linux-64/libarrow-substrait-18.0.0-h5c8f2c3_1_cpu.conda#295696a3696d039787fbf4f733a4f296 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.9.2-py312h7900ff3_1.conda#07d5646ea9f22f4b1c46c2947d1b2f58 -https://conda.anaconda.org/conda-forge/linux-64/pyarrow-17.0.0-py312h9cebb41_1.conda#7e8ddbd44fb99ba376b09c4e9e61e509 -https://conda.anaconda.org/pytorch/linux-64/pytorch-2.5.0-py3.12_cuda12.4_cudnn9.1.0_0.tar.bz2#80bf6cbaa284bd896b82b5b17f6ccb61 +https://conda.anaconda.org/conda-forge/linux-64/pyarrow-18.0.0-py312h9cebb41_0.conda#e110b1f861e749bc1dd48ad5467adab8 +https://conda.anaconda.org/pytorch/linux-64/pytorch-2.5.1-py3.12_cuda12.4_cudnn9.1.0_0.tar.bz2#42164c6ce8e563c20a542686a8b9b964 https://conda.anaconda.org/pytorch/linux-64/torchtriton-3.1.0-py312.tar.bz2#bb4b2d07cb6b9b476e78740c08ba69fe diff --git a/doc/about.rst b/doc/about.rst index b003999a1de57..8fc1404e3535d 100644 --- a/doc/about.rst +++ b/doc/about.rst @@ -8,8 +8,8 @@ History ======= This project was started in 2007 as a Google Summer of Code project by -David Cournapeau. Later that year, Matthieu Brucher started work on -this project as part of his thesis. +David Cournapeau. Later that year, Matthieu Brucher started working on this project +as part of his thesis. In 2010 Fabian Pedregosa, Gael Varoquaux, Alexandre Gramfort and Vincent Michel of INRIA took leadership of the project and made the first public diff --git a/doc/api_reference.py b/doc/api_reference.py index 42be5b161787f..b3e658bd22120 100644 --- a/doc/api_reference.py +++ b/doc/api_reference.py @@ -460,6 +460,16 @@ def _get_submodule(module_name, submodule_name): }, ], }, + "sklearn.frozen": { + "short_summary": "Frozen estimators.", + "description": None, + "sections": [ + { + "title": None, + "autosummary": ["FrozenEstimator"], + }, + ], + }, "sklearn.gaussian_process": { "short_summary": "Gaussian processes.", "description": _get_guide("gaussian_process"), @@ -1166,7 +1176,6 @@ def _get_submodule(module_name, submodule_name): "ClassifierTags", "RegressorTags", "TransformerTags", - "default_tags", "get_tags", ], }, diff --git a/doc/conf.py b/doc/conf.py index 47f04b7cbafa4..98e36f4fe36de 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -488,6 +488,9 @@ def add_js_css_files(app, pagename, templatename, context, doctree): "auto_examples/datasets/plot_iris_dataset": ( "auto_examples/decomposition/plot_pca_iris" ), + "auto_examples/linear_model/plot_iris_logistic": ( + "auto_examples/linear_model/plot_logistic_multinomial" + ), "auto_examples/linear_model/plot_ols_3d": ("auto_examples/linear_model/plot_ols"), } html_context["redirects"] = redirects diff --git a/doc/developers/contributing.rst b/doc/developers/contributing.rst index 9f31f8cddf278..129325e275963 100644 --- a/doc/developers/contributing.rst +++ b/doc/developers/contributing.rst @@ -51,7 +51,7 @@ There are many ways to contribute to scikit-learn, with the most common ones being contribution of code or documentation to the project. Improving the documentation is no less important than improving the library itself. If you find a typo in the documentation, or have made improvements, do not hesitate to -send an email to the mailing list or preferably submit a GitHub pull request. +create a GitHub issue or preferably submit a GitHub pull request. Full documentation can be found under the doc/ directory. But there are many other ways to help. In particular helping to @@ -345,8 +345,10 @@ The next steps now describe the process of modifying code and submitting a PR: 12. Follow `these `_ instructions to create a pull request from your fork. This will send an - email to the committers. You may want to consider sending an email to the - mailing list for more visibility. + notification to potential reviewers. You may want to consider sending an message to + the `discord `_ in the development + channel for more visibility if your pull request does not receive attention after + a couple of days (instant replies are not guaranteed though). It is often helpful to keep your local feature branch synchronized with the latest changes of the main scikit-learn repository: @@ -431,13 +433,17 @@ complies with the following rules before marking a PR as "ready for review". The non-regression tests should fail for the code base in the ``main`` branch and pass for the PR code. -5. Follow the :ref:`coding-guidelines`. +5. If your PR is likely to affect users, you need to add a changelog entry describing + your PR changes, see the `following README ` + for more details. -6. When applicable, use the validation tools and scripts in the :mod:`sklearn.utils` +6. Follow the :ref:`coding-guidelines`. + +7. When applicable, use the validation tools and scripts in the :mod:`sklearn.utils` module. A list of utility routines available for developers can be found in the :ref:`developers-utils` page. -7. Often pull requests resolve one or more other issues (or pull requests). +8. Often pull requests resolve one or more other issues (or pull requests). If merging your pull request means that some other issues/PRs should be closed, you should `use keywords to create link to them `_ @@ -447,7 +453,7 @@ complies with the following rules before marking a PR as "ready for review". The related to some other issues/PRs, or it only partially resolves the target issue, create a link to them without using the keywords (e.g., ``Towards #1234``). -8. PRs should often substantiate the change, through benchmarks of +9. PRs should often substantiate the change, through benchmarks of performance and efficiency (see :ref:`monitoring_performances`) or through examples of usage. Examples also illustrate the features and intricacies of the library to users. Have a look at other examples in the `examples/ @@ -456,14 +462,14 @@ complies with the following rules before marking a PR as "ready for review". The functionality is useful in practice and, if possible, compare it to other methods available in scikit-learn. -9. New features have some maintenance overhead. We expect PR authors - to take part in the maintenance for the code they submit, at least - initially. New features need to be illustrated with narrative - documentation in the user guide, with small code snippets. - If relevant, please also add references in the literature, with PDF links - when possible. +10. New features have some maintenance overhead. We expect PR authors + to take part in the maintenance for the code they submit, at least + initially. New features need to be illustrated with narrative + documentation in the user guide, with small code snippets. + If relevant, please also add references in the literature, with PDF links + when possible. -10. The user guide should also include expected time and space complexity +11. The user guide should also include expected time and space complexity of the algorithm and scalability, e.g. "this algorithm can scale to a large number of samples > 100000, but does not scale in dimensionality: `n_features` is expected to be lower than 100". diff --git a/doc/developers/develop.rst b/doc/developers/develop.rst index 606df429340aa..96061891946c1 100644 --- a/doc/developers/develop.rst +++ b/doc/developers/develop.rst @@ -432,27 +432,8 @@ if ``safe=False`` is passed to ``clone``. Estimators can customize the behavior of :func:`base.clone` by defining a `__sklearn_clone__` method. `__sklearn_clone__` must return an instance of the estimator. `__sklearn_clone__` is useful when an estimator needs to hold on to -some state when :func:`base.clone` is called on the estimator. For example, a -frozen meta-estimator for transformers can be defined as follows:: - - class FrozenTransformer(BaseEstimator): - def __init__(self, fitted_transformer): - self.fitted_transformer = fitted_transformer - - def __getattr__(self, name): - # `fitted_transformer`'s attributes are now accessible - return getattr(self.fitted_transformer, name) - - def __sklearn_clone__(self): - return self - - def fit(self, X, y): - # Fitting does not change the state of the estimator - return self - - def fit_transform(self, X, y=None): - # fit_transform only transforms the data - return self.fitted_transformer.transform(X, y) +some state when :func:`base.clone` is called on the estimator. For example, +:class:`~sklearn.frozen.FrozenEstimator` makes use of this. Pipeline compatibility ---------------------- @@ -468,26 +449,19 @@ accepts an optional ``y``. Estimator types --------------- -Some common functionality depends on the kind of estimator passed. -For example, cross-validation in :class:`model_selection.GridSearchCV` and -:func:`model_selection.cross_val_score` defaults to being stratified when used -on a classifier, but not otherwise. Similarly, scorers for average precision -that take a continuous prediction need to call ``decision_function`` for classifiers, -but ``predict`` for regressors. This distinction between classifiers and regressors -is implemented using the ``_estimator_type`` attribute, which takes a string value. -This attribute should have the following values to work as expected: - -- ``"classifier"`` for classifiers -- ``"regressor"`` for regressors -- ``"clusterer"`` for clustering methods -- ``"outlier_detector"`` for outlier detectors -- ``"DensityEstimator"`` for density estimators - -Inheriting from :class:`~base.ClassifierMixin`, :class:`~base.RegressorMixin`, :class:`~base.ClusterMixin`, -:class:`~base.OutlierMixin` or :class:`~base.DensityMixin`, -will set the attribute automatically. When a meta-estimator needs to distinguish -among estimator types, instead of checking ``_estimator_type`` directly, helpers -like :func:`base.is_classifier` should be used. +Some common functionality depends on the kind of estimator passed. For example, +cross-validation in :class:`model_selection.GridSearchCV` and +:func:`model_selection.cross_val_score` defaults to being stratified when used on a +classifier, but not otherwise. Similarly, scorers for average precision that take a +continuous prediction need to call ``decision_function`` for classifiers, but +``predict`` for regressors. This distinction between classifiers and regressors is +implemented by inheriting from :class:`~base.ClassifierMixin`, +:class:`~base.RegressorMixin`, :class:`~base.ClusterMixin`, :class:`~base.OutlierMixin` +or :class:`~base.DensityMixin`, which will set the corresponding :term:`estimator tags` +correctly. + +When a meta-estimator needs to distinguish among estimator types, instead of checking +the value of the tags directly, helpers like :func:`base.is_classifier` should be used. Specific models --------------- diff --git a/doc/developers/maintainer.rst.template b/doc/developers/maintainer.rst.template index d429ad892459a..73a4572bab645 100644 --- a/doc/developers/maintainer.rst.template +++ b/doc/developers/maintainer.rst.template @@ -29,11 +29,6 @@ We adopted the following release schedule: done as a runnable example and check that its HTML rendering looks correct. It should be linked from the what's new file for the new version of scikit-learn. -- Ensure that the changelog and commits correspond, and that the changelog is reasonably - well curated. In particular, make sure that the changelog entries are labeled and - ordered within each section. The order of the labels should be `|MajorFeature|`, - `|Feature|`, `|Efficiency|`, `|Enhancement|`, `|Fix|`, and `|API|`. - .. rubric:: Permissions - The release manager must be a **maintainer** of the @@ -102,7 +97,7 @@ Reference Steps - Do not remove lines but drop commit by replacing `pick` with `drop`. - Commits to pick for a bug-fix release are *generally* prefixed with `FIX`, `CI`, and `DOC`. They should at least include all the commits of the merged PRs that - were milestoned for this release and/or documented as such in the changelog. + were milestoned for this release. - Commits to `drop` for a bug-fix release are *generally* prefixed with `FEAT`, `MAINT`, `ENH`, and `API`. Reasons for not including them is to prevent change of behavior (which should only happen in major/minor releases). @@ -148,9 +143,8 @@ Reference Steps variable in `sklearn/__init__.py`. This means while we are in the release candidate period, the latest stable is two version behind the `main` branch, instead of one. In this PR targeting `main`, you should also include a new what's - new file under the `doc/whats_new/` directory so PRs that target the next version - can contribute their changelog entries to this file in parallel to the release - process. + new file under the `doc/whats_new/` directory so that we prepare the + changelog for the next release. {% endif %} - In the release branch, change the version number `__version__` in diff --git a/doc/developers/tips.rst b/doc/developers/tips.rst index 1c6ea5ba6f6f4..70c201b688578 100644 --- a/doc/developers/tips.rst +++ b/doc/developers/tips.rst @@ -234,7 +234,7 @@ PR-MRG: Add to what's new :: - Please add an entry to the change log at `doc/whats_new/v*.rst`. Like the other entries there, please reference this pull request with `:pr:` and credit yourself (and other contributors if applicable) with `:user:`. + Please add an entry to the future changelog by adding an RST fragment into the module associated with your change located in `doc/whats_new/upcoming_changes`. Refer to the following [README](https://github.com/scikit-learn/scikit-learn/blob/main/doc/whats_new/upcoming_changes/README.md) for full instructions. PR: Don't change unrelated diff --git a/doc/glossary.rst b/doc/glossary.rst index d2df0d959a9c0..691f8df0d308c 100644 --- a/doc/glossary.rst +++ b/doc/glossary.rst @@ -416,15 +416,6 @@ General Concepts the :term:`duck typing` of methods like ``predict_proba`` and through some special attributes on estimator objects: - .. glossary:: - - ``_estimator_type`` - This string-valued attribute identifies an estimator as being a - classifier, regressor, etc. It is set by mixins such as - :class:`base.ClassifierMixin`, but needs to be more explicitly - adopted on a :term:`meta-estimator`. Its value should usually be - checked by way of a helper such as :func:`base.is_classifier`. - For more detailed info, see :ref:`estimator_tags`. feature @@ -709,6 +700,9 @@ General Concepts Elsewhere a sample is called an instance, data point, or observation. ``n_samples`` indicates the number of samples in a dataset, being the number of rows in a data array :term:`X`. + Note that this definition is standard in machine learning and deviates from + statistics where it means *a set of individuals or objects collected or + selected*. sample property sample properties @@ -856,8 +850,8 @@ Class APIs and Estimator Types strategy over the binary classification problem. Classifiers must store a :term:`classes_` attribute after fitting, - and usually inherit from :class:`base.ClassifierMixin`, which sets - their :term:`_estimator_type` attribute. + and inherit from :class:`base.ClassifierMixin`, which sets + their corresponding :term:`estimator tags` correctly. A classifier can be distinguished from other estimators with :func:`~base.is_classifier`. @@ -1000,8 +994,8 @@ Class APIs and Estimator Types A :term:`supervised` (or :term:`semi-supervised`) :term:`predictor` with :term:`continuous` output values. - Regressors usually inherit from :class:`base.RegressorMixin`, which - sets their :term:`_estimator_type` attribute. + Regressors inherit from :class:`base.RegressorMixin`, which sets their + :term:`estimator tags` correctly. A regressor can be distinguished from other estimators with :func:`~base.is_regressor`. diff --git a/doc/governance.rst b/doc/governance.rst index d281b610253fc..5601f80573651 100644 --- a/doc/governance.rst +++ b/doc/governance.rst @@ -67,7 +67,7 @@ The following teams form the core contributors group: repeating patterns where people might struggle, and to help with improving those aspects of the project. - To this end, they have the required permissions on github to label and close + To this end, they have the required permissions on GitHub to label and close issues. :ref:`Their work ` is crucial to improve the communication in the project and limit the crowding of the issue tracker. @@ -158,7 +158,7 @@ are made according to the following rules: consensus), happens on the issue of pull-request page. * **Changes to the API principles and changes to dependencies or supported - versions** happen via a :ref:`slep` and follows the decision-making process + versions** happen via :ref:`slep` and follows the decision-making process outlined above. * **Changes to the governance model** follow the process outlined in `SLEP020 diff --git a/doc/modules/calibration.rst b/doc/modules/calibration.rst index ad183aa79c6c4..0527dcdb81c81 100644 --- a/doc/modules/calibration.rst +++ b/doc/modules/calibration.rst @@ -193,10 +193,11 @@ The main advantage of using `ensemble=False` is computational: it reduces the overall fit time by training only a single base classifier and calibrator pair, decreases the final model size and increases prediction speed. -Alternatively an already fitted classifier can be calibrated by setting -`cv="prefit"`. In this case, the data is not split and all of it is used to -fit the regressor. It is up to the user to -make sure that the data used for fitting the classifier is disjoint from the +Alternatively an already fitted classifier can be calibrated by using a +:class:`~sklearn.frozen.FrozenEstimator` as +``CalibratedClassifierCV(estimator=FrozenEstimator(estimator))``. +It is up to the user to make sure that the data used for fitting the classifier +is disjoint from the data used for fitting the regressor. data used for fitting the regressor. :class:`CalibratedClassifierCV` supports the use of two regression techniques diff --git a/doc/modules/classification_threshold.rst b/doc/modules/classification_threshold.rst index 236c0736f7d23..8b3e6e3a68438 100644 --- a/doc/modules/classification_threshold.rst +++ b/doc/modules/classification_threshold.rst @@ -144,7 +144,9 @@ Manually setting the decision threshold The previous sections discussed strategies to find an optimal decision threshold. It is also possible to manually set the decision threshold using the class :class:`~sklearn.model_selection.FixedThresholdClassifier`. In case that you don't want -to refit the model when calling `fit`, you can set the parameter `prefit=True`. +to refit the model when calling `fit`, wrap your sub-estimator with a +:class:`~sklearn.frozen.FrozenEstimator` and do +``FixedThresholdClassifier(FrozenEstimator(estimator), ...)``. Examples -------- diff --git a/doc/modules/clustering.rst b/doc/modules/clustering.rst index 5fe50db97eaf1..7cf593baf20d1 100644 --- a/doc/modules/clustering.rst +++ b/doc/modules/clustering.rst @@ -241,6 +241,9 @@ to the dataset :math:`X`. * :ref:`sphx_glr_auto_examples_text_plot_document_clustering.py`: Document clustering using :class:`KMeans` and :class:`MiniBatchKMeans` based on sparse data +* :ref:`sphx_glr_auto_examples_cluster_plot_kmeans_plusplus.py`: Using K-means++ + to select seeds for other clustering algorithms. + Low-level parallelism --------------------- diff --git a/doc/modules/cross_validation.rst b/doc/modules/cross_validation.rst index defcd91a6008a..766ab712d72d9 100644 --- a/doc/modules/cross_validation.rst +++ b/doc/modules/cross_validation.rst @@ -665,9 +665,11 @@ Here is a visualization of the cross-validation behavior. :scale: 75% Similar to :class:`KFold`, the test sets from :class:`GroupKFold` will form a -complete partition of all the data. Unlike :class:`KFold`, :class:`GroupKFold` -is not randomized at all, whereas :class:`KFold` is randomized when -``shuffle=True``. +complete partition of all the data. + +While :class:`GroupKFold` attempts to place the same number of samples in each +fold when ``shuffle=False``, when ``shuffle=True`` it attempts to place equal +number of distinct groups in each fold (but doesn not account for group sizes). .. _stratified_group_k_fold: diff --git a/doc/sphinxext/allow_nan_estimators.py b/doc/sphinxext/allow_nan_estimators.py index d2eb0e940b6a1..3b85ce6c87508 100755 --- a/doc/sphinxext/allow_nan_estimators.py +++ b/doc/sphinxext/allow_nan_estimators.py @@ -24,18 +24,18 @@ def make_paragraph_for_estimator_type(estimator_type): # sub-estimator. est = next(_construct_instances(est_class)) - if est.__sklearn_tags__().input_tags.allow_nan: - module_name = ".".join(est_class.__module__.split(".")[:2]) - class_title = f"{est_class.__name__}" - class_url = f"./generated/{module_name}.{class_title}.html" - item = nodes.list_item() - para = nodes.paragraph() - para += nodes.reference( - class_title, text=class_title, internal=False, refuri=class_url - ) - exists = True - item += para - lst += item + if est.__sklearn_tags__().input_tags.allow_nan: + module_name = ".".join(est_class.__module__.split(".")[:2]) + class_title = f"{est_class.__name__}" + class_url = f"./generated/{module_name}.{class_title}.html" + item = nodes.list_item() + para = nodes.paragraph() + para += nodes.reference( + class_title, text=class_title, internal=False, refuri=class_url + ) + exists = True + item += para + lst += item intro += lst return [intro] if exists else None diff --git a/doc/whats_new/upcoming_changes/metadata-routing/29920.fix.rst b/doc/whats_new/upcoming_changes/metadata-routing/29920.fix.rst new file mode 100644 index 0000000000000..a15a66ce6c74f --- /dev/null +++ b/doc/whats_new/upcoming_changes/metadata-routing/29920.fix.rst @@ -0,0 +1,3 @@ +- Many method arguments which shouldn't be included in the routing mechanism are + now excluded and the `set_{method}_request` methods are not generated for them. + By `Adrin Jalali`_ diff --git a/doc/whats_new/upcoming_changes/sklearn.base/30122.api.rst b/doc/whats_new/upcoming_changes/sklearn.base/30122.api.rst new file mode 100644 index 0000000000000..370a2adc1996d --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.base/30122.api.rst @@ -0,0 +1,5 @@ +- Passing a class object to:func:`~sklearn.base.is_classifier`, + :func:`~sklearn.base.is_regressor`, :func:`~sklearn.base.is_transformer`, and + :func:`~sklearn.base.is_outlier_detector` is now deprecated. Pass an instance + instead. + By `Adrin Jalali`_ diff --git a/doc/whats_new/upcoming_changes/sklearn.calibration/30171.api.rst b/doc/whats_new/upcoming_changes/sklearn.calibration/30171.api.rst new file mode 100644 index 0000000000000..4d550af598278 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.calibration/30171.api.rst @@ -0,0 +1,4 @@ +- `cv="prefit"` is deprecated for :class:`~sklearn.calibration.CalibratedClassifierCV`. + Use :class:`~sklearn.frozen.FrozenEstimator` instead, as + `CalibratedClassifierCV(FrozenEstimator(estimator))`. + By `Adrin Jalali`_. diff --git a/doc/whats_new/upcoming_changes/sklearn.frozen/.gitkeep b/doc/whats_new/upcoming_changes/sklearn.frozen/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/doc/whats_new/upcoming_changes/sklearn.frozen/29705.major-feature.rst b/doc/whats_new/upcoming_changes/sklearn.frozen/29705.major-feature.rst new file mode 100644 index 0000000000000..e94a50efd86fa --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.frozen/29705.major-feature.rst @@ -0,0 +1,4 @@ +- :class:`~sklearn.frozen.FrozenEstimator` is now introduced which allows + freezing an estimator. This means calling `.fit` on it has no effect, and doing a + `clone(frozenestimator)` returns the same estimator instead of an unfitted clone. + :pr:`29705` By `Adrin Jalali`_ diff --git a/doc/whats_new/upcoming_changes/sklearn.impute/29451.fix.rst b/doc/whats_new/upcoming_changes/sklearn.impute/29451.fix.rst new file mode 100644 index 0000000000000..fe2551736f698 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.impute/29451.fix.rst @@ -0,0 +1,4 @@ +- When `min_value` and `max_value` are array-like and some features are dropped due to + `keep_empty_features=False`, :class:`impute.IterativeImputer` no longer raises an + error and now indexes correctly. + By :user:`Guntitat Sawadwuthikul ` diff --git a/doc/whats_new/upcoming_changes/sklearn.impute/29950.api.rst b/doc/whats_new/upcoming_changes/sklearn.impute/29950.api.rst new file mode 100644 index 0000000000000..27ac9e06ac320 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.impute/29950.api.rst @@ -0,0 +1,4 @@ +- Add a warning in :class:`impute.SimpleImputer` when `keep_empty_feature=False` and + `strategy="constant"`. In this case empty features are not dropped and this behaviour + will change in 1.8. + By :user:`Arthur Courselle ` and :user:`Simon Riou ` \ No newline at end of file diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/19746.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/19746.fix.rst new file mode 100644 index 0000000000000..6508ca562afe1 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.linear_model/19746.fix.rst @@ -0,0 +1,3 @@ +- In :class:`linear_model.Ridge` and :class:`linear_model.RidgeCV`, after `fit`, + the `coef_` attribute is now of shape `(n_samples,)` like other linear models. + By :user:`Maxwell Liu`, `Guillaume Lemaitre`_, and `AdrinJalali`_ diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30040.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30040.fix.rst new file mode 100644 index 0000000000000..f4a91911345e3 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.linear_model/30040.fix.rst @@ -0,0 +1,6 @@ +- :class:`linear_model.LinearRegression` now sets the `cond` parameter when + calling the `scipy.linalg.lstsq` solver on dense input data. This ensures + more numerically robust results on rank-deficient data. In particular, it + empirically fixes the expected equivalence property between fitting with + reweighted or with repeated data points. + :pr:`30040` by :user:`Antoine Baker `. diff --git a/doc/whats_new/upcoming_changes/sklearn.linear_model/30227.fix.rst b/doc/whats_new/upcoming_changes/sklearn.linear_model/30227.fix.rst new file mode 100644 index 0000000000000..d3a76ced7fc6b --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.linear_model/30227.fix.rst @@ -0,0 +1,3 @@ +- :class:`~sklearn.linear_model.SGDOneClassSVM` now correctly inherits from + :class:`~sklearn.base.OutlierMixin` and the tags are correctly set. + By :user:`Guillaume Lemaitre ` \ No newline at end of file diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/26367.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/26367.enhancement.rst new file mode 100644 index 0000000000000..0fc5bd059c42f --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.metrics/26367.enhancement.rst @@ -0,0 +1,6 @@ +- :meth:`metrics.RocCurveDisplay.from_estimator`, + :meth:`metrics.RocCurveDisplay.from_predictions`, + :meth:`metrics.PrecisionRecallDisplay.from_estimator`, and + :meth:`metrics.PrecisionRecallDisplay.from_predictions` now accept a new keyword + `despine` to remove the top and right spines of the plot in order to make it clearer. + By :user:`Yao Xiao `. \ No newline at end of file diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/27412.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/27412.fix.rst index b62c1c2b91790..350bd92a19478 100644 --- a/doc/whats_new/upcoming_changes/sklearn.metrics/27412.fix.rst +++ b/doc/whats_new/upcoming_changes/sklearn.metrics/27412.fix.rst @@ -1,3 +1,3 @@ -- :func:`metrics.roc_auc_score` will now correctly return 0.0 and +- :func:`metrics.roc_auc_score` will now correctly return np.nan and warn user if only one class is present in the labels. - By :user:`Gleb Levitski ` + By :user:`Gleb Levitski ` and :user:`Janez Demšar ` diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/28509.feature.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/28509.feature.rst new file mode 100644 index 0000000000000..755d586dbce2b --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.metrics/28509.feature.rst @@ -0,0 +1,3 @@ +- Adds `zero_division` to :func:`metrics.matthews_corrcoef`. + When there is a zero division, the metric is undefined and this value is returned. + By :user:`Marc Torrellas Socastro ` and :user:`Noam Keidar ` \ No newline at end of file diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/29213.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/29213.enhancement.rst new file mode 100644 index 0000000000000..35ad57056050d --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.metrics/29213.enhancement.rst @@ -0,0 +1,3 @@ +- :func:`sklearn.metrics.accuracy_score` now includes a `zero_division` + parameter to raise a warning when `y_true` and `y_pred` are empty. + By :user:`Jaimin Chauhan `. diff --git a/doc/whats_new/upcoming_changes/sklearn.metrics/30013.fix.rst b/doc/whats_new/upcoming_changes/sklearn.metrics/30013.fix.rst new file mode 100644 index 0000000000000..4cee2ec523fb8 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.metrics/30013.fix.rst @@ -0,0 +1,3 @@ +- :func:`metrics.roc_auc_score` will now correctly return np.nan and + warn user if only one class is present in the labels. + By :user:`Gleb Levitski ` and :user:`Janez Demšar ` \ No newline at end of file diff --git a/doc/whats_new/upcoming_changes/sklearn.model_selection/28519.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.model_selection/28519.enhancement.rst new file mode 100644 index 0000000000000..72098ca04ead5 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.model_selection/28519.enhancement.rst @@ -0,0 +1,3 @@ +- :class:`~model_selection.GroupKFold` now has the ability to shuffle groups into + different folds when `shuffle=True`. + By :user:`Zachary Vealey ` diff --git a/doc/whats_new/upcoming_changes/sklearn.model_selection/29067.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.model_selection/29067.enhancement.rst deleted file mode 100644 index 9775da0486ffa..0000000000000 --- a/doc/whats_new/upcoming_changes/sklearn.model_selection/29067.enhancement.rst +++ /dev/null @@ -1,4 +0,0 @@ -- Add the parameter `prefit` to - :class:`model_selection.FixedThresholdClassifier` allowing the use of a pre-fitted - estimator without re-fitting it. - By :user:`Guillaume Lemaitre ` diff --git a/doc/whats_new/upcoming_changes/sklearn.model_selection/30172.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.model_selection/30172.enhancement.rst new file mode 100644 index 0000000000000..266525cf5ba24 --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.model_selection/30172.enhancement.rst @@ -0,0 +1,4 @@ +- There is no need to call `fit` on a + :class:`~sklearn.model_selection.FixedThresholdClassifier` if the underlying + estimator is already fitted. + By :user:`Adrin Jalali ` diff --git a/doc/whats_new/upcoming_changes/sklearn.neighbors/25330.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.neighbors/25330.enhancement.rst new file mode 100644 index 0000000000000..ed95889127afc --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.neighbors/25330.enhancement.rst @@ -0,0 +1,6 @@ +- :class:`neighbors.NearestNeighbors`, :class:`KNeighborsClassifier`, + :class:`KNeighborsRegressor`, :class:`RadiusNeighborsClassifier`, + :class:`RadiusNeighborsRegressor`, :class:`KNeighborsTransformer`, + :class:`RadiusNeighborsTransformer`, and :class:`LocalOutlierFactor` + now work with `metric="nan_euclidean"`, supporting `nan` inputs. + By :user:`Carlo Lemos `, `Guillaume Lemaitre`_, and `Adrin Jalali`_ diff --git a/doc/whats_new/upcoming_changes/sklearn.neighbors/26689.enhancement.rst b/doc/whats_new/upcoming_changes/sklearn.neighbors/26689.enhancement.rst new file mode 100644 index 0000000000000..ebc50d1bc6aaa --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.neighbors/26689.enhancement.rst @@ -0,0 +1,7 @@ +- Add :meth:`neighbors.NearestCentroid.decision_function`, + :meth:`neighbors.NearestCentroid.predict_proba` and + :meth:`neighbors.NearestCentroid.predict_log_proba` + to the :class:`neighbors.NearestCentroid` estimator class. + Support the case when `X` is sparse and `shrinking_threshold` + is not `None` in :class:`neighbors.NearestCentroid`. + By :user:`Matthew Ning ` diff --git a/doc/whats_new/upcoming_changes/sklearn.tree/17575.fix.rst b/doc/whats_new/upcoming_changes/sklearn.tree/17575.fix.rst new file mode 100644 index 0000000000000..f04954244f19c --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.tree/17575.fix.rst @@ -0,0 +1,3 @@ +- Escape double quotes for labels and feature names when exporting trees to Graphviz + format. + By :user:`Santiago M. Mola `. diff --git a/doc/whats_new/upcoming_changes/sklearn.utils/30122.api.rst b/doc/whats_new/upcoming_changes/sklearn.utils/30122.api.rst new file mode 100644 index 0000000000000..50dec6ff8c82d --- /dev/null +++ b/doc/whats_new/upcoming_changes/sklearn.utils/30122.api.rst @@ -0,0 +1,6 @@ +- Using `_estimator_type` to set the estimator type is deprecated. Inherit from + :class:`~sklearn.base.ClassifierMixin`, :class:`~sklearn.base.RegressorMixin`, + :class:`~sklearn.base.TransformerMixin`, or :class:`~sklearn.base.OutlierMixin` + instead. Alternatively, you can set `estimator_type` in :class:`~sklearn.utils.Tags` + in the `__sklearn_tags__` method. + By `Adrin Jalali`_ diff --git a/doc/whats_new/upcoming_changes/towncrier_template.rst.jinja2 b/doc/whats_new/upcoming_changes/towncrier_template.rst.jinja2 index b03c978fba83f..b10ce4bedec27 100644 --- a/doc/whats_new/upcoming_changes/towncrier_template.rst.jinja2 +++ b/doc/whats_new/upcoming_changes/towncrier_template.rst.jinja2 @@ -1,12 +1,15 @@ -{% if render_title %} -{% if versiondata.name %} -{{ versiondata.name }} {{ versiondata.version }} ({{ versiondata.date }}) -{{ top_underline * ((versiondata.name + versiondata.version + versiondata.date)|length + 4)}} -{% else %} -{{ versiondata.version }} ({{ versiondata.date }}) -{{ top_underline * ((versiondata.version + versiondata.date)|length + 3)}} -{% endif %} -{% endif %} +{% set title = "Version " + versiondata.version %} +{{ title }} +{{ top_underline * title|length }} + +{% set month_names = { + '01': 'January', '02': 'February', '03': 'March', '04': 'April', + '05': 'May', '06': 'June', '07': 'July', '08': 'August', + '09': 'September', '10': 'October', '11': 'November', '12': 'December' +} %} +{% set year, month, _ = versiondata.date.split('-') %} +{% set release_date = month_names[month] + ' ' + year %} +**{{ release_date }}** {% set underline = underlines[0] %} {% for section, content_per_category in sections.items() if content_per_category %} diff --git a/doc/whats_new/v1.6.rst b/doc/whats_new/v1.6.rst index 6930dca21cf1f..2251b46b3c137 100644 --- a/doc/whats_new/v1.6.rst +++ b/doc/whats_new/v1.6.rst @@ -13,6 +13,15 @@ Version 1.6 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.6.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 + .. include:: changelog_legend.inc .. _changes_1_6: diff --git a/examples/calibration/plot_calibration_multiclass.py b/examples/calibration/plot_calibration_multiclass.py index 8525c76221a8f..2208292d1ccc9 100644 --- a/examples/calibration/plot_calibration_multiclass.py +++ b/examples/calibration/plot_calibration_multiclass.py @@ -64,10 +64,11 @@ class of an instance (red: class 1, green: class 2, blue: class 3). # using the valid data subset (400 samples) in a 2-stage process. from sklearn.calibration import CalibratedClassifierCV +from sklearn.frozen import FrozenEstimator clf = RandomForestClassifier(n_estimators=25) clf.fit(X_train, y_train) -cal_clf = CalibratedClassifierCV(clf, method="sigmoid", cv="prefit") +cal_clf = CalibratedClassifierCV(FrozenEstimator(clf), method="sigmoid") cal_clf.fit(X_valid, y_valid) # %% diff --git a/examples/frozen/plot_frozen_examples.py b/examples/frozen/plot_frozen_examples.py new file mode 100644 index 0000000000000..373e47ff2d68c --- /dev/null +++ b/examples/frozen/plot_frozen_examples.py @@ -0,0 +1,98 @@ +""" +=================================== +Examples of Using `FrozenEstimator` +=================================== + +This examples 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 +to a meta-estimator, such as :class:`~sklearn.model_selection.FixedThresholdClassifier` +without letting the meta-estimator refit the estimator. +""" + +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +# %% +# Setting a decision threshold for a pre-fitted classifier +# -------------------------------------------------------- +# Fitted classifiers in scikit-learn use an arbitrary decision threshold to decide +# which class the given sample belongs to. The decision threshold is either `0.0` on the +# value returned by :term:`decision_function`, or `0.5` on the probability returned by +# :term:`predict_proba`. +# +# However, one might want to set a custom decision threshold. We can do this by +# using :class:`~sklearn.model_selection.FixedThresholdClassifier` and wrapping the +# classifier with :class:`~sklearn.frozen.FrozenEstimator`. +from sklearn.datasets import make_classification +from sklearn.frozen import FrozenEstimator +from sklearn.linear_model import LogisticRegression +from sklearn.model_selection import FixedThresholdClassifier, train_test_split + +X, y = make_classification(n_samples=1000, random_state=0) +X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) +classifier = LogisticRegression().fit(X_train, y_train) + +print( + "Probability estimates for three data points:\n" + f"{classifier.predict_proba(X_test[-3:]).round(3)}" +) +print( + "Predicted class for the same three data points:\n" + f"{classifier.predict(X_test[-3:])}" +) + +# %% +# Now imagine you'd want to set a different decision threshold on the probability +# estimates. We can do this by wrapping the classifier with +# :class:`~sklearn.frozen.FrozenEstimator` and passing it to +# :class:`~sklearn.model_selection.FixedThresholdClassifier`. + +threshold_classifier = FixedThresholdClassifier( + estimator=FrozenEstimator(classifier), threshold=0.9 +) + +# %% +# Note that in the above piece of code, calling `fit` on +# :class:`~sklearn.model_selection.FixedThresholdClassifier` does not refit the +# underlying classifier. +# +# Now, let's see how the predictions changed with respect to the probability +# threshold. +print( + "Probability estimates for three data points with FixedThresholdClassifier:\n" + f"{threshold_classifier.predict_proba(X_test[-3:]).round(3)}" +) +print( + "Predicted class for the same three data points with FixedThresholdClassifier:\n" + f"{threshold_classifier.predict(X_test[-3:])}" +) + +# %% +# We see that the probability estimates stay the same, but since a different decision +# threshold is used, the predicted classes are different. +# +# Please refer to +# :ref:`sphx_glr_auto_examples_model_selection_plot_cost_sensitive_learning.py` +# to learn about cost-sensitive learning and decision threshold tuning. + +# %% +# Calibration of a pre-fitted classifier +# -------------------------------------- +# You can use :class:`~sklearn.frozen.FrozenEstimator` to calibrate a pre-fitted +# classifier using :class:`~sklearn.calibration.CalibratedClassifierCV`. +from sklearn.calibration import CalibratedClassifierCV +from sklearn.metrics import brier_score_loss + +calibrated_classifier = CalibratedClassifierCV( + estimator=FrozenEstimator(classifier) +).fit(X_train, y_train) + +prob_pos_clf = classifier.predict_proba(X_test)[:, 1] +clf_score = brier_score_loss(y_test, prob_pos_clf) +print(f"No calibration: {clf_score:.3f}") + +prob_pos_calibrated = calibrated_classifier.predict_proba(X_test)[:, 1] +calibrated_score = brier_score_loss(y_test, prob_pos_calibrated) +print(f"With calibration: {calibrated_score:.3f}") diff --git a/examples/linear_model/plot_iris_logistic.py b/examples/linear_model/plot_iris_logistic.py deleted file mode 100644 index 481312c94c789..0000000000000 --- a/examples/linear_model/plot_iris_logistic.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -========================================================= -Logistic Regression 3-class Classifier -========================================================= - -Show below is a logistic-regression classifiers decision boundaries on the -first two dimensions (sepal length and width) of the `iris -`_ dataset. The datapoints -are colored according to their labels. - -""" - -# Authors: The scikit-learn developers -# SPDX-License-Identifier: BSD-3-Clause - -import matplotlib.pyplot as plt - -from sklearn import datasets -from sklearn.inspection import DecisionBoundaryDisplay -from sklearn.linear_model import LogisticRegression - -# import some data to play with -iris = datasets.load_iris() -X = iris.data[:, :2] # we only take the first two features. -Y = iris.target - -# Create an instance of Logistic Regression Classifier and fit the data. -logreg = LogisticRegression(C=1e5) -logreg.fit(X, Y) - -_, ax = plt.subplots(figsize=(4, 3)) -DecisionBoundaryDisplay.from_estimator( - logreg, - X, - cmap=plt.cm.Paired, - ax=ax, - response_method="predict", - plot_method="pcolormesh", - shading="auto", - xlabel="Sepal length", - ylabel="Sepal width", - eps=0.5, -) - -# Plot also the training points -plt.scatter(X[:, 0], X[:, 1], c=Y, edgecolors="k", cmap=plt.cm.Paired) - - -plt.xticks(()) -plt.yticks(()) - -plt.show() diff --git a/examples/linear_model/plot_logistic_multinomial.py b/examples/linear_model/plot_logistic_multinomial.py index ca9f1717fe346..c12229c81c7f1 100644 --- a/examples/linear_model/plot_logistic_multinomial.py +++ b/examples/linear_model/plot_logistic_multinomial.py @@ -1,70 +1,193 @@ """ -==================================================== -Plot multinomial and One-vs-Rest Logistic Regression -==================================================== +====================================================================== +Decision Boundaries of Multinomial and One-vs-Rest Logistic Regression +====================================================================== -Plot decision surface of multinomial and One-vs-Rest Logistic Regression. -The hyperplanes corresponding to the three One-vs-Rest (OVR) classifiers -are represented by the dashed lines. +This example compares decision boundaries of multinomial and one-vs-rest +logistic regression on a 2D dataset with three classes. +We make a comparison of the decision boundaries of both methods that is equivalent +to call the method `predict`. In addition, we plot the hyperplanes that correspond to +the line when the probability estimate for a class is of 0.5. """ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause +# %% +# Dataset Generation +# ------------------ +# +# We generate a synthetic dataset using :func:`~sklearn.datasets.make_blobs` function. +# The dataset consists of 1,000 samples from three different classes, +# centered around [-5, 0], [0, 1.5], and [5, -1]. After generation, we apply a linear +# transformation to introduce some correlation between features and make the problem +# more challenging. This results in a 2D dataset with three overlapping classes, +# suitable for demonstrating the differences between multinomial and one-vs-rest +# logistic regression. import matplotlib.pyplot as plt import numpy as np from sklearn.datasets import make_blobs -from sklearn.inspection import DecisionBoundaryDisplay -from sklearn.linear_model import LogisticRegression -from sklearn.multiclass import OneVsRestClassifier -# make 3-class dataset for classification centers = [[-5, 0], [0, 1.5], [5, -1]] -X, y = make_blobs(n_samples=1000, centers=centers, random_state=40) +X, y = make_blobs(n_samples=1_000, centers=centers, random_state=40) transformation = [[0.4, 0.2], [-0.4, 1.2]] X = np.dot(X, transformation) -for multi_class in ("multinomial", "ovr"): - clf = LogisticRegression(solver="sag", max_iter=100, random_state=42) - if multi_class == "ovr": - clf = OneVsRestClassifier(clf) - clf.fit(X, y) +fig, ax = plt.subplots(figsize=(6, 4)) + +scatter = ax.scatter(X[:, 0], X[:, 1], c=y, edgecolor="black") +ax.set(title="Synthetic Dataset", xlabel="Feature 1", ylabel="Feature 2") +_ = ax.legend(*scatter.legend_elements(), title="Classes") + + +# %% +# Classifier Training +# ------------------- +# +# We train two different logistic regression classifiers: multinomial and one-vs-rest. +# The multinomial classifier handles all classes simultaneously, while the one-vs-rest +# approach trains a binary classifier for each class against all others. +from sklearn.linear_model import LogisticRegression +from sklearn.multiclass import OneVsRestClassifier + +logistic_regression_multinomial = LogisticRegression().fit(X, y) +logistic_regression_ovr = OneVsRestClassifier(LogisticRegression()).fit(X, y) + +accuracy_multinomial = logistic_regression_multinomial.score(X, y) +accuracy_ovr = logistic_regression_ovr.score(X, y) - # print the training scores - print("training score : %.3f (%s)" % (clf.score(X, y), multi_class)) +# %% +# Decision Boundaries Visualization +# --------------------------------- +# +# Let's visualize the decision boundaries of both models that is provided by the +# method `predict` of the classifiers. +from sklearn.inspection import DecisionBoundaryDisplay + +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), sharex=True, sharey=True) - _, ax = plt.subplots() +for model, title, ax in [ + ( + logistic_regression_multinomial, + f"Multinomial Logistic Regression\n(Accuracy: {accuracy_multinomial:.3f})", + ax1, + ), + ( + logistic_regression_ovr, + f"One-vs-Rest Logistic Regression\n(Accuracy: {accuracy_ovr:.3f})", + ax2, + ), +]: DecisionBoundaryDisplay.from_estimator( - clf, X, response_method="predict", cmap=plt.cm.Paired, ax=ax + model, + X, + ax=ax, + response_method="predict", + alpha=0.8, ) - plt.title("Decision surface of LogisticRegression (%s)" % multi_class) - plt.axis("tight") - - # Plot also the training points - colors = "bry" - for i, color in zip(clf.classes_, colors): - idx = np.where(y == i) - plt.scatter(X[idx, 0], X[idx, 1], c=color, edgecolor="black", s=20) - - # Plot the three one-against-all classifiers - xmin, xmax = plt.xlim() - ymin, ymax = plt.ylim() - if multi_class == "ovr": - coef = np.concatenate([est.coef_ for est in clf.estimators_]) - intercept = np.concatenate([est.intercept_ for est in clf.estimators_]) + scatter = ax.scatter(X[:, 0], X[:, 1], c=y, edgecolor="k") + legend = ax.legend(*scatter.legend_elements(), title="Classes") + ax.add_artist(legend) + ax.set_title(title) + + +# %% +# We see that the decision boundaries are different. This difference stems from their +# approaches: +# +# - Multinomial logistic regression considers all classes simultaneously during +# optimization. +# - One-vs-rest logistic regression fits each class independently against all others. +# +# These distinct strategies can lead to varying decision boundaries, especially in +# complex multi-class problems. +# +# Hyperplanes Visualization +# -------------------------- +# +# We also visualize the hyperplanes that correspond to the line when the probability +# estimate for a class is of 0.5. +def plot_hyperplanes(classifier, X, ax): + xmin, xmax = X[:, 0].min(), X[:, 0].max() + ymin, ymax = X[:, 1].min(), X[:, 1].max() + ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax)) + + if isinstance(classifier, OneVsRestClassifier): + coef = np.concatenate([est.coef_ for est in classifier.estimators_]) + intercept = np.concatenate([est.intercept_ for est in classifier.estimators_]) else: - coef = clf.coef_ - intercept = clf.intercept_ + coef = classifier.coef_ + intercept = classifier.intercept_ - def plot_hyperplane(c, color): - def line(x0): - return (-(x0 * coef[c, 0]) - intercept[c]) / coef[c, 1] + for i in range(coef.shape[0]): + w = coef[i] + a = -w[0] / w[1] + xx = np.linspace(xmin, xmax) + yy = a * xx - (intercept[i]) / w[1] + ax.plot(xx, yy, "--", linewidth=3, label=f"Class {i}") - plt.plot([xmin, xmax], [line(xmin), line(xmax)], ls="--", color=color) + return ax.get_legend_handles_labels() - for i, color in zip(clf.classes_, colors): - plot_hyperplane(i, color) + +# %% +fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5), sharex=True, sharey=True) + +for model, title, ax in [ + ( + logistic_regression_multinomial, + "Multinomial Logistic Regression Hyperplanes", + ax1, + ), + (logistic_regression_ovr, "One-vs-Rest Logistic Regression Hyperplanes", ax2), +]: + hyperplane_handles, hyperplane_labels = plot_hyperplanes(model, X, ax) + scatter = ax.scatter(X[:, 0], X[:, 1], c=y, edgecolor="k") + scatter_handles, scatter_labels = scatter.legend_elements() + + all_handles = hyperplane_handles + scatter_handles + all_labels = hyperplane_labels + scatter_labels + + ax.legend(all_handles, all_labels, title="Classes") + ax.set_title(title) plt.show() + +# %% +# While the hyperplanes for classes 0 and 2 are quite similar between the two methods, +# we observe that the hyperplane for class 1 is notably different. This difference stems +# from the fundamental approaches of one-vs-rest and multinomial logistic regression: +# +# For one-vs-rest logistic regression: +# +# - Each hyperplane is determined independently by considering one class against all +# others. +# - For class 1, the hyperplane represents the decision boundary that best separates +# class 1 from the combined classes 0 and 2. +# - This binary approach can lead to simpler decision boundaries but may not capture +# complex relationships between all classes simultaneously. +# - There is no possible interpretation of the conditional class probabilities. +# +# For multinomial logistic regression: +# +# - All hyperplanes are determined simultaneously, considering the relationships between +# all classes at once. +# - The loss minimized by the model is a proper scoring rule, which means that the model +# is optimized to estimate the conditional class probabilities that are, therefore, +# meaningful. +# - Each hyperplane represents the decision boundary where the probability of one class +# becomes higher than the others, based on the overall probability distribution. +# - This approach can capture more nuanced relationships between classes, potentially +# leading to more accurate classification in multi-class problems. +# +# The difference in hyperplanes, especially for class 1, highlights how these methods +# can produce different decision boundaries despite similar overall accuracy. +# +# In practice, using multinomial logistic regression is recommended since it minimizes a +# well-formulated loss function, leading to better-calibrated class probabilities and +# thus more interpretable results. When it comes to decision boundaries, one should +# formulate a utility function to transform the class probabilities into a meaningful +# quantity for the problem at hand. One-vs-rest allows for different decision boundaries +# but does not allow for fine-grained control over the trade-off between the classes as +# a utility function would. diff --git a/examples/model_selection/plot_cost_sensitive_learning.py b/examples/model_selection/plot_cost_sensitive_learning.py index 3021d5aaab53d..b247b252586ba 100644 --- a/examples/model_selection/plot_cost_sensitive_learning.py +++ b/examples/model_selection/plot_cost_sensitive_learning.py @@ -660,15 +660,18 @@ def business_metric(y_true, y_pred, amount): # # The class :class:`~sklearn.model_selection.FixedThresholdClassifier` allows us to # manually set the decision threshold. At prediction time, it behave as the previous -# tuned model but no search is performed during the fitting process. +# tuned model but no search is performed during the fitting process. Note that here +# we use :class:`~sklearn.frozen.FrozenEstimator` to wrap the predictive model to +# avoid any refitting. # # Here, we will reuse the decision threshold found in the previous section to create a # new model and check that it gives the same results. +from sklearn.frozen import FrozenEstimator from sklearn.model_selection import FixedThresholdClassifier model_fixed_threshold = FixedThresholdClassifier( - estimator=model, threshold=tuned_model.best_threshold_, prefit=True -).fit(data_train, target_train) + estimator=FrozenEstimator(model), threshold=tuned_model.best_threshold_ +) # %% business_score = business_scorer( diff --git a/examples/model_selection/plot_precision_recall.py b/examples/model_selection/plot_precision_recall.py index f40c7262fb03f..7ce8c96e09d01 100644 --- a/examples/model_selection/plot_precision_recall.py +++ b/examples/model_selection/plot_precision_recall.py @@ -147,7 +147,7 @@ from sklearn.metrics import PrecisionRecallDisplay display = PrecisionRecallDisplay.from_estimator( - classifier, X_test, y_test, name="LinearSVC", plot_chance_level=True + classifier, X_test, y_test, name="LinearSVC", plot_chance_level=True, despine=True ) _ = display.ax_.set_title("2-class Precision-Recall curve") @@ -158,7 +158,7 @@ y_score = classifier.decision_function(X_test) display = PrecisionRecallDisplay.from_predictions( - y_test, y_score, name="LinearSVC", plot_chance_level=True + y_test, y_score, name="LinearSVC", plot_chance_level=True, despine=True ) _ = display.ax_.set_title("2-class Precision-Recall curve") @@ -228,7 +228,7 @@ average_precision=average_precision["micro"], prevalence_pos_label=Counter(Y_test.ravel())[1] / Y_test.size, ) -display.plot(plot_chance_level=True) +display.plot(plot_chance_level=True, despine=True) _ = display.ax_.set_title("Micro-averaged over all classes") # %% @@ -264,7 +264,9 @@ precision=precision[i], average_precision=average_precision[i], ) - display.plot(ax=ax, name=f"Precision-recall for class {i}", color=color) + display.plot( + ax=ax, name=f"Precision-recall for class {i}", color=color, despine=True + ) # add the legend for the iso-f1 curves handles, labels = display.ax_.get_legend_handles_labels() diff --git a/examples/model_selection/plot_roc.py b/examples/model_selection/plot_roc.py index 1b2a9760342a3..70bf3bd3f486d 100644 --- a/examples/model_selection/plot_roc.py +++ b/examples/model_selection/plot_roc.py @@ -131,6 +131,7 @@ name=f"{class_of_interest} vs the rest", color="darkorange", plot_chance_level=True, + despine=True, ) _ = display.ax_.set( xlabel="False Positive Rate", @@ -166,6 +167,7 @@ name="micro-average OvR", color="darkorange", plot_chance_level=True, + despine=True, ) _ = display.ax_.set( xlabel="False Positive Rate", @@ -285,6 +287,7 @@ color=color, ax=ax, plot_chance_level=(class_id == 2), + despine=True, ) _ = ax.set( @@ -366,6 +369,7 @@ ax=ax, name=f"{label_b} as positive class", plot_chance_level=True, + despine=True, ) ax.set( xlabel="False Positive Rate", diff --git a/pyproject.toml b/pyproject.toml index 5e2ce0740bdc6..0985132468a35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -266,7 +266,6 @@ package = "sklearn" # name of your package directory = "doc/whats_new/upcoming_changes" issue_format = ":pr:`{issue}`" template = "doc/whats_new/upcoming_changes/towncrier_template.rst.jinja2" - title_format = "Version {version} ({project_date})" all_bullets = false [[tool.towncrier.type]] @@ -384,6 +383,10 @@ package = "sklearn" # name of your package name = ":mod:`sklearn.feature_selection`" path = "sklearn.feature_selection" + [[tool.towncrier.section]] + name = ":mod:`sklearn.frozen`" + path = "sklearn.frozen" + [[tool.towncrier.section]] name = ":mod:`sklearn.gaussian_process`" path = "sklearn.gaussian_process" diff --git a/sklearn/__init__.py b/sklearn/__init__.py index 32a0087ec9fae..0f6ad7a71c645 100644 --- a/sklearn/__init__.py +++ b/sklearn/__init__.py @@ -87,6 +87,7 @@ "externals", "feature_extraction", "feature_selection", + "frozen", "gaussian_process", "inspection", "isotonic", diff --git a/sklearn/base.py b/sklearn/base.py index b1121af3f06db..bd5e07c2167dd 100644 --- a/sklearn/base.py +++ b/sklearn/base.py @@ -20,7 +20,14 @@ from .utils._metadata_requests import _MetadataRequester, _routing_enabled from .utils._param_validation import validate_parameter_constraints from .utils._set_output import _SetOutputMixin -from .utils._tags import default_tags +from .utils._tags import ( + ClassifierTags, + RegressorTags, + Tags, + TargetTags, + TransformerTags, + get_tags, +) from .utils.fixes import _IS_32BIT from .utils.validation import ( _check_feature_names_in, @@ -380,7 +387,13 @@ def __setstate__(self, state): self.__dict__.update(state) def __sklearn_tags__(self): - return default_tags(self) + return Tags( + estimator_type=None, + target_tags=TargetTags(required=False), + transformer_tags=None, + regressor_tags=None, + classifier_tags=None, + ) def _validate_params(self): """Validate types and values of constructor parameters @@ -432,9 +445,10 @@ class ClassifierMixin: This mixin defines the following functionality: - - `_estimator_type` class attribute defaulting to `"classifier"`; + - set estimator type to `"classifier"` through the `estimator_type` tag; - `score` method that default to :func:`~sklearn.metrics.accuracy_score`. - - enforce that `fit` requires `y` to be passed through the `requires_y` tag. + - enforce that `fit` requires `y` to be passed through the `requires_y` tag, + which is done by setting the classifier type tag. Read more in the :ref:`User Guide `. @@ -460,8 +474,16 @@ class ClassifierMixin: 0.66... """ + # TODO(1.8): Remove this attribute _estimator_type = "classifier" + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.estimator_type = "classifier" + tags.classifier_tags = ClassifierTags() + tags.target_tags.required = True + return tags + def score(self, X, y, sample_weight=None): """ Return the mean accuracy on the given test data and labels. @@ -496,9 +518,10 @@ class RegressorMixin: This mixin defines the following functionality: - - `_estimator_type` class attribute defaulting to `"regressor"`; + - set estimator type to `"regressor"` through the `estimator_type` tag; - `score` method that default to :func:`~sklearn.metrics.r2_score`. - - enforce that `fit` requires `y` to be passed through the `requires_y` tag. + - enforce that `fit` requires `y` to be passed through the `requires_y` tag, + which is done by setting the regressor type tag. Read more in the :ref:`User Guide `. @@ -524,8 +547,16 @@ class RegressorMixin: 0.0 """ + # TODO(1.8): Remove this attribute _estimator_type = "regressor" + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.estimator_type = "regressor" + tags.regressor_tags = RegressorTags() + tags.target_tags.required = True + return tags + def score(self, X, y, sample_weight=None): """Return the coefficient of determination of the prediction. @@ -576,7 +607,7 @@ def score(self, X, y, sample_weight=None): class ClusterMixin: """Mixin class for all cluster estimators in scikit-learn. - - `_estimator_type` class attribute defaulting to `"clusterer"`; + - set estimator type to `"clusterer"` through the `estimator_type` tag; - `fit_predict` method returning the cluster labels associated to each sample. Examples @@ -592,8 +623,16 @@ class ClusterMixin: array([1, 1, 1]) """ + # TODO(1.8): Remove this attribute _estimator_type = "clusterer" + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.estimator_type = "clusterer" + if tags.transformer_tags is not None: + tags.transformer_tags.preserves_dtype = [] + return tags + def fit_predict(self, X, y=None, **kwargs): """ Perform clustering on `X` and returns cluster labels. @@ -621,12 +660,6 @@ def fit_predict(self, X, y=None, **kwargs): self.fit(X, **kwargs) return self.labels_ - def __sklearn_tags__(self): - tags = super().__sklearn_tags__() - if tags.transformer_tags is not None: - tags.transformer_tags.preserves_dtype = [] - return tags - class BiclusterMixin: """Mixin class for all bicluster estimators in scikit-learn. @@ -763,6 +796,11 @@ class TransformerMixin(_SetOutputMixin): array([1, 1, 1]) """ + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.transformer_tags = TransformerTags() + return tags + def fit_transform(self, X, y=None, **fit_params): """ Fit to data, then transform it. @@ -833,8 +871,8 @@ class OneToOneFeatureMixin: Examples -------- >>> import numpy as np - >>> from sklearn.base import OneToOneFeatureMixin - >>> class MyEstimator(OneToOneFeatureMixin): + >>> from sklearn.base import OneToOneFeatureMixin, BaseEstimator + >>> class MyEstimator(OneToOneFeatureMixin, BaseEstimator): ... def fit(self, X, y=None): ... self.n_features_in_ = X.shape[1] ... return self @@ -885,8 +923,8 @@ class ClassNamePrefixFeaturesOutMixin: Examples -------- >>> import numpy as np - >>> from sklearn.base import ClassNamePrefixFeaturesOutMixin - >>> class MyEstimator(ClassNamePrefixFeaturesOutMixin): + >>> from sklearn.base import ClassNamePrefixFeaturesOutMixin, BaseEstimator + >>> class MyEstimator(ClassNamePrefixFeaturesOutMixin, BaseEstimator): ... def fit(self, X, y=None): ... self._n_features_out = X.shape[1] ... return self @@ -923,7 +961,7 @@ class DensityMixin: This mixin defines the following functionality: - - `_estimator_type` class attribute defaulting to `"DensityEstimator"`; + - sets estimator type to `"density_estimator"` through the `estimator_type` tag; - `score` method that default that do no-op. Examples @@ -938,8 +976,14 @@ class DensityMixin: True """ + # TODO(1.8): Remove this attribute _estimator_type = "DensityEstimator" + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.estimator_type = "density_estimator" + return tags + def score(self, X, y=None): """Return the score of the model on the data `X`. @@ -963,7 +1007,7 @@ class OutlierMixin: This mixin defines the following functionality: - - `_estimator_type` class attribute defaulting to `outlier_detector`; + - set estimator type to `"outlier_detector"` through the `estimator_type` tag; - `fit_predict` method that default to `fit` and `predict`. Examples @@ -982,8 +1026,14 @@ class OutlierMixin: array([1., 1., 1.]) """ + # TODO(1.8): Remove this attribute _estimator_type = "outlier_detector" + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.estimator_type = "outlier_detector" + return tags + def fit_predict(self, X, y=None, **kwargs): """Perform fit on X and returns labels for X. @@ -1116,7 +1166,16 @@ def is_classifier(estimator): >>> is_classifier(kmeans) False """ - return getattr(estimator, "_estimator_type", None) == "classifier" + # TODO(1.8): Remove this check + if isinstance(estimator, type): + warnings.warn( + f"passing a class to {print(inspect.stack()[0][3])} is deprecated and " + "will be removed in 1.8. Use an instance of the class instead.", + FutureWarning, + ) + return getattr(estimator, "_estimator_type", None) == "classifier" + + return get_tags(estimator).estimator_type == "classifier" def is_regressor(estimator): @@ -1147,7 +1206,16 @@ def is_regressor(estimator): >>> is_regressor(kmeans) False """ - return getattr(estimator, "_estimator_type", None) == "regressor" + # TODO(1.8): Remove this check + if isinstance(estimator, type): + warnings.warn( + f"passing a class to {print(inspect.stack()[0][3])} is deprecated and " + "will be removed in 1.8. Use an instance of the class instead.", + FutureWarning, + ) + return getattr(estimator, "_estimator_type", None) == "regressor" + + return get_tags(estimator).estimator_type == "regressor" def is_clusterer(estimator): @@ -1180,7 +1248,16 @@ def is_clusterer(estimator): >>> is_clusterer(kmeans) True """ - return getattr(estimator, "_estimator_type", None) == "clusterer" + # TODO(1.8): Remove this check + if isinstance(estimator, type): + warnings.warn( + f"passing a class to {print(inspect.stack()[0][3])} is deprecated and " + "will be removed in 1.8. Use an instance of the class instead.", + FutureWarning, + ) + return getattr(estimator, "_estimator_type", None) == "clusterer" + + return get_tags(estimator).estimator_type == "clusterer" def is_outlier_detector(estimator): @@ -1196,7 +1273,16 @@ def is_outlier_detector(estimator): out : bool True if estimator is an outlier detector and False otherwise. """ - return getattr(estimator, "_estimator_type", None) == "outlier_detector" + # TODO(1.8): Remove this check + if isinstance(estimator, type): + warnings.warn( + f"passing a class to {print(inspect.stack()[0][3])} is deprecated and " + "will be removed in 1.8. Use an instance of the class instead.", + FutureWarning, + ) + return getattr(estimator, "_estimator_type", None) == "outlier_detector" + + return get_tags(estimator).estimator_type == "outlier_detector" def _fit_context(*, prefer_skip_nested_validation): diff --git a/sklearn/calibration.py b/sklearn/calibration.py index 93035fef52b45..b4023172bb20c 100644 --- a/sklearn/calibration.py +++ b/sklearn/calibration.py @@ -23,6 +23,7 @@ _fit_context, clone, ) +from .frozen import FrozenEstimator from .isotonic import IsotonicRegression from .model_selection import LeaveOneOut, check_cv, cross_val_predict from .preprocessing import LabelEncoder, label_binarize @@ -34,6 +35,7 @@ ) from .utils._param_validation import ( HasMethods, + Hidden, Interval, StrOptions, validate_params, @@ -75,8 +77,8 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) `probabilities=True` for :class:`~sklearn.svm.SVC` and :class:`~sklearn.svm.NuSVC` estimators (see :ref:`User Guide ` for details). - Already fitted classifiers can be calibrated via the parameter - `cv="prefit"`. In this case, no cross-validation is used and all provided + Already fitted classifiers can be calibrated by wrapping the model in a + :class:`~sklearn.frozen.FrozenEstimator`. In this case all provided data is used for calibration. The user has to take care manually that data for model fitting and calibration are disjoint. @@ -106,8 +108,7 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) use isotonic calibration with too few calibration samples ``(<<1000)`` since it tends to overfit. - cv : int, cross-validation generator, iterable or "prefit", \ - default=None + cv : int, cross-validation generator, or iterable, default=None Determines the cross-validation splitting strategy. Possible inputs for cv are: @@ -124,12 +125,13 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) Refer to the :ref:`User Guide ` for the various cross-validation strategies that can be used here. - If "prefit" is passed, it is assumed that `estimator` has been - fitted already and all data is used for calibration. - .. versionchanged:: 0.22 ``cv`` default value if None changed from 3-fold to 5-fold. + .. versionchanged:: 1.6 + `"prefit"` is deprecated. Use :class:`~sklearn.frozen.FrozenEstimator` + instead. + n_jobs : int, default=None Number of jobs to run in parallel. ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context. @@ -142,9 +144,11 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) .. versionadded:: 0.24 - ensemble : bool, default=True - Determines how the calibrator is fitted when `cv` is not `'prefit'`. - Ignored if `cv='prefit'`. + ensemble : bool, or "auto", default="auto" + Determines how the calibrator is fitted. + + "auto" will use `False` if the `estimator` is a + :class:`~sklearn.frozen.FrozenEstimator`, and `True` otherwise. If `True`, the `estimator` is fitted using training data, and calibrated using testing data, for each `cv` fold. The final estimator @@ -161,6 +165,9 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) .. versionadded:: 0.24 + .. versionchanged:: 1.6 + `"auto"` option is added and is the default. + Attributes ---------- classes_ : ndarray of shape (n_classes,) @@ -178,17 +185,13 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) .. versionadded:: 1.0 - calibrated_classifiers_ : list (len() equal to cv or 1 if `cv="prefit"` \ - or `ensemble=False`) + calibrated_classifiers_ : list (len() equal to cv or 1 if `ensemble=False`) The list of classifier and calibrator pairs. - - When `cv="prefit"`, the fitted `estimator` and fitted + - When `ensemble=True`, `n_cv` fitted `estimator` and calibrator pairs. + `n_cv` is the number of cross-validation folds. + - When `ensemble=False`, the `estimator`, fitted on all the data, and fitted calibrator. - - When `cv` is not "prefit" and `ensemble=True`, `n_cv` fitted - `estimator` and calibrator pairs. `n_cv` is the number of - cross-validation folds. - - When `cv` is not "prefit" and `ensemble=False`, the `estimator`, - fitted on all the data, and fitted calibrator. .. versionchanged:: 0.24 Single calibrated classifier case when `ensemble=False`. @@ -240,7 +243,8 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) >>> base_clf = GaussianNB() >>> base_clf.fit(X_train, y_train) GaussianNB() - >>> calibrated_clf = CalibratedClassifierCV(base_clf, cv="prefit") + >>> from sklearn.frozen import FrozenEstimator + >>> calibrated_clf = CalibratedClassifierCV(FrozenEstimator(base_clf)) >>> calibrated_clf.fit(X_calib, y_calib) CalibratedClassifierCV(...) >>> len(calibrated_clf.calibrated_classifiers_) @@ -256,9 +260,9 @@ class CalibratedClassifierCV(ClassifierMixin, MetaEstimatorMixin, BaseEstimator) None, ], "method": [StrOptions({"isotonic", "sigmoid"})], - "cv": ["cv_object", StrOptions({"prefit"})], + "cv": ["cv_object", Hidden(StrOptions({"prefit"}))], "n_jobs": [Integral, None], - "ensemble": ["boolean"], + "ensemble": ["boolean", StrOptions({"auto"})], } def __init__( @@ -268,7 +272,7 @@ def __init__( method="sigmoid", cv=None, n_jobs=None, - ensemble=True, + ensemble="auto", ): self.estimator = estimator self.method = method @@ -323,8 +327,18 @@ def fit(self, X, y, sample_weight=None, **fit_params): estimator = self._get_estimator() + _ensemble = self.ensemble + if _ensemble == "auto": + _ensemble = not isinstance(estimator, FrozenEstimator) + self.calibrated_classifiers_ = [] if self.cv == "prefit": + # TODO(1.8): Remove this code branch and cv='prefit' + warnings.warn( + "The `cv='prefit'` option is deprecated in 1.6 and will be removed in" + " 1.8. You can use CalibratedClassifierCV(FrozenEstimator(estimator))" + " instead." + ) # `classes_` should be consistent with that of estimator check_is_fitted(self.estimator, attributes=["classes_"]) self.classes_ = self.estimator.classes_ @@ -404,7 +418,7 @@ def fit(self, X, y, sample_weight=None, **fit_params): ) cv = check_cv(self.cv, y, classifier=True) - if self.ensemble: + if _ensemble: parallel = Parallel(n_jobs=self.n_jobs) self.calibrated_classifiers_ = parallel( delayed(_fit_classifier_calibrator_pair)( diff --git a/sklearn/cluster/_agglomerative.py b/sklearn/cluster/_agglomerative.py index 1bddf03be8175..23f2255c723e2 100644 --- a/sklearn/cluster/_agglomerative.py +++ b/sklearn/cluster/_agglomerative.py @@ -32,7 +32,6 @@ from ..utils._fast_dict import IntFloatDict from ..utils._param_validation import ( HasMethods, - Hidden, Interval, StrOptions, validate_params, @@ -799,7 +798,9 @@ class AgglomerativeClustering(ClusterMixin, BaseEstimator): Metric used to compute the linkage. Can be "euclidean", "l1", "l2", "manhattan", "cosine", or "precomputed". If linkage is "ward", only "euclidean" is accepted. If "precomputed", a distance matrix is needed - as input for the fit method. + as input for the fit method. If connectivity is None, linkage is + "single" and affinity is not "precomputed" any valid pairwise distance + metric can be assigned. .. versionadded:: 1.2 @@ -1114,7 +1115,7 @@ def fit_predict(self, X, y=None): class FeatureAgglomeration( - ClassNamePrefixFeaturesOutMixin, AgglomerativeClustering, AgglomerationTransform + ClassNamePrefixFeaturesOutMixin, AgglomerationTransform, AgglomerativeClustering ): """Agglomerate features. @@ -1141,10 +1142,6 @@ class FeatureAgglomeration( .. versionadded:: 1.2 - .. deprecated:: 1.4 - `metric=None` is deprecated in 1.4 and will be removed in 1.6. - Let `metric` be the default value (i.e. `"euclidean"`) instead. - memory : str or object with the joblib.Memory interface, default=None Used to cache the output of the computation of the tree. By default, no caching is done. If a string is given, it is the @@ -1271,7 +1268,6 @@ class FeatureAgglomeration( "metric": [ StrOptions(set(_VALID_METRICS) | {"precomputed"}), callable, - Hidden(None), ], "memory": [str, HasMethods("cache"), None], "connectivity": ["array-like", "sparse matrix", callable, None], diff --git a/sklearn/cluster/_optics.py b/sklearn/cluster/_optics.py index 3b72dba4aae1b..62e128dd6c75c 100755 --- a/sklearn/cluster/_optics.py +++ b/sklearn/cluster/_optics.py @@ -100,7 +100,7 @@ class OPTICS(ClusterMixin, BaseEstimator): metrics. .. note:: - `'kulsinski'` is deprecated from SciPy 1.9 and will removed in SciPy 1.11. + `'kulsinski'` is deprecated from SciPy 1.9 and will be removed in SciPy 1.11. p : float, default=2 Parameter for the Minkowski metric from diff --git a/sklearn/covariance/_empirical_covariance.py b/sklearn/covariance/_empirical_covariance.py index fc3d1dc07f10d..955046fa37d4b 100644 --- a/sklearn/covariance/_empirical_covariance.py +++ b/sklearn/covariance/_empirical_covariance.py @@ -12,6 +12,8 @@ import numpy as np from scipy import linalg +from sklearn.utils import metadata_routing + from .. import config_context from ..base import BaseEstimator, _fit_context from ..metrics.pairwise import pairwise_distances @@ -181,6 +183,9 @@ class EmpiricalCovariance(BaseEstimator): array([0.0622..., 0.0193...]) """ + # X_test should have been called X + __metadata_request__score = {"X_test": metadata_routing.UNUSED} + _parameter_constraints: dict = { "store_precision": ["boolean"], "assume_centered": ["boolean"], diff --git a/sklearn/datasets/_samples_generator.py b/sklearn/datasets/_samples_generator.py index dd95a63b57820..291d545f26177 100644 --- a/sklearn/datasets/_samples_generator.py +++ b/sklearn/datasets/_samples_generator.py @@ -7,7 +7,6 @@ import array import numbers -import warnings from collections.abc import Iterable from numbers import Integral, Real @@ -18,7 +17,7 @@ from ..preprocessing import MultiLabelBinarizer from ..utils import check_array, check_random_state from ..utils import shuffle as util_shuffle -from ..utils._param_validation import Hidden, Interval, StrOptions, validate_params +from ..utils._param_validation import Interval, StrOptions, validate_params from ..utils.random import sample_without_replacement @@ -1667,7 +1666,7 @@ def make_spd_matrix(n_dim, *, random_state=None): @validate_params( { - "n_dim": [Hidden(None), Interval(Integral, 1, None, closed="left")], + "n_dim": [Interval(Integral, 1, None, closed="left")], "alpha": [Interval(Real, 0, 1, closed="both")], "norm_diag": ["boolean"], "smallest_coef": [Interval(Real, 0, 1, closed="both")], @@ -1677,15 +1676,11 @@ def make_spd_matrix(n_dim, *, random_state=None): None, ], "random_state": ["random_state"], - "dim": [ - Interval(Integral, 1, None, closed="left"), - Hidden(StrOptions({"deprecated"})), - ], }, prefer_skip_nested_validation=True, ) def make_sparse_spd_matrix( - n_dim=None, + n_dim=1, *, alpha=0.95, norm_diag=False, @@ -1693,7 +1688,6 @@ def make_sparse_spd_matrix( largest_coef=0.9, sparse_format=None, random_state=None, - dim="deprecated", ): """Generate a sparse symmetric definite positive matrix. @@ -1732,12 +1726,6 @@ def make_sparse_spd_matrix( for reproducible output across multiple function calls. See :term:`Glossary `. - dim : int, default=1 - The size of the random matrix to generate. - - .. deprecated:: 1.4 - `dim` is deprecated and will be removed in 1.6. - Returns ------- prec : ndarray or sparse matrix of shape (dim, dim) @@ -1765,32 +1753,10 @@ def make_sparse_spd_matrix( """ random_state = check_random_state(random_state) - # TODO(1.6): remove in 1.6 - # Also make sure to change `n_dim` default back to 1 and deprecate None - if n_dim is not None and dim != "deprecated": - raise ValueError( - "`dim` and `n_dim` cannot be both specified. Please use `n_dim` only " - "as `dim` is deprecated in v1.4 and will be removed in v1.6." - ) - - if dim != "deprecated": - warnings.warn( - ( - "dim was deprecated in version 1.4 and will be removed in 1.6." - "Please use ``n_dim`` instead." - ), - FutureWarning, - ) - _n_dim = dim - elif n_dim is None: - _n_dim = 1 - else: - _n_dim = n_dim - - chol = -sp.eye(_n_dim) + chol = -sp.eye(n_dim) aux = sp.random( - m=_n_dim, - n=_n_dim, + m=n_dim, + n=n_dim, density=1 - alpha, data_rvs=lambda x: random_state.uniform( low=smallest_coef, high=largest_coef, size=x @@ -1802,7 +1768,7 @@ def make_sparse_spd_matrix( # Permute the lines: we don't want to have asymmetries in the final # SPD matrix - permutation = random_state.permutation(_n_dim) + permutation = random_state.permutation(n_dim) aux = aux[permutation].T[permutation] chol += aux prec = chol.T @ chol diff --git a/sklearn/datasets/tests/test_samples_generator.py b/sklearn/datasets/tests/test_samples_generator.py index a2524fd7561fe..f4bc6384f763f 100644 --- a/sklearn/datasets/tests/test_samples_generator.py +++ b/sklearn/datasets/tests/test_samples_generator.py @@ -552,26 +552,6 @@ def test_make_sparse_spd_matrix(norm_diag, sparse_format, global_random_seed): assert_array_almost_equal(Xarr.diagonal(), np.ones(n_dim)) -# TODO(1.6): remove -def test_make_sparse_spd_matrix_deprecation_warning(): - """Check the message for future deprecation.""" - warn_msg = "dim was deprecated in version 1.4" - with pytest.warns(FutureWarning, match=warn_msg): - make_sparse_spd_matrix( - dim=1, - ) - - error_msg = "`dim` and `n_dim` cannot be both specified" - with pytest.raises(ValueError, match=error_msg): - make_sparse_spd_matrix( - dim=1, - n_dim=1, - ) - - X = make_sparse_spd_matrix() - assert X.shape[1] == 1 - - @pytest.mark.parametrize("hole", [False, True]) def test_make_swiss_roll(hole): X, t = make_swiss_roll(n_samples=5, noise=0.0, random_state=0, hole=hole) diff --git a/sklearn/decomposition/_incremental_pca.py b/sklearn/decomposition/_incremental_pca.py index fa442101839cd..b2caf81aa9793 100644 --- a/sklearn/decomposition/_incremental_pca.py +++ b/sklearn/decomposition/_incremental_pca.py @@ -8,6 +8,8 @@ import numpy as np from scipy import linalg, sparse +from sklearn.utils import metadata_routing + from ..base import _fit_context from ..utils import gen_batches from ..utils._param_validation import Interval @@ -184,6 +186,8 @@ class IncrementalPCA(_BasePCA): (1797, 7) """ + __metadata_request__partial_fit = {"check_input": metadata_routing.UNUSED} + _parameter_constraints: dict = { "n_components": [Interval(Integral, 1, None, closed="left"), None], "whiten": ["boolean"], diff --git a/sklearn/discriminant_analysis.py b/sklearn/discriminant_analysis.py index 69339491d214d..6a851c07dc896 100644 --- a/sklearn/discriminant_analysis.py +++ b/sklearn/discriminant_analysis.py @@ -168,6 +168,84 @@ def _class_cov(X, y, priors, shrinkage=None, covariance_estimator=None): return cov +class DiscriminantAnalysisPredictionMixin: + """Mixin class for QuadraticDiscriminantAnalysis and NearestCentroid.""" + + def decision_function(self, X): + """Apply decision function to an array of samples. + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + Array of samples (test vectors). + + Returns + ------- + y_scores : ndarray of shape (n_samples,) or (n_samples, n_classes) + Decision function values related to each class, per sample. + In the two-class case, the shape is `(n_samples,)`, giving the + log likelihood ratio of the positive class. + """ + y_scores = self._decision_function(X) + if len(self.classes_) == 2: + return y_scores[:, 1] - y_scores[:, 0] + return y_scores + + def predict(self, X): + """Perform classification on an array of vectors `X`. + + Returns the class label for each sample. + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + Input vectors, where `n_samples` is the number of samples and + `n_features` is the number of features. + + Returns + ------- + y_pred : ndarray of shape (n_samples,) + Class label for each sample. + """ + scores = self._decision_function(X) + return self.classes_.take(scores.argmax(axis=1)) + + def predict_proba(self, X): + """Estimate class probabilities. + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + Input data. + + Returns + ------- + y_proba : ndarray of shape (n_samples, n_classes) + Probability estimate of the sample for each class in the + model, where classes are ordered as they are in `self.classes_`. + """ + return np.exp(self.predict_log_proba(X)) + + def predict_log_proba(self, X): + """Estimate log class probabilities. + + Parameters + ---------- + X : {array-like, sparse matrix} of shape (n_samples, n_features) + Input data. + + Returns + ------- + y_log_proba : ndarray of shape (n_samples, n_classes) + Estimated log probabilities. + """ + scores = self._decision_function(X) + log_likelihood = scores - scores.max(axis=1)[:, np.newaxis] + return log_likelihood - np.log( + np.exp(log_likelihood).sum(axis=1)[:, np.newaxis] + ) + + class LinearDiscriminantAnalysis( ClassNamePrefixFeaturesOutMixin, LinearClassifierMixin, @@ -744,9 +822,9 @@ def decision_function(self, X): Returns ------- - C : ndarray of shape (n_samples,) or (n_samples, n_classes) + y_scores : ndarray of shape (n_samples,) or (n_samples, n_classes) Decision function values related to each class, per sample. - In the two-class case, the shape is (n_samples,), giving the + In the two-class case, the shape is `(n_samples,)`, giving the log likelihood ratio of the positive class. """ # Only override for the doc @@ -758,7 +836,9 @@ def __sklearn_tags__(self): return tags -class QuadraticDiscriminantAnalysis(ClassifierMixin, BaseEstimator): +class QuadraticDiscriminantAnalysis( + DiscriminantAnalysisPredictionMixin, ClassifierMixin, BaseEstimator +): """Quadratic Discriminant Analysis. A classifier with a quadratic decision boundary, generated @@ -992,14 +1072,10 @@ def decision_function(self, X): ------- C : ndarray of shape (n_samples,) or (n_samples, n_classes) Decision function values related to each class, per sample. - In the two-class case, the shape is (n_samples,), giving the + In the two-class case, the shape is `(n_samples,)`, giving the log likelihood ratio of the positive class. """ - dec_func = self._decision_function(X) - # handle special case of two classes - if len(self.classes_) == 2: - return dec_func[:, 1] - dec_func[:, 0] - return dec_func + return super().decision_function(X) def predict(self, X): """Perform classification on an array of test vectors X. @@ -1017,9 +1093,7 @@ def predict(self, X): C : ndarray of shape (n_samples,) Estimated probabilities. """ - d = self._decision_function(X) - y_pred = self.classes_.take(d.argmax(1)) - return y_pred + return super().predict(X) def predict_proba(self, X): """Return posterior probabilities of classification. @@ -1034,12 +1108,9 @@ def predict_proba(self, X): C : ndarray of shape (n_samples, n_classes) Posterior probabilities of classification per class. """ - values = self._decision_function(X) # compute the likelihood of the underlying gaussian models # up to a multiplicative constant. - likelihood = np.exp(values - values.max(axis=1)[:, np.newaxis]) - # compute posterior probabilities - return likelihood / likelihood.sum(axis=1)[:, np.newaxis] + return super().predict_proba(X) def predict_log_proba(self, X): """Return log of posterior probabilities of classification. @@ -1055,5 +1126,4 @@ def predict_log_proba(self, X): Posterior log-probabilities of classification per class. """ # XXX : can do better to avoid precision overflows - probas_ = self.predict_proba(X) - return np.log(probas_) + return super().predict_log_proba(X) diff --git a/sklearn/ensemble/_bagging.py b/sklearn/ensemble/_bagging.py index 423fc0ec6449a..dd39b8cb607a8 100644 --- a/sklearn/ensemble/_bagging.py +++ b/sklearn/ensemble/_bagging.py @@ -41,6 +41,7 @@ _check_method_params, _check_sample_weight, _deprecate_positional_args, + _estimator_has, check_is_fitted, has_fit_parameter, validate_data, @@ -269,22 +270,6 @@ def _parallel_predict_regression(estimators, estimators_features, X): ) -def _estimator_has(attr): - """Check if we can delegate a method to the underlying estimator. - - First, we check the first fitted estimator if available, otherwise we - check the estimator attribute. - """ - - def check(self): - if hasattr(self, "estimators_"): - return hasattr(self.estimators_[0], attr) - else: # self.estimator is not None - return hasattr(self.estimator, attr) - - return check - - class BaseBagging(BaseEnsemble, metaclass=ABCMeta): """Base class for Bagging meta-estimator. @@ -1033,7 +1018,9 @@ def predict_log_proba(self, X): return log_proba - @available_if(_estimator_has("decision_function")) + @available_if( + _estimator_has("decision_function", delegates=("estimators_", "estimator")) + ) def decision_function(self, X): """Average of the decision functions of the base classifiers. diff --git a/sklearn/ensemble/_base.py b/sklearn/ensemble/_base.py index 9adae766cebad..45c4fda13999b 100644 --- a/sklearn/ensemble/_base.py +++ b/sklearn/ensemble/_base.py @@ -304,5 +304,4 @@ def __sklearn_tags__(self): # validation will raise an error during `fit`. allow_nan = False tags.input_tags.allow_nan = allow_nan - tags.transformer_tags.preserves_dtype = [] return tags diff --git a/sklearn/ensemble/_forest.py b/sklearn/ensemble/_forest.py index 0d7a19431fe84..57a4750c612bd 100644 --- a/sklearn/ensemble/_forest.py +++ b/sklearn/ensemble/_forest.py @@ -610,8 +610,7 @@ def fit(self, X, y, sample_weight=None, classes=None): ): y_type = type_of_target(y) if y_type == "unknown" or ( - self._estimator_type == "classifier" - and y_type == "multiclass-multioutput" + is_classifier(self) and y_type == "multiclass-multioutput" ): # FIXME: we could consider to support multiclass-multioutput if # we introduce or reuse a constructor parameter (e.g. diff --git a/sklearn/ensemble/_stacking.py b/sklearn/ensemble/_stacking.py index 57bc63a1862e9..bf5ff39c13165 100644 --- a/sklearn/ensemble/_stacking.py +++ b/sklearn/ensemble/_stacking.py @@ -40,31 +40,13 @@ _check_feature_names_in, _check_response_method, _deprecate_positional_args, + _estimator_has, check_is_fitted, column_or_1d, ) from ._base import _BaseHeterogeneousEnsemble, _fit_single_estimator -def _estimator_has(attr): - """Check if we can delegate a method to the underlying estimator. - - First, we check the fitted `final_estimator_` if available, otherwise we check the - unfitted `final_estimator`. We raise the original `AttributeError` if `attr` does - not exist. This function is used together with `available_if`. - """ - - def check(self): - if hasattr(self, "final_estimator_"): - getattr(self.final_estimator_, attr) - else: - getattr(self.final_estimator, attr) - - return True - - return check - - class _BaseStacking(TransformerMixin, _BaseHeterogeneousEnsemble, metaclass=ABCMeta): """Base class for stacking method.""" @@ -364,7 +346,9 @@ def get_feature_names_out(self, input_features=None): return np.asarray(meta_names, dtype=object) - @available_if(_estimator_has("predict")) + @available_if( + _estimator_has("predict", delegates=("final_estimator_", "final_estimator")) + ) def predict(self, X, **predict_params): """Predict target for X. @@ -732,7 +716,9 @@ def fit(self, X, y, *, sample_weight=None, **fit_params): fit_params["sample_weight"] = sample_weight return super().fit(X, y_encoded, **fit_params) - @available_if(_estimator_has("predict")) + @available_if( + _estimator_has("predict", delegates=("final_estimator_", "final_estimator")) + ) def predict(self, X, **predict_params): """Predict target for X. @@ -785,7 +771,11 @@ def predict(self, X, **predict_params): y_pred = self._label_encoder.inverse_transform(y_pred) return y_pred - @available_if(_estimator_has("predict_proba")) + @available_if( + _estimator_has( + "predict_proba", delegates=("final_estimator_", "final_estimator") + ) + ) def predict_proba(self, X): """Predict class probabilities for `X` using the final estimator. @@ -809,7 +799,11 @@ def predict_proba(self, X): y_pred = np.array([preds[:, 0] for preds in y_pred]).T return y_pred - @available_if(_estimator_has("decision_function")) + @available_if( + _estimator_has( + "decision_function", delegates=("final_estimator_", "final_estimator") + ) + ) def decision_function(self, X): """Decision function for samples in `X` using the final estimator. @@ -1125,7 +1119,9 @@ def fit_transform(self, X, y, *, sample_weight=None, **fit_params): fit_params["sample_weight"] = sample_weight return super().fit_transform(X, y, **fit_params) - @available_if(_estimator_has("predict")) + @available_if( + _estimator_has("predict", delegates=("final_estimator_", "final_estimator")) + ) def predict(self, X, **predict_params): """Predict target for X. diff --git a/sklearn/ensemble/_voting.py b/sklearn/ensemble/_voting.py index 5c9a4de5882d6..bcf2d749725ff 100644 --- a/sklearn/ensemble/_voting.py +++ b/sklearn/ensemble/_voting.py @@ -547,6 +547,11 @@ def get_feature_names_out(self, input_features=None): ] return np.asarray(names_out, dtype=object) + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.transformer_tags.preserves_dtype = [] + return tags + class VotingRegressor(RegressorMixin, _BaseVoting): """Prediction voting regressor for unfitted estimators. diff --git a/sklearn/feature_extraction/_dict_vectorizer.py b/sklearn/feature_extraction/_dict_vectorizer.py index 64c9a5704652d..a754b92824585 100644 --- a/sklearn/feature_extraction/_dict_vectorizer.py +++ b/sklearn/feature_extraction/_dict_vectorizer.py @@ -9,6 +9,8 @@ import numpy as np import scipy.sparse as sp +from sklearn.utils import metadata_routing + from ..base import BaseEstimator, TransformerMixin, _fit_context from ..utils import check_array from ..utils.validation import check_is_fitted @@ -91,6 +93,9 @@ class DictVectorizer(TransformerMixin, BaseEstimator): array([[0., 0., 4.]]) """ + # This isn't something that people should be routing / using in a pipeline. + __metadata_request__inverse_transform = {"dict_type": metadata_routing.UNUSED} + _parameter_constraints: dict = { "dtype": "no_validation", # validation delegated to numpy, "separator": [str], diff --git a/sklearn/feature_extraction/_hash.py b/sklearn/feature_extraction/_hash.py index 45570a523dbbf..ac0bed3110c4e 100644 --- a/sklearn/feature_extraction/_hash.py +++ b/sklearn/feature_extraction/_hash.py @@ -7,6 +7,8 @@ import numpy as np import scipy.sparse as sp +from sklearn.utils import metadata_routing + from ..base import BaseEstimator, TransformerMixin, _fit_context from ..utils._param_validation import Interval, StrOptions from ._hashing_fast import transform as _hashing_transform @@ -104,6 +106,9 @@ class FeatureHasher(TransformerMixin, BaseEstimator): [ 0., -1., 0., 0., 0., 0., 0., 1.]]) """ + # raw_X should have been called X + __metadata_request__transform = {"raw_X": metadata_routing.UNUSED} + _parameter_constraints: dict = { "n_features": [Interval(Integral, 1, np.iinfo(np.int32).max, closed="both")], "input_type": [StrOptions({"dict", "pair", "string"})], diff --git a/sklearn/feature_extraction/text.py b/sklearn/feature_extraction/text.py index 2f21b3ccbe254..e1bdfd5a7dee5 100644 --- a/sklearn/feature_extraction/text.py +++ b/sklearn/feature_extraction/text.py @@ -16,6 +16,8 @@ import numpy as np import scipy.sparse as sp +from sklearn.utils import metadata_routing + from ..base import BaseEstimator, OneToOneFeatureMixin, TransformerMixin, _fit_context from ..exceptions import NotFittedError from ..preprocessing import normalize @@ -1118,6 +1120,11 @@ class CountVectorizer(_VectorizerMixin, BaseEstimator): [0 0 1 0 1 0 1 0 0 0 0 0 1]] """ + # raw_documents should not be in the routing mechanism. It should have been + # called X in the first place. + __metadata_request__fit = {"raw_documents": metadata_routing.UNUSED} + __metadata_request__transform = {"raw_documents": metadata_routing.UNUSED} + _parameter_constraints: dict = { "input": [StrOptions({"filename", "file", "content"})], "encoding": [str], diff --git a/sklearn/feature_selection/_from_model.py b/sklearn/feature_selection/_from_model.py index d5476e3f06abf..28af66d524623 100644 --- a/sklearn/feature_selection/_from_model.py +++ b/sklearn/feature_selection/_from_model.py @@ -19,6 +19,7 @@ from ..utils.metaestimators import available_if from ..utils.validation import ( _check_feature_names, + _estimator_has, _num_features, check_is_fitted, check_scalar, @@ -76,25 +77,6 @@ def _calculate_threshold(estimator, importances, threshold): return threshold -def _estimator_has(attr): - """Check if we can delegate a method to the underlying estimator. - - First, we check the fitted `estimator_` if available, otherwise we check the - unfitted `estimator`. We raise the original `AttributeError` if `attr` does - not exist. This function is used together with `available_if`. - """ - - def check(self): - if hasattr(self, "estimator_"): - getattr(self.estimator_, attr) - else: - getattr(self.estimator, attr) - - return True - - return check - - class SelectFromModel(MetaEstimatorMixin, SelectorMixin, BaseEstimator): """Meta-transformer for selecting features based on importance weights. diff --git a/sklearn/feature_selection/_rfe.py b/sklearn/feature_selection/_rfe.py index 0282facf9fd31..bd6a28b97b557 100644 --- a/sklearn/feature_selection/_rfe.py +++ b/sklearn/feature_selection/_rfe.py @@ -4,6 +4,7 @@ """Recursive feature elimination for feature ranking""" import warnings +from copy import deepcopy from numbers import Integral import numpy as np @@ -28,6 +29,7 @@ from ..utils.validation import ( _check_method_params, _deprecate_positional_args, + _estimator_has, check_is_fitted, validate_data, ) @@ -63,25 +65,6 @@ def _rfe_single_fit(rfe, estimator, X, y, train, test, scorer, routed_params): return rfe.step_scores_, rfe.step_n_features_ -def _estimator_has(attr): - """Check if we can delegate a method to the underlying estimator. - - First, we check the fitted `estimator_` if available, otherwise we check the - unfitted `estimator`. We raise the original `AttributeError` if `attr` does - not exist. This function is used together with `available_if`. - """ - - def check(self): - if hasattr(self, "estimator_"): - getattr(self.estimator_, attr) - else: - getattr(self.estimator, attr) - - return True - - return check - - class RFE(SelectorMixin, MetaEstimatorMixin, BaseEstimator): """Feature ranking with recursive feature elimination. @@ -239,6 +222,7 @@ def __init__( self.importance_getter = importance_getter self.verbose = verbose + # TODO(1.8) remove this property @property def _estimator_type(self): return self.estimator._estimator_type @@ -528,12 +512,16 @@ def predict_log_proba(self, X): def __sklearn_tags__(self): tags = super().__sklearn_tags__() + sub_estimator_tags = get_tags(self.estimator) + tags.estimator_type = sub_estimator_tags.estimator_type + tags.classifier_tags = deepcopy(sub_estimator_tags.classifier_tags) + tags.regressor_tags = deepcopy(sub_estimator_tags.regressor_tags) if tags.classifier_tags is not None: tags.classifier_tags.poor_score = True if tags.regressor_tags is not None: tags.regressor_tags.poor_score = True tags.target_tags.required = True - tags.input_tags.allow_nan = get_tags(self.estimator).input_tags.allow_nan + tags.input_tags.allow_nan = sub_estimator_tags.input_tags.allow_nan return tags def get_metadata_routing(self): @@ -882,7 +870,7 @@ def fit(self, X, y, *, groups=None, **params): func = delayed(_rfe_single_fit) scores_features = parallel( - func(rfe, self.estimator, X, y, train, test, scorer, routed_params) + func(clone(rfe), self.estimator, X, y, train, test, scorer, routed_params) for train, test in cv.split(X, y, **routed_params.splitter.split) ) scores, step_n_features = zip(*scores_features) diff --git a/sklearn/feature_selection/tests/test_rfe.py b/sklearn/feature_selection/tests/test_rfe.py index 98b55366c5853..ae11de2fadf59 100644 --- a/sklearn/feature_selection/tests/test_rfe.py +++ b/sklearn/feature_selection/tests/test_rfe.py @@ -6,9 +6,10 @@ import numpy as np import pytest +from joblib import parallel_backend from numpy.testing import assert_allclose, assert_array_almost_equal, assert_array_equal -from sklearn.base import BaseEstimator, ClassifierMixin +from sklearn.base import BaseEstimator, ClassifierMixin, is_classifier from sklearn.compose import TransformedTargetRegressor from sklearn.cross_decomposition import CCA, PLSCanonical, PLSRegression from sklearn.datasets import load_iris, make_classification, make_friedman1 @@ -22,12 +23,11 @@ from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC, SVR, LinearSVR from sklearn.utils import check_random_state -from sklearn.utils._tags import default_tags from sklearn.utils._testing import ignore_warnings from sklearn.utils.fixes import CSR_CONTAINERS -class MockClassifier(ClassifierMixin): +class MockClassifier(ClassifierMixin, BaseEstimator): """ Dummy classifier to test recursive feature elimination """ @@ -58,7 +58,7 @@ def set_params(self, **params): return self def __sklearn_tags__(self): - tags = default_tags(self) + tags = super().__sklearn_tags__() tags.input_tags.allow_nan = True return tags @@ -325,7 +325,7 @@ def test_rfecv_cv_results_size(global_random_seed): def test_rfe_estimator_tags(): rfe = RFE(SVC(kernel="linear")) - assert rfe._estimator_type == "classifier" + assert is_classifier(rfe) # make sure that cross-validation is stratified iris = load_iris() score = cross_val_score(rfe, iris.data, iris.target) @@ -703,3 +703,21 @@ def test_rfe_with_sample_weight(): rfe_sw_2.fit(X, y, sample_weight=sample_weight_2) assert not np.array_equal(rfe_sw_2.ranking_, rfe.ranking_) + + +def test_rfe_with_joblib_threading_backend(global_random_seed): + X, y = make_classification(random_state=global_random_seed) + + clf = LogisticRegression() + rfe = RFECV( + estimator=clf, + n_jobs=2, + ) + + rfe.fit(X, y) + ranking_ref = rfe.ranking_ + + with parallel_backend("threading"): + rfe.fit(X, y) + + assert_array_equal(ranking_ref, rfe.ranking_) diff --git a/sklearn/frozen/__init__.py b/sklearn/frozen/__init__.py new file mode 100644 index 0000000000000..8ca540b79229c --- /dev/null +++ b/sklearn/frozen/__init__.py @@ -0,0 +1,6 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +from ._frozen import FrozenEstimator + +__all__ = ["FrozenEstimator"] diff --git a/sklearn/frozen/_frozen.py b/sklearn/frozen/_frozen.py new file mode 100644 index 0000000000000..7585ea2597b59 --- /dev/null +++ b/sklearn/frozen/_frozen.py @@ -0,0 +1,166 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +from copy import deepcopy + +from ..base import BaseEstimator +from ..exceptions import NotFittedError +from ..utils import get_tags +from ..utils.metaestimators import available_if +from ..utils.validation import check_is_fitted + + +def _estimator_has(attr): + """Check that final_estimator has `attr`. + + Used together with `available_if`. + """ + + def check(self): + # raise original `AttributeError` if `attr` does not exist + getattr(self.estimator, attr) + return True + + return check + + +class FrozenEstimator(BaseEstimator): + """Estimator that wraps a fitted estimator to prevent re-fitting. + + This meta-estimator takes an estimator and freezes it, in the sense that calling + `fit` on it has no effect. `fit_predict` and `fit_transform` are also disabled. + All other methods are delegated to the original estimator and original estimator's + attributes are accessible as well. + + This is particularly useful when you have a fitted or a pre-trained model as a + transformer in a pipeline, and you'd like `pipeline.fit` to have no effect on this + step. + + Parameters + ---------- + estimator : estimator + The estimator which is to be kept frozen. + + See Also + -------- + None: No similar entry in the scikit-learn documentation. + + Examples + -------- + >>> from sklearn.datasets import make_classification + >>> from sklearn.frozen import FrozenEstimator + >>> from sklearn.linear_model import LogisticRegression + >>> X, y = make_classification(random_state=0) + >>> clf = LogisticRegression(random_state=0).fit(X, y) + >>> frozen_clf = FrozenEstimator(clf) + >>> frozen_clf.fit(X, y) # No-op + FrozenEstimator(estimator=LogisticRegression(random_state=0)) + >>> frozen_clf.predict(X) # Predictions from `clf.predict` + array(...) + """ + + def __init__(self, estimator): + self.estimator = estimator + + @available_if(_estimator_has("__getitem__")) + def __getitem__(self, *args, **kwargs): + """__getitem__ is defined in :class:`~sklearn.pipeline.Pipeline` and \ + :class:`~sklearn.compose.ColumnTransformer`. + """ + return self.estimator.__getitem__(*args, **kwargs) + + def __getattr__(self, name): + # `estimator`'s attributes are now accessible except `fit_predict` and + # `fit_transform` + if name in ["fit_predict", "fit_transform"]: + raise AttributeError(f"{name} is not available for frozen estimators.") + return getattr(self.estimator, name) + + def __sklearn_clone__(self): + return self + + def __sklearn_is_fitted__(self): + try: + check_is_fitted(self.estimator) + return True + except NotFittedError: + return False + + def fit(self, X, y, *args, **kwargs): + """No-op. + + As a frozen estimator, calling `fit` has no effect. + + Parameters + ---------- + X : object + Ignored. + + y : object + Ignored. + + *args : tuple + Additional positional arguments. Ignored, but present for API compatibility + with `self.estimator`. + + **kwargs : dict + Additional keyword arguments. Ignored, but present for API compatibility + with `self.estimator`. + + Returns + ------- + self : object + Returns the instance itself. + """ + check_is_fitted(self.estimator) + return self + + def set_params(self, **kwargs): + """Set the parameters of this estimator. + + The only valid key here is `estimator`. You cannot set the parameters of the + inner estimator. + + Parameters + ---------- + **kwargs : dict + Estimator parameters. + + Returns + ------- + self : FrozenEstimator + This estimator. + """ + estimator = kwargs.pop("estimator", None) + if estimator is not None: + self.estimator = estimator + if kwargs: + raise ValueError( + "You cannot set parameters of the inner estimator in a frozen " + "estimator since calling `fit` has no effect. You can use " + "`frozenestimator.estimator.set_params` to set parameters of the inner " + "estimator." + ) + + def get_params(self, deep=True): + """Get parameters for this estimator. + + Returns a `{"estimator": estimator}` dict. The parameters of the inner + estimator are not included. + + Parameters + ---------- + deep : bool, default=True + Ignored. + + Returns + ------- + params : dict + Parameter names mapped to their values. + """ + return {"estimator": self.estimator} + + def __sklearn_tags__(self): + tags = deepcopy(get_tags(self.estimator)) + tags._skip_test = True + return tags diff --git a/sklearn/frozen/tests/__init__.py b/sklearn/frozen/tests/__init__.py new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/sklearn/frozen/tests/test_frozen.py b/sklearn/frozen/tests/test_frozen.py new file mode 100644 index 0000000000000..b304d3ac0aa2c --- /dev/null +++ b/sklearn/frozen/tests/test_frozen.py @@ -0,0 +1,223 @@ +# Authors: The scikit-learn developers +# SPDX-License-Identifier: BSD-3-Clause + +import re + +import numpy as np +import pytest +from numpy.testing import assert_array_equal + +from sklearn import config_context +from sklearn.base import ( + BaseEstimator, + clone, + is_classifier, + is_clusterer, + is_outlier_detector, + is_regressor, +) +from sklearn.cluster import KMeans +from sklearn.compose import make_column_transformer +from sklearn.datasets import make_classification, make_regression +from sklearn.exceptions import NotFittedError, UnsetMetadataPassedError +from sklearn.frozen import FrozenEstimator +from sklearn.linear_model import LinearRegression, LogisticRegression +from sklearn.neighbors import LocalOutlierFactor +from sklearn.pipeline import make_pipeline +from sklearn.preprocessing import RobustScaler, StandardScaler +from sklearn.utils._testing import set_random_state +from sklearn.utils.validation import check_is_fitted + + +@pytest.fixture +def regression_dataset(): + return make_regression() + + +@pytest.fixture +def classification_dataset(): + return make_classification() + + +@pytest.mark.parametrize( + "estimator, dataset", + [ + (LinearRegression(), "regression_dataset"), + (LogisticRegression(), "classification_dataset"), + (make_pipeline(StandardScaler(), LinearRegression()), "regression_dataset"), + ( + make_pipeline(StandardScaler(), LogisticRegression()), + "classification_dataset", + ), + (StandardScaler(), "regression_dataset"), + (KMeans(), "regression_dataset"), + (LocalOutlierFactor(), "regression_dataset"), + ( + make_column_transformer( + (StandardScaler(), [0]), + (RobustScaler(), [1]), + ), + "regression_dataset", + ), + ], +) +@pytest.mark.parametrize( + "method", + ["predict", "predict_proba", "predict_log_proba", "decision_function", "transform"], +) +def test_frozen_methods(estimator, dataset, request, method): + """Test that frozen.fit doesn't do anything, and that all other methods are + exposed by the frozen estimator and return the same values as the estimator. + """ + X, y = request.getfixturevalue(dataset) + set_random_state(estimator) + estimator.fit(X, y) + frozen = FrozenEstimator(estimator) + # this should be no-op + frozen.fit([[1]], [1]) + + if hasattr(estimator, method): + assert_array_equal(getattr(estimator, method)(X), getattr(frozen, method)(X)) + + assert is_classifier(estimator) == is_classifier(frozen) + assert is_regressor(estimator) == is_regressor(frozen) + assert is_clusterer(estimator) == is_clusterer(frozen) + assert is_outlier_detector(estimator) == is_outlier_detector(frozen) + + +@config_context(enable_metadata_routing=True) +def test_frozen_metadata_routing(regression_dataset): + """Test that metadata routing works with frozen estimators.""" + + class ConsumesMetadata(BaseEstimator): + def __init__(self, on_fit=None, on_predict=None): + self.on_fit = on_fit + self.on_predict = on_predict + + def fit(self, X, y, metadata=None): + if self.on_fit: + assert metadata is not None + self.fitted_ = True + return self + + def predict(self, X, metadata=None): + if self.on_predict: + assert metadata is not None + return np.ones(len(X)) + + X, y = regression_dataset + pipeline = make_pipeline( + ConsumesMetadata(on_fit=True, on_predict=True) + .set_fit_request(metadata=True) + .set_predict_request(metadata=True) + ) + + pipeline.fit(X, y, metadata="test") + frozen = FrozenEstimator(pipeline) + pipeline.predict(X, metadata="test") + frozen.predict(X, metadata="test") + + frozen["consumesmetadata"].set_predict_request(metadata=False) + with pytest.raises( + TypeError, + match=re.escape( + "Pipeline.predict got unexpected argument(s) {'metadata'}, which are not " + "routed to any object." + ), + ): + frozen.predict(X, metadata="test") + + frozen["consumesmetadata"].set_predict_request(metadata=None) + with pytest.raises(UnsetMetadataPassedError): + frozen.predict(X, metadata="test") + + +def test_composite_fit(classification_dataset): + """Test that calling fit_transform and fit_predict doesn't call fit.""" + + class Estimator(BaseEstimator): + def fit(self, X, y): + try: + self._fit_counter += 1 + except AttributeError: + self._fit_counter = 1 + return self + + def fit_transform(self, X, y=None): + # only here to test that it doesn't get called + ... # pragma: no cover + + def fit_predict(self, X, y=None): + # only here to test that it doesn't get called + ... # pragma: no cover + + X, y = classification_dataset + est = Estimator().fit(X, y) + frozen = FrozenEstimator(est) + + with pytest.raises(AttributeError): + frozen.fit_predict(X, y) + with pytest.raises(AttributeError): + frozen.fit_transform(X, y) + + assert frozen._fit_counter == 1 + + +def test_clone_frozen(regression_dataset): + """Test that cloning a frozen estimator keeps the frozen state.""" + X, y = regression_dataset + estimator = LinearRegression().fit(X, y) + frozen = FrozenEstimator(estimator) + cloned = clone(frozen) + assert cloned.estimator is estimator + + +def test_check_is_fitted(regression_dataset): + """Test that check_is_fitted works on frozen estimators.""" + X, y = regression_dataset + + estimator = LinearRegression() + frozen = FrozenEstimator(estimator) + with pytest.raises(NotFittedError): + check_is_fitted(frozen) + + estimator = LinearRegression().fit(X, y) + frozen = FrozenEstimator(estimator) + check_is_fitted(frozen) + + +def test_frozen_tags(): + """Test that frozen estimators have the same tags as the original estimator + except for the skip_test tag.""" + + class Estimator(BaseEstimator): + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.input_tags.categorical = True + return tags + + estimator = Estimator() + frozen = FrozenEstimator(estimator) + frozen_tags = frozen.__sklearn_tags__() + estimator_tags = estimator.__sklearn_tags__() + + assert frozen_tags._skip_test is True + assert estimator_tags._skip_test is False + + assert estimator_tags.input_tags.categorical is True + assert frozen_tags.input_tags.categorical is True + + +def test_frozen_params(): + """Test that FrozenEstimator only exposes the estimator parameter.""" + est = LogisticRegression() + frozen = FrozenEstimator(est) + + with pytest.raises(ValueError, match="You cannot set parameters of the inner"): + frozen.set_params(estimator__C=1) + + assert frozen.get_params() == {"estimator": est} + + other_est = LocalOutlierFactor() + frozen.set_params(estimator=other_est) + assert frozen.get_params() == {"estimator": other_est} diff --git a/sklearn/impute/_base.py b/sklearn/impute/_base.py index aecc235ecd6ac..faf1f9e23b678 100644 --- a/sklearn/impute/_base.py +++ b/sklearn/impute/_base.py @@ -225,6 +225,11 @@ class SimpleImputer(_BaseImputer): .. versionadded:: 1.2 + .. versionchanged:: 1.6 + Currently, when `keep_empty_feature=False` and `strategy="constant"`, + empty features are not dropped. This behaviour will change in version + 1.8. Set `keep_empty_feature=True` to preserve this behaviour. + Attributes ---------- statistics_ : array of shape (n_features,) @@ -458,6 +463,19 @@ def _sparse_fit(self, X, strategy, missing_values, fill_value): statistics = np.empty(X.shape[1]) if strategy == "constant": + # TODO(1.8): Remove FutureWarning and add `np.nan` as a statistic + # for empty features to drop them later. + if not self.keep_empty_features and any( + [all(missing_mask[:, i].data) for i in range(missing_mask.shape[1])] + ): + warnings.warn( + "Currently, when `keep_empty_feature=False` and " + '`strategy="constant"`, empty features are not dropped. ' + "This behaviour will change in version 1.8. Set " + "`keep_empty_feature=True` to preserve this behaviour.", + FutureWarning, + ) + # for constant strategy, self.statistics_ is used to store # fill_value in each column statistics.fill(fill_value) @@ -548,6 +566,17 @@ def _dense_fit(self, X, strategy, missing_values, fill_value): # Constant elif strategy == "constant": + # TODO(1.8): Remove FutureWarning and add `np.nan` as a statistic + # for empty features to drop them later. + if not self.keep_empty_features and ma.getmask(masked_X).all(axis=0).any(): + warnings.warn( + "Currently, when `keep_empty_feature=False` and " + '`strategy="constant"`, empty features are not dropped. ' + "This behaviour will change in version 1.8. Set " + "`keep_empty_feature=True` to preserve this behaviour.", + FutureWarning, + ) + # for constant strategy, self.statistcs_ is used to store # fill_value in each column return np.full(X.shape[1], fill_value, dtype=X.dtype) diff --git a/sklearn/impute/_iterative.py b/sklearn/impute/_iterative.py index d2d47bae3f32a..86723c8245d44 100644 --- a/sklearn/impute/_iterative.py +++ b/sklearn/impute/_iterative.py @@ -26,6 +26,7 @@ from ..utils.validation import ( FLOAT_DTYPES, _check_feature_names_in, + _num_samples, check_is_fitted, validate_data, ) @@ -635,6 +636,13 @@ def _initial_imputation(self, X, in_fit=False): X_missing_mask = _get_mask(X, self.missing_values) mask_missing_values = X_missing_mask.copy() + + # TODO (1.8): remove this once the deprecation is removed. In the meantime, + # we need to catch the warning to avoid false positives. + catch_warning = ( + self.initial_strategy == "constant" and not self.keep_empty_features + ) + if self.initial_imputer_ is None: self.initial_imputer_ = SimpleImputer( missing_values=self.missing_values, @@ -642,9 +650,24 @@ def _initial_imputation(self, X, in_fit=False): fill_value=self.fill_value, keep_empty_features=self.keep_empty_features, ).set_output(transform="default") - X_filled = self.initial_imputer_.fit_transform(X) + + # TODO (1.8): remove this once the deprecation is removed to keep only + # the code in the else case. + if catch_warning: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + X_filled = self.initial_imputer_.fit_transform(X) + else: + X_filled = self.initial_imputer_.fit_transform(X) else: - X_filled = self.initial_imputer_.transform(X) + # TODO (1.8): remove this once the deprecation is removed to keep only + # the code in the else case. + if catch_warning: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", FutureWarning) + X_filled = self.initial_imputer_.transform(X) + else: + X_filled = self.initial_imputer_.transform(X) if in_fit: self._is_empty_feature = np.all(mask_missing_values, axis=0) @@ -658,7 +681,8 @@ def _initial_imputation(self, X, in_fit=False): # The constant strategy has a specific behavior and preserve empty # features even with ``keep_empty_features=False``. We need to drop # the column for consistency. - # TODO: remove this `if` branch once the following issue is addressed: + # TODO (1.8): remove this `if` branch once the following issue is + # addressed: # https://github.com/scikit-learn/scikit-learn/issues/29827 X_filled = X_filled[:, ~self._is_empty_feature] @@ -672,7 +696,9 @@ def _initial_imputation(self, X, in_fit=False): return Xt, X_filled, mask_missing_values, X_missing_mask @staticmethod - def _validate_limit(limit, limit_type, n_features): + def _validate_limit( + limit, limit_type, n_features, is_empty_feature, keep_empty_feature + ): """Validate the limits (min/max) of the feature values. Converts scalar min/max limits to vectors of shape `(n_features,)`. @@ -685,23 +711,37 @@ def _validate_limit(limit, limit_type, n_features): Type of limit to validate. n_features: int Number of features in the dataset. + is_empty_feature: ndarray, shape (n_features, ) + Mask array indicating empty feature imputer has seen during fit. + keep_empty_feature: bool + If False, remove empty-feature indices from the limit. Returns ------- limit: ndarray, shape(n_features,) Array of limits, one for each feature. """ + n_features_in = _num_samples(is_empty_feature) + if ( + limit is not None + and not np.isscalar(limit) + and _num_samples(limit) != n_features_in + ): + raise ValueError( + f"'{limit_type}_value' should be of shape ({n_features_in},) when an" + f" array-like is provided. Got {len(limit)}, instead." + ) + limit_bound = np.inf if limit_type == "max" else -np.inf limit = limit_bound if limit is None else limit if np.isscalar(limit): limit = np.full(n_features, limit) limit = check_array(limit, ensure_all_finite=False, copy=False, ensure_2d=False) - if not limit.shape[0] == n_features: - raise ValueError( - f"'{limit_type}_value' should be of " - f"shape ({n_features},) when an array-like " - f"is provided. Got {limit.shape}, instead." - ) + + # Make sure to remove the empty feature elements from the bounds + if not keep_empty_feature and len(limit) == len(is_empty_feature): + limit = limit[~is_empty_feature] + return limit @_fit_context( @@ -774,8 +814,20 @@ def fit_transform(self, X, y=None, **params): self.n_iter_ = 0 return super()._concatenate_indicator(Xt, X_indicator) - self._min_value = self._validate_limit(self.min_value, "min", X.shape[1]) - self._max_value = self._validate_limit(self.max_value, "max", X.shape[1]) + self._min_value = self._validate_limit( + self.min_value, + "min", + X.shape[1], + self._is_empty_feature, + self.keep_empty_features, + ) + self._max_value = self._validate_limit( + self.max_value, + "max", + X.shape[1], + self._is_empty_feature, + self.keep_empty_features, + ) if not np.all(np.greater(self._max_value, self._min_value)): raise ValueError("One (or more) features have min_value >= max_value.") diff --git a/sklearn/impute/tests/test_impute.py b/sklearn/impute/tests/test_impute.py index a7fe9e7255197..b92e8ecd8f01f 100644 --- a/sklearn/impute/tests/test_impute.py +++ b/sklearn/impute/tests/test_impute.py @@ -410,18 +410,24 @@ def test_imputation_constant_error_invalid_type(X_data, missing_value): imputer.fit_transform(X) +# TODO (1.8): check that `keep_empty_features=False` drop the +# empty features due to the behaviour change. def test_imputation_constant_integer(): # Test imputation using the constant strategy on integers X = np.array([[-1, 2, 3, -1], [4, -1, 5, -1], [6, 7, -1, -1], [8, 9, 0, -1]]) X_true = np.array([[0, 2, 3, 0], [4, 0, 5, 0], [6, 7, 0, 0], [8, 9, 0, 0]]) - imputer = SimpleImputer(missing_values=-1, strategy="constant", fill_value=0) + imputer = SimpleImputer( + missing_values=-1, strategy="constant", fill_value=0, keep_empty_features=True + ) X_trans = imputer.fit_transform(X) assert_array_equal(X_trans, X_true) +# TODO (1.8): check that `keep_empty_features=False` drop the +# empty features due to the behaviour change. @pytest.mark.parametrize("array_constructor", CSR_CONTAINERS + [np.asarray]) def test_imputation_constant_float(array_constructor): # Test imputation using the constant strategy on floats @@ -442,12 +448,16 @@ def test_imputation_constant_float(array_constructor): X_true = array_constructor(X_true) - imputer = SimpleImputer(strategy="constant", fill_value=-1) + imputer = SimpleImputer( + strategy="constant", fill_value=-1, keep_empty_features=True + ) X_trans = imputer.fit_transform(X) assert_allclose_dense_sparse(X_trans, X_true) +# TODO (1.8): check that `keep_empty_features=False` drop the +# empty features due to the behaviour change. @pytest.mark.parametrize("marker", [None, np.nan, "NAN", "", 0]) def test_imputation_constant_object(marker): # Test imputation using the constant strategy on objects @@ -472,13 +482,18 @@ def test_imputation_constant_object(marker): ) imputer = SimpleImputer( - missing_values=marker, strategy="constant", fill_value="missing" + missing_values=marker, + strategy="constant", + fill_value="missing", + keep_empty_features=True, ) X_trans = imputer.fit_transform(X) assert_array_equal(X_trans, X_true) +# TODO (1.8): check that `keep_empty_features=False` drop the +# empty features due to the behaviour change. @pytest.mark.parametrize("dtype", [object, "category"]) def test_imputation_constant_pandas(dtype): # Test imputation using the constant strategy on pandas df @@ -498,7 +513,7 @@ def test_imputation_constant_pandas(dtype): dtype=object, ) - imputer = SimpleImputer(strategy="constant") + imputer = SimpleImputer(strategy="constant", keep_empty_features=True) X_trans = imputer.fit_transform(df) assert_array_equal(X_trans, X_true) @@ -1013,6 +1028,7 @@ def test_iterative_imputer_min_max_array_like(min_value, max_value, correct_outp (100, 0, "min_value >= max_value."), (np.inf, -np.inf, "min_value >= max_value."), ([-5, 5], [100, 200, 0], "_value' should be of shape"), + ([-5, 5, 5], [100, 200], "_value' should be of shape"), ], ) def test_iterative_imputer_catch_min_max_error(min_value, max_value, err_msg): @@ -1513,6 +1529,26 @@ def test_most_frequent(expected, array, dtype, extra_value, n_repeat): ) +@pytest.mark.parametrize( + "initial_strategy", ["mean", "median", "most_frequent", "constant"] +) +def test_iterative_imputer_keep_empty_features(initial_strategy): + """Check the behaviour of the iterative imputer with different initial strategy + and keeping empty features (i.e. features containing only missing values). + """ + X = np.array([[1, np.nan, 2], [3, np.nan, np.nan]]) + + imputer = IterativeImputer( + initial_strategy=initial_strategy, keep_empty_features=True + ) + X_imputed = imputer.fit_transform(X) + assert_allclose(X_imputed[:, 1], 0) + X_imputed = imputer.transform(X) + assert_allclose(X_imputed[:, 1], 0) + + +# TODO (1.8): check that `keep_empty_features=False` drop the +# empty features due to the behaviour change. def test_iterative_imputer_constant_fill_value(): """Check that we propagate properly the parameter `fill_value`.""" X = np.array([[-1, 2, 3, -1], [4, -1, 5, -1], [6, 7, -1, -1], [8, 9, 0, -1]]) @@ -1523,11 +1559,67 @@ def test_iterative_imputer_constant_fill_value(): initial_strategy="constant", fill_value=fill_value, max_iter=0, + keep_empty_features=True, ) imputer.fit_transform(X) assert_array_equal(imputer.initial_imputer_.statistics_, fill_value) +def test_iterative_imputer_min_max_value_remove_empty(): + """Check that we properly apply the empty feature mask to `min_value` and + `max_value`. + + Non-regression test for https://github.com/scikit-learn/scikit-learn/issues/29355 + """ + # Intentionally make column 2 as a missing column, then the bound of the imputed + # value of column 3 should be (4, 5) + X = np.array( + [ + [1, 2, np.nan, np.nan], + [4, 5, np.nan, 6], + [7, 8, np.nan, np.nan], + [10, 11, np.nan, 12], + ] + ) + min_value = [-np.inf, -np.inf, -np.inf, 4] + max_value = [np.inf, np.inf, np.inf, 5] + + X_imputed = IterativeImputer( + min_value=min_value, + max_value=max_value, + keep_empty_features=False, + ).fit_transform(X) + + X_without_missing_column = np.delete(X, 2, axis=1) + assert X_imputed.shape == X_without_missing_column.shape + assert np.min(X_imputed[np.isnan(X_without_missing_column)]) == pytest.approx(4) + assert np.max(X_imputed[np.isnan(X_without_missing_column)]) == pytest.approx(5) + + # Intentionally make column 3 as a missing column, then the bound of the imputed + # value of column 2 should be (3.5, 6) + X = np.array( + [ + [1, 2, np.nan, np.nan], + [4, 5, 6, np.nan], + [7, 8, np.nan, np.nan], + [10, 11, 12, np.nan], + ] + ) + min_value = [-np.inf, -np.inf, 3.5, -np.inf] + max_value = [np.inf, np.inf, 6, np.inf] + + X_imputed = IterativeImputer( + min_value=min_value, + max_value=max_value, + keep_empty_features=False, + ).fit_transform(X) + + X_without_missing_column = X[:, :3] + assert X_imputed.shape == X_without_missing_column.shape + assert np.min(X_imputed[np.isnan(X_without_missing_column)]) == pytest.approx(3.5) + assert np.max(X_imputed[np.isnan(X_without_missing_column)]) == pytest.approx(6) + + @pytest.mark.parametrize("keep_empty_features", [True, False]) def test_knn_imputer_keep_empty_features(keep_empty_features): """Check the behaviour of `keep_empty_features` for `KNNImputer`.""" @@ -1666,7 +1758,13 @@ def test_simple_imputer_constant_keep_empty_features(array_type, keep_empty_feat ) for method in ["fit_transform", "transform"]: - X_imputed = getattr(imputer, method)(X) + # TODO(1.8): Remove the condition and still call getattr(imputer, method)(X) + if method.startswith("fit") and not keep_empty_features: + warn_msg = '`strategy="constant"`, empty features are not dropped. ' + with pytest.warns(FutureWarning, match=warn_msg): + X_imputed = getattr(imputer, method)(X) + else: + X_imputed = getattr(imputer, method)(X) assert X_imputed.shape == X.shape constant_feature = ( X_imputed[:, 0].toarray() if array_type == "sparse" else X_imputed[:, 0] diff --git a/sklearn/inspection/_plot/tests/test_boundary_decision_display.py b/sklearn/inspection/_plot/tests/test_boundary_decision_display.py index b6767cbe72857..d0aabbbb15db9 100644 --- a/sklearn/inspection/_plot/tests/test_boundary_decision_display.py +++ b/sklearn/inspection/_plot/tests/test_boundary_decision_display.py @@ -334,7 +334,7 @@ def test_decision_boundary_display_regressor(pyplot, response_method, plot_metho def test_error_bad_response(pyplot, response_method, msg): """Check errors for bad response.""" - class MyClassifier(BaseEstimator, ClassifierMixin): + class MyClassifier(ClassifierMixin, BaseEstimator): def fit(self, X, y): self.fitted_ = True self.classes_ = [0, 1] diff --git a/sklearn/isotonic.py b/sklearn/isotonic.py index 7312fdba7f63d..fb47ca1dde68f 100644 --- a/sklearn/isotonic.py +++ b/sklearn/isotonic.py @@ -11,6 +11,8 @@ from scipy import interpolate, optimize from scipy.stats import spearmanr +from sklearn.utils import metadata_routing + from ._isotonic import _inplace_contiguous_isotonic_regression, _make_unique from .base import BaseEstimator, RegressorMixin, TransformerMixin, _fit_context from .utils import check_array, check_consistent_length @@ -272,6 +274,10 @@ class IsotonicRegression(RegressorMixin, TransformerMixin, BaseEstimator): array([1.8628..., 3.7256...]) """ + # T should have been called X + __metadata_request__predict = {"T": metadata_routing.UNUSED} + __metadata_request__transform = {"T": metadata_routing.UNUSED} + _parameter_constraints: dict = { "y_min": [Interval(Real, None, None, closed="both"), None], "y_max": [Interval(Real, None, None, closed="both"), None], diff --git a/sklearn/linear_model/_base.py b/sklearn/linear_model/_base.py index 6f86387a1c355..3bb3b8b7626d8 100644 --- a/sklearn/linear_model/_base.py +++ b/sklearn/linear_model/_base.py @@ -350,7 +350,11 @@ def decision_function(self, X): X = validate_data(self, X, accept_sparse="csr", reset=False) scores = safe_sparse_dot(X, self.coef_.T, dense_output=True) + self.intercept_ - return xp.reshape(scores, (-1,)) if scores.shape[1] == 1 else scores + return ( + xp.reshape(scores, (-1,)) + if (scores.ndim > 1 and scores.shape[1] == 1) + else scores + ) def predict(self, X): """ @@ -673,7 +677,9 @@ def rmatvec(b): ) self.coef_ = np.vstack([out[0] for out in outs]) else: - self.coef_, _, self.rank_, self.singular_ = linalg.lstsq(X, y) + # cut-off ratio for small singular values + cond = max(X.shape) * np.finfo(X.dtype).eps + self.coef_, _, self.rank_, self.singular_ = linalg.lstsq(X, y, cond=cond) self.coef_ = self.coef_.T if y.ndim == 1: @@ -681,22 +687,6 @@ def rmatvec(b): self._set_intercept(X_offset, y_offset, X_scale) return self - def __sklearn_tags__(self): - tags = super().__sklearn_tags__() - # TODO: investigate failure see meta-issue #16298 - # - # Note: this model should converge to the minimum norm solution of the - # least squares problem and as result be numerically stable enough when - # running the equivalence check even if n_features > n_samples. Maybe - # this is is not the case and a different choice of solver could fix - # this problem. - tags._xfail_checks = { - "check_sample_weight_equivalence": ( - "sample_weight is not equivalent to removing/repeating samples." - ), - } - return tags - def _check_precomputed_gram_matrix( X, precompute, X_offset, X_scale, rtol=None, atol=1e-5 diff --git a/sklearn/linear_model/_coordinate_descent.py b/sklearn/linear_model/_coordinate_descent.py index b13535bab512d..2dbb83c82fbaa 100644 --- a/sklearn/linear_model/_coordinate_descent.py +++ b/sklearn/linear_model/_coordinate_descent.py @@ -12,6 +12,8 @@ from joblib import effective_n_jobs from scipy import sparse +from sklearn.utils import metadata_routing + from ..base import MultiOutputMixin, RegressorMixin, _fit_context from ..model_selection import check_cv from ..utils import Bunch, check_array, check_scalar @@ -875,6 +877,10 @@ class ElasticNet(MultiOutputMixin, RegressorMixin, LinearModel): [1.451...] """ + # "check_input" is used for optimisation and isn't something to be passed + # around in a pipeline. + __metadata_request__fit = {"check_input": metadata_routing.UNUSED} + _parameter_constraints: dict = { "alpha": [Interval(Real, 0, None, closed="left")], "l1_ratio": [Interval(Real, 0, 1, closed="both")], diff --git a/sklearn/linear_model/_least_angle.py b/sklearn/linear_model/_least_angle.py index 403090f8503c8..25f956e5fadda 100644 --- a/sklearn/linear_model/_least_angle.py +++ b/sklearn/linear_model/_least_angle.py @@ -91,8 +91,8 @@ def lars_path( Parameters ---------- X : None or ndarray of shape (n_samples, n_features) - Input data. Note that if X is `None` then the Gram matrix must be - specified, i.e., cannot be `None` or `False`. + Input data. If X is `None`, Gram must also be `None`. + If only the Gram matrix is available, use `lars_path_gram` instead. y : None or ndarray of shape (n_samples,) Input targets. diff --git a/sklearn/linear_model/_ridge.py b/sklearn/linear_model/_ridge.py index c92a535994fef..56bb9fbc50570 100644 --- a/sklearn/linear_model/_ridge.py +++ b/sklearn/linear_model/_ridge.py @@ -16,6 +16,8 @@ from scipy import linalg, optimize, sparse from scipy.sparse import linalg as sp_linalg +from sklearn.base import BaseEstimator + from ..base import MultiOutputMixin, RegressorMixin, _fit_context, is_classifier from ..exceptions import ConvergenceWarning from ..metrics import check_scoring, get_scorer_names @@ -807,7 +809,7 @@ def _ridge_regression( raise TypeError("SVD solver does not support sparse inputs currently") coef = _solve_svd(X, y, alpha, xp) - if ravel: + if n_targets == 1: coef = _ravel(coef) coef = xp.asarray(coef) @@ -1682,7 +1684,7 @@ def _matmat(self, v): return res -class _IdentityRegressor: +class _IdentityRegressor(RegressorMixin, BaseEstimator): """Fake regressor which will directly output the prediction.""" def decision_function(self, y_predict): @@ -1692,7 +1694,7 @@ def predict(self, y_predict): return y_predict -class _IdentityClassifier(LinearClassifierMixin): +class _IdentityClassifier(LinearClassifierMixin, BaseEstimator): """Fake classifier which will directly output the prediction. We inherit from LinearClassifierMixin to get the proper shape for the @@ -2240,6 +2242,8 @@ def fit(self, X, y, sample_weight=None, score_params=None): self.best_score_ = best_score self.dual_coef_ = best_coef self.coef_ = safe_sparse_dot(self.dual_coef_.T, X) + if y.ndim == 1 or y.shape[1] == 1: + self.coef_ = self.coef_.ravel() if sparse.issparse(X): X_offset = X_mean * X_scale diff --git a/sklearn/linear_model/_stochastic_gradient.py b/sklearn/linear_model/_stochastic_gradient.py index 4d924a1ad00a6..d5f2247e2af34 100644 --- a/sklearn/linear_model/_stochastic_gradient.py +++ b/sklearn/linear_model/_stochastic_gradient.py @@ -2084,7 +2084,7 @@ def __sklearn_tags__(self): return tags -class SGDOneClassSVM(BaseSGD, OutlierMixin): +class SGDOneClassSVM(OutlierMixin, BaseSGD): """Solves linear One-Class SVM using Stochastic Gradient Descent. This implementation is meant to be used with a kernel approximation diff --git a/sklearn/linear_model/tests/test_base.py b/sklearn/linear_model/tests/test_base.py index f6bb0c975a973..be8e85b9703fa 100644 --- a/sklearn/linear_model/tests/test_base.py +++ b/sklearn/linear_model/tests/test_base.py @@ -692,10 +692,23 @@ def test_fused_types_make_dataset(csr_container): assert_array_equal(yi_64, yicsr_64) -@pytest.mark.parametrize("sparse_container", [None] + CSR_CONTAINERS) +@pytest.mark.parametrize("X_shape", [(10, 5), (10, 20), (100, 100)]) +@pytest.mark.parametrize( + "sparse_container", + [None] + + [ + pytest.param( + container, + marks=pytest.mark.xfail( + reason="Known to fail for CSR arrays, see issue #30131." + ), + ) + for container in CSR_CONTAINERS + ], +) @pytest.mark.parametrize("fit_intercept", [False, True]) def test_linear_regression_sample_weight_consistency( - sparse_container, fit_intercept, global_random_seed + X_shape, sparse_container, fit_intercept, global_random_seed ): """Test that the impact of sample_weight is consistent. @@ -704,7 +717,7 @@ def test_linear_regression_sample_weight_consistency( It is very similar to test_enet_sample_weight_consistency. """ rng = np.random.RandomState(global_random_seed) - n_samples, n_features = 10, 5 + n_samples, n_features = X_shape X = rng.rand(n_samples, n_features) y = rng.rand(n_samples) @@ -754,17 +767,9 @@ def test_linear_regression_sample_weight_consistency( if fit_intercept: intercept_0 = reg.intercept_ reg.fit(X[:-5], y[:-5], sample_weight=sample_weight[:-5]) - if fit_intercept and sparse_container is None: - # FIXME: https://github.com/scikit-learn/scikit-learn/issues/26164 - # This often fails, e.g. when calling - # SKLEARN_TESTS_GLOBAL_RANDOM_SEED="all" pytest \ - # sklearn/linear_model/tests/test_base.py\ - # ::test_linear_regression_sample_weight_consistency - pass - else: - assert_allclose(reg.coef_, coef_0, rtol=1e-5) - if fit_intercept: - assert_allclose(reg.intercept_, intercept_0) + assert_allclose(reg.coef_, coef_0, rtol=1e-5) + if fit_intercept: + assert_allclose(reg.intercept_, intercept_0) # 5) check that multiplying sample_weight by 2 is equivalent to repeating # corresponding samples twice diff --git a/sklearn/linear_model/tests/test_common.py b/sklearn/linear_model/tests/test_common.py index 6aa7c737983ac..2483a26644cbb 100644 --- a/sklearn/linear_model/tests/test_common.py +++ b/sklearn/linear_model/tests/test_common.py @@ -6,16 +6,19 @@ import pytest from sklearn.base import is_classifier -from sklearn.datasets import make_low_rank_matrix +from sklearn.datasets import make_classification, make_low_rank_matrix, make_regression from sklearn.linear_model import ( ARDRegression, BayesianRidge, ElasticNet, ElasticNetCV, + GammaRegressor, + HuberRegressor, Lars, LarsCV, Lasso, LassoCV, + LassoLars, LassoLarsCV, LassoLarsIC, LinearRegression, @@ -27,12 +30,22 @@ MultiTaskLassoCV, OrthogonalMatchingPursuit, OrthogonalMatchingPursuitCV, + PassiveAggressiveClassifier, + PassiveAggressiveRegressor, + Perceptron, PoissonRegressor, Ridge, + RidgeClassifier, + RidgeClassifierCV, RidgeCV, + SGDClassifier, SGDRegressor, + TheilSenRegressor, TweedieRegressor, ) +from sklearn.preprocessing import MinMaxScaler +from sklearn.svm import LinearSVC, LinearSVR +from sklearn.utils._testing import set_random_state # Note: GammaRegressor() and TweedieRegressor(power != 1) have a non-canonical link. @@ -135,7 +148,6 @@ def test_balance_property(model, with_sample_weight, global_random_seed): model.fit(X, y, sample_weight=sw) else: model.fit(X, y) - # Assert balance property. if is_classifier(model): assert np.average(model.predict_proba(X)[:, 1], weights=sw) == pytest.approx( @@ -145,3 +157,78 @@ def test_balance_property(model, with_sample_weight, global_random_seed): assert np.average(model.predict(X), weights=sw, axis=0) == pytest.approx( np.average(y, weights=sw, axis=0), rel=rel ) + + +@pytest.mark.filterwarnings("ignore:The default of 'normalize'") +@pytest.mark.filterwarnings("ignore:lbfgs failed to converge") +@pytest.mark.parametrize( + "Regressor", + [ + ARDRegression, + BayesianRidge, + ElasticNet, + ElasticNetCV, + GammaRegressor, + HuberRegressor, + Lars, + LarsCV, + Lasso, + LassoCV, + LassoLars, + LassoLarsCV, + LassoLarsIC, + LinearSVR, + LinearRegression, + OrthogonalMatchingPursuit, + OrthogonalMatchingPursuitCV, + PassiveAggressiveRegressor, + PoissonRegressor, + Ridge, + RidgeCV, + SGDRegressor, + TheilSenRegressor, + TweedieRegressor, + ], +) +@pytest.mark.parametrize("ndim", [1, 2]) +def test_linear_model_regressor_coef_shape(Regressor, ndim): + """Check the consistency of linear models `coef` shape.""" + if Regressor is LinearRegression: + pytest.xfail("LinearRegression does not follow `coef_` shape contract!") + + X, y = make_regression(random_state=0, n_samples=200, n_features=20) + y = MinMaxScaler().fit_transform(y.reshape(-1, 1))[:, 0] + 1 + y = y[:, np.newaxis] if ndim == 2 else y + + regressor = Regressor() + set_random_state(regressor) + regressor.fit(X, y) + assert regressor.coef_.shape == (X.shape[1],) + + +@pytest.mark.parametrize( + "Classifier", + [ + LinearSVC, + LogisticRegression, + LogisticRegressionCV, + PassiveAggressiveClassifier, + Perceptron, + RidgeClassifier, + RidgeClassifierCV, + SGDClassifier, + ], +) +@pytest.mark.parametrize("n_classes", [2, 3]) +def test_linear_model_classifier_coef_shape(Classifier, n_classes): + if Classifier in (RidgeClassifier, RidgeClassifierCV): + pytest.xfail(f"{Classifier} does not follow `coef_` shape contract!") + + X, y = make_classification(n_informative=10, n_classes=n_classes, random_state=0) + n_features = X.shape[1] + + classifier = Classifier() + set_random_state(classifier) + classifier.fit(X, y) + expected_shape = (1, n_features) if n_classes == 2 else (n_classes, n_features) + assert classifier.coef_.shape == expected_shape diff --git a/sklearn/linear_model/tests/test_quantile.py b/sklearn/linear_model/tests/test_quantile.py index da96593de00f2..1d166b14091cc 100644 --- a/sklearn/linear_model/tests/test_quantile.py +++ b/sklearn/linear_model/tests/test_quantile.py @@ -10,7 +10,7 @@ from sklearn.exceptions import ConvergenceWarning from sklearn.linear_model import HuberRegressor, QuantileRegressor from sklearn.metrics import mean_pinball_loss -from sklearn.utils._testing import assert_allclose, skip_if_32bit +from sklearn.utils._testing import assert_allclose from sklearn.utils.fixes import ( COO_CONTAINERS, CSC_CONTAINERS, @@ -233,26 +233,40 @@ def test_linprog_failure(): reg.fit(X, y) -@skip_if_32bit @pytest.mark.parametrize( "sparse_container", CSC_CONTAINERS + CSR_CONTAINERS + COO_CONTAINERS ) @pytest.mark.parametrize("solver", ["highs", "highs-ds", "highs-ipm"]) @pytest.mark.parametrize("fit_intercept", [True, False]) -def test_sparse_input(sparse_container, solver, fit_intercept): +def test_sparse_input(sparse_container, solver, fit_intercept, global_random_seed): """Test that sparse and dense X give same results.""" - X, y = make_regression(n_samples=100, n_features=20, random_state=1, noise=1.0) + n_informative = 10 + quantile_level = 0.6 + X, y = make_regression( + n_samples=300, + n_features=20, + n_informative=10, + random_state=global_random_seed, + noise=1.0, + ) X_sparse = sparse_container(X) - alpha = 1e-4 - quant_dense = QuantileRegressor(alpha=alpha, fit_intercept=fit_intercept).fit(X, y) + alpha = 0.1 + quant_dense = QuantileRegressor( + quantile=quantile_level, alpha=alpha, fit_intercept=fit_intercept + ).fit(X, y) quant_sparse = QuantileRegressor( - alpha=alpha, fit_intercept=fit_intercept, solver=solver + quantile=quantile_level, alpha=alpha, fit_intercept=fit_intercept, solver=solver ).fit(X_sparse, y) assert_allclose(quant_sparse.coef_, quant_dense.coef_, rtol=1e-2) + sparse_support = quant_sparse.coef_ != 0 + dense_support = quant_dense.coef_ != 0 + assert dense_support.sum() == pytest.approx(n_informative, abs=1) + assert sparse_support.sum() == pytest.approx(n_informative, abs=1) if fit_intercept: assert quant_sparse.intercept_ == approx(quant_dense.intercept_) # check that we still predict fraction - assert 0.45 <= np.mean(y < quant_sparse.predict(X_sparse)) <= 0.57 + empirical_coverage = np.mean(y < quant_sparse.predict(X_sparse)) + assert empirical_coverage == approx(quantile_level, abs=3e-2) def test_error_interior_point_future(X_y_data, monkeypatch): diff --git a/sklearn/linear_model/tests/test_ridge.py b/sklearn/linear_model/tests/test_ridge.py index 32c7f7423e554..05cd49545d653 100644 --- a/sklearn/linear_model/tests/test_ridge.py +++ b/sklearn/linear_model/tests/test_ridge.py @@ -549,7 +549,7 @@ def test_ridge_shapes_type(): assert isinstance(ridge.intercept_, float) ridge.fit(X, Y1) - assert ridge.coef_.shape == (1, n_features) + assert ridge.coef_.shape == (n_features,) assert ridge.intercept_.shape == (1,) assert isinstance(ridge.coef_, np.ndarray) assert isinstance(ridge.intercept_, np.ndarray) @@ -913,6 +913,8 @@ def test_ridge_gcv_sample_weights( ridge_reg = Ridge(alpha=kfold.alpha_, fit_intercept=fit_intercept) splits = cv.split(X_tiled, y_tiled, groups=indices) predictions = cross_val_predict(ridge_reg, X_tiled, y_tiled, cv=splits) + if predictions.shape != y_tiled.shape: + predictions = predictions.reshape(y_tiled.shape) kfold_errors = (y_tiled - predictions) ** 2 kfold_errors = [ np.sum(kfold_errors[indices == i], axis=0) for i in np.arange(X.shape[0]) diff --git a/sklearn/linear_model/tests/test_sgd.py b/sklearn/linear_model/tests/test_sgd.py index 6f53d2826cc11..c902de2d66480 100644 --- a/sklearn/linear_model/tests/test_sgd.py +++ b/sklearn/linear_model/tests/test_sgd.py @@ -20,6 +20,7 @@ from sklearn.pipeline import make_pipeline from sklearn.preprocessing import LabelEncoder, MinMaxScaler, StandardScaler, scale from sklearn.svm import OneClassSVM +from sklearn.utils import get_tags from sklearn.utils._testing import ( assert_allclose, assert_almost_equal, @@ -2170,3 +2171,12 @@ def test_passive_aggressive_deprecated_average(Estimator): est = Estimator(average=0) with pytest.warns(FutureWarning, match="average=0"): est.fit(X, Y) + + +def test_sgd_one_class_svm_estimator_type(): + """Check that SGDOneClassSVM has the correct estimator type. + + Non-regression test for if the mixin was not on the left. + """ + sgd_ocsvm = SGDOneClassSVM() + assert get_tags(sgd_ocsvm).estimator_type == "outlier_detector" diff --git a/sklearn/meson.build b/sklearn/meson.build index 4099346f554ca..7c01ad73f9ce4 100644 --- a/sklearn/meson.build +++ b/sklearn/meson.build @@ -48,7 +48,7 @@ if not meson.is_cross_build() # meson-python is required only when going through pip. Using meson directly # should not check meson-python version. meson_python_version_command_result = run_command(py, - ['-c', 'import mesonpy; print(mesonpy.__version__)'], check: false) + ['-c', 'import importlib.metadata; print(importlib.metadata.version("meson-python"))'], check: false) meson_python_installed = meson_python_version_command_result.returncode() == 0 if meson_python_installed meson_python_version = meson_python_version_command_result.stdout().strip() diff --git a/sklearn/metrics/_classification.py b/sklearn/metrics/_classification.py index 65bedb99d573d..c320183380a07 100644 --- a/sklearn/metrics/_classification.py +++ b/sklearn/metrics/_classification.py @@ -152,10 +152,16 @@ def _check_targets(y_true, y_pred): "y_pred": ["array-like", "sparse matrix"], "normalize": ["boolean"], "sample_weight": ["array-like", None], + "zero_division": [ + Options(Real, {0.0, 1.0, np.nan}), + StrOptions({"warn"}), + ], }, prefer_skip_nested_validation=True, ) -def accuracy_score(y_true, y_pred, *, normalize=True, sample_weight=None): +def accuracy_score( + y_true, y_pred, *, normalize=True, sample_weight=None, zero_division="warn" +): """Accuracy classification score. In multilabel classification, this function computes subset accuracy: @@ -179,6 +185,13 @@ def accuracy_score(y_true, y_pred, *, normalize=True, sample_weight=None): sample_weight : array-like of shape (n_samples,), default=None Sample weights. + zero_division : {"warn", 0.0, 1.0, np.nan}, default="warn" + Sets the value to return when there is a zero division, + e.g. when `y_true` and `y_pred` are empty. + If set to "warn", returns 0.0 input, but a warning is also raised. + + versionadded:: 1.6 + Returns ------- score : float or int @@ -220,6 +233,16 @@ def accuracy_score(y_true, y_pred, *, normalize=True, sample_weight=None): y_true, y_pred = attach_unique(y_true, y_pred) y_type, y_true, y_pred = _check_targets(y_true, y_pred) check_consistent_length(y_true, y_pred, sample_weight) + + if _num_samples(y_true) == 0: + if zero_division == "warn": + msg = ( + "accuracy() is ill-defined and set to 0.0. Use the `zero_division` " + "param to control this behavior." + ) + warnings.warn(msg, UndefinedMetricWarning) + return _check_zero_division(zero_division) + if y_type.startswith("multilabel"): if _is_numpy_namespace(xp): differing_labels = count_nonzero(y_true - y_pred, axis=1) @@ -992,10 +1015,15 @@ def jaccard_score( "y_true": ["array-like"], "y_pred": ["array-like"], "sample_weight": ["array-like", None], + "zero_division": [ + Options(Real, {0.0, 1.0}), + "nan", + StrOptions({"warn"}), + ], }, prefer_skip_nested_validation=True, ) -def matthews_corrcoef(y_true, y_pred, *, sample_weight=None): +def matthews_corrcoef(y_true, y_pred, *, sample_weight=None, zero_division="warn"): """Compute the Matthews correlation coefficient (MCC). The Matthews correlation coefficient is used in machine learning as a @@ -1026,6 +1054,13 @@ def matthews_corrcoef(y_true, y_pred, *, sample_weight=None): .. versionadded:: 0.18 + zero_division : {"warn", 0.0, 1.0, np.nan}, default="warn" + Sets the value to return when there is a zero division, i.e. when all + predictions and labels are negative. If set to "warn", this acts like 0, + but a warning is also raised. + + .. versionadded:: 1.6 + Returns ------- mcc : float @@ -1079,7 +1114,13 @@ def matthews_corrcoef(y_true, y_pred, *, sample_weight=None): cov_ytyt = n_samples**2 - np.dot(t_sum, t_sum) if cov_ypyp * cov_ytyt == 0: - return 0.0 + if zero_division == "warn": + msg = ( + "Matthews correlation coefficient is ill-defined and being set to 0.0. " + "Use `zero_division` to control this behaviour." + ) + warnings.warn(msg, UndefinedMetricWarning, stacklevel=2) + return _check_zero_division(zero_division) else: return cov_ytyp / np.sqrt(cov_ytyt * cov_ypyp) @@ -1487,6 +1528,7 @@ def fbeta_score( predictions and labels are negative. Notes: + - If set to "warn", this acts like 0, but a warning is also raised. - If set to `np.nan`, such values will be excluded from the average. @@ -1776,11 +1818,13 @@ def precision_recall_fscore_support( zero_division : {"warn", 0.0, 1.0, np.nan}, default="warn" Sets the value to return when there is a zero division: - - recall: when there are no positive labels - - precision: when there are no positive predictions - - f-score: both + + - recall: when there are no positive labels + - precision: when there are no positive predictions + - f-score: both Notes: + - If set to "warn", this acts like 0, but a warning is also raised. - If set to `np.nan`, such values will be excluded from the average. @@ -2205,6 +2249,7 @@ def precision_score( Sets the value to return when there is a zero division. Notes: + - If set to "warn", this acts like 0, but a warning is also raised. - If set to `np.nan`, such values will be excluded from the average. @@ -2384,6 +2429,7 @@ def recall_score( Sets the value to return when there is a zero division. Notes: + - If set to "warn", this acts like 0, but a warning is also raised. - If set to `np.nan`, such values will be excluded from the average. diff --git a/sklearn/metrics/_plot/precision_recall_curve.py b/sklearn/metrics/_plot/precision_recall_curve.py index bed7df4156d9b..f3866db6c9865 100644 --- a/sklearn/metrics/_plot/precision_recall_curve.py +++ b/sklearn/metrics/_plot/precision_recall_curve.py @@ -5,6 +5,7 @@ from ...utils._plotting import ( _BinaryClassifierCurveDisplayMixin, + _despine, _validate_style_kwargs, ) from .._ranking import average_precision_score, precision_recall_curve @@ -131,6 +132,7 @@ def plot( name=None, plot_chance_level=False, chance_level_kw=None, + despine=False, **kwargs, ): """Plot visualization. @@ -160,6 +162,11 @@ def plot( .. versionadded:: 1.3 + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + .. versionadded:: 1.6 + **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. @@ -241,6 +248,9 @@ def plot( else: self.chance_level_ = None + if despine: + _despine(self.ax_) + if "label" in line_kwargs or plot_chance_level: self.ax_.legend(loc="lower left") @@ -261,6 +271,7 @@ def from_estimator( ax=None, plot_chance_level=False, chance_level_kw=None, + despine=False, **kwargs, ): """Plot precision-recall curve given an estimator and some data. @@ -318,6 +329,11 @@ def from_estimator( .. versionadded:: 1.3 + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + .. versionadded:: 1.6 + **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. @@ -378,6 +394,7 @@ def from_estimator( ax=ax, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, + despine=despine, **kwargs, ) @@ -394,6 +411,7 @@ def from_predictions( ax=None, plot_chance_level=False, chance_level_kw=None, + despine=False, **kwargs, ): """Plot precision-recall curve given binary class predictions. @@ -440,6 +458,11 @@ def from_predictions( .. versionadded:: 1.3 + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + .. versionadded:: 1.6 + **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. @@ -514,5 +537,6 @@ def from_predictions( name=name, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, + despine=despine, **kwargs, ) diff --git a/sklearn/metrics/_plot/roc_curve.py b/sklearn/metrics/_plot/roc_curve.py index bcff8fc7cd071..058b3612baa61 100644 --- a/sklearn/metrics/_plot/roc_curve.py +++ b/sklearn/metrics/_plot/roc_curve.py @@ -3,6 +3,7 @@ from ...utils._plotting import ( _BinaryClassifierCurveDisplayMixin, + _despine, _validate_style_kwargs, ) from .._ranking import auc, roc_curve @@ -95,6 +96,7 @@ def plot( name=None, plot_chance_level=False, chance_level_kw=None, + despine=False, **kwargs, ): """Plot visualization. @@ -122,6 +124,11 @@ def plot( .. versionadded:: 1.3 + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + .. versionadded:: 1.6 + **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. @@ -175,6 +182,9 @@ def plot( else: self.chance_level_ = None + if despine: + _despine(self.ax_) + if "label" in line_kwargs or "label" in chance_level_kw: self.ax_.legend(loc="lower right") @@ -195,6 +205,7 @@ def from_estimator( ax=None, plot_chance_level=False, chance_level_kw=None, + despine=False, **kwargs, ): """Create a ROC Curve display from an estimator. @@ -249,6 +260,11 @@ def from_estimator( .. versionadded:: 1.3 + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + .. versionadded:: 1.6 + **kwargs : dict Keyword arguments to be passed to matplotlib's `plot`. @@ -299,6 +315,7 @@ def from_estimator( pos_label=pos_label, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, + despine=despine, **kwargs, ) @@ -315,6 +332,7 @@ def from_predictions( ax=None, plot_chance_level=False, chance_level_kw=None, + despine=False, **kwargs, ): """Plot ROC curve given the true and predicted values. @@ -365,6 +383,11 @@ def from_predictions( .. versionadded:: 1.3 + despine : bool, default=False + Whether to remove the top and right spines from the plot. + + .. versionadded:: 1.6 + **kwargs : dict Additional keywords arguments passed to matplotlib `plot` function. @@ -423,5 +446,6 @@ def from_predictions( name=name, plot_chance_level=plot_chance_level, chance_level_kw=chance_level_kw, + despine=despine, **kwargs, ) diff --git a/sklearn/metrics/_plot/tests/test_common_curve_display.py b/sklearn/metrics/_plot/tests/test_common_curve_display.py index 7fe0f0fc6fa7f..0014a73055e41 100644 --- a/sklearn/metrics/_plot/tests/test_common_curve_display.py +++ b/sklearn/metrics/_plot/tests/test_common_curve_display.py @@ -1,7 +1,7 @@ import numpy as np import pytest -from sklearn.base import ClassifierMixin, clone +from sklearn.base import BaseEstimator, ClassifierMixin, clone from sklearn.calibration import CalibrationDisplay from sklearn.compose import make_column_transformer from sklearn.datasets import load_iris @@ -121,7 +121,7 @@ def test_display_curve_error_no_response( is not defined for the given trained classifier.""" X, y = data_binary - class MyClassifier(ClassifierMixin): + class MyClassifier(ClassifierMixin, BaseEstimator): def fit(self, X, y): self.classes_ = [0, 1] return self diff --git a/sklearn/metrics/_plot/tests/test_precision_recall_display.py b/sklearn/metrics/_plot/tests/test_precision_recall_display.py index 8fbdfd19295af..2ec34feb224da 100644 --- a/sklearn/metrics/_plot/tests/test_precision_recall_display.py +++ b/sklearn/metrics/_plot/tests/test_precision_recall_display.py @@ -353,3 +353,30 @@ def test_precision_recall_raise_no_prevalence(pyplot): with pytest.raises(ValueError, match=msg): display.plot(plot_chance_level=True) + + +@pytest.mark.parametrize("despine", [True, False]) +@pytest.mark.parametrize("constructor_name", ["from_estimator", "from_predictions"]) +def test_plot_precision_recall_despine(pyplot, despine, constructor_name): + # Check that the despine keyword is working correctly + X, y = make_classification(n_classes=2, n_samples=50, random_state=0) + + clf = LogisticRegression().fit(X, y) + clf.fit(X, y) + + y_pred = clf.decision_function(X) + + # safe guard for the binary if/else construction + assert constructor_name in ("from_estimator", "from_predictions") + + if constructor_name == "from_estimator": + display = PrecisionRecallDisplay.from_estimator(clf, X, y, despine=despine) + else: + display = PrecisionRecallDisplay.from_predictions(y, y_pred, despine=despine) + + for s in ["top", "right"]: + assert display.ax_.spines[s].get_visible() is not despine + + if despine: + for s in ["bottom", "left"]: + assert display.ax_.spines[s].get_bounds() == (0, 1) diff --git a/sklearn/metrics/_plot/tests/test_roc_curve_display.py b/sklearn/metrics/_plot/tests/test_roc_curve_display.py index 4a2fc2e3cbfdd..8c8562e3833e4 100644 --- a/sklearn/metrics/_plot/tests/test_roc_curve_display.py +++ b/sklearn/metrics/_plot/tests/test_roc_curve_display.py @@ -334,3 +334,30 @@ def test_plot_roc_curve_pos_label(pyplot, response_method, constructor_name): assert display.roc_auc == pytest.approx(roc_auc_limit) assert trapezoid(display.tpr, display.fpr) == pytest.approx(roc_auc_limit) + + +@pytest.mark.parametrize("despine", [True, False]) +@pytest.mark.parametrize("constructor_name", ["from_estimator", "from_predictions"]) +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) + + y_pred = lr.decision_function(X) + + # safe guard for the binary if/else construction + assert constructor_name in ("from_estimator", "from_predictions") + + if constructor_name == "from_estimator": + display = RocCurveDisplay.from_estimator(lr, X, y, despine=despine) + else: + display = RocCurveDisplay.from_predictions(y, y_pred, despine=despine) + + for s in ["top", "right"]: + assert display.ax_.spines[s].get_visible() is not despine + + if despine: + for s in ["bottom", "left"]: + assert display.ax_.spines[s].get_bounds() == (0, 1) diff --git a/sklearn/metrics/_ranking.py b/sklearn/metrics/_ranking.py index 0d24a68bf464b..958ab3be9cc0d 100644 --- a/sklearn/metrics/_ranking.py +++ b/sklearn/metrics/_ranking.py @@ -375,12 +375,11 @@ def _binary_roc_auc_score(y_true, y_score, sample_weight=None, max_fpr=None): warnings.warn( ( "Only one class is present in y_true. ROC AUC score " - "is not defined in that case. The score is set to " - "0.0." + "is not defined in that case." ), UndefinedMetricWarning, ) - return 0.0 + return np.nan fpr, tpr, _ = roc_curve(y_true, y_score, sample_weight=sample_weight) if max_fpr is None or max_fpr == 1: diff --git a/sklearn/metrics/pairwise.py b/sklearn/metrics/pairwise.py index 9b62a0f73f130..3e1fe1d68420f 100644 --- a/sklearn/metrics/pairwise.py +++ b/sklearn/metrics/pairwise.py @@ -768,7 +768,7 @@ def pairwise_distances_argmin_min( Valid values for metric are: - from scikit-learn: ['cityblock', 'cosine', 'euclidean', 'l1', 'l2', - 'manhattan'] + 'manhattan', 'nan_euclidean'] - from scipy.spatial.distance: ['braycurtis', 'canberra', 'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski', @@ -814,7 +814,8 @@ def pairwise_distances_argmin_min( >>> distances array([1., 1.]) """ - X, Y = check_pairwise_arrays(X, Y) + ensure_all_finite = "allow-nan" if metric == "nan_euclidean" else True + X, Y = check_pairwise_arrays(X, Y, ensure_all_finite=ensure_all_finite) if axis == 0: X, Y = Y, X @@ -915,7 +916,7 @@ def pairwise_distances_argmin(X, Y, *, axis=1, metric="euclidean", metric_kwargs Valid values for metric are: - from scikit-learn: ['cityblock', 'cosine', 'euclidean', 'l1', 'l2', - 'manhattan'] + 'manhattan', 'nan_euclidean'] - from scipy.spatial.distance: ['braycurtis', 'canberra', 'chebyshev', 'correlation', 'dice', 'hamming', 'jaccard', 'kulsinski', @@ -954,7 +955,8 @@ def pairwise_distances_argmin(X, Y, *, axis=1, metric="euclidean", metric_kwargs >>> pairwise_distances_argmin(X, Y) array([0, 1]) """ - X, Y = check_pairwise_arrays(X, Y) + ensure_all_finite = "allow-nan" if metric == "nan_euclidean" else True + X, Y = check_pairwise_arrays(X, Y, ensure_all_finite=ensure_all_finite) if axis == 0: X, Y = Y, X diff --git a/sklearn/metrics/tests/test_classification.py b/sklearn/metrics/tests/test_classification.py index cd6ad93da85a7..d0e9f3d9a08b0 100644 --- a/sklearn/metrics/tests/test_classification.py +++ b/sklearn/metrics/tests/test_classification.py @@ -795,9 +795,22 @@ def test_cohen_kappa(): ) -def test_matthews_corrcoef_nan(): - assert matthews_corrcoef([0], [1]) == 0.0 - assert matthews_corrcoef([0, 0], [0, 1]) == 0.0 +@pytest.mark.parametrize("zero_division", ["warn", 0, 1, np.nan]) +@pytest.mark.parametrize("y_true, y_pred", [([0], [1]), ([0, 0], [0, 1])]) +def test_matthews_corrcoef_zero_division(zero_division, y_true, y_pred): + """Check the behaviour of `zero_division` in `matthews_corrcoef`.""" + expected_result = 0.0 if zero_division == "warn" else zero_division + + if zero_division == "warn": + with pytest.warns(UndefinedMetricWarning): + result = matthews_corrcoef(y_true, y_pred, zero_division=zero_division) + else: + result = matthews_corrcoef(y_true, y_pred, zero_division=zero_division) + + if np.isnan(expected_result): + assert np.isnan(result) + else: + assert result == expected_result @pytest.mark.parametrize("zero_division", [0, 1, np.nan]) @@ -809,6 +822,7 @@ def test_matthews_corrcoef_nan(): partial(fbeta_score, beta=1), precision_score, recall_score, + accuracy_score, partial(cohen_kappa_score, labels=[0, 1]), ], ) @@ -816,6 +830,11 @@ def test_zero_division_nan_no_warning(metric, y_true, y_pred, zero_division): """Check the behaviour of `zero_division` when setting to 0, 1 or np.nan. No warnings should be raised. """ + if metric is accuracy_score and len(y_true): + pytest.skip( + reason="zero_division is only used with empty y_true/y_pred for accuracy" + ) + with warnings.catch_warnings(): warnings.simplefilter("error") result = metric(y_true, y_pred, zero_division=zero_division) @@ -834,6 +853,7 @@ def test_zero_division_nan_no_warning(metric, y_true, y_pred, zero_division): partial(fbeta_score, beta=1), precision_score, recall_score, + accuracy_score, cohen_kappa_score, ], ) @@ -841,6 +861,11 @@ def test_zero_division_nan_warning(metric, y_true, y_pred): """Check the behaviour of `zero_division` when setting to "warn". A `UndefinedMetricWarning` should be raised. """ + if metric is accuracy_score and len(y_true): + pytest.skip( + reason="zero_division is only used with empty y_true/y_pred for accuracy" + ) + with pytest.warns(UndefinedMetricWarning): result = metric(y_true, y_pred, zero_division="warn") assert result == 0.0 @@ -912,15 +937,19 @@ def test_matthews_corrcoef(): # For the zero vector case, the corrcoef cannot be calculated and should # output 0 - assert_almost_equal(matthews_corrcoef([0, 0, 0, 0], [0, 0, 0, 0]), 0.0) + assert_almost_equal( + matthews_corrcoef([0, 0, 0, 0], [0, 0, 0, 0], zero_division=0), 0.0 + ) # And also for any other vector with 0 variance - assert_almost_equal(matthews_corrcoef(y_true, ["a"] * len(y_true)), 0.0) + assert_almost_equal( + matthews_corrcoef(y_true, ["a"] * len(y_true), zero_division=0), 0.0 + ) # These two vectors have 0 correlation and hence mcc should be 0 y_1 = [1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1] y_2 = [1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1] - assert_almost_equal(matthews_corrcoef(y_1, y_2), 0.0) + assert_almost_equal(matthews_corrcoef(y_1, y_2, zero_division=0), 0.0) # Check that sample weight is able to selectively exclude mask = [1] * 10 + [0] * 10 @@ -953,17 +982,17 @@ def test_matthews_corrcoef_multiclass(): # Zero variance will result in an mcc of zero y_true = [0, 1, 2] y_pred = [3, 3, 3] - assert_almost_equal(matthews_corrcoef(y_true, y_pred), 0.0) + assert_almost_equal(matthews_corrcoef(y_true, y_pred, zero_division=0), 0.0) # Also for ground truth with zero variance y_true = [3, 3, 3] y_pred = [0, 1, 2] - assert_almost_equal(matthews_corrcoef(y_true, y_pred), 0.0) + assert_almost_equal(matthews_corrcoef(y_true, y_pred, zero_division=0), 0.0) # These two vectors have 0 correlation and hence mcc should be 0 y_1 = [0, 1, 2, 0, 1, 2, 0, 1, 2] y_2 = [1, 1, 1, 2, 2, 2, 0, 0, 0] - assert_almost_equal(matthews_corrcoef(y_1, y_2), 0.0) + assert_almost_equal(matthews_corrcoef(y_1, y_2, zero_division=0), 0.0) # We can test that binary assumptions hold using the multiclass computation # by masking the weight of samples not in the first two classes @@ -982,7 +1011,10 @@ def test_matthews_corrcoef_multiclass(): y_pred = [0, 0, 1, 2] sample_weight = [1, 1, 0, 0] assert_almost_equal( - matthews_corrcoef(y_true, y_pred, sample_weight=sample_weight), 0.0 + matthews_corrcoef( + y_true, y_pred, sample_weight=sample_weight, zero_division=0.0 + ), + 0.0, ) diff --git a/sklearn/metrics/tests/test_common.py b/sklearn/metrics/tests/test_common.py index f70f0bfa50137..e6abc8c433013 100644 --- a/sklearn/metrics/tests/test_common.py +++ b/sklearn/metrics/tests/test_common.py @@ -1,3 +1,4 @@ +import math from functools import partial from inspect import signature from itertools import chain, permutations, product @@ -843,9 +844,9 @@ def test_format_invariance_with_1d_vectors(name): ): if "roc_auc" in name: # for consistency between the `roc_cuve` and `roc_auc_score` - # 0.0 is returned and an `UndefinedMetricWarning` is raised + # np.nan is returned and an `UndefinedMetricWarning` is raised with pytest.warns(UndefinedMetricWarning): - assert metric(y1_row, y2_row) == pytest.approx(0.0) + assert math.isnan(metric(y1_row, y2_row)) else: with pytest.raises(ValueError): metric(y1_row, y2_row) diff --git a/sklearn/metrics/tests/test_pairwise.py b/sklearn/metrics/tests/test_pairwise.py index f93dbcd6d8288..96f9ec256e800 100644 --- a/sklearn/metrics/tests/test_pairwise.py +++ b/sklearn/metrics/tests/test_pairwise.py @@ -1582,6 +1582,33 @@ def test_numeric_pairwise_distances_datatypes(metric, global_dtype, y_is_x): assert_allclose(dist, expected_dist) +@pytest.mark.parametrize( + "pairwise_distances_func", + [pairwise_distances, pairwise_distances_argmin, pairwise_distances_argmin_min], +) +def test_nan_euclidean_support(pairwise_distances_func): + """Check that `nan_euclidean` is lenient with `nan` values.""" + + X = [[0, 1], [1, np.nan], [2, 3], [3, 5]] + output = pairwise_distances_func(X, X, metric="nan_euclidean") + + assert not np.isnan(output).any() + + +def test_nan_euclidean_constant_input_argmin(): + """Check that the behavior of constant input is the same in the case of + full of nan vector and full of zero vector. + """ + + X_nan = [[np.nan, np.nan], [np.nan, np.nan], [np.nan, np.nan]] + argmin_nan = pairwise_distances_argmin(X_nan, X_nan, metric="nan_euclidean") + + X_const = [[0, 0], [0, 0], [0, 0]] + argmin_const = pairwise_distances_argmin(X_const, X_const, metric="nan_euclidean") + + assert_allclose(argmin_nan, argmin_const) + + @pytest.mark.parametrize( "X,Y,expected_distance", [ diff --git a/sklearn/metrics/tests/test_ranking.py b/sklearn/metrics/tests/test_ranking.py index 7e7d784522524..c92fee002595f 100644 --- a/sklearn/metrics/tests/test_ranking.py +++ b/sklearn/metrics/tests/test_ranking.py @@ -1,3 +1,4 @@ +import math import re import numpy as np @@ -370,7 +371,8 @@ def test_roc_curve_toydata(): "ROC AUC score is not defined in that case." ) with pytest.warns(UndefinedMetricWarning, match=expected_message): - roc_auc_score(y_true, y_score) + auc = roc_auc_score(y_true, y_score) + assert math.isnan(auc) # case with no negative samples y_true = [1, 1] @@ -388,7 +390,8 @@ def test_roc_curve_toydata(): "ROC AUC score is not defined in that case." ) with pytest.warns(UndefinedMetricWarning, match=expected_message): - roc_auc_score(y_true, y_score) + auc = roc_auc_score(y_true, y_score) + assert math.isnan(auc) # Multi-label classification task y_true = np.array([[0, 1], [0, 1]]) diff --git a/sklearn/metrics/tests/test_score_objects.py b/sklearn/metrics/tests/test_score_objects.py index 58d6561d566db..66bf521e43ec5 100644 --- a/sklearn/metrics/tests/test_score_objects.py +++ b/sklearn/metrics/tests/test_score_objects.py @@ -3,7 +3,6 @@ import warnings from copy import deepcopy from functools import partial -from unittest.mock import Mock import joblib import numpy as np @@ -11,7 +10,7 @@ from numpy.testing import assert_allclose from sklearn import config_context -from sklearn.base import BaseEstimator +from sklearn.base import BaseEstimator, ClassifierMixin from sklearn.cluster import KMeans from sklearn.datasets import ( load_diabetes, @@ -185,7 +184,7 @@ def fit(self, X, y): return self -class EstimatorWithFitAndScore: +class EstimatorWithFitAndScore(BaseEstimator): """Dummy estimator to test scoring validators""" def fit(self, X, y): @@ -195,7 +194,7 @@ def score(self, X, y): return 1.0 -class EstimatorWithFitAndPredict: +class EstimatorWithFitAndPredict(BaseEstimator): """Dummy estimator to test scoring validators""" def fit(self, X, y): @@ -748,37 +747,41 @@ def test_multimetric_scorer_calls_method_once( expected_decision_func_count, ): X, y = np.array([[1], [1], [0], [0], [0]]), np.array([0, 1, 1, 1, 0]) - - mock_est = Mock() - mock_est._estimator_type = "classifier" - fit_func = Mock(return_value=mock_est, name="fit") - fit_func.__name__ = "fit" - predict_func = Mock(return_value=y, name="predict") - predict_func.__name__ = "predict" - pos_proba = np.random.rand(X.shape[0]) proba = np.c_[1 - pos_proba, pos_proba] - predict_proba_func = Mock(return_value=proba, name="predict_proba") - predict_proba_func.__name__ = "predict_proba" - decision_function_func = Mock(return_value=pos_proba, name="decision_function") - decision_function_func.__name__ = "decision_function" - - mock_est.fit = fit_func - mock_est.predict = predict_func - mock_est.predict_proba = predict_proba_func - mock_est.decision_function = decision_function_func - # add the classes that would be found during fit - mock_est.classes_ = np.array([0, 1]) + class MyClassifier(ClassifierMixin, BaseEstimator): + def __init__(self): + self._expected_predict_count = 0 + self._expected_predict_proba_count = 0 + self._expected_decision_function_count = 0 + + def fit(self, X, y): + self.classes_ = np.unique(y) + return self + + def predict(self, X): + self._expected_predict_count += 1 + return y + + def predict_proba(self, X): + self._expected_predict_proba_count += 1 + return proba + + def decision_function(self, X): + self._expected_decision_function_count += 1 + return pos_proba + + mock_est = MyClassifier().fit(X, y) scorer_dict = _check_multimetric_scoring(LogisticRegression(), scorers) multi_scorer = _MultimetricScorer(scorers=scorer_dict) results = multi_scorer(mock_est, X, y) assert set(scorers) == set(results) # compare dict keys - assert predict_func.call_count == expected_predict_count - assert predict_proba_func.call_count == expected_predict_proba_count - assert decision_function_func.call_count == expected_decision_func_count + assert mock_est._expected_predict_count == expected_predict_count + assert mock_est._expected_predict_proba_count == expected_predict_proba_count + assert mock_est._expected_decision_function_count == expected_decision_func_count @pytest.mark.parametrize( diff --git a/sklearn/model_selection/_classification_threshold.py b/sklearn/model_selection/_classification_threshold.py index 56bc26299a442..8ac7a67a03433 100644 --- a/sklearn/model_selection/_classification_threshold.py +++ b/sklearn/model_selection/_classification_threshold.py @@ -36,6 +36,7 @@ from ..utils.parallel import Parallel, delayed from ..utils.validation import ( _check_method_params, + _estimator_has, _num_samples, check_is_fitted, indexable, @@ -43,21 +44,11 @@ from ._split import StratifiedShuffleSplit, check_cv -def _estimator_has(attr): - """Check if we can delegate a method to the underlying estimator. - - First, we check the fitted estimator if available, otherwise we - check the unfitted estimator. - """ - - def check(self): - if hasattr(self, "estimator_"): - getattr(self.estimator_, attr) - else: - getattr(self.estimator, attr) - return True - - return check +def _check_is_fitted(estimator): + try: + check_is_fitted(estimator.estimator) + except NotFittedError: + check_is_fitted(estimator, "estimator_") class BaseThresholdClassifier(ClassifierMixin, MetaEstimatorMixin, BaseEstimator): @@ -170,8 +161,9 @@ def predict_proba(self, X): probabilities : ndarray of shape (n_samples, n_classes) The class probabilities of the input samples. """ - check_is_fitted(self, "estimator_") - return self.estimator_.predict_proba(X) + _check_is_fitted(self) + estimator = getattr(self, "estimator_", self.estimator) + return estimator.predict_proba(X) @available_if(_estimator_has("predict_log_proba")) def predict_log_proba(self, X): @@ -188,8 +180,9 @@ def predict_log_proba(self, X): log_probabilities : ndarray of shape (n_samples, n_classes) The logarithm class probabilities of the input samples. """ - check_is_fitted(self, "estimator_") - return self.estimator_.predict_log_proba(X) + _check_is_fitted(self) + estimator = getattr(self, "estimator_", self.estimator) + return estimator.predict_log_proba(X) @available_if(_estimator_has("decision_function")) def decision_function(self, X): @@ -206,8 +199,9 @@ def decision_function(self, X): decisions : ndarray of shape (n_samples,) The decision function computed the fitted estimator. """ - check_is_fitted(self, "estimator_") - return self.estimator_.decision_function(X) + _check_is_fitted(self) + estimator = getattr(self, "estimator_", self.estimator) + return estimator.decision_function(X) def __sklearn_tags__(self): tags = super().__sklearn_tags__() @@ -264,13 +258,6 @@ class FixedThresholdClassifier(BaseThresholdClassifier): If the method is not implemented by the classifier, it will raise an error. - prefit : bool, default=False - Whether a pre-fitted model is expected to be passed into the constructor - directly or not. If `True`, `estimator` must be a fitted estimator. If `False`, - `estimator` is fitted and updated by calling `fit`. - - .. versionadded:: 1.6 - Attributes ---------- estimator_ : estimator instance @@ -322,7 +309,6 @@ class FixedThresholdClassifier(BaseThresholdClassifier): **BaseThresholdClassifier._parameter_constraints, "threshold": [StrOptions({"auto"}), Real], "pos_label": [Real, str, "boolean", None], - "prefit": ["boolean"], } def __init__( @@ -332,12 +318,22 @@ def __init__( threshold="auto", pos_label=None, response_method="auto", - prefit=False, ): super().__init__(estimator=estimator, response_method=response_method) self.pos_label = pos_label self.threshold = threshold - self.prefit = prefit + + @property + def classes_(self): + if estimator := getattr(self, "estimator_", None): + return estimator.classes_ + try: + check_is_fitted(self.estimator) + return self.estimator.classes_ + except NotFittedError: + raise AttributeError( + "The underlying estimator is not fitted yet." + ) from NotFittedError def _fit(self, X, y, **params): """Fit the classifier. @@ -360,13 +356,7 @@ def _fit(self, X, y, **params): Returns an instance of self. """ routed_params = process_routing(self, "fit", **params) - if self.prefit: - check_is_fitted(self.estimator) - self.estimator_ = self.estimator - else: - self.estimator_ = clone(self.estimator).fit( - X, y, **routed_params.estimator.fit - ) + self.estimator_ = clone(self.estimator).fit(X, y, **routed_params.estimator.fit) return self def predict(self, X): @@ -382,9 +372,12 @@ def predict(self, X): class_labels : ndarray of shape (n_samples,) The predicted class. """ - check_is_fitted(self, "estimator_") + _check_is_fitted(self) + + estimator = getattr(self, "estimator_", self.estimator) + y_score, _, response_method_used = _get_response_values_binary( - self.estimator_, + estimator, X, self._get_response_method(), pos_label=self.pos_label, diff --git a/sklearn/model_selection/_search.py b/sklearn/model_selection/_search.py index 2935f7ce2465c..a8431b74259b4 100644 --- a/sklearn/model_selection/_search.py +++ b/sklearn/model_selection/_search.py @@ -13,6 +13,7 @@ from abc import ABCMeta, abstractmethod from collections import defaultdict from collections.abc import Iterable, Mapping, Sequence +from copy import deepcopy from functools import partial, reduce from itertools import product @@ -355,7 +356,7 @@ def _check_refit(search_cv, attr): ) -def _estimator_has(attr): +def _search_estimator_has(attr): """Check if we can delegate a method to the underlying estimator. Calling a prediction method will only be available if `refit=True`. In @@ -476,18 +477,23 @@ def __init__( self.return_train_score = return_train_score @property + # TODO(1.8) remove this property def _estimator_type(self): return self.estimator._estimator_type def __sklearn_tags__(self): tags = super().__sklearn_tags__() + sub_estimator_tags = get_tags(self.estimator) + tags.estimator_type = sub_estimator_tags.estimator_type + tags.classifier_tags = deepcopy(sub_estimator_tags.classifier_tags) + tags.regressor_tags = deepcopy(sub_estimator_tags.regressor_tags) # allows cross-validation to see 'precomputed' metrics - tags.input_tags.pairwise = get_tags(self.estimator).input_tags.pairwise + tags.input_tags.pairwise = sub_estimator_tags.input_tags.pairwise tags._xfail_checks = { "check_supervised_y_2d": "DataConversionWarning not caught", "check_requires_y_none": "Doesn't fail gracefully", } - tags.array_api_support = get_tags(self.estimator).array_api_support + tags.array_api_support = sub_estimator_tags.array_api_support return tags def score(self, X, y=None, **params): @@ -549,7 +555,7 @@ def score(self, X, y=None, **params): score = score[self.refit] return score - @available_if(_estimator_has("score_samples")) + @available_if(_search_estimator_has("score_samples")) def score_samples(self, X): """Call score_samples on the estimator with the best found parameters. @@ -572,7 +578,7 @@ def score_samples(self, X): check_is_fitted(self) return self.best_estimator_.score_samples(X) - @available_if(_estimator_has("predict")) + @available_if(_search_estimator_has("predict")) def predict(self, X): """Call predict on the estimator with the best found parameters. @@ -594,7 +600,7 @@ def predict(self, X): check_is_fitted(self) return self.best_estimator_.predict(X) - @available_if(_estimator_has("predict_proba")) + @available_if(_search_estimator_has("predict_proba")) def predict_proba(self, X): """Call predict_proba on the estimator with the best found parameters. @@ -617,7 +623,7 @@ def predict_proba(self, X): check_is_fitted(self) return self.best_estimator_.predict_proba(X) - @available_if(_estimator_has("predict_log_proba")) + @available_if(_search_estimator_has("predict_log_proba")) def predict_log_proba(self, X): """Call predict_log_proba on the estimator with the best found parameters. @@ -640,7 +646,7 @@ def predict_log_proba(self, X): check_is_fitted(self) return self.best_estimator_.predict_log_proba(X) - @available_if(_estimator_has("decision_function")) + @available_if(_search_estimator_has("decision_function")) def decision_function(self, X): """Call decision_function on the estimator with the best found parameters. @@ -663,7 +669,7 @@ def decision_function(self, X): check_is_fitted(self) return self.best_estimator_.decision_function(X) - @available_if(_estimator_has("transform")) + @available_if(_search_estimator_has("transform")) def transform(self, X): """Call transform on the estimator with the best found parameters. @@ -685,7 +691,7 @@ def transform(self, X): check_is_fitted(self) return self.best_estimator_.transform(X) - @available_if(_estimator_has("inverse_transform")) + @available_if(_search_estimator_has("inverse_transform")) def inverse_transform(self, X=None, Xt=None): """Call inverse_transform on the estimator with the best found params. @@ -740,7 +746,7 @@ def classes_(self): Only available when `refit=True` and the estimator is a classifier. """ - _estimator_has("classes_")(self) + _search_estimator_has("classes_")(self) return self.best_estimator_.classes_ def _run_search(self, evaluate_candidates): diff --git a/sklearn/model_selection/_split.py b/sklearn/model_selection/_split.py index 15bb580b58454..1efd7c2a3122f 100644 --- a/sklearn/model_selection/_split.py +++ b/sklearn/model_selection/_split.py @@ -536,7 +536,7 @@ class GroupKFold(GroupsConsumerMixin, _BaseKFold): number of distinct groups has to be at least equal to the number of folds). The folds are approximately balanced in the sense that the number of - samples is approximately the same in each test fold. + samples is approximately the same in each test fold when `shuffle` is True. Read more in the :ref:`User Guide `. @@ -552,6 +552,21 @@ class GroupKFold(GroupsConsumerMixin, _BaseKFold): .. versionchanged:: 0.22 ``n_splits`` default value changed from 3 to 5. + shuffle : bool, default=False + Whether to shuffle the groups before splitting into batches. + Note that the samples within each split will not be shuffled. + + .. versionadded:: 1.6 + + random_state : int, RandomState instance or None, default=None + When `shuffle` is True, `random_state` affects the ordering of the + indices, which controls the randomness of each fold. Otherwise, this + parameter has no effect. + Pass an int for reproducible output across multiple function calls. + See :term:`Glossary `. + + .. versionadded:: 1.6 + Notes ----- Groups appear in an arbitrary order throughout the folds. @@ -567,7 +582,7 @@ class GroupKFold(GroupsConsumerMixin, _BaseKFold): >>> group_kfold.get_n_splits(X, y, groups) 2 >>> print(group_kfold) - GroupKFold(n_splits=2) + GroupKFold(n_splits=2, random_state=None, shuffle=False) >>> for i, (train_index, test_index) in enumerate(group_kfold.split(X, y, groups)): ... print(f"Fold {i}:") ... print(f" Train: index={train_index}, group={groups[train_index]}") @@ -589,15 +604,15 @@ class GroupKFold(GroupsConsumerMixin, _BaseKFold): classification tasks). """ - def __init__(self, n_splits=5): - super().__init__(n_splits, shuffle=False, random_state=None) + def __init__(self, n_splits=5, *, shuffle=False, random_state=None): + super().__init__(n_splits, shuffle=shuffle, random_state=random_state) def _iter_test_indices(self, X, y, groups): if groups is None: raise ValueError("The 'groups' parameter should not be None.") groups = check_array(groups, input_name="groups", ensure_2d=False, dtype=None) - unique_groups, groups = np.unique(groups, return_inverse=True) + unique_groups, group_idx = np.unique(groups, return_inverse=True) n_groups = len(unique_groups) if self.n_splits > n_groups: @@ -606,29 +621,40 @@ def _iter_test_indices(self, X, y, groups): " than the number of groups: %d." % (self.n_splits, n_groups) ) - # Weight groups by their number of occurrences - n_samples_per_group = np.bincount(groups) + if self.shuffle: + # Split and shuffle unique groups across n_splits + rng = check_random_state(self.random_state) + unique_groups = rng.permutation(unique_groups) + split_groups = np.array_split(unique_groups, self.n_splits) + + for test_group_ids in split_groups: + test_mask = np.isin(groups, test_group_ids) + yield np.where(test_mask)[0] + + else: + # Weight groups by their number of occurrences + n_samples_per_group = np.bincount(group_idx) - # Distribute the most frequent groups first - indices = np.argsort(n_samples_per_group)[::-1] - n_samples_per_group = n_samples_per_group[indices] + # Distribute the most frequent groups first + indices = np.argsort(n_samples_per_group)[::-1] + n_samples_per_group = n_samples_per_group[indices] - # Total weight of each fold - n_samples_per_fold = np.zeros(self.n_splits) + # Total weight of each fold + n_samples_per_fold = np.zeros(self.n_splits) - # Mapping from group index to fold index - group_to_fold = np.zeros(len(unique_groups)) + # Mapping from group index to fold index + group_to_fold = np.zeros(len(unique_groups)) - # Distribute samples by adding the largest weight to the lightest fold - for group_index, weight in enumerate(n_samples_per_group): - lightest_fold = np.argmin(n_samples_per_fold) - n_samples_per_fold[lightest_fold] += weight - group_to_fold[indices[group_index]] = lightest_fold + # Distribute samples by adding the largest weight to the lightest fold + for group_index, weight in enumerate(n_samples_per_group): + lightest_fold = np.argmin(n_samples_per_fold) + n_samples_per_fold[lightest_fold] += weight + group_to_fold[indices[group_index]] = lightest_fold - indices = group_to_fold[groups] + indices = group_to_fold[group_idx] - for f in range(self.n_splits): - yield np.where(indices == f)[0] + for f in range(self.n_splits): + yield np.where(indices == f)[0] def split(self, X, y=None, groups=None): """Generate indices to split data into training and test set. diff --git a/sklearn/model_selection/tests/test_classification_threshold.py b/sklearn/model_selection/tests/test_classification_threshold.py index 12d2f20e26c4c..1ba4dcea36974 100644 --- a/sklearn/model_selection/tests/test_classification_threshold.py +++ b/sklearn/model_selection/tests/test_classification_threshold.py @@ -2,7 +2,7 @@ import pytest from sklearn import config_context -from sklearn.base import BaseEstimator, ClassifierMixin, clone +from sklearn.base import clone from sklearn.datasets import ( load_breast_cancer, load_iris, @@ -593,41 +593,26 @@ def test_fixed_threshold_classifier_metadata_routing(): assert_allclose(classifier_default_threshold.estimator_.coef_, classifier.coef_) -class ClassifierLoggingFit(ClassifierMixin, BaseEstimator): - """Classifier that logs the number of `fit` calls.""" - - def __init__(self, fit_calls=0): - self.fit_calls = fit_calls - - def fit(self, X, y, **fit_params): - self.fit_calls += 1 - self.is_fitted_ = True - return self - - def predict_proba(self, X): - return np.ones((X.shape[0], 2), np.float64) # pragma: nocover - - -def test_fixed_threshold_classifier_prefit(): - """Check the behaviour of the `FixedThresholdClassifier` with the `prefit` - parameter.""" +@pytest.mark.parametrize( + "method", ["predict_proba", "decision_function", "predict", "predict_log_proba"] +) +def test_fixed_threshold_classifier_fitted_estimator(method): + """Check that if the underlying estimator is already fitted, no fit is required.""" X, y = make_classification(random_state=0) + classifier = LogisticRegression().fit(X, y) + fixed_threshold_classifier = FixedThresholdClassifier(estimator=classifier) + # This should not raise an error + getattr(fixed_threshold_classifier, method)(X) - estimator = ClassifierLoggingFit() - model = FixedThresholdClassifier(estimator=estimator, prefit=True) - with pytest.raises(NotFittedError): - model.fit(X, y) - # check that we don't clone the classifier when `prefit=True`. - estimator.fit(X, y) - model.fit(X, y) - assert estimator.fit_calls == 1 - assert model.estimator_ is estimator +def test_fixed_threshold_classifier_classes_(): + """Check that the classes_ attribute is properly set.""" + X, y = make_classification(random_state=0) + with pytest.raises( + AttributeError, match="The underlying estimator is not fitted yet." + ): + FixedThresholdClassifier(estimator=LogisticRegression()).classes_ - # check that we clone the classifier when `prefit=False`. - estimator = ClassifierLoggingFit() - model = FixedThresholdClassifier(estimator=estimator, prefit=False) - model.fit(X, y) - assert estimator.fit_calls == 0 - assert model.estimator_.fit_calls == 1 - assert model.estimator_ is not estimator + classifier = LogisticRegression().fit(X, y) + fixed_threshold_classifier = FixedThresholdClassifier(estimator=classifier) + assert_array_equal(fixed_threshold_classifier.classes_, classifier.classes_) diff --git a/sklearn/model_selection/tests/test_search.py b/sklearn/model_selection/tests/test_search.py index c0442906d99de..5313e5d28a1a7 100644 --- a/sklearn/model_selection/tests/test_search.py +++ b/sklearn/model_selection/tests/test_search.py @@ -100,7 +100,7 @@ # Neither of the following two estimators inherit from BaseEstimator, # to test hyperparameter search on user-defined classifiers. -class MockClassifier: +class MockClassifier(ClassifierMixin, BaseEstimator): """Dummy classifier to test the parameter search algorithms""" def __init__(self, foo_param=0): @@ -213,7 +213,7 @@ def test_parameter_grid(): def test_grid_search(): # Test that the best estimator contains the right value for foo_param clf = MockClassifier() - grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, cv=3, verbose=3) + grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, cv=2, verbose=3) # make sure it selects the smallest parameter in case of ties old_stdout = sys.stdout sys.stdout = StringIO() @@ -383,11 +383,11 @@ def test_classes__property(): def test_trivial_cv_results_attr(): # Test search over a "grid" with only one point. clf = MockClassifier() - grid_search = GridSearchCV(clf, {"foo_param": [1]}, cv=3) + grid_search = GridSearchCV(clf, {"foo_param": [1]}, cv=2) grid_search.fit(X, y) assert hasattr(grid_search, "cv_results_") - random_search = RandomizedSearchCV(clf, {"foo_param": [0]}, n_iter=1, cv=3) + random_search = RandomizedSearchCV(clf, {"foo_param": [0]}, n_iter=1, cv=2) random_search.fit(X, y) assert hasattr(grid_search, "cv_results_") @@ -396,7 +396,7 @@ def test_no_refit(): # Test that GSCV can be used for model selection alone without refitting clf = MockClassifier() for scoring in [None, ["accuracy", "precision"]]: - grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, refit=False, cv=3) + grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, refit=False, cv=2) grid_search.fit(X, y) assert ( not hasattr(grid_search, "best_estimator_") @@ -463,7 +463,7 @@ def test_grid_search_when_param_grid_includes_range(): # Test that the best estimator contains the right value for foo_param clf = MockClassifier() grid_search = None - grid_search = GridSearchCV(clf, {"foo_param": range(1, 4)}, cv=3) + grid_search = GridSearchCV(clf, {"foo_param": range(1, 4)}, cv=2) grid_search.fit(X, y) assert grid_search.best_estimator_.foo_param == 2 @@ -1496,13 +1496,13 @@ def test_grid_search_correct_score_results(): def test_pickle(): # Test that a fit search can be pickled clf = MockClassifier() - grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, refit=True, cv=3) + grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, refit=True, cv=2) grid_search.fit(X, y) grid_search_pickled = pickle.loads(pickle.dumps(grid_search)) assert_array_almost_equal(grid_search.predict(X), grid_search_pickled.predict(X)) random_search = RandomizedSearchCV( - clf, {"foo_param": [1, 2, 3]}, refit=True, n_iter=3, cv=3 + clf, {"foo_param": [1, 2, 3]}, refit=True, n_iter=3, cv=2 ) random_search.fit(X, y) random_search_pickled = pickle.loads(pickle.dumps(random_search)) @@ -1901,7 +1901,7 @@ def _pop_time_keys(cv_results): def test_transform_inverse_transform_round_trip(): clf = MockClassifier() - grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, cv=3, verbose=3) + grid_search = GridSearchCV(clf, {"foo_param": [1, 2, 3]}, cv=2, verbose=3) grid_search.fit(X, y) X_round_trip = grid_search.inverse_transform(grid_search.transform(X)) @@ -2571,7 +2571,7 @@ def test_search_html_repr(): @pytest.mark.parametrize("SearchCV", [GridSearchCV, RandomizedSearchCV]) def test_inverse_transform_Xt_deprecation(SearchCV): clf = MockClassifier() - search = SearchCV(clf, {"foo_param": [1, 2, 3]}, cv=3, verbose=3) + search = SearchCV(clf, {"foo_param": [1, 2, 3]}, cv=2, verbose=3) X2 = search.fit(X, y).transform(X) diff --git a/sklearn/model_selection/tests/test_split.py b/sklearn/model_selection/tests/test_split.py index 79f195263be93..f26c9bd2b34ff 100644 --- a/sklearn/model_selection/tests/test_split.py +++ b/sklearn/model_selection/tests/test_split.py @@ -594,6 +594,30 @@ def test_shuffle_stratifiedkfold(): assert test_set1 != test_set2 +def test_shuffle_groupkfold(): + # Check that shuffling is happening when requested, and for proper + # sample coverage + X = np.ones(40) + y = [0] * 20 + [1] * 20 + groups = np.arange(40) // 3 + gkf0 = GroupKFold(4, shuffle=True, random_state=0) + gkf1 = GroupKFold(4, shuffle=True, random_state=1) + + # Check that the groups are shuffled differently + test_groups0 = [ + set(groups[test_idx]) for _, test_idx in gkf0.split(X, None, groups) + ] + test_groups1 = [ + set(groups[test_idx]) for _, test_idx in gkf1.split(X, None, groups) + ] + for g0, g1 in zip(test_groups0, test_groups1): + assert g0 != g1, "Test groups should differ with different random states" + + # Check coverage and splits + check_cv_coverage(gkf0, X, y, groups, expected_n_splits=4) + check_cv_coverage(gkf1, X, y, groups, expected_n_splits=4) + + def test_kfold_can_detect_dependent_samples_on_digits(): # see #2372 # The digits samples are dependent: they are apparently grouped by authors # although we don't have any information on the groups segment locations @@ -1601,8 +1625,9 @@ def test_cv_iterable_wrapper(): @pytest.mark.parametrize("kfold", [GroupKFold, StratifiedGroupKFold]) -def test_group_kfold(kfold): - rng = np.random.RandomState(0) +@pytest.mark.parametrize("shuffle", [True, False]) +def test_group_kfold(kfold, shuffle, global_random_seed): + rng = np.random.RandomState(global_random_seed) # Parameters of the test n_groups = 15 @@ -1620,7 +1645,8 @@ def test_group_kfold(kfold): len(np.unique(groups)) # Get the test fold indices from the test set indices of each fold folds = np.zeros(n_samples) - lkf = kfold(n_splits=n_splits) + random_state = None if not shuffle else global_random_seed + lkf = kfold(n_splits=n_splits, shuffle=shuffle, random_state=random_state) for i, (_, test) in enumerate(lkf.split(X, y, groups)): folds[test] = i @@ -1697,8 +1723,9 @@ def test_group_kfold(kfold): # Check that folds have approximately the same size assert len(folds) == len(groups) - for i in np.unique(folds): - assert tolerance >= abs(sum(folds == i) - ideal_n_groups_per_fold) + if not shuffle: + for i in np.unique(folds): + assert tolerance >= abs(sum(folds == i) - ideal_n_groups_per_fold) # Check that each group appears only in 1 fold with warnings.catch_warnings(): @@ -1712,8 +1739,10 @@ def test_group_kfold(kfold): assert len(np.intersect1d(groups[train], groups[test])) == 0 # groups can also be a list + # use a new instance for reproducibility when shuffle=True + lkf_copy = kfold(n_splits=n_splits, shuffle=shuffle, random_state=random_state) cv_iter = list(lkf.split(X, y, groups.tolist())) - for (train1, test1), (train2, test2) in zip(lkf.split(X, y, groups), cv_iter): + for (train1, test1), (train2, test2) in zip(lkf_copy.split(X, y, groups), cv_iter): assert_array_equal(train1, train2) assert_array_equal(test1, test2) @@ -1975,7 +2004,9 @@ def test_leave_p_out_empty_trainset(): next(cv.split(X, y)) -@pytest.mark.parametrize("Klass", (KFold, StratifiedKFold, StratifiedGroupKFold)) +@pytest.mark.parametrize( + "Klass", (KFold, StratifiedKFold, StratifiedGroupKFold, GroupKFold) +) def test_random_state_shuffle_false(Klass): # passing a non-default random_state when shuffle=False makes no sense with pytest.raises(ValueError, match="has no effect since shuffle is False"): @@ -1997,6 +2028,7 @@ def test_random_state_shuffle_false(Klass): (GroupShuffleSplit(random_state=123), True), (StratifiedShuffleSplit(random_state=123), True), (GroupKFold(), True), + (GroupKFold(shuffle=True, random_state=123), True), (TimeSeriesSplit(), True), (LeaveOneOut(), True), (LeaveOneGroupOut(), True), diff --git a/sklearn/model_selection/tests/test_validation.py b/sklearn/model_selection/tests/test_validation.py index 81f716ed88516..2d579772b1fbe 100644 --- a/sklearn/model_selection/tests/test_validation.py +++ b/sklearn/model_selection/tests/test_validation.py @@ -14,7 +14,7 @@ from scipy.sparse import issparse from sklearn import config_context -from sklearn.base import BaseEstimator, clone +from sklearn.base import BaseEstimator, ClassifierMixin, clone from sklearn.cluster import KMeans from sklearn.datasets import ( load_diabetes, @@ -186,7 +186,7 @@ def predict(self, X): raise NotImplementedError -class MockClassifier: +class MockClassifier(ClassifierMixin, BaseEstimator): """Dummy classifier to test the cross-validation""" def __init__(self, a=0, allow_nd=False): @@ -254,7 +254,7 @@ def fit( P.shape[0], P.shape[1], ) - self.fitted_ = True + self.classes_ = np.unique(y) return self def predict(self, T): @@ -274,11 +274,11 @@ def get_params(self, deep=False): # XXX: use 2D array, since 1D X is being detected as a single sample in # check_consistent_length -X = np.ones((10, 2)) -y = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4]) +X = np.ones((15, 2)) +y = np.array([0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6]) # The number of samples per class needs to be > n_splits, # for StratifiedKFold(n_splits=3) -y2 = np.array([1, 1, 1, 2, 2, 2, 3, 3, 3, 3]) +y2 = np.array([1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]) P = np.eye(5) @@ -694,7 +694,7 @@ def test_cross_val_score_fit_params(coo_container): n_classes = len(np.unique(y)) W_sparse = coo_container( - (np.array([1]), (np.array([1]), np.array([0]))), shape=(10, 1) + (np.array([1]), (np.array([1]), np.array([0]))), shape=(15, 1) ) P_sparse = coo_container(np.eye(5)) @@ -720,7 +720,7 @@ def assert_fit_params(clf): "dummy_obj": DUMMY_OBJ, "callback": assert_fit_params, } - cross_val_score(clf, X, y, params=fit_params) + cross_val_score(clf, X, y2, params=fit_params) def test_cross_val_score_score_func(): diff --git a/sklearn/neighbors/_base.py b/sklearn/neighbors/_base.py index 1925e0dbc758c..cdcd8929da6ca 100644 --- a/sklearn/neighbors/_base.py +++ b/sklearn/neighbors/_base.py @@ -25,6 +25,7 @@ from ..utils import ( check_array, gen_even_slices, + get_tags, ) from ..utils._param_validation import Interval, StrOptions, validate_params from ..utils.fixes import parse_version, sp_base_version @@ -471,10 +472,17 @@ def _check_algorithm_metric(self): ) def _fit(self, X, y=None): + ensure_all_finite = "allow-nan" if get_tags(self).input_tags.allow_nan else True if self.__sklearn_tags__().target_tags.required: if not isinstance(X, (KDTree, BallTree, NeighborsBase)): X, y = validate_data( - self, X, y, accept_sparse="csr", multi_output=True, order="C" + self, + X, + y, + accept_sparse="csr", + multi_output=True, + order="C", + ensure_all_finite=ensure_all_finite, ) if is_classifier(self): @@ -515,7 +523,13 @@ def _fit(self, X, y=None): else: if not isinstance(X, (KDTree, BallTree, NeighborsBase)): - X = validate_data(self, X, accept_sparse="csr", order="C") + X = validate_data( + self, + X, + ensure_all_finite=ensure_all_finite, + accept_sparse="csr", + order="C", + ) self._check_algorithm_metric() if self.metric_params is None: @@ -695,6 +709,7 @@ def __sklearn_tags__(self): tags = super().__sklearn_tags__() # For cross-validation routines to split data correctly tags.input_tags.pairwise = self.metric == "precomputed" + tags.input_tags.allow_nan = self.metric == "nan_euclidean" return tags @@ -806,6 +821,7 @@ class from an array representing our data set and ask who's % type(n_neighbors) ) + ensure_all_finite = "allow-nan" if get_tags(self).input_tags.allow_nan else True query_is_train = X is None if query_is_train: X = self._fit_X @@ -816,7 +832,14 @@ class from an array representing our data set and ask who's if self.metric == "precomputed": X = _check_precomputed(X) else: - X = validate_data(self, X, accept_sparse="csr", reset=False, order="C") + X = validate_data( + self, + X, + ensure_all_finite=ensure_all_finite, + accept_sparse="csr", + reset=False, + order="C", + ) n_samples_fit = self.n_samples_fit_ if n_neighbors > n_samples_fit: @@ -1145,6 +1168,7 @@ class from an array representing our data set and ask who's if sort_results and not return_distance: raise ValueError("return_distance must be True if sort_results is True.") + ensure_all_finite = "allow-nan" if get_tags(self).input_tags.allow_nan else True query_is_train = X is None if query_is_train: X = self._fit_X @@ -1152,7 +1176,14 @@ class from an array representing our data set and ask who's if self.metric == "precomputed": X = _check_precomputed(X) else: - X = validate_data(self, X, accept_sparse="csr", reset=False, order="C") + X = validate_data( + self, + X, + ensure_all_finite=ensure_all_finite, + accept_sparse="csr", + reset=False, + order="C", + ) if radius is None: radius = self.radius @@ -1363,3 +1394,8 @@ def radius_neighbors_graph( A_indptr = np.concatenate((np.zeros(1, dtype=int), np.cumsum(n_neighbors))) return csr_matrix((A_data, A_ind, A_indptr), shape=(n_queries, n_samples_fit)) + + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.input_tags.allow_nan = self.metric == "nan_euclidean" + return tags diff --git a/sklearn/neighbors/_nearest_centroid.py b/sklearn/neighbors/_nearest_centroid.py index f92ae68973741..b30dc309b2dd7 100644 --- a/sklearn/neighbors/_nearest_centroid.py +++ b/sklearn/neighbors/_nearest_centroid.py @@ -5,21 +5,30 @@ # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause +import warnings from numbers import Real import numpy as np from scipy import sparse as sp from ..base import BaseEstimator, ClassifierMixin, _fit_context -from ..metrics.pairwise import pairwise_distances_argmin +from ..discriminant_analysis import DiscriminantAnalysisPredictionMixin +from ..metrics.pairwise import ( + pairwise_distances, + pairwise_distances_argmin, +) from ..preprocessing import LabelEncoder +from ..utils import get_tags +from ..utils._available_if import available_if from ..utils._param_validation import Interval, StrOptions from ..utils.multiclass import check_classification_targets from ..utils.sparsefuncs import csc_median_axis_0 from ..utils.validation import check_is_fitted, validate_data -class NearestCentroid(ClassifierMixin, BaseEstimator): +class NearestCentroid( + DiscriminantAnalysisPredictionMixin, ClassifierMixin, BaseEstimator +): """Nearest centroid classifier. Each class is represented by its centroid, with test samples classified to @@ -47,6 +56,13 @@ class is the arithmetic mean, which minimizes the sum of squared L1 distances. shrink_threshold : float, default=None Threshold for shrinking centroids to remove features. + priors : {"uniform", "empirical"} or array-like of shape (n_classes,), \ + default="uniform" + The class prior probabilities. By default, the class proportions are + inferred from the training data. + + .. versionadded:: 1.6 + Attributes ---------- centroids_ : array-like of shape (n_classes, n_features) @@ -66,6 +82,24 @@ class is the arithmetic mean, which minimizes the sum of squared L1 distances. .. versionadded:: 1.0 + deviations_ : ndarray of shape (n_classes, n_features) + Deviations (or shrinkages) of the centroids of each class from the + overall centroid. Equal to eq. (18.4) if `shrink_threshold=None`, + else (18.5) p. 653 of [2]. Can be used to identify features used + for classification. + + .. versionadded:: 1.6 + + within_class_std_dev_ : ndarray of shape (n_features,) + Pooled or within-class standard deviation of input data. + + .. versionadded:: 1.6 + + class_prior_ : ndarray of shape (n_classes,) + The class prior probabilities. + + .. versionadded:: 1.6 + See Also -------- KNeighborsClassifier : Nearest neighbors classifier. @@ -77,11 +111,14 @@ class is the arithmetic mean, which minimizes the sum of squared L1 distances. References ---------- - Tibshirani, R., Hastie, T., Narasimhan, B., & Chu, G. (2002). Diagnosis of + [1] Tibshirani, R., Hastie, T., Narasimhan, B., & Chu, G. (2002). Diagnosis of multiple cancer types by shrunken centroids of gene expression. Proceedings of the National Academy of Sciences of the United States of America, 99(10), 6567-6572. The National Academy of Sciences. + [2] Hastie, T., Tibshirani, R., Friedman, J. (2009). The Elements of Statistical + Learning Data Mining, Inference, and Prediction. 2nd Edition. New York, Springer. + Examples -------- >>> from sklearn.neighbors import NearestCentroid @@ -93,19 +130,24 @@ class is the arithmetic mean, which minimizes the sum of squared L1 distances. NearestCentroid() >>> print(clf.predict([[-0.8, -1]])) [1] - - For a more detailed example see: - :ref:`sphx_glr_auto_examples_neighbors_plot_nearest_centroid.py` """ _parameter_constraints: dict = { "metric": [StrOptions({"manhattan", "euclidean"})], "shrink_threshold": [Interval(Real, 0, None, closed="neither"), None], + "priors": ["array-like", StrOptions({"empirical", "uniform"})], } - def __init__(self, metric="euclidean", *, shrink_threshold=None): + def __init__( + self, + metric="euclidean", + *, + shrink_threshold=None, + priors="uniform", + ): self.metric = metric self.shrink_threshold = shrink_threshold + self.priors = priors @_fit_context(prefer_skip_nested_validation=True) def fit(self, X, y): @@ -131,10 +173,17 @@ def fit(self, X, y): if self.metric == "manhattan": X, y = validate_data(self, X, y, accept_sparse=["csc"]) else: - X, y = validate_data(self, X, y, accept_sparse=["csr", "csc"]) + ensure_all_finite = ( + "allow-nan" if get_tags(self).input_tags.allow_nan else True + ) + X, y = validate_data( + self, + X, + y, + ensure_all_finite=ensure_all_finite, + accept_sparse=["csr", "csc"], + ) is_X_sparse = sp.issparse(X) - if is_X_sparse and self.shrink_threshold: - raise ValueError("threshold shrinking not supported for sparse input") check_classification_targets(y) n_samples, n_features = X.shape @@ -148,8 +197,26 @@ def fit(self, X, y): % (n_classes) ) + if self.priors == "empirical": # estimate priors from sample + _, class_counts = np.unique(y, return_inverse=True) # non-negative ints + self.class_prior_ = np.bincount(class_counts) / float(len(y)) + elif self.priors == "uniform": + self.class_prior_ = np.asarray([1 / n_classes] * n_classes) + else: + self.class_prior_ = np.asarray(self.priors) + + if (self.class_prior_ < 0).any(): + raise ValueError("priors must be non-negative") + if not np.isclose(self.class_prior_.sum(), 1.0): + warnings.warn( + "The priors do not sum to 1. Normalizing such that it sums to one.", + UserWarning, + ) + self.class_prior_ = self.class_prior_ / self.class_prior_.sum() + # Mask mapping each class to its members. self.centroids_ = np.empty((n_classes, n_features), dtype=np.float64) + # Number of clusters in each class. nk = np.zeros(n_classes) @@ -168,30 +235,44 @@ def fit(self, X, y): else: # metric == "euclidean" self.centroids_[cur_class] = X[center_mask].mean(axis=0) + # Compute within-class std_dev with unshrunked centroids + variance = np.array(X - self.centroids_[y_ind], copy=False) ** 2 + self.within_class_std_dev_ = np.array( + np.sqrt(variance.sum(axis=0) / (n_samples - n_classes)), copy=False + ) + if any(self.within_class_std_dev_ == 0): + warnings.warn( + "self.within_class_std_dev_ has at least 1 zero standard deviation." + "Inputs within the same classes for at least 1 feature are identical." + ) + + err_msg = "All features have zero variance. Division by zero." + if is_X_sparse and np.all((X.max(axis=0) - X.min(axis=0)).toarray() == 0): + raise ValueError(err_msg) + elif not is_X_sparse and np.all(np.ptp(X, axis=0) == 0): + raise ValueError(err_msg) + + dataset_centroid_ = X.mean(axis=0) + # m parameter for determining deviation + m = np.sqrt((1.0 / nk) - (1.0 / n_samples)) + # Calculate deviation using the standard deviation of centroids. + # To deter outliers from affecting the results. + s = self.within_class_std_dev_ + np.median(self.within_class_std_dev_) + mm = m.reshape(len(m), 1) # Reshape to allow broadcasting. + ms = mm * s + self.deviations_ = np.array( + (self.centroids_ - dataset_centroid_) / ms, copy=False + ) + # Soft thresholding: if the deviation crosses 0 during shrinking, + # it becomes zero. if self.shrink_threshold: - if np.all(np.ptp(X, axis=0) == 0): - raise ValueError("All features have zero variance. Division by zero.") - dataset_centroid_ = np.mean(X, axis=0) - - # m parameter for determining deviation - m = np.sqrt((1.0 / nk) - (1.0 / n_samples)) - # Calculate deviation using the standard deviation of centroids. - variance = (X - self.centroids_[y_ind]) ** 2 - variance = variance.sum(axis=0) - s = np.sqrt(variance / (n_samples - n_classes)) - s += np.median(s) # To deter outliers from affecting the results. - mm = m.reshape(len(m), 1) # Reshape to allow broadcasting. - ms = mm * s - deviation = (self.centroids_ - dataset_centroid_) / ms - # Soft thresholding: if the deviation crosses 0 during shrinking, - # it becomes zero. - signs = np.sign(deviation) - deviation = np.abs(deviation) - self.shrink_threshold - np.clip(deviation, 0, None, out=deviation) - deviation *= signs + signs = np.sign(self.deviations_) + self.deviations_ = np.abs(self.deviations_) - self.shrink_threshold + np.clip(self.deviations_, 0, None, out=self.deviations_) + self.deviations_ *= signs # Now adjust the centroids using the deviation - msd = ms * deviation - self.centroids_ = dataset_centroid_[np.newaxis, :] + msd + msd = ms * self.deviations_ + self.centroids_ = np.array(dataset_centroid_ + msd, copy=False) return self def predict(self, X): @@ -202,16 +283,76 @@ def predict(self, X): Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) - Test samples. + Input data. Returns ------- - C : ndarray of shape (n_samples,) + y_pred : ndarray of shape (n_samples,) The predicted classes. """ check_is_fitted(self) + if np.isclose(self.class_prior_, 1 / len(self.classes_)).all(): + # `validate_data` is called here since we are not calling `super()` + ensure_all_finite = ( + "allow-nan" if get_tags(self).input_tags.allow_nan else True + ) + X = validate_data( + self, + X, + ensure_all_finite=ensure_all_finite, + accept_sparse="csr", + reset=False, + ) + return self.classes_[ + pairwise_distances_argmin(X, self.centroids_, metric=self.metric) + ] + else: + return super().predict(X) + + def _decision_function(self, X): + # return discriminant scores, see eq. (18.2) p. 652 of the ESL. + check_is_fitted(self, "centroids_") + + X_normalized = validate_data( + self, X, copy=True, reset=False, accept_sparse="csr", dtype=np.float64 + ) + + discriminant_score = np.empty( + (X_normalized.shape[0], self.classes_.size), dtype=np.float64 + ) + + mask = self.within_class_std_dev_ != 0 + X_normalized[:, mask] /= self.within_class_std_dev_[mask] + centroids_normalized = self.centroids_.copy() + centroids_normalized[:, mask] /= self.within_class_std_dev_[mask] + + for class_idx in range(self.classes_.size): + distances = pairwise_distances( + X_normalized, centroids_normalized[[class_idx]], metric=self.metric + ).ravel() + distances **= 2 + discriminant_score[:, class_idx] = np.squeeze( + -distances + 2.0 * np.log(self.class_prior_[class_idx]) + ) + + return discriminant_score + + def _check_euclidean_metric(self): + return self.metric == "euclidean" + + decision_function = available_if(_check_euclidean_metric)( + DiscriminantAnalysisPredictionMixin.decision_function + ) + + predict_proba = available_if(_check_euclidean_metric)( + DiscriminantAnalysisPredictionMixin.predict_proba + ) + + predict_log_proba = available_if(_check_euclidean_metric)( + DiscriminantAnalysisPredictionMixin.predict_log_proba + ) - X = validate_data(self, X, accept_sparse="csr", reset=False) - return self.classes_[ - pairwise_distances_argmin(X, self.centroids_, metric=self.metric) - ] + def __sklearn_tags__(self): + tags = super().__sklearn_tags__() + tags.input_tags.allow_nan = self.metric == "nan_euclidean" + return tags diff --git a/sklearn/neighbors/tests/test_nearest_centroid.py b/sklearn/neighbors/tests/test_nearest_centroid.py index 5ce792ac29d56..1aa9274cd28a8 100644 --- a/sklearn/neighbors/tests/test_nearest_centroid.py +++ b/sklearn/neighbors/tests/test_nearest_centroid.py @@ -4,10 +4,14 @@ import numpy as np import pytest -from numpy.testing import assert_array_equal from sklearn import datasets from sklearn.neighbors import NearestCentroid +from sklearn.utils._testing import ( + assert_allclose, + assert_array_almost_equal, + assert_array_equal, +) from sklearn.utils.fixes import CSR_CONTAINERS # toy sample @@ -15,6 +19,11 @@ y = [-1, -1, -1, 1, 1, 1] T = [[-1, -1], [2, 2], [3, 2]] true_result = [-1, 1, 1] +true_result_prior1 = [-1, 1, 1] + +true_discriminant_scores = [-32, 64, 80] +true_proba = [[1, 1.26642e-14], [1.60381e-28, 1], [1.80485e-35, 1]] + # also load the iris dataset # and randomly permute it @@ -31,9 +40,30 @@ def test_classification_toy(csr_container): X_csr = csr_container(X) T_csr = csr_container(T) + # Check classification on a toy dataset, including sparse versions. clf = NearestCentroid() clf.fit(X, y) assert_array_equal(clf.predict(T), true_result) + assert_array_almost_equal(clf.decision_function(T), true_discriminant_scores) + assert_array_almost_equal(clf.predict_proba(T), true_proba) + + # Test uniform priors + clf = NearestCentroid(priors="uniform") + clf.fit(X, y) + assert_array_equal(clf.predict(T), true_result) + assert_array_almost_equal(clf.decision_function(T), true_discriminant_scores) + assert_array_almost_equal(clf.predict_proba(T), true_proba) + + clf = NearestCentroid(priors="empirical") + clf.fit(X, y) + assert_array_equal(clf.predict(T), true_result) + assert_array_almost_equal(clf.decision_function(T), true_discriminant_scores) + assert_array_almost_equal(clf.predict_proba(T), true_proba) + + # Test custom priors + clf = NearestCentroid(priors=[0.25, 0.75]) + clf.fit(X, y) + assert_array_equal(clf.predict(T), true_result_prior1) # Same test, but with a sparse matrix to fit and test. clf = NearestCentroid() @@ -159,3 +189,49 @@ def test_features_zero_var(): clf = NearestCentroid(shrink_threshold=0.1) with pytest.raises(ValueError): clf.fit(X, y) + + +def test_negative_priors_error(): + """Check that we raise an error when the user-defined priors are negative.""" + clf = NearestCentroid(priors=[-2, 4]) + with pytest.raises(ValueError, match="priors must be non-negative"): + clf.fit(X, y) + + +def test_warn_non_normalized_priors(): + """Check that we raise a warning and normalize the user-defined priors when they + don't sum to 1. + """ + priors = [2, 4] + clf = NearestCentroid(priors=priors) + with pytest.warns( + UserWarning, + match="The priors do not sum to 1. Normalizing such that it sums to one.", + ): + clf.fit(X, y) + + assert_allclose(clf.class_prior_, np.asarray(priors) / np.asarray(priors).sum()) + + +@pytest.mark.parametrize( + "response_method", ["decision_function", "predict_proba", "predict_log_proba"] +) +def test_method_not_available_with_manhattan(response_method): + """Check that we raise an AttributeError with Manhattan metric when trying + to call a non-thresholded response method. + """ + clf = NearestCentroid(metric="manhattan").fit(X, y) + with pytest.raises(AttributeError): + getattr(clf, response_method)(T) + + +@pytest.mark.parametrize("array_constructor", [np.array] + CSR_CONTAINERS) +def test_error_zero_variances(array_constructor): + """Check that we raise an error when the variance for all features is zero.""" + X = np.ones((len(y), 2)) + X[:, 1] *= 2 + X = array_constructor(X) + + clf = NearestCentroid() + with pytest.raises(ValueError, match="All features have zero variance"): + clf.fit(X, y) diff --git a/sklearn/neighbors/tests/test_neighbors.py b/sklearn/neighbors/tests/test_neighbors.py index 9622e85c39718..c9fb85fec9908 100644 --- a/sklearn/neighbors/tests/test_neighbors.py +++ b/sklearn/neighbors/tests/test_neighbors.py @@ -2337,6 +2337,38 @@ def _weights(dist): assert_allclose(est.predict([[0, 2.5]]), [6]) +@pytest.mark.parametrize( + "Estimator, params", + [ + (neighbors.KNeighborsClassifier, {"n_neighbors": 2}), + (neighbors.KNeighborsRegressor, {"n_neighbors": 2}), + (neighbors.RadiusNeighborsRegressor, {}), + (neighbors.RadiusNeighborsClassifier, {}), + (neighbors.KNeighborsTransformer, {"n_neighbors": 2}), + (neighbors.RadiusNeighborsTransformer, {"radius": 1.5}), + (neighbors.LocalOutlierFactor, {"n_neighbors": 1}), + ], +) +def test_nan_euclidean_support(Estimator, params): + """Check that the different neighbor estimators are lenient towards `nan` + values if using `metric="nan_euclidean"`. + """ + + X = [[0, 1], [1, np.nan], [2, 3], [3, 5]] + y = [0, 0, 1, 1] + + params.update({"metric": "nan_euclidean"}) + estimator = Estimator().set_params(**params).fit(X, y) + + for response_method in ("kneighbors", "predict", "transform", "fit_predict"): + if hasattr(estimator, response_method): + output = getattr(estimator, response_method)(X) + if hasattr(output, "toarray"): + assert not np.isnan(output.data).any() + else: + assert not np.isnan(output).any() + + def test_predict_dataframe(): """Check that KNN predict works with dataframes diff --git a/sklearn/neural_network/_multilayer_perceptron.py b/sklearn/neural_network/_multilayer_perceptron.py index 2094c995aaccb..196203ce46763 100644 --- a/sklearn/neural_network/_multilayer_perceptron.py +++ b/sklearn/neural_network/_multilayer_perceptron.py @@ -1538,14 +1538,16 @@ class MLPRegressor(RegressorMixin, BaseMultilayerPerceptron): >>> from sklearn.neural_network import MLPRegressor >>> from sklearn.datasets import make_regression >>> from sklearn.model_selection import train_test_split - >>> X, y = make_regression(n_samples=200, random_state=1) + >>> X, y = make_regression(n_samples=200, n_features=20, random_state=1) >>> X_train, X_test, y_train, y_test = train_test_split(X, y, ... random_state=1) - >>> regr = MLPRegressor(random_state=1, max_iter=500).fit(X_train, y_train) + >>> regr = MLPRegressor(random_state=1, max_iter=2000, tol=0.1) + >>> regr.fit(X_train, y_train) + MLPRegressor(max_iter=2000, random_state=1, tol=0.1) >>> regr.predict(X_test[:2]) - array([-0.9..., -7.1...]) + array([ 28..., -290...]) >>> regr.score(X_test, y_test) - 0.4... + 0.98... """ def __init__( diff --git a/sklearn/neural_network/tests/test_mlp.py b/sklearn/neural_network/tests/test_mlp.py index edba508204b22..969b452d687fd 100644 --- a/sklearn/neural_network/tests/test_mlp.py +++ b/sklearn/neural_network/tests/test_mlp.py @@ -203,7 +203,9 @@ def test_gradient(): max_iter=1, random_state=1, ) - mlp.fit(X, y) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) + mlp.fit(X, y) theta = np.hstack([l.ravel() for l in mlp.coefs_ + mlp.intercepts_]) @@ -276,7 +278,8 @@ def test_lbfgs_regression(X, y): mlp = MLPRegressor( solver="lbfgs", hidden_layer_sizes=50, - max_iter=150, + max_iter=200, + tol=1e-3, shuffle=True, random_state=1, activation=activation, @@ -397,9 +400,9 @@ def test_multilabel_classification(): def test_multioutput_regression(): # Test that multi-output regression works as expected - X, y = make_regression(n_samples=200, n_targets=5) + X, y = make_regression(n_samples=200, n_targets=5, random_state=11) mlp = MLPRegressor( - solver="lbfgs", hidden_layer_sizes=50, max_iter=200, random_state=1 + solver="lbfgs", hidden_layer_sizes=50, max_iter=200, tol=1e-2, random_state=1 ) mlp.fit(X, y) assert mlp.score(X, y) > 0.9 @@ -468,8 +471,8 @@ def test_partial_fit_regression(): batch_size=X.shape[0], momentum=momentum, ) - with warnings.catch_warnings(record=True): - # catch convergence warning + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) mlp.fit(X, y) pred1 = mlp.predict(X) mlp = MLPRegressor( @@ -517,7 +520,10 @@ def test_nonfinite_params(): " values and need to be preprocessed." ) with pytest.raises(ValueError, match=msg): - clf.fit(X, y) + with warnings.catch_warnings(): + # RuntimeWarning: overflow encountered in square + warnings.simplefilter("ignore") + clf.fit(X, y) def test_predict_proba_binary(): @@ -608,8 +614,10 @@ def test_shuffle(): random_state=0, shuffle=shuffle, ) - mlp1.fit(X, y) - mlp2.fit(X, y) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) + mlp1.fit(X, y) + mlp2.fit(X, y) assert np.array_equal(mlp1.coefs_[0], mlp2.coefs_[0]) @@ -620,8 +628,10 @@ def test_shuffle(): mlp2 = MLPRegressor( hidden_layer_sizes=1, max_iter=1, batch_size=1, random_state=0, shuffle=False ) - mlp1.fit(X, y) - mlp2.fit(X, y) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) + mlp1.fit(X, y) + mlp2.fit(X, y) assert not np.array_equal(mlp1.coefs_[0], mlp2.coefs_[0]) @@ -720,14 +730,20 @@ def test_warm_start(): y_5classes = np.array([0] * 30 + [1] * 30 + [2] * 30 + [3] * 30 + [4] * 30) # No error raised - clf = MLPClassifier(hidden_layer_sizes=2, solver="lbfgs", warm_start=True).fit(X, y) + clf = MLPClassifier( + hidden_layer_sizes=2, solver="lbfgs", warm_start=True, random_state=42, tol=1e-2 + ).fit(X, y) clf.fit(X, y) clf.fit(X, y_3classes) for y_i in (y_2classes, y_3classes_alt, y_4classes, y_5classes): - clf = MLPClassifier(hidden_layer_sizes=2, solver="lbfgs", warm_start=True).fit( - X, y - ) + clf = MLPClassifier( + hidden_layer_sizes=2, + solver="lbfgs", + warm_start=True, + random_state=42, + tol=1e-2, + ).fit(X, y) message = ( "warm_start can only be used where `y` has the same " "classes as in the previous call to fit." @@ -748,10 +764,12 @@ def test_warm_start_full_iteration(MLPEstimator): clf = MLPEstimator( hidden_layer_sizes=2, solver="sgd", warm_start=True, max_iter=max_iter ) - clf.fit(X, y) - assert max_iter == clf.n_iter_ - clf.fit(X, y) - assert max_iter == clf.n_iter_ + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) + clf.fit(X, y) + assert max_iter == clf.n_iter_ + clf.fit(X, y) + assert max_iter == clf.n_iter_ def test_n_iter_no_change(): @@ -815,14 +833,14 @@ def test_early_stopping_stratified(): def test_mlp_classifier_dtypes_casting(): # Compare predictions for different dtypes mlp_64 = MLPClassifier( - alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=50 + alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=100, tol=1e-1 ) mlp_64.fit(X_digits[:300], y_digits[:300]) pred_64 = mlp_64.predict(X_digits[300:]) proba_64 = mlp_64.predict_proba(X_digits[300:]) mlp_32 = MLPClassifier( - alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=50 + alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=100, tol=1e-1 ) mlp_32.fit(X_digits[:300].astype(np.float32), y_digits[:300]) pred_32 = mlp_32.predict(X_digits[300:].astype(np.float32)) @@ -834,18 +852,18 @@ def test_mlp_classifier_dtypes_casting(): def test_mlp_regressor_dtypes_casting(): mlp_64 = MLPRegressor( - alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=50 + alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=150, tol=1e-3 ) mlp_64.fit(X_digits[:300], y_digits[:300]) pred_64 = mlp_64.predict(X_digits[300:]) mlp_32 = MLPRegressor( - alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=50 + alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=150, tol=1e-3 ) mlp_32.fit(X_digits[:300].astype(np.float32), y_digits[:300]) pred_32 = mlp_32.predict(X_digits[300:].astype(np.float32)) - assert_allclose(pred_64, pred_32, rtol=1e-04) + assert_allclose(pred_64, pred_32, rtol=5e-04) @pytest.mark.parametrize("dtype", [np.float32, np.float64]) @@ -854,7 +872,9 @@ def test_mlp_param_dtypes(dtype, Estimator): # Checks if input dtype is used for network parameters # and predictions X, y = X_digits.astype(dtype), y_digits - mlp = Estimator(alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=50) + mlp = Estimator( + alpha=1e-5, hidden_layer_sizes=(5, 3), random_state=1, max_iter=50, tol=1e-1 + ) mlp.fit(X[:300], y[:300]) pred = mlp.predict(X[300:]) @@ -920,10 +940,12 @@ def test_mlp_warm_start_with_early_stopping(MLPEstimator): mlp = MLPEstimator( max_iter=10, random_state=0, warm_start=True, early_stopping=True ) - mlp.fit(X_iris, y_iris) - n_validation_scores = len(mlp.validation_scores_) - mlp.set_params(max_iter=20) - mlp.fit(X_iris, y_iris) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", ConvergenceWarning) + mlp.fit(X_iris, y_iris) + n_validation_scores = len(mlp.validation_scores_) + mlp.set_params(max_iter=20) + mlp.fit(X_iris, y_iris) assert len(mlp.validation_scores_) > n_validation_scores @@ -987,7 +1009,12 @@ def test_mlp_diverging_loss(): random_state=0, ) - mlp.fit(X_iris, y_iris) + with warnings.catch_warnings(): + # RuntimeWarning: overflow encountered in matmul + # ConvergenceWarning: Stochastic Optimizer: Maximum iteration + warnings.simplefilter("ignore", RuntimeWarning) + warnings.simplefilter("ignore", ConvergenceWarning) + mlp.fit(X_iris, y_iris) # In python, float("nan") != float("nan") assert str(mlp.validation_scores_[-1]) == str(np.nan) diff --git a/sklearn/pipeline.py b/sklearn/pipeline.py index 90a62d9e4e8ab..54fc572e12672 100644 --- a/sklearn/pipeline.py +++ b/sklearn/pipeline.py @@ -6,6 +6,7 @@ import warnings from collections import Counter, defaultdict from contextlib import contextmanager +from copy import deepcopy from itertools import chain, islice import numpy as np @@ -344,6 +345,7 @@ def __getitem__(self, ind): return self.named_steps[ind] return est + # TODO(1.8): Remove this property @property def _estimator_type(self): return self.steps[-1][1]._estimator_type @@ -1059,16 +1061,23 @@ def __sklearn_tags__(self): } try: - tags.input_tags.pairwise = get_tags(self.steps[0][1]).input_tags.pairwise + if self.steps[0][1] is not None and self.steps[0][1] != "passthrough": + tags.input_tags.pairwise = get_tags( + self.steps[0][1] + ).input_tags.pairwise except (ValueError, AttributeError, TypeError): # This happens when the `steps` is not a list of (name, estimator) # tuples and `fit` is not called yet to validate the steps. pass try: - tags.target_tags.multi_output = get_tags( - self.steps[-1][1] - ).target_tags.multi_output + if self.steps[-1][1] is not None and self.steps[-1][1] != "passthrough": + last_step_tags = get_tags(self.steps[-1][1]) + tags.estimator_type = last_step_tags.estimator_type + tags.target_tags.multi_output = last_step_tags.target_tags.multi_output + tags.classifier_tags = deepcopy(last_step_tags.classifier_tags) + tags.regressor_tags = deepcopy(last_step_tags.regressor_tags) + tags.transformer_tags = deepcopy(last_step_tags.transformer_tags) except (ValueError, AttributeError, TypeError): # This happens when the `steps` is not a list of (name, estimator) # tuples and `fit` is not called yet to validate the steps. diff --git a/sklearn/preprocessing/_data.py b/sklearn/preprocessing/_data.py index 007e5b0029f23..74ea7431a5d72 100644 --- a/sklearn/preprocessing/_data.py +++ b/sklearn/preprocessing/_data.py @@ -9,6 +9,8 @@ from scipy import optimize, sparse, stats from scipy.special import boxcox, inv_boxcox +from sklearn.utils import metadata_routing + from ..base import ( BaseEstimator, ClassNamePrefixFeaturesOutMixin, @@ -2422,6 +2424,10 @@ class KernelCenterer(ClassNamePrefixFeaturesOutMixin, TransformerMixin, BaseEsti [ -5., -14., 19.]]) """ + # X is called K in these methods. + __metadata_request__transform = {"K": metadata_routing.UNUSED} + __metadata_request__fit = {"K": metadata_routing.UNUSED} + def fit(self, K, y=None): """Fit KernelCenterer. diff --git a/sklearn/semi_supervised/_self_training.py b/sklearn/semi_supervised/_self_training.py index 3e1709adaa267..d56ebf887828c 100644 --- a/sklearn/semi_supervised/_self_training.py +++ b/sklearn/semi_supervised/_self_training.py @@ -4,6 +4,8 @@ import numpy as np +from sklearn.base import ClassifierMixin + from ..base import BaseEstimator, MetaEstimatorMixin, _fit_context, clone from ..utils import Bunch, safe_mask from ..utils._param_validation import HasMethods, Hidden, Interval, StrOptions @@ -15,7 +17,7 @@ process_routing, ) from ..utils.metaestimators import available_if -from ..utils.validation import check_is_fitted, validate_data +from ..utils.validation import _estimator_has, check_is_fitted, validate_data __all__ = ["SelfTrainingClassifier"] @@ -23,26 +25,7 @@ # SPDX-License-Identifier: BSD-3-Clause -def _estimator_has(attr): - """Check if we can delegate a method to the underlying estimator. - - First, we check the fitted `estimator_` if available, otherwise we check - the unfitted `estimator`. We raise the original `AttributeError` if - `attr` does not exist. This function is used together with `available_if`. - """ - - def check(self): - if hasattr(self, "estimator_"): - getattr(self.estimator_, attr) - else: - getattr(self.estimator, attr) - - return True - - return check - - -class SelfTrainingClassifier(MetaEstimatorMixin, BaseEstimator): +class SelfTrainingClassifier(ClassifierMixin, MetaEstimatorMixin, BaseEstimator): """Self-training classifier. This :term:`metaestimator` allows a given supervised classifier to function as a @@ -171,8 +154,6 @@ class SelfTrainingClassifier(MetaEstimatorMixin, BaseEstimator): SelfTrainingClassifier(...) """ - _estimator_type = "classifier" - _parameter_constraints: dict = { # We don't require `predic_proba` here to allow passing a meta-estimator # that only exposes `predict_proba` after fitting. diff --git a/sklearn/tests/test_base.py b/sklearn/tests/test_base.py index e23c347708b1f..b65baa78802bc 100644 --- a/sklearn/tests/test_base.py +++ b/sklearn/tests/test_base.py @@ -19,10 +19,12 @@ clone, is_classifier, is_clusterer, + is_outlier_detector, is_regressor, ) from sklearn.cluster import KMeans from sklearn.decomposition import PCA +from sklearn.ensemble import IsolationForest from sklearn.exceptions import InconsistentVersionWarning from sklearn.model_selection import GridSearchCV from sklearn.pipeline import Pipeline @@ -266,6 +268,21 @@ def test_get_params(): test.set_params(a__a=2) +# TODO(1.8): Remove this test when the deprecation is removed +def test_is_estimator_type_class(): + with pytest.warns(FutureWarning, match="passing a class to.*is deprecated"): + assert is_classifier(SVC) + + with pytest.warns(FutureWarning, match="passing a class to.*is deprecated"): + assert is_regressor(SVR) + + with pytest.warns(FutureWarning, match="passing a class to.*is deprecated"): + assert is_clusterer(KMeans) + + with pytest.warns(FutureWarning, match="passing a class to.*is deprecated"): + assert is_outlier_detector(IsolationForest) + + @pytest.mark.parametrize( "estimator, expected_result", [ diff --git a/sklearn/tests/test_calibration.py b/sklearn/tests/test_calibration.py index 0f23bb7463126..6e5900e4fa4a6 100644 --- a/sklearn/tests/test_calibration.py +++ b/sklearn/tests/test_calibration.py @@ -5,7 +5,7 @@ import pytest from numpy.testing import assert_allclose -from sklearn.base import BaseEstimator, clone +from sklearn.base import BaseEstimator, ClassifierMixin, clone from sklearn.calibration import ( CalibratedClassifierCV, CalibrationDisplay, @@ -22,6 +22,7 @@ ) from sklearn.exceptions import NotFittedError from sklearn.feature_extraction import DictVectorizer +from sklearn.frozen import FrozenEstimator from sklearn.impute import SimpleImputer from sklearn.isotonic import IsotonicRegression from sklearn.linear_model import LogisticRegression, SGDClassifier @@ -45,6 +46,7 @@ assert_almost_equal, assert_array_almost_equal, assert_array_equal, + ignore_warnings, ) from sklearn.utils.extmath import softmax from sklearn.utils.fixes import CSR_CONTAINERS @@ -299,9 +301,11 @@ def predict(self, X): assert_allclose(probas, 1.0 / clf.n_classes_) +@ignore_warnings(category=FutureWarning) @pytest.mark.parametrize("csr_container", CSR_CONTAINERS) def test_calibration_prefit(csr_container): """Test calibration for prefitted classifiers""" + # TODO(1.8): Remove cv="prefit" options here and the @ignore_warnings of the test n_samples = 50 X, y = make_classification(n_samples=3 * n_samples, n_features=6, random_state=42) sample_weight = np.random.RandomState(seed=42).uniform(size=y.size) @@ -333,17 +337,25 @@ def test_calibration_prefit(csr_container): (csr_container(X_calib), csr_container(X_test)), ]: for method in ["isotonic", "sigmoid"]: - cal_clf = CalibratedClassifierCV(clf, method=method, cv="prefit") + cal_clf_prefit = CalibratedClassifierCV(clf, method=method, cv="prefit") + cal_clf_frozen = CalibratedClassifierCV(FrozenEstimator(clf), method=method) for sw in [sw_calib, None]: - cal_clf.fit(this_X_calib, y_calib, sample_weight=sw) - y_prob = cal_clf.predict_proba(this_X_test) - y_pred = cal_clf.predict(this_X_test) - prob_pos_cal_clf = y_prob[:, 1] - assert_array_equal(y_pred, np.array([0, 1])[np.argmax(y_prob, axis=1)]) - + cal_clf_prefit.fit(this_X_calib, y_calib, sample_weight=sw) + cal_clf_frozen.fit(this_X_calib, y_calib, sample_weight=sw) + + y_prob_prefit = cal_clf_prefit.predict_proba(this_X_test) + y_prob_frozen = cal_clf_frozen.predict_proba(this_X_test) + y_pred_prefit = cal_clf_prefit.predict(this_X_test) + y_pred_frozen = cal_clf_frozen.predict(this_X_test) + prob_pos_cal_clf_prefit = y_prob_prefit[:, 1] + prob_pos_cal_clf_frozen = y_prob_frozen[:, 1] + assert_array_equal(y_pred_prefit, y_pred_frozen) + assert_array_equal( + y_pred_prefit, np.array([0, 1])[np.argmax(y_prob_prefit, axis=1)] + ) assert brier_score_loss(y_test, prob_pos_clf) > brier_score_loss( - y_test, prob_pos_cal_clf + y_test, prob_pos_cal_clf_frozen ) @@ -491,11 +503,9 @@ def test_calibration_accepts_ndarray(X): """Test that calibration accepts n-dimensional arrays as input""" y = [1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0] - class MockTensorClassifier(BaseEstimator): + class MockTensorClassifier(ClassifierMixin, BaseEstimator): """A toy estimator that accepts tensor inputs""" - _estimator_type = "classifier" - def fit(self, X, y): self.classes_ = np.unique(y) return self @@ -515,8 +525,10 @@ def dict_data(): {"state": "NY", "age": "adult"}, {"state": "TX", "age": "adult"}, {"state": "VT", "age": "child"}, + {"state": "CT", "age": "adult"}, + {"state": "BR", "age": "child"}, ] - text_labels = [1, 0, 1] + text_labels = [1, 0, 1, 1, 0] return dict_data, text_labels @@ -540,7 +552,7 @@ def test_calibration_dict_pipeline(dict_data, dict_data_pipeline): """ X, y = dict_data clf = dict_data_pipeline - calib_clf = CalibratedClassifierCV(clf, cv="prefit") + calib_clf = CalibratedClassifierCV(FrozenEstimator(clf), cv=2) calib_clf.fit(X, y) # Check attributes are obtained from fitted estimator assert_array_equal(calib_clf.classes_, clf.classes_) @@ -584,7 +596,7 @@ def test_calibration_inconsistent_prefit_n_features_in(): # is consistent with training set X, y = make_classification(n_samples=10, n_features=5, n_classes=2, random_state=7) clf = LinearSVC(C=1).fit(X, y) - calib_clf = CalibratedClassifierCV(clf, cv="prefit") + calib_clf = CalibratedClassifierCV(FrozenEstimator(clf)) msg = "X has 3 features, but LinearSVC is expecting 5 features as input." with pytest.raises(ValueError, match=msg): @@ -602,7 +614,7 @@ def test_calibration_votingclassifier(): ) vote.fit(X, y) - calib_clf = CalibratedClassifierCV(estimator=vote, cv="prefit") + calib_clf = CalibratedClassifierCV(estimator=FrozenEstimator(vote)) # smoke test: should not raise an error calib_clf.fit(X, y) diff --git a/sklearn/tests/test_common.py b/sklearn/tests/test_common.py index a985f6a02289a..455234adfad5b 100644 --- a/sklearn/tests/test_common.py +++ b/sklearn/tests/test_common.py @@ -35,7 +35,14 @@ StandardScaler, ) from sklearn.utils import all_estimators -from sklearn.utils._tags import get_tags +from sklearn.utils._tags import ( + ClassifierTags, + InputTags, + RegressorTags, + TargetTags, + TransformerTags, + get_tags, +) from sklearn.utils._test_common.instance_generator import ( _get_check_estimator_ids, _tested_estimators, @@ -217,29 +224,49 @@ def test_class_support_removed(): ) def test_valid_tag_types(estimator): """Check that estimator tags are valid.""" - from dataclasses import fields - - from ..utils._tags import default_tags - - def check_field_types(tags, defaults): - if tags is None: - return - tags_fields = fields(tags) - for field in tags_fields: - correct_tags = type(getattr(defaults, field.name)) - if field.name == "_xfail_checks": - # _xfail_checks can be a dictionary - correct_tags = (correct_tags, dict) - assert isinstance(getattr(tags, field.name), correct_tags) - tags = get_tags(estimator) - defaults = default_tags(estimator) - check_field_types(tags, defaults) - check_field_types(tags.input_tags, defaults.input_tags) - check_field_types(tags.target_tags, defaults.target_tags) - check_field_types(tags.classifier_tags, defaults.classifier_tags) - check_field_types(tags.regressor_tags, defaults.regressor_tags) - check_field_types(tags.transformer_tags, defaults.transformer_tags) + assert isinstance(tags.estimator_type, (str, type(None))) + assert isinstance(tags.target_tags, TargetTags) + assert isinstance(tags.classifier_tags, (ClassifierTags, type(None))) + assert isinstance(tags.regressor_tags, (RegressorTags, type(None))) + assert isinstance(tags.transformer_tags, (TransformerTags, type(None))) + assert isinstance(tags.input_tags, InputTags) + assert isinstance(tags.array_api_support, bool) + assert isinstance(tags.no_validation, bool) + assert isinstance(tags.non_deterministic, bool) + assert isinstance(tags.requires_fit, bool) + assert isinstance(tags._skip_test, bool) + assert isinstance(tags._xfail_checks, dict) + + assert isinstance(tags.target_tags.required, bool) + assert isinstance(tags.target_tags.one_d_labels, bool) + assert isinstance(tags.target_tags.two_d_labels, bool) + assert isinstance(tags.target_tags.positive_only, bool) + assert isinstance(tags.target_tags.multi_output, bool) + assert isinstance(tags.target_tags.single_output, bool) + + assert isinstance(tags.input_tags.pairwise, bool) + assert isinstance(tags.input_tags.allow_nan, bool) + assert isinstance(tags.input_tags.sparse, bool) + assert isinstance(tags.input_tags.categorical, bool) + assert isinstance(tags.input_tags.string, bool) + assert isinstance(tags.input_tags.dict, bool) + assert isinstance(tags.input_tags.one_d_array, bool) + assert isinstance(tags.input_tags.two_d_array, bool) + assert isinstance(tags.input_tags.three_d_array, bool) + assert isinstance(tags.input_tags.positive_only, bool) + + if tags.classifier_tags is not None: + assert isinstance(tags.classifier_tags.poor_score, bool) + assert isinstance(tags.classifier_tags.multi_class, bool) + assert isinstance(tags.classifier_tags.multi_label, bool) + + if tags.regressor_tags is not None: + assert isinstance(tags.regressor_tags.poor_score, bool) + assert isinstance(tags.regressor_tags.multi_label, bool) + + if tags.transformer_tags is not None: + assert isinstance(tags.transformer_tags.preserves_dtype, list) def _estimators_that_predict_in_fit(): diff --git a/sklearn/tests/test_docstring_parameters.py b/sklearn/tests/test_docstring_parameters.py index 6d44a3546f1ea..f3a6ba999f7f6 100644 --- a/sklearn/tests/test_docstring_parameters.py +++ b/sklearn/tests/test_docstring_parameters.py @@ -177,9 +177,6 @@ def _construct_sparse_coder(Estimator): @pytest.mark.filterwarnings("ignore::sklearn.exceptions.ConvergenceWarning") -# TODO(1.6): remove "@pytest.mark.filterwarnings" as SAMME.R will be removed -# and substituted with the SAMME algorithm as a default -@pytest.mark.filterwarnings("ignore:The SAMME.R algorithm") @pytest.mark.parametrize("name, Estimator", all_estimators()) def test_fit_docstring_attributes(name, Estimator): pytest.importorskip("numpydoc") @@ -203,6 +200,9 @@ def test_fit_docstring_attributes(name, Estimator): est = _construct_compose_pipeline_instance(Estimator) elif Estimator.__name__ == "SparseCoder": est = _construct_sparse_coder(Estimator) + elif Estimator.__name__ == "FrozenEstimator": + X, y = make_classification(n_samples=20, n_features=5, random_state=0) + est = Estimator(LogisticRegression().fit(X, y)) else: # TODO(devtools): use _tested_estimators instead of all_estimators in the # decorator @@ -225,10 +225,6 @@ def test_fit_docstring_attributes(name, Estimator): # default raises an error, perplexity must be less than n_samples est.set_params(perplexity=2) - # TODO(1.6): remove (avoid FutureWarning) - if Estimator.__name__ in ("NMF", "MiniBatchNMF"): - est.set_params(n_components="auto") - # Low max iter to speed up tests: we are only interested in checking the existence # of fitted attributes. This should be invariant to whether it has converged or not. if "max_iter" in est.get_params(): diff --git a/sklearn/tests/test_metaestimators.py b/sklearn/tests/test_metaestimators.py index 9c12afd60c206..214fc75a68364 100644 --- a/sklearn/tests/test_metaestimators.py +++ b/sklearn/tests/test_metaestimators.py @@ -1,6 +1,7 @@ """Common tests for metaestimators""" import functools +from contextlib import suppress from inspect import signature import numpy as np @@ -18,7 +19,8 @@ from sklearn.preprocessing import MaxAbsScaler, StandardScaler from sklearn.semi_supervised import SelfTrainingClassifier from sklearn.utils import all_estimators -from sklearn.utils._testing import set_random_state +from sklearn.utils._test_common.instance_generator import _construct_instances +from sklearn.utils._testing import SkipTest, set_random_state from sklearn.utils.estimator_checks import ( _enforce_estimator_tags_X, _enforce_estimator_tags_y, @@ -197,61 +199,80 @@ def score(self, X, y, *args, **kwargs): ) +def _get_instance_with_pipeline(meta_estimator, init_params): + """Given a single meta-estimator instance, generate an instance with a pipeline""" + if {"estimator", "base_estimator", "regressor"} & init_params: + if is_regressor(meta_estimator): + estimator = make_pipeline(TfidfVectorizer(), Ridge()) + param_grid = {"ridge__alpha": [0.1, 1.0]} + else: + estimator = make_pipeline(TfidfVectorizer(), LogisticRegression()) + param_grid = {"logisticregression__C": [0.1, 1.0]} + + if init_params.intersection( + {"param_grid", "param_distributions"} + ): # SearchCV estimators + extra_params = {"n_iter": 2} if "n_iter" in init_params else {} + return type(meta_estimator)(estimator, param_grid, **extra_params) + else: + return type(meta_estimator)(estimator) + + if "transformer_list" in init_params: + # FeatureUnion + transformer_list = [ + ("trans1", make_pipeline(TfidfVectorizer(), MaxAbsScaler())), + ( + "trans2", + make_pipeline(TfidfVectorizer(), StandardScaler(with_mean=False)), + ), + ] + return type(meta_estimator)(transformer_list) + + if "estimators" in init_params: + # stacking, voting + if is_regressor(meta_estimator): + estimator = [ + ("est1", make_pipeline(TfidfVectorizer(), Ridge(alpha=0.1))), + ("est2", make_pipeline(TfidfVectorizer(), Ridge(alpha=1))), + ] + else: + estimator = [ + ( + "est1", + make_pipeline(TfidfVectorizer(), LogisticRegression(C=0.1)), + ), + ("est2", make_pipeline(TfidfVectorizer(), LogisticRegression(C=1))), + ] + return type(meta_estimator)(estimator) + + def _generate_meta_estimator_instances_with_pipeline(): """Generate instances of meta-estimators fed with a pipeline Are considered meta-estimators all estimators accepting one of "estimator", "base_estimator" or "estimators". """ + print("estimators: ", len(all_estimators())) for _, Estimator in sorted(all_estimators()): sig = set(signature(Estimator).parameters) - if "estimator" in sig or "base_estimator" in sig or "regressor" in sig: - if is_regressor(Estimator): - estimator = make_pipeline(TfidfVectorizer(), Ridge()) - param_grid = {"ridge__alpha": [0.1, 1.0]} - else: - estimator = make_pipeline(TfidfVectorizer(), LogisticRegression()) - param_grid = {"logisticregression__C": [0.1, 1.0]} - - if "param_grid" in sig or "param_distributions" in sig: - # SearchCV estimators - extra_params = {"n_iter": 2} if "n_iter" in sig else {} - yield Estimator(estimator, param_grid, **extra_params) - else: - yield Estimator(estimator) - - elif "transformer_list" in sig: - # FeatureUnion - transformer_list = [ - ("trans1", make_pipeline(TfidfVectorizer(), MaxAbsScaler())), - ( - "trans2", - make_pipeline(TfidfVectorizer(), StandardScaler(with_mean=False)), - ), - ] - yield Estimator(transformer_list) - - elif "estimators" in sig: - # stacking, voting - if is_regressor(Estimator): - estimator = [ - ("est1", make_pipeline(TfidfVectorizer(), Ridge(alpha=0.1))), - ("est2", make_pipeline(TfidfVectorizer(), Ridge(alpha=1))), - ] - else: - estimator = [ - ( - "est1", - make_pipeline(TfidfVectorizer(), LogisticRegression(C=0.1)), - ), - ("est2", make_pipeline(TfidfVectorizer(), LogisticRegression(C=1))), - ] - yield Estimator(estimator) - - else: + print("\n", Estimator.__name__, sig) + if not sig.intersection( + { + "estimator", + "base_estimator", + "regressor", + "transformer_list", + "estimators", + } + ): continue + with suppress(SkipTest): + for meta_estimator in _construct_instances(Estimator): + print(meta_estimator) + yield _get_instance_with_pipeline(meta_estimator, sig) + # TODO: remove data validation for the following estimators # They should be able to work on any data and delegate data validation to @@ -262,6 +283,7 @@ def _generate_meta_estimator_instances_with_pipeline(): "BaggingClassifier", "BaggingRegressor", "ClassifierChain", # data validation is necessary + "FrozenEstimator", # this estimator cannot be tested like others. "IterativeImputer", "OneVsOneClassifier", # input validation can't be avoided "RANSACRegressor", diff --git a/sklearn/tests/test_metaestimators_metadata_routing.py b/sklearn/tests/test_metaestimators_metadata_routing.py index 7117c27e32e42..b733f4d119f5e 100644 --- a/sklearn/tests/test_metaestimators_metadata_routing.py +++ b/sklearn/tests/test_metaestimators_metadata_routing.py @@ -5,7 +5,7 @@ import pytest from sklearn import config_context -from sklearn.base import is_classifier +from sklearn.base import BaseEstimator, is_classifier from sklearn.calibration import CalibratedClassifierCV from sklearn.compose import TransformedTargetRegressor from sklearn.covariance import GraphicalLassoCV @@ -551,13 +551,13 @@ def get_init_args(metaestimator_info, sub_estimator_consumes): ) -def set_requests(estimator, *, method_mapping, methods, metadata_name, value=True): +def set_requests(obj, *, method_mapping, methods, metadata_name, value=True): """Call `set_{method}_request` on a list of methods from the sub-estimator. Parameters ---------- - estimator : BaseEstimator - The estimator for which `set_{method}_request` methods are called. + obj : BaseEstimator + The object for which `set_{method}_request` methods are called. method_mapping : dict The method mapping in the form of `{caller: [callee, ...]}`. @@ -577,9 +577,13 @@ def set_requests(estimator, *, method_mapping, methods, metadata_name, value=Tru """ for caller in methods: for callee in method_mapping.get(caller, [caller]): - set_request_for_method = getattr(estimator, f"set_{callee}_request") + set_request_for_method = getattr(obj, f"set_{callee}_request") set_request_for_method(**{metadata_name: value}) - if is_classifier(estimator) and callee == "partial_fit": + if ( + isinstance(obj, BaseEstimator) + and is_classifier(obj) + and callee == "partial_fit" + ): set_request_for_method(classes=True) diff --git a/sklearn/tests/test_pipeline.py b/sklearn/tests/test_pipeline.py index 1a876e050f4f4..d425c00f114a2 100644 --- a/sklearn/tests/test_pipeline.py +++ b/sklearn/tests/test_pipeline.py @@ -71,7 +71,7 @@ ) -class NoFit: +class NoFit(BaseEstimator): """Small class to test parameter dispatching.""" def __init__(self, a=None, b=None): @@ -80,7 +80,7 @@ def __init__(self, a=None, b=None): class NoTrans(NoFit): - def fit(self, X, y): + def fit(self, X, y=None): return self def get_params(self, deep=False): @@ -91,7 +91,7 @@ def set_params(self, **params): return self -class NoInvTransf(NoTrans): +class NoInvTransf(TransformerMixin, NoTrans): def transform(self, X): return X @@ -105,19 +105,19 @@ def inverse_transform(self, X): class TransfFitParams(Transf): - def fit(self, X, y, **fit_params): + def fit(self, X, y=None, **fit_params): self.fit_params = fit_params return self -class Mult(BaseEstimator): +class Mult(TransformerMixin, BaseEstimator): def __init__(self, mult=1): self.mult = mult def __sklearn_is_fitted__(self): return True - def fit(self, X, y): + def fit(self, X, y=None): return self def transform(self, X): diff --git a/sklearn/tree/_classes.py b/sklearn/tree/_classes.py index 131ad12ab48f9..7a49c6dc93485 100644 --- a/sklearn/tree/_classes.py +++ b/sklearn/tree/_classes.py @@ -15,6 +15,7 @@ import numpy as np from scipy.sparse import issparse +from sklearn.utils import metadata_routing from sklearn.base import ( BaseEstimator, ClassifierMixin, @@ -97,6 +98,10 @@ class BaseDecisionTree(MultiOutputMixin, BaseEstimator, metaclass=ABCMeta): Use derived classes instead. """ + # "check_input" is used for optimisation and isn't something to be passed + # around in a pipeline. + __metadata_request__predict = {"check_input": metadata_routing.UNUSED} + _parameter_constraints: dict = { "splitter": [StrOptions({"best", "random"})], "max_depth": [Interval(Integral, 1, None, closed="left"), None], @@ -1252,6 +1257,11 @@ class DecisionTreeClassifier(ClassifierMixin, BaseDecisionTree): 0.93..., 0.93..., 1. , 0.93..., 1. ]) """ + # "check_input" is used for optimisation and isn't something to be passed + # around in a pipeline. + __metadata_request__predict_proba = {"check_input": metadata_routing.UNUSED} + __metadata_request__fit = {"check_input": metadata_routing.UNUSED} + _parameter_constraints: dict = { **BaseDecisionTree._parameter_constraints, "criterion": [ @@ -1770,6 +1780,10 @@ class DecisionTreeRegressor(RegressorMixin, BaseDecisionTree): 0.16..., 0.11..., -0.73..., -0.30..., -0.00...]) """ + # "check_input" is used for optimisation and isn't something to be passed + # around in a pipeline. + __metadata_request__fit = {"check_input": metadata_routing.UNUSED} + _parameter_constraints: dict = { **BaseDecisionTree._parameter_constraints, "criterion": [ diff --git a/sklearn/tree/_export.py b/sklearn/tree/_export.py index a96ccf00eb1c4..e8e45af79fb28 100644 --- a/sklearn/tree/_export.py +++ b/sklearn/tree/_export.py @@ -314,6 +314,7 @@ def node_to_str(self, tree, node_id, criterion): # Always write node decision criteria, except for leaves if self.feature_names is not None: feature = self.feature_names[tree.feature[node_id]] + feature = self.str_escape(feature) else: feature = "x%s%s%s" % ( characters[1], @@ -389,6 +390,7 @@ def node_to_str(self, tree, node_id, criterion): node_string += "class = " if self.class_names is not True: class_name = self.class_names[np.argmax(value)] + class_name = self.str_escape(class_name) else: class_name = "y%s%s%s" % ( characters[1], @@ -403,6 +405,9 @@ def node_to_str(self, tree, node_id, criterion): return node_string + characters[5] + def str_escape(self, string): + return string + class _DOTTreeExporter(_BaseTreeExporter): def __init__( @@ -577,6 +582,10 @@ def recurse(self, tree, node_id, criterion, parent=None, depth=0): # Add edge to parent self.out_file.write("%d -> %d ;\n" % (parent, node_id)) + def str_escape(self, string): + # override default escaping for graphviz + return string.replace('"', r"\"") + class _MPLTreeExporter(_BaseTreeExporter): def __init__( diff --git a/sklearn/tree/tests/test_export.py b/sklearn/tree/tests/test_export.py index cd4a106ee7606..d05e657072b17 100644 --- a/sklearn/tree/tests/test_export.py +++ b/sklearn/tree/tests/test_export.py @@ -52,6 +52,90 @@ def test_graphviz_toy(): 'headlabel="False"] ;\n' "}" ) + assert contents1 == contents2 + + # Test with feature_names + contents1 = export_graphviz( + clf, feature_names=["feature0", "feature1"], out_file=None + ) + contents2 = ( + "digraph Tree {\n" + 'node [shape=box, fontname="helvetica"] ;\n' + 'edge [fontname="helvetica"] ;\n' + '0 [label="feature0 <= 0.0\\ngini = 0.5\\nsamples = 6\\n' + 'value = [3, 3]"] ;\n' + '1 [label="gini = 0.0\\nsamples = 3\\nvalue = [3, 0]"] ;\n' + "0 -> 1 [labeldistance=2.5, labelangle=45, " + 'headlabel="True"] ;\n' + '2 [label="gini = 0.0\\nsamples = 3\\nvalue = [0, 3]"] ;\n' + "0 -> 2 [labeldistance=2.5, labelangle=-45, " + 'headlabel="False"] ;\n' + "}" + ) + + assert contents1 == contents2 + + # Test with feature_names (escaped) + contents1 = export_graphviz( + clf, feature_names=['feature"0"', 'feature"1"'], out_file=None + ) + contents2 = ( + "digraph Tree {\n" + 'node [shape=box, fontname="helvetica"] ;\n' + 'edge [fontname="helvetica"] ;\n' + '0 [label="feature\\"0\\" <= 0.0\\n' + "gini = 0.5\\nsamples = 6\\n" + 'value = [3, 3]"] ;\n' + '1 [label="gini = 0.0\\nsamples = 3\\nvalue = [3, 0]"] ;\n' + "0 -> 1 [labeldistance=2.5, labelangle=45, " + 'headlabel="True"] ;\n' + '2 [label="gini = 0.0\\nsamples = 3\\nvalue = [0, 3]"] ;\n' + "0 -> 2 [labeldistance=2.5, labelangle=-45, " + 'headlabel="False"] ;\n' + "}" + ) + + assert contents1 == contents2 + + # Test with class_names + contents1 = export_graphviz(clf, class_names=["yes", "no"], out_file=None) + contents2 = ( + "digraph Tree {\n" + 'node [shape=box, fontname="helvetica"] ;\n' + 'edge [fontname="helvetica"] ;\n' + '0 [label="x[0] <= 0.0\\ngini = 0.5\\nsamples = 6\\n' + 'value = [3, 3]\\nclass = yes"] ;\n' + '1 [label="gini = 0.0\\nsamples = 3\\nvalue = [3, 0]\\n' + 'class = yes"] ;\n' + "0 -> 1 [labeldistance=2.5, labelangle=45, " + 'headlabel="True"] ;\n' + '2 [label="gini = 0.0\\nsamples = 3\\nvalue = [0, 3]\\n' + 'class = no"] ;\n' + "0 -> 2 [labeldistance=2.5, labelangle=-45, " + 'headlabel="False"] ;\n' + "}" + ) + + assert contents1 == contents2 + + # Test with class_names (escaped) + contents1 = export_graphviz(clf, class_names=['"yes"', '"no"'], out_file=None) + contents2 = ( + "digraph Tree {\n" + 'node [shape=box, fontname="helvetica"] ;\n' + 'edge [fontname="helvetica"] ;\n' + '0 [label="x[0] <= 0.0\\ngini = 0.5\\nsamples = 6\\n' + 'value = [3, 3]\\nclass = \\"yes\\""] ;\n' + '1 [label="gini = 0.0\\nsamples = 3\\nvalue = [3, 0]\\n' + 'class = \\"yes\\""] ;\n' + "0 -> 1 [labeldistance=2.5, labelangle=45, " + 'headlabel="True"] ;\n' + '2 [label="gini = 0.0\\nsamples = 3\\nvalue = [0, 3]\\n' + 'class = \\"no\\""] ;\n' + "0 -> 2 [labeldistance=2.5, labelangle=-45, " + 'headlabel="False"] ;\n' + "}" + ) assert contents1 == contents2 diff --git a/sklearn/utils/__init__.py b/sklearn/utils/__init__.py index 1a7a43fdbc01f..58bce9cfd6fe4 100644 --- a/sklearn/utils/__init__.py +++ b/sklearn/utils/__init__.py @@ -33,7 +33,6 @@ Tags, TargetTags, TransformerTags, - default_tags, get_tags, ) from .class_weight import compute_class_weight, compute_sample_weight @@ -99,7 +98,6 @@ class parallel_backend(_joblib.parallel_backend): "ClassifierTags", "RegressorTags", "TransformerTags", - "default_tags", "get_tags", ] diff --git a/sklearn/utils/_plotting.py b/sklearn/utils/_plotting.py index 3b85349ff31a7..946c95186374b 100644 --- a/sklearn/utils/_plotting.py +++ b/sklearn/utils/_plotting.py @@ -163,3 +163,17 @@ def _validate_style_kwargs(default_style_kwargs, user_style_kwargs): valid_style_kwargs[key] = user_style_kwargs[key] return valid_style_kwargs + + +def _despine(ax): + """Remove the top and right spines of the plot. + + Parameters + ---------- + ax : matplotlib.axes.Axes + The axes of the plot to despine. + """ + for s in ["top", "right"]: + ax.spines[s].set_visible(False) + for s in ["bottom", "left"]: + ax.spines[s].set_bounds(0, 1) diff --git a/sklearn/utils/_tags.py b/sklearn/utils/_tags.py index fc70492277a28..de756901d98ef 100644 --- a/sklearn/utils/_tags.py +++ b/sklearn/utils/_tags.py @@ -1,12 +1,15 @@ from __future__ import annotations +import warnings from dataclasses import dataclass, field +from .fixes import _dataclass_args + # Authors: The scikit-learn developers # SPDX-License-Identifier: BSD-3-Clause -@dataclass +@dataclass(**_dataclass_args()) class InputTags: """Tags for the input data. @@ -67,7 +70,7 @@ class InputTags: pairwise: bool = False -@dataclass +@dataclass(**_dataclass_args()) class TargetTags: """Tags for the target data. @@ -106,7 +109,7 @@ class TargetTags: single_output: bool = True -@dataclass +@dataclass(**_dataclass_args()) class TransformerTags: """Tags for the transformer. @@ -126,7 +129,7 @@ class TransformerTags: preserves_dtype: list[str] = field(default_factory=lambda: ["float64"]) -@dataclass +@dataclass(**_dataclass_args()) class ClassifierTags: """Tags for the classifier. @@ -154,7 +157,7 @@ class ClassifierTags: multi_label: bool = False -@dataclass +@dataclass(**_dataclass_args()) class RegressorTags: """Tags for the regressor. @@ -176,7 +179,7 @@ class RegressorTags: multi_label: bool = False -@dataclass +@dataclass(**_dataclass_args()) class Tags: """Tags for the estimator. @@ -184,6 +187,15 @@ class Tags: Parameters ---------- + estimator_type : str or None + The type of the estimator. Can be one of: + - "classifier" + - "regressor" + - "transformer" + - "clusterer" + - "outlier_detector" + - "density_estimator" + target_tags : :class:`TargetTags` The target(y) tags. @@ -230,6 +242,7 @@ class Tags: The input data(X) tags. """ + estimator_type: str | None target_tags: TargetTags transformer_tags: TransformerTags | None classifier_tags: ClassifierTags | None @@ -243,6 +256,7 @@ class Tags: input_tags: InputTags = field(default_factory=InputTags) +# TODO(1.8): Remove this function def default_tags(estimator) -> Tags: """Get the default tags for an estimator. @@ -272,19 +286,20 @@ def default_tags(estimator) -> Tags: tags : Tags The default tags for the estimator. """ - from ..base import is_classifier, is_regressor - - target_required = is_classifier(estimator) or is_regressor(estimator) + est_is_classifier = getattr(estimator, "_estimator_type", None) == "classifier" + est_is_regressor = getattr(estimator, "_estimator_type", None) == "regressor" + target_required = est_is_classifier or est_is_regressor return Tags( + estimator_type=getattr(estimator, "_estimator_type", None), target_tags=TargetTags(required=target_required), transformer_tags=( TransformerTags() if hasattr(estimator, "transform") or hasattr(estimator, "fit_transform") else None ), - classifier_tags=ClassifierTags() if is_classifier(estimator) else None, - regressor_tags=RegressorTags() if is_regressor(estimator) else None, + classifier_tags=ClassifierTags() if est_is_classifier else None, + regressor_tags=RegressorTags() if est_is_regressor else None, ) @@ -314,6 +329,16 @@ def get_tags(estimator) -> Tags: if hasattr(estimator, "__sklearn_tags__"): tags = estimator.__sklearn_tags__() else: + warnings.warn( + f"Estimator {estimator} has no __sklearn_tags__ attribute, which is " + "defined in `sklearn.base.BaseEstimator`. This will raise an error in " + "scikit-learn 1.8. Please define the __sklearn_tags__ method, or inherit " + "from `sklearn.base.BaseEstimator` and other appropriate mixins such as " + "`sklearn.base.TransformerMixin`, `sklearn.base.ClassifierMixin`, " + "`sklearn.base.RegressorMixin`, and `sklearn.base.ClusterMixin`, and " + "`sklearn.base.OutlierMixin`.", + category=FutureWarning, + ) tags = default_tags(estimator) return tags diff --git a/sklearn/utils/_test_common/instance_generator.py b/sklearn/utils/_test_common/instance_generator.py index 158726c3574c4..7fe6724aaff9a 100644 --- a/sklearn/utils/_test_common/instance_generator.py +++ b/sklearn/utils/_test_common/instance_generator.py @@ -4,6 +4,7 @@ import re import warnings +from contextlib import suppress from functools import partial from inspect import isfunction @@ -74,6 +75,7 @@ SelectKBest, SequentialFeatureSelector, ) +from sklearn.frozen import FrozenEstimator from sklearn.kernel_approximation import ( Nystroem, PolynomialCountSketch, @@ -622,15 +624,13 @@ def _tested_estimators(type_filter=None): - for name, Estimator in all_estimators(type_filter=type_filter): - try: + for _, Estimator in all_estimators(type_filter=type_filter): + with suppress(SkipTest): for estimator in _construct_instances(Estimator): yield estimator - except SkipTest: - continue -SKIPPED_ESTIMATORS = [SparseCoder] +SKIPPED_ESTIMATORS = [SparseCoder, FrozenEstimator] def _construct_instances(Estimator): diff --git a/sklearn/utils/_testing.py b/sklearn/utils/_testing.py index ef683089eb64d..91efe88eeb354 100644 --- a/sklearn/utils/_testing.py +++ b/sklearn/utils/_testing.py @@ -38,6 +38,13 @@ ) import sklearn +from sklearn.utils import ( + ClassifierTags, + RegressorTags, + Tags, + TargetTags, + TransformerTags, +) from sklearn.utils._array_api import _check_array_api_dispatch from sklearn.utils.fixes import ( _IS_32BIT, @@ -1089,8 +1096,6 @@ class MinimalClassifier: * within a `SearchCV` in `test_search.py`. """ - _estimator_type = "classifier" - def __init__(self, param=None): self.param = param @@ -1127,6 +1132,15 @@ def score(self, X, y): return accuracy_score(y, self.predict(X)) + def __sklearn_tags__(self): + return Tags( + estimator_type="classifier", + classifier_tags=ClassifierTags(), + regressor_tags=None, + transformer_tags=None, + target_tags=TargetTags(required=True), + ) + class MinimalRegressor: """Minimal regressor implementation without inheriting from BaseEstimator. @@ -1138,8 +1152,6 @@ class MinimalRegressor: * within a `SearchCV` in `test_search.py`. """ - _estimator_type = "regressor" - def __init__(self, param=None): self.param = param @@ -1167,6 +1179,15 @@ def score(self, X, y): return r2_score(y, self.predict(X)) + def __sklearn_tags__(self): + return Tags( + estimator_type="regressor", + classifier_tags=None, + regressor_tags=RegressorTags(), + transformer_tags=None, + target_tags=TargetTags(required=True), + ) + class MinimalTransformer: """Minimal transformer implementation without inheriting from @@ -1203,6 +1224,15 @@ def transform(self, X, y=None): def fit_transform(self, X, y=None): return self.fit(X, y).transform(X, y) + def __sklearn_tags__(self): + return Tags( + estimator_type="transformer", + classifier_tags=None, + regressor_tags=None, + transformer_tags=TransformerTags(), + target_tags=TargetTags(required=False), + ) + def _array_api_for_tests(array_namespace, device): try: diff --git a/sklearn/utils/deprecation.py b/sklearn/utils/deprecation.py index ff08ec2aceb81..35b9dfc8a47f6 100644 --- a/sklearn/utils/deprecation.py +++ b/sklearn/utils/deprecation.py @@ -3,6 +3,7 @@ import functools import warnings +from inspect import signature __all__ = ["deprecated"] @@ -64,17 +65,21 @@ def _decorate_class(self, cls): msg += "; %s" % self.extra new = cls.__new__ + sig = signature(cls) def wrapped(cls, *args, **kwargs): warnings.warn(msg, category=FutureWarning) if new is object.__new__: return object.__new__(cls) + return new(cls, *args, **kwargs) cls.__new__ = wrapped wrapped.__name__ = "__new__" wrapped.deprecated_original = new + # Restore the original signature, see PEP 362. + cls.__signature__ = sig return cls diff --git a/sklearn/utils/estimator_checks.py b/sklearn/utils/estimator_checks.py index 728fd71844118..f5d542d9a59fc 100644 --- a/sklearn/utils/estimator_checks.py +++ b/sklearn/utils/estimator_checks.py @@ -1820,7 +1820,7 @@ def check_transformer_preserve_dtypes(name, transformer_orig): # check that the output dtype is preserved assert Xt.dtype == dtype, ( f"{name} (method={method}) does not preserve dtype. " - f"Original/Expected dtype={dtype.__name__}, got dtype={Xt.dtype}." + f"Original/Expected dtype={dtype}, got dtype={Xt.dtype}." ) diff --git a/sklearn/utils/fixes.py b/sklearn/utils/fixes.py index 10b5427509539..56f18c98f44d1 100644 --- a/sklearn/utils/fixes.py +++ b/sklearn/utils/fixes.py @@ -426,3 +426,10 @@ def _create_pandas_dataframe_from_non_pandas_container(X, *, index, copy): def _create_pandas_dataframe_from_non_pandas_container(X, *, index, copy): return pd.DataFrame(X, index=index, copy=copy) + + +# TODO: Remove when python>=3.10 is the minimum supported version +def _dataclass_args(): + if sys.version_info < (3, 10): + return {} + return {"slots": True} diff --git a/sklearn/utils/tests/test_deprecation.py b/sklearn/utils/tests/test_deprecation.py index 468be71ced157..7368af3041a19 100644 --- a/sklearn/utils/tests/test_deprecation.py +++ b/sklearn/utils/tests/test_deprecation.py @@ -3,6 +3,7 @@ import pickle +from inspect import signature import pytest @@ -86,3 +87,12 @@ def test_is_deprecated(): def test_pickle(): pickle.loads(pickle.dumps(mock_function)) + + +def test_deprecated_class_signature(): + @deprecated() + class MockClass: + def __init__(self, a, b=1, c=2): + pass + + assert list(signature(MockClass).parameters.keys()) == ["a", "b", "c"] diff --git a/sklearn/utils/tests/test_estimator_checks.py b/sklearn/utils/tests/test_estimator_checks.py index 29611a853938f..846ee75a9bbd4 100644 --- a/sklearn/utils/tests/test_estimator_checks.py +++ b/sklearn/utils/tests/test_estimator_checks.py @@ -13,7 +13,7 @@ import scipy.sparse as sp from sklearn import config_context, get_config -from sklearn.base import BaseEstimator, ClassifierMixin, OutlierMixin +from sklearn.base import BaseEstimator, ClassifierMixin, OutlierMixin, TransformerMixin from sklearn.cluster import MiniBatchKMeans from sklearn.datasets import ( load_iris, @@ -33,7 +33,6 @@ from sklearn.svm import SVC, NuSVC from sklearn.utils import _array_api, all_estimators, deprecated from sklearn.utils._param_validation import Interval, StrOptions -from sklearn.utils._tags import default_tags from sklearn.utils._testing import ( MinimalClassifier, MinimalRegressor, @@ -422,7 +421,7 @@ def fit(self, X, y): return self -class SparseTransformer(BaseEstimator): +class SparseTransformer(TransformerMixin, BaseEstimator): def __init__(self, sparse_container=None): self.sparse_container = sparse_container @@ -1276,18 +1275,18 @@ def fit(self, X, y): def test_non_deterministic_estimator_skip_tests(): # check estimators with non_deterministic tag set to True # will skip certain tests, refer to issue #22313 for details - for est in [MinimalTransformer, MinimalRegressor, MinimalClassifier]: - all_tests = list(_yield_all_checks(est(), legacy=True)) + for Estimator in [MinimalTransformer, MinimalRegressor, MinimalClassifier]: + all_tests = list(_yield_all_checks(Estimator(), legacy=True)) assert check_methods_sample_order_invariance in all_tests assert check_methods_subset_invariance in all_tests - class Estimator(est): + class MyEstimator(Estimator): def __sklearn_tags__(self): - tags = default_tags(self) + tags = super().__sklearn_tags__() tags.non_deterministic = True return tags - all_tests = list(_yield_all_checks(Estimator(), legacy=True)) + all_tests = list(_yield_all_checks(MyEstimator(), legacy=True)) assert check_methods_sample_order_invariance not in all_tests assert check_methods_subset_invariance not in all_tests diff --git a/sklearn/utils/tests/test_plotting.py b/sklearn/utils/tests/test_plotting.py index 40678a8db4074..1f0c675577bca 100644 --- a/sklearn/utils/tests/test_plotting.py +++ b/sklearn/utils/tests/test_plotting.py @@ -2,6 +2,7 @@ import pytest from sklearn.utils._plotting import ( + _despine, _interval_max_min_ratio, _validate_score_name, _validate_style_kwargs, @@ -128,3 +129,12 @@ def test_validate_style_kwargs_error(default_kwargs, user_kwargs): """Check that `validate_style_kwargs` raises TypeError""" with pytest.raises(TypeError): _validate_style_kwargs(default_kwargs, user_kwargs) + + +def test_despine(pyplot): + ax = pyplot.gca() + _despine(ax) + assert ax.spines["top"].get_visible() is False + assert ax.spines["right"].get_visible() is False + assert ax.spines["bottom"].get_bounds() == (0, 1) + assert ax.spines["left"].get_bounds() == (0, 1) diff --git a/sklearn/utils/tests/test_tags.py b/sklearn/utils/tests/test_tags.py index a6dab5078e5ac..5768a0d2b6b27 100644 --- a/sklearn/utils/tests/test_tags.py +++ b/sklearn/utils/tests/test_tags.py @@ -1,6 +1,10 @@ import pytest -from sklearn.base import BaseEstimator, RegressorMixin, TransformerMixin +from sklearn.base import ( + BaseEstimator, + RegressorMixin, + TransformerMixin, +) from sklearn.utils._tags import get_tags @@ -13,13 +17,22 @@ class ClassifierEstimator: _estimator_type = "classifier" +class EmptyTransformer(TransformerMixin, BaseEstimator): + pass + + +class EmptyRegressor(RegressorMixin, BaseEstimator): + pass + + +@pytest.mark.filterwarnings("ignore:.*no __sklearn_tags__ attribute.*:FutureWarning") @pytest.mark.parametrize( "estimator, value", [ [NoTagsEstimator(), False], [ClassifierEstimator(), True], - [TransformerMixin(), False], - [RegressorMixin(), True], + [EmptyTransformer(), False], + [EmptyRegressor(), True], [BaseEstimator(), False], ], ) diff --git a/sklearn/utils/tests/test_validation.py b/sklearn/utils/tests/test_validation.py index 62627ecb1bae8..5ae5a003d0d0a 100644 --- a/sklearn/utils/tests/test_validation.py +++ b/sklearn/utils/tests/test_validation.py @@ -67,6 +67,7 @@ _check_sample_weight, _check_y, _deprecate_positional_args, + _estimator_has, _get_feature_names, _is_fitted, _is_pandas_df, @@ -946,7 +947,7 @@ def test_check_is_fitted(): def test_check_is_fitted_attributes(): - class MyEstimator: + class MyEstimator(BaseEstimator): def fit(self, X, y): return self @@ -1163,6 +1164,93 @@ def test_check_array_memmap(copy): assert X_checked.flags["WRITEABLE"] == copy +@pytest.mark.parametrize( + "estimator_name, estimator_value, delegates, expected_result, expected_exception", + [ + ( + "estimator_", + type("SubEstimator", (), {"attribute_present": True}), + None, # default delegates - ["estimator_", "estimator"] + True, # expected_result is True b/c delegate and attribute are present + None, # expected_exception not relevant for this case + ), + ( + "estimator", + type("SubEstimator", (), {"attribute_present": True}), + None, # default delegates - ["estimator_", "estimator"] + True, # expected_result is True b/c delegate and attribute are present + None, # expected_exception not relevant for this case + ), + ( + "estimators_", + [ + type("SubEstimator", (), {"attribute_present": True}) + ], # list of sub-estimators + ["estimators_"], + True, # expected_result is True b/c delegate and attribute are present + None, # expected_exception not relevant for this case + ), + ( + "custom_estimator", # custom estimator attribute name + type("SubEstimator", (), {"attribute_present": True}), + ["custom_estimator"], # custom delegates + True, # expected_result is True b/c delegate and attribute are present + None, # expected_exception not relevant for this case + ), + ( + "no_estimator", # no estimator attribute name + type("SubEstimator", (), {"attribute_present": True}), + None, # default delegates - ["estimator_", "estimator"] + None, # expected_result is not relevant for this case + ValueError, # should raise ValueError b/c no estimator found from delegates + ), + ( + "estimator", + type("SubEstimator", (), {"attribute_absent": True}), # attribute_absent + None, # default delegates - ["estimator_", "estimator"] + None, # expected_result is not relevant for this case + AttributeError, # should raise AttributeError b/c attribute is absent + ), + ], + ids=[ + "fitted_estimator_with_default_delegates", + "estimator_with_default_delegates", + "list_of_estimators_with_estimators_", + "custom_estimator_with_custom_delegates", + "no_estimator_with_default_delegates", + "estimator_with_default_delegates_but_absent_attribute", + ], +) +def test_estimator_has( + estimator_name, estimator_value, delegates, expected_result, expected_exception +): + """ + Tests the _estimator_has function by verifying: + - Functionality with default and custom delegates. + - Raises ValueError if delegates are missing. + - Raises AttributeError if the specified attribute is missing. + """ + + # always checks for attribute - "attribute_present" + # ["estimator_", "estimator"] is default value for delegates + if delegates is None: + check = _estimator_has("attribute_present") + else: + check = _estimator_has("attribute_present", delegates=delegates) + + class MockEstimator: + pass + + a = MockEstimator() + setattr(a, estimator_name, estimator_value) + + if expected_exception: + with pytest.raises(expected_exception): + check(a) + else: + assert check(a) == expected_result + + @pytest.mark.parametrize( "retype", [ diff --git a/sklearn/utils/validation.py b/sklearn/utils/validation.py index 383e262e0971e..649df1de8f223 100644 --- a/sklearn/utils/validation.py +++ b/sklearn/utils/validation.py @@ -7,6 +7,7 @@ import operator import sys import warnings +from collections.abc import Sequence from contextlib import suppress from functools import reduce, wraps from inspect import Parameter, isclass, signature @@ -1738,6 +1739,48 @@ def check_is_fitted(estimator, attributes=None, *, msg=None, all_or_any=all): raise NotFittedError(msg % {"name": type(estimator).__name__}) +def _estimator_has(attr, *, delegates=("estimator_", "estimator")): + """Check if we can delegate a method to the underlying estimator. + + We check the `delegates` in the order they are passed. By default, we first check + the fitted estimator if available, otherwise we check the unfitted estimator. + + Parameters + ---------- + attr : str + Name of the attribute the delegate might or might not have. + + delegates: tuple of str, default=("estimator_", "estimator") + A tuple of sub-estimator(s) to check if we can delegate the `attr` method. + + Returns + ------- + check : function + Function to check if the delegate has the attribute. + + Raises + ------ + ValueError + Raised when none of the delegates are present in the object. + """ + + def check(self): + for delegate in delegates: + # In meta estimators with multiple sub estimators, + # only the attribute of the first sub estimator is checked, + # assuming uniformity across all sub estimators. + if hasattr(self, delegate): + delegator = getattr(self, delegate) + if isinstance(delegator, Sequence): + return getattr(delegator[0], attr) + else: + return getattr(delegator, attr) + + raise ValueError(f"None of the delegates {delegates} are present in the class.") + + return check + + def check_non_negative(X, whom): """ Check if there is any negative value in an array.