diff --git a/pixi.lock b/pixi.lock index a1b4e648..4e7c877e 100644 --- a/pixi.lock +++ b/pixi.lock @@ -806,6 +806,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda - conda: https://prefix.dev/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/win-64/ruff-0.15.2-h213852a_0.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py314h221f224_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/win-64/sleef-3.9.0-h67fd636_0.conda - conda: https://prefix.dev/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda @@ -1595,6 +1596,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda - conda: https://prefix.dev/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://prefix.dev/conda-forge/win-64/ruff-0.15.2-h213852a_0.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/win-64/sleef-3.9.0-h67fd636_0.conda - conda: https://prefix.dev/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda @@ -2576,6 +2578,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda @@ -2628,6 +2631,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda @@ -2681,6 +2685,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py314hfc1f868_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda @@ -2732,6 +2737,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda - conda: https://prefix.dev/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py314h221f224_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda @@ -3128,6 +3134,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.10.0-cpu_mkl_py314_hf6ce55f_102.conda - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py314h221f224_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/win-64/sleef-3.9.0-h67fd636_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda @@ -3528,6 +3535,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.10.0-cpu_mkl_py311_hd35562e_102.conda - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_1.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py311h9c22a71_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/win-64/sleef-3.9.0-h67fd636_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda @@ -3995,6 +4003,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.10.0-cuda128_mkl_py314_h5a71295_302.conda - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/win-64/sleef-3.9.0-h67fd636_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda @@ -4459,6 +4468,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/win-64/pytorch-2.10.0-cuda128_mkl_py311_ha828eda_302.conda - conda: https://prefix.dev/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_1.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.16.3-py311h9c22a71_2.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/win-64/sleef-3.9.0-h67fd636_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda @@ -4542,6 +4552,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313t.conda - conda: https://prefix.dev/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_1.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.17.1-py313h0dc34c3_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda @@ -4607,6 +4618,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313t.conda - conda: https://prefix.dev/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_1.conda - conda: https://prefix.dev/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py313habc7bd5_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda @@ -4672,6 +4684,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313t.conda - conda: https://prefix.dev/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py313hf3ca9c9_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda @@ -4735,6 +4748,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/python-freethreading-3.13.12-h92d6c8b_0.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.13-8_cp313t.conda - conda: https://prefix.dev/conda-forge/noarch/pyyaml-6.0.3-pyh7db6752_1.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py313hff732fb_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda @@ -4806,6 +4820,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python-3.11.14-hd63d673_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.15.2-py311h8f841c2_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda @@ -4857,6 +4872,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-64/python-3.11.14-h74c2667_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.15.2-py311h0c91ca8_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda @@ -4908,6 +4924,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.11.14-h18782d2_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.15.2-py311h0675101_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda @@ -4957,6 +4974,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda - conda: https://prefix.dev/conda-forge/win-64/python-3.11.14-h0159041_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.15.2-py311h99d06ae_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda @@ -5025,6 +5043,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python-3.11.14-hd63d673_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda @@ -5076,6 +5095,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-64/python-3.11.14-h74c2667_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py311h556693a_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda @@ -5127,6 +5147,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.11.14-h18782d2_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py311he9931d0_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda @@ -5176,6 +5197,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda - conda: https://prefix.dev/conda-forge/win-64/python-3.11.14-h0159041_3_cpython.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.11-8_cp311.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py311h9c22a71_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda @@ -5241,6 +5263,7 @@ environments: - conda: https://prefix.dev/conda-forge/linux-64/python-3.14.3-h32b2ec7_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/linux-64/readline-8.3-h853b02a_0.conda + - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda @@ -5293,6 +5316,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-64/python-3.14.3-h4f44bb5_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/osx-64/readline-8.3-h68b038d_0.conda + - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda @@ -5346,6 +5370,7 @@ environments: - conda: https://prefix.dev/conda-forge/osx-arm64/python-3.14.3-h4c637c5_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://prefix.dev/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda + - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py314hfc1f868_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda @@ -5397,6 +5422,7 @@ environments: - conda: https://prefix.dev/conda-forge/noarch/pytest-cov-7.0.0-pyhcf101f3_1.conda - conda: https://prefix.dev/conda-forge/win-64/python-3.14.3-h4b44e0e_101_cp314.conda - conda: https://prefix.dev/conda-forge/noarch/python_abi-3.14-8_cp314.conda + - conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py314h221f224_0.conda - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda - conda: https://prefix.dev/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda - conda: https://prefix.dev/conda-forge/win-64/tbb-2022.3.0-h3155e25_2.conda @@ -13138,6 +13164,29 @@ packages: - pkg:pypi/ruff?source=hash-mapping size: 9699892 timestamp: 1771570027913 +- conda: https://prefix.dev/conda-forge/linux-64/scipy-1.15.2-py311h8f841c2_0.conda + sha256: 6d0902775e3ff96dd1d36ac627e03fe6c0b3d2159bb71e115dd16a1f31693b25 + md5: 5ec0a1732a05376241e1e4c6d50e0e91 + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=13 + - libgfortran + - libgfortran5 >=13.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=13 + - numpy <2.5 + - numpy >=1.19,<3 + - numpy >=1.23.5 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 17193126 + timestamp: 1739791897768 - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.16.3-py311hbe70eeb_2.conda sha256: a13084f1556674ea74de2ecbe50333d938dab8ef27f536408592ba312363c400 md5: 1f9587850322d7d77ea14d4fee3d16d8 @@ -13206,6 +13255,29 @@ packages: - pkg:pypi/scipy?source=compressed-mapping size: 17099824 timestamp: 1771880736635 +- conda: https://prefix.dev/conda-forge/linux-64/scipy-1.17.1-py313h0dc34c3_0.conda + sha256: 7933e9846630b6e5a720c607d0a5ed2154d5e15a21077240f1378d7fa4b5b7eb + md5: eb1b9c20267655bf049fb09c155c4d20 + depends: + - __glibc >=2.17,<3.0.a0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libgcc >=14 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - libstdcxx >=14 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313t + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 17175042 + timestamp: 1771880794357 - conda: https://prefix.dev/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_0.conda sha256: 1ae427836d7979779c9005388a05993a3addabcc66c4422694639a4272d7d972 md5: d0510124f87c75403090e220db1e9d41 @@ -13224,10 +13296,33 @@ packages: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause + license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 17225275 timestamp: 1771880751368 +- conda: https://prefix.dev/conda-forge/osx-64/scipy-1.15.2-py311h0c91ca8_0.conda + sha256: 796252d7772df42edd29a45ae70eb18843a7e476d42c96c273cd6e677ec148c8 + md5: 58c17d411ed0cd1220ed3e824a3efc82 + depends: + - __osx >=10.13 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=18 + - libgfortran >=5 + - libgfortran5 >=13.2.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.5 + - numpy >=1.19,<3 + - numpy >=1.23.5 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15759628 + timestamp: 1739792317052 - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py311h556693a_0.conda sha256: 88c4a0f534b9ca58aea2341a5aff706e66582f09a46425512f088aaaecbca938 md5: 1f0877b391f47bae72e67b066337c9e5 @@ -13249,6 +13344,28 @@ packages: - pkg:pypi/scipy?source=compressed-mapping size: 15306181 timestamp: 1771880807305 +- conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py313habc7bd5_0.conda + sha256: 8abeb45a97e8300e39846e931b7d4a01ad8c85ffa3a3f4a319397875fad3914a + md5: 1d5fedd4e2126df7ba1e8182ba74e72a + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313t + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15627768 + timestamp: 1771881574707 - conda: https://prefix.dev/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_0.conda sha256: 115267259f529f1539c6ab1098a18ca488fac02542fa9ca657a7dd46bd9ea675 md5: adbed17bd17ac00193e6dce1f1a37781 @@ -13266,10 +13383,34 @@ packages: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause + license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 15400833 timestamp: 1771881194227 +- conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.15.2-py311h0675101_0.conda + sha256: bc3e873e85c55deaaad446c410d9001d12a133c1b48fa2cb0050b4f46f926aa3 + md5: df904770f3fdb6c0265a09cdc22acf54 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=18 + - libgfortran >=5 + - libgfortran5 >=13.2.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.5 + - numpy >=1.19,<3 + - numpy >=1.23.5 + - python >=3.11,<3.12.0a0 + - python >=3.11,<3.12.0a0 *_cpython + - python_abi 3.11.* *_cp311 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 14569129 + timestamp: 1739792318601 - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py311he9931d0_0.conda sha256: f212138a7eb86cf8bcdb46f003ae552967cd7e48d2c539f7f8dd6e800c74b70d md5: 40606bef4834281d5f746626338829dc @@ -13292,6 +13433,29 @@ packages: - pkg:pypi/scipy?source=compressed-mapping size: 14107248 timestamp: 1771880859933 +- conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py313hf3ca9c9_0.conda + sha256: 683b1d029ce027a9ca059898073d3c450fb049cab2104841cacfc522db33109e + md5: 0708282b2709afd240f7d5b8db8c6135 + depends: + - __osx >=11.0 + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - libcxx >=19 + - libgfortran + - libgfortran5 >=14.3.0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313t + - python_abi 3.13.* *_cp313t + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 14231641 + timestamp: 1771880960734 - conda: https://prefix.dev/conda-forge/osx-arm64/scipy-1.17.1-py314hfc1f868_0.conda sha256: 6ca2abcaff2cd071aabaabd82b10a87fc7de3a4619f6c98820cc28e90cc2cb20 md5: 7806ce54b78b0b11517b465a3398e910 @@ -13310,10 +13474,137 @@ packages: - python >=3.14,<3.15.0a0 *_cp314 - python_abi 3.14.* *_cp314 license: BSD-3-Clause + license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 13986990 timestamp: 1771881110844 +- conda: https://prefix.dev/conda-forge/win-64/scipy-1.15.2-py311h99d06ae_0.conda + sha256: 62ae1a1e02c919513213351474d1c72480fb70388a345fa81f1c95fa822d98bf + md5: c7ec15b5ea6a27bb71af2ea5f7c97cbb + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.5 + - numpy >=1.19,<3 + - numpy >=1.23.5 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - ucrt >=10.0.20348.0 + - vc >=14.2,<15 + - vc14_runtime >=14.29.30139 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15487645 + timestamp: 1739793313482 +- conda: https://prefix.dev/conda-forge/win-64/scipy-1.16.3-py311h9c22a71_2.conda + sha256: 49129601dc89d49742d342ace70f4ec0127a5eb24a50d66f95f91db01b3a23d5 + md5: 4b663de0f0c8ac0fbb4a4d9ee8536b0f + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.6 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15129579 + timestamp: 1766109708812 +- conda: https://prefix.dev/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda + sha256: 99d6198dc05171610073083c9d218d2a9adfa756659b391183d21cca55f888f1 + md5: b600c47282ee91e492b89f65708a5c9a + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.6 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15082636 + timestamp: 1766109482825 +- conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py311h9c22a71_0.conda + sha256: f8b0eed844e466434f115e5cd7da71b59e6939c08fcf42ef52fa5b3f84b20c94 + md5: 24047b0ff1fa264427c456e4c7f68283 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.11,<3.12.0a0 + - python_abi 3.11.* *_cp311 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=compressed-mapping + size: 15280874 + timestamp: 1771881523220 +- conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py313hff732fb_0.conda + sha256: 7b351554b9256468ebcd0233c5ec6c74a3384a4d1298399a48c048037a56ff6c + md5: cf669ec96a5d028c8fa2bd1620ad50cb + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313t + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=hash-mapping + size: 15201678 + timestamp: 1771881882965 +- conda: https://prefix.dev/conda-forge/win-64/scipy-1.17.1-py314h221f224_0.conda + sha256: d9a7b6d3a306195eef4db814614a74746aae4b63e570f6db15769bd28d19a957 + md5: cfcd38938ee0137f4bf0ca824dfb0887 + depends: + - libblas >=3.9.0,<4.0a0 + - libcblas >=3.9.0,<4.0a0 + - liblapack >=3.9.0,<4.0a0 + - numpy <2.7 + - numpy >=1.23,<3 + - numpy >=1.25.2 + - python >=3.14,<3.15.0a0 + - python_abi 3.14.* *_cp314 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/scipy?source=compressed-mapping + size: 14970549 + timestamp: 1771881565717 - conda: https://prefix.dev/conda-forge/noarch/setuptools-82.0.0-pyh332efcf_0.conda sha256: fd7201e38e38bf7f25818d624ca8da97b8998957ca9ae3fb7fdc9c17e6b25fcd md5: 1d00d46c634177fc8ede8b99d6089239 diff --git a/pyproject.toml b/pyproject.toml index 15e4d8c8..1bd40c6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -134,6 +134,7 @@ pytest-cov = ">=7.0.0" hypothesis = ">=6.151.2" array-api-strict = ">=2.4.1,<2.5" numpy = ">=1.22.0" +scipy = ">=1.15.2,<2" [tool.pixi.feature.tests.tasks] tests = { cmd = "pytest -v", description = "Run tests" } diff --git a/src/array_api_extra/testing.py b/src/array_api_extra/testing.py index d40fea1a..9f2b0d38 100644 --- a/src/array_api_extra/testing.py +++ b/src/array_api_extra/testing.py @@ -10,8 +10,9 @@ import enum import warnings from collections.abc import Callable, Generator, Iterator, Sequence -from functools import wraps -from types import ModuleType +from functools import update_wrapper, wraps +from inspect import getattr_static +from types import FunctionType, ModuleType from typing import TYPE_CHECKING, Any, ParamSpec, TypeVar, cast from ._lib._utils._compat import is_dask_namespace, is_jax_namespace @@ -48,8 +49,21 @@ class Deprecated(enum.Enum): DEPRECATED = Deprecated.DEPRECATED +def _clone_function(f: Callable[..., Any]) -> Callable[..., Any]: + """Returns a clone of an existing function.""" + f_new = FunctionType( + f.__code__, + f.__globals__, + name=f.__name__, + argdefs=f.__defaults__, + closure=f.__closure__, + ) + f_new.__kwdefaults__ = f.__kwdefaults__ + return update_wrapper(f_new, f) + + def lazy_xp_function( - func: Callable[..., Any], + func: Callable[..., Any] | tuple[type, str], *, allow_dask_compute: bool | int = False, jax_jit: bool = True, @@ -69,8 +83,9 @@ def lazy_xp_function( Parameters ---------- - func : callable - Function to be tested. + func : callable | tuple[type, str] + Function to be tested, or a tuple containing an (uninstantiated) class and a + method name to specify a class method to be tested. allow_dask_compute : bool | int, optional Whether `func` is allowed to internally materialize the Dask graph, or maximum number of times it is allowed to do so. This is typically triggered by @@ -204,15 +219,49 @@ def test_myfunc(xp): DeprecationWarning, stacklevel=2, ) - tags = { + tags: dict[str, bool | int | type] = { "allow_dask_compute": allow_dask_compute, "jax_jit": jax_jit, } + if isinstance(func, tuple): + # Replace the method with a clone before adding tags + # to avoid adding unwanted tags to a parent method when + # the method was inherited from a parent class. + # Note: can't just accept an unbound method `cls.method_name` because in + # case of inheritance it would be impossible to attribute it to the child class. + # This also makes it so tagged methods will appear in their class's ``__dict__`` + # and thus findable by ``iter_tagged_modules`` below. + cls, method_name = func + # The method might be a staticmethod or classmethod so we need to do a dance + # to ensure that this is preserved. + raw_attr = getattr_static(cls, method_name) + method = getattr(cls, method_name) + if isinstance(raw_attr, classmethod): + method = method.__func__ + cloned_method = _clone_function(method) + + method_to_set: Any + if isinstance(raw_attr, staticmethod): + method_to_set = staticmethod(cloned_method) + elif isinstance(raw_attr, classmethod): + method_to_set = classmethod(cloned_method) + else: + method_to_set = cloned_method + + setattr(cls, method_name, method_to_set) + f = getattr(cls, method_name) + if isinstance(raw_attr, classmethod): + f = f.__func__ + # Annotate that cls owns this method so we can check that later. + tags["owner"] = cls + else: + f = func + try: - func._lazy_xp_function = tags # type: ignore[attr-defined] # pylint: disable=protected-access # pyright: ignore[reportFunctionMemberAccess] + f._lazy_xp_function = tags # pylint: disable=protected-access # pyright: ignore[reportFunctionMemberAccess] except AttributeError: # @cython.vectorize - _ufuncs_tags[func] = tags + _ufuncs_tags[f] = tags def patch_lazy_xp_functions( @@ -224,10 +273,11 @@ def patch_lazy_xp_functions( """ Test lazy execution of functions tagged with :func:`lazy_xp_function`. - If ``xp==jax.numpy``, search for all functions which have been tagged with - :func:`lazy_xp_function` in the globals of the module that defines the current test, - as well as in the ``lazy_xp_modules`` list in the globals of the same module, - and wrap them with :func:`jax.jit`. Unwrap them at the end of the test. + If ``xp==jax.numpy``, search for all functions and methods which have been tagged + with :func:`lazy_xp_function` in the globals of the module that defines the current + test, as well as in the ``lazy_xp_modules`` list in the globals of the same module, + and wrap them with :func:`jax.jit`. + Unwrap them at the end of the test. If ``xp==dask.array``, wrap the functions with a decorator that disables ``compute()`` and ``persist()`` and ensures that exceptions and warnings are raised @@ -271,18 +321,34 @@ def xp(request): the example above. """ mod = cast(ModuleType, request.module) - mods = [mod, *cast(list[ModuleType], getattr(mod, "lazy_xp_modules", []))] - - to_revert: list[tuple[ModuleType, str, object]] = [] - - def temp_setattr(mod: ModuleType, name: str, func: object) -> None: + search_targets: list[ModuleType | type] = [ + mod, + *cast(list[ModuleType], getattr(mod, "lazy_xp_modules", [])), + ] + # Also search for classes within the above modules which have had lazy_xp_function + # applied to methods through ``lazy_xp_function((cls, method_name))`` syntax. + # We might end up adding classes incidentally imported into modules, so using a + # set here to cut down on potential redundancy. + classes: set[type] = set() + for target in search_targets: + for obj in target.__dict__.values(): + if isinstance(obj, type): + classes.add(obj) + search_targets.extend(classes) + + to_revert: list[tuple[ModuleType | type, str, object]] = [] + + def temp_setattr(target: ModuleType | type, name: str, func: object) -> None: """ Variant of monkeypatch.setattr, which allows monkey-patching only selected parameters of a test so that pytest-run-parallel can run on the remainder. """ - assert hasattr(mod, name) - to_revert.append((mod, name, getattr(mod, name))) - setattr(mod, name, func) + assert hasattr(target, name) + # Need getattr_static because the attr could be a staticmethod or other + # descriptor and we don't want that to be stripped away. + original = getattr_static(target, name) + to_revert.append((target, name, original)) + setattr(target, name, func) if monkeypatch is not None: warnings.warn( @@ -298,10 +364,19 @@ def temp_setattr(mod: ModuleType, name: str, func: object) -> None: temp_setattr = monkeypatch.setattr # type: ignore[assignment] # pyright: ignore[reportAssignmentType] def iter_tagged() -> Iterator[ - tuple[ModuleType, str, Callable[..., Any], dict[str, Any]] + tuple[ModuleType | type, str, Any, Callable[..., Any], dict[str, Any]] ]: - for mod in mods: - for name, func in mod.__dict__.items(): + for target in search_targets: + for name, attr in target.__dict__.items(): + # attr might be a staticmethod or classmethod. If so we need + # to peel it back and wrap the underlying function and later + # make sure not to accidentally replace it with a regular + # method. + func: Any = ( + attr.__func__ + if isinstance(attr, (staticmethod, classmethod)) + else attr + ) tags: dict[str, Any] | None = None with contextlib.suppress(AttributeError): tags = func._lazy_xp_function # pylint: disable=protected-access @@ -309,23 +384,49 @@ def iter_tagged() -> Iterator[ with contextlib.suppress(KeyError, TypeError): tags = _ufuncs_tags[func] if tags is not None: - yield mod, name, func, tags - + if isinstance(target, type) and tags.get("owner") is not target: + # There's a common pattern to wrap functions in namespace + # classes to bypass lazy_xp_function like this: + # + # class naked: + # myfunc = mymodule.myfunc + # + # To ensure this still works when checking for tags in + # attributes of classes, ensure that target is the actual + # owning class where func was defined. + continue + # put attr, and func in the outputs so we can later tell + # if this was a staticmethod or classmethod. + yield target, name, attr, func, tags + + wrapped: Any if is_dask_namespace(xp): - for mod, name, func, tags in iter_tagged(): + for target, name, attr, func, tags in iter_tagged(): n = tags["allow_dask_compute"] if n is True: n = 1_000_000 elif n is False: n = 0 wrapped = _dask_wrap(func, n) - temp_setattr(mod, name, wrapped) + # If we're dealing with a staticmethod or classmethod, make + # sure things stay that way. + if isinstance(attr, staticmethod): + wrapped = staticmethod(wrapped) + elif isinstance(attr, classmethod): + wrapped = classmethod(wrapped) + temp_setattr(target, name, wrapped) elif is_jax_namespace(xp): - for mod, name, func, tags in iter_tagged(): + for target, name, attr, func, tags in iter_tagged(): if tags["jax_jit"]: wrapped = jax_autojit(func) - temp_setattr(mod, name, wrapped) + # If we're dealing with a staticmethod or classmethod, make + # sure things stay that way. + if isinstance(attr, staticmethod): + wrapped = staticmethod(wrapped) + elif isinstance(attr, classmethod): + wrapped = classmethod(wrapped) + temp_setattr(target, name, wrapped) # We can't just decorate patch_lazy_xp_functions with # @contextlib.contextmanager because it would not work with the @@ -335,8 +436,8 @@ def revert_on_exit() -> Generator[None]: try: yield finally: - for mod, name, orig_func in to_revert: - setattr(mod, name, orig_func) + for target, name, orig_func in to_revert: + setattr(target, name, orig_func) return revert_on_exit() diff --git a/tests/test_testing.py b/tests/test_testing.py index 7e72ffbf..b70de734 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -1,9 +1,10 @@ from collections.abc import Callable, Iterator from types import ModuleType -from typing import cast +from typing import Any, cast, final import numpy as np import pytest +from typing_extensions import override from array_api_extra._lib._backends import Backend from array_api_extra._lib._testing import ( @@ -321,6 +322,128 @@ def test_lazy_xp_function_cython_ufuncs(xp: ModuleType, library: Backend): xp_assert_equal(cast(Array, erf(x)), xp.asarray([1.0, 1.0])) +class A: + def __init__(self, x: Array): + xp = array_namespace(x) + self._xp: ModuleType = xp + self.x: Any = np.asarray(x) + + def f(self, y: Array) -> Array: + return self._xp.asarray(np.matmul(self.x, np.asarray(y))) + + def g(self, y: Array, z: Array) -> Array: + return self.f(y) + self.f(z) + + def h(self, y: Array) -> bool: + return bool(self._xp.any(y)) + + +class B(A): + @override + def __init__(self, x: Array): # pyright: ignore[reportMissingSuperCall] + xp = array_namespace(x) + self._xp: ModuleType = xp + self.x: Any = xp.asarray(x) + + @override + def f(self, y: Array) -> Array: + return self._xp.matmul(self.x, y) + + @staticmethod + def k(y: Array) -> "B": + return B(2.0 * y) + + @staticmethod + def j(y: Array) -> "B": + xp = array_namespace(y) + y = xp.asarray(y) + if bool(xp.any(y)): + return B(y) + return B(y + 1.0) + + @classmethod + def w(cls, y: Array) -> "B": + xp = array_namespace(y) + y = xp.asarray(y) + if bool(xp.any(y)): + return B(y) + return B(y + 1.0) + + +@final +class eager: + # this needs to be a staticmethod to appease the type checker + non_materializable5 = staticmethod(non_materializable5) + + +lazy_xp_function((B, "g")) +lazy_xp_function((B, "h")) +lazy_xp_function((B, "k")) +lazy_xp_function((B, "j")) +lazy_xp_function((B, "w")) + + +class TestLazyXpFunctionClasses: + def test_parent_method_not_tagged(self): + assert hasattr(B.g, "_lazy_xp_function") + assert not hasattr(A.g, "_lazy_xp_function") + + @pytest.mark.skip_xp_backend(Backend.SPARSE, reason="converts to NumPy") + @pytest.mark.skip_xp_backend(Backend.CUPY, reason="converts to NumPy") + @pytest.mark.skip_xp_backend(Backend.JAX_GPU, reason="converts to NumPy") + @pytest.mark.skip_xp_backend(Backend.TORCH_GPU, reason="converts to NumPy") + def test_lazy_xp_function_classes(self, xp: ModuleType, library: Backend): + x = xp.asarray([1.1, 2.2, 3.3]) + y = xp.asarray([1.0, 2.0, 3.0]) + foo = A(x) + bar = B(x) + + if library.like(Backend.JAX): + with pytest.raises( + TypeError, match="Attempted boolean conversion of traced array" + ): + assert bar.h(y) + + assert foo.h(y) + + def test_static_methods_preserved(self, xp: ModuleType): + # Tests that static methods stay static methods when + # lazy_xp_function is applied. + x = xp.asarray([1.1, 2.2, 3.3]) + foo = B(x) + bar = foo.k(x) + xp_assert_equal(bar.x, 2.0 * foo.x) + + @pytest.mark.skip_xp_backend(Backend.DASK, reason="calls dask.compute()") + def test_static_methods_wrapped(self, xp: ModuleType, library: Backend): + x = xp.asarray([1.1, 2.2, 3.3]) + foo = B(x) + + if library.like(Backend.JAX): + with pytest.raises( + TypeError, match="Attempted boolean conversion of traced array" + ): + assert isinstance(foo.j(x), B) + else: + assert isinstance(foo.j(x), B) + + @pytest.mark.skip_xp_backend(Backend.DASK, reason="calls dask.compute()") + def test_class_methods_wrapped(self, xp: ModuleType, library: Backend): + x = xp.asarray([1.1, 2.2, 3.3]) + if library.like(Backend.JAX): + with pytest.raises( + TypeError, match="Attempted boolean conversion of traced array" + ): + assert isinstance(B.w(x), B) + else: + assert isinstance(B.w(x), B) + + def test_circumvention(self, xp: ModuleType): + x = xp.asarray([1.0, 2.0]) + y = eager.non_materializable5(x) + xp_assert_equal(y, x) + + def dask_raises(x: Array) -> Array: def _raises(x: Array) -> Array: # Test that map_blocks doesn't eagerly call the function;