scipy 1.15.3__cp312-cp312-musllinux_1_2_aarch64.whl → 1.16.0rc2__cp312-cp312-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scipy/__config__.py +10 -10
- scipy/__init__.py +3 -6
- scipy/_cyutility.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_array_api.py +486 -161
- scipy/_lib/_array_api_compat_vendor.py +9 -0
- scipy/_lib/_bunch.py +4 -0
- scipy/_lib/_ccallback_c.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_docscrape.py +1 -1
- scipy/_lib/_elementwise_iterative_method.py +15 -26
- scipy/_lib/_fpumode.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_ccallback.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_call.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_def.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_testutils.py +6 -2
- scipy/_lib/_uarray/_uarray.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/_util.py +222 -125
- scipy/_lib/array_api_compat/__init__.py +4 -4
- scipy/_lib/array_api_compat/_internal.py +19 -6
- scipy/_lib/array_api_compat/common/__init__.py +1 -1
- scipy/_lib/array_api_compat/common/_aliases.py +365 -193
- scipy/_lib/array_api_compat/common/_fft.py +94 -64
- scipy/_lib/array_api_compat/common/_helpers.py +413 -180
- scipy/_lib/array_api_compat/common/_linalg.py +116 -40
- scipy/_lib/array_api_compat/common/_typing.py +179 -10
- scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
- scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
- scipy/_lib/array_api_compat/cupy/_info.py +16 -6
- scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
- scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
- scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
- scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
- scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
- scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
- scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
- scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
- scipy/_lib/array_api_compat/numpy/_info.py +36 -16
- scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
- scipy/_lib/array_api_compat/numpy/fft.py +11 -5
- scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
- scipy/_lib/array_api_compat/torch/__init__.py +3 -5
- scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
- scipy/_lib/array_api_compat/torch/_info.py +27 -16
- scipy/_lib/array_api_compat/torch/_typing.py +3 -0
- scipy/_lib/array_api_compat/torch/fft.py +17 -18
- scipy/_lib/array_api_compat/torch/linalg.py +16 -16
- scipy/_lib/array_api_extra/__init__.py +26 -3
- scipy/_lib/array_api_extra/_delegation.py +171 -0
- scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_at.py +463 -0
- scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
- scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
- scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
- scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
- scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
- scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
- scipy/_lib/array_api_extra/testing.py +359 -0
- scipy/_lib/decorator.py +2 -2
- scipy/_lib/doccer.py +1 -7
- scipy/_lib/messagestream.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/_lib/pyprima/__init__.py +212 -0
- scipy/_lib/pyprima/cobyla/__init__.py +0 -0
- scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
- scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
- scipy/_lib/pyprima/cobyla/geometry.py +226 -0
- scipy/_lib/pyprima/cobyla/initialize.py +215 -0
- scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
- scipy/_lib/pyprima/cobyla/update.py +289 -0
- scipy/_lib/pyprima/common/__init__.py +0 -0
- scipy/_lib/pyprima/common/_bounds.py +34 -0
- scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
- scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
- scipy/_lib/pyprima/common/_project.py +173 -0
- scipy/_lib/pyprima/common/checkbreak.py +93 -0
- scipy/_lib/pyprima/common/consts.py +47 -0
- scipy/_lib/pyprima/common/evaluate.py +99 -0
- scipy/_lib/pyprima/common/history.py +38 -0
- scipy/_lib/pyprima/common/infos.py +30 -0
- scipy/_lib/pyprima/common/linalg.py +435 -0
- scipy/_lib/pyprima/common/message.py +290 -0
- scipy/_lib/pyprima/common/powalg.py +131 -0
- scipy/_lib/pyprima/common/preproc.py +277 -0
- scipy/_lib/pyprima/common/present.py +5 -0
- scipy/_lib/pyprima/common/ratio.py +54 -0
- scipy/_lib/pyprima/common/redrho.py +47 -0
- scipy/_lib/pyprima/common/selectx.py +296 -0
- scipy/_lib/tests/test__util.py +105 -121
- scipy/_lib/tests/test_array_api.py +166 -35
- scipy/_lib/tests/test_bunch.py +7 -0
- scipy/_lib/tests/test_ccallback.py +2 -10
- scipy/_lib/tests/test_public_api.py +13 -0
- scipy/cluster/_hierarchy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/_optimal_leaf_ordering.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/_vq.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/cluster/hierarchy.py +393 -223
- scipy/cluster/tests/test_hierarchy.py +273 -335
- scipy/cluster/tests/test_vq.py +45 -61
- scipy/cluster/vq.py +39 -35
- scipy/conftest.py +263 -157
- scipy/constants/_constants.py +4 -1
- scipy/constants/tests/test_codata.py +2 -2
- scipy/constants/tests/test_constants.py +11 -18
- scipy/datasets/_download_all.py +15 -1
- scipy/datasets/_fetchers.py +7 -1
- scipy/datasets/_utils.py +1 -1
- scipy/differentiate/_differentiate.py +25 -25
- scipy/differentiate/tests/test_differentiate.py +24 -25
- scipy/fft/_basic.py +20 -0
- scipy/fft/_helper.py +3 -34
- scipy/fft/_pocketfft/helper.py +29 -1
- scipy/fft/_pocketfft/pypocketfft.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/fft/_pocketfft/tests/test_basic.py +2 -4
- scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
- scipy/fft/_realtransforms.py +13 -0
- scipy/fft/tests/test_basic.py +27 -25
- scipy/fft/tests/test_fftlog.py +16 -7
- scipy/fft/tests/test_helper.py +18 -34
- scipy/fft/tests/test_real_transforms.py +8 -10
- scipy/fftpack/convolve.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/fftpack/tests/test_basic.py +2 -4
- scipy/fftpack/tests/test_real_transforms.py +8 -9
- scipy/integrate/_bvp.py +9 -3
- scipy/integrate/_cubature.py +3 -2
- scipy/integrate/_dop.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_lsoda.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_ode.py +9 -2
- scipy/integrate/_odepack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quad_vec.py +21 -29
- scipy/integrate/_quadpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quadpack_py.py +11 -7
- scipy/integrate/_quadrature.py +3 -3
- scipy/integrate/_rules/_base.py +2 -2
- scipy/integrate/_tanhsinh.py +48 -47
- scipy/integrate/_test_multivariate.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_test_odeint_banded.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/_vode.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/integrate/tests/test__quad_vec.py +0 -6
- scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
- scipy/integrate/tests/test_cubature.py +21 -35
- scipy/integrate/tests/test_quadrature.py +6 -8
- scipy/integrate/tests/test_tanhsinh.py +56 -48
- scipy/interpolate/__init__.py +70 -58
- scipy/interpolate/_bary_rational.py +22 -22
- scipy/interpolate/_bsplines.py +119 -66
- scipy/interpolate/_cubic.py +65 -50
- scipy/interpolate/_dfitpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_dierckx.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack2.py +9 -6
- scipy/interpolate/_fitpack_impl.py +32 -26
- scipy/interpolate/_fitpack_repro.py +23 -19
- scipy/interpolate/_interpnd.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_interpolate.py +30 -12
- scipy/interpolate/_ndbspline.py +13 -18
- scipy/interpolate/_ndgriddata.py +5 -8
- scipy/interpolate/_polyint.py +95 -31
- scipy/interpolate/_ppoly.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rbf.py +2 -2
- scipy/interpolate/_rbfinterp.py +1 -1
- scipy/interpolate/_rbfinterp_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rgi.py +31 -26
- scipy/interpolate/_rgi_cython.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/interpolate/dfitpack.py +0 -20
- scipy/interpolate/interpnd.py +1 -2
- scipy/interpolate/tests/test_bary_rational.py +2 -2
- scipy/interpolate/tests/test_bsplines.py +97 -1
- scipy/interpolate/tests/test_fitpack2.py +39 -1
- scipy/interpolate/tests/test_interpnd.py +32 -20
- scipy/interpolate/tests/test_interpolate.py +48 -4
- scipy/interpolate/tests/test_rgi.py +2 -1
- scipy/io/_fast_matrix_market/__init__.py +2 -0
- scipy/io/_fast_matrix_market/_fmm_core.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
- scipy/io/_harwell_boeing/hb.py +7 -11
- scipy/io/_idl.py +5 -7
- scipy/io/_netcdf.py +15 -5
- scipy/io/_test_fortran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/arff/tests/test_arffread.py +3 -3
- scipy/io/matlab/__init__.py +5 -3
- scipy/io/matlab/_mio.py +4 -1
- scipy/io/matlab/_mio5.py +19 -13
- scipy/io/matlab/_mio5_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_mio_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_miobase.py +4 -1
- scipy/io/matlab/_streams.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/tests/test_mio.py +46 -18
- scipy/io/matlab/tests/test_mio_funcs.py +1 -1
- scipy/io/tests/test_mmio.py +7 -1
- scipy/io/tests/test_wavfile.py +41 -0
- scipy/io/wavfile.py +57 -10
- scipy/linalg/_basic.py +113 -86
- scipy/linalg/_cythonized_array_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp.py +22 -9
- scipy/linalg/_decomp_cholesky.py +28 -13
- scipy/linalg/_decomp_cossin.py +45 -30
- scipy/linalg/_decomp_interpolative.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_ldl.py +4 -1
- scipy/linalg/_decomp_lu.py +18 -6
- scipy/linalg/_decomp_lu_cython.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_polar.py +2 -0
- scipy/linalg/_decomp_qr.py +6 -2
- scipy/linalg/_decomp_qz.py +3 -0
- scipy/linalg/_decomp_schur.py +3 -1
- scipy/linalg/_decomp_svd.py +13 -2
- scipy/linalg/_decomp_update.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_expm_frechet.py +4 -0
- scipy/linalg/_fblas.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_flapack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_linalg_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs.py +187 -4
- scipy/linalg/_matfuncs_expm.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +1 -99
- scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_procrustes.py +2 -0
- scipy/linalg/_sketches.py +17 -6
- scipy/linalg/_solve_toeplitz.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/_solvers.py +7 -2
- scipy/linalg/_special_matrices.py +26 -36
- scipy/linalg/cython_blas.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/cython_lapack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/linalg/lapack.py +22 -2
- scipy/linalg/tests/_cython_examples/meson.build +7 -0
- scipy/linalg/tests/test_basic.py +31 -16
- scipy/linalg/tests/test_batch.py +588 -0
- scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
- scipy/linalg/tests/test_decomp.py +40 -3
- scipy/linalg/tests/test_decomp_cossin.py +14 -0
- scipy/linalg/tests/test_decomp_ldl.py +1 -1
- scipy/linalg/tests/test_lapack.py +115 -7
- scipy/linalg/tests/test_matfuncs.py +157 -102
- scipy/linalg/tests/test_procrustes.py +0 -7
- scipy/linalg/tests/test_solve_toeplitz.py +1 -1
- scipy/linalg/tests/test_special_matrices.py +1 -5
- scipy/ndimage/__init__.py +1 -0
- scipy/ndimage/_ctest.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_cytest.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_delegators.py +8 -2
- scipy/ndimage/_filters.py +453 -5
- scipy/ndimage/_interpolation.py +36 -6
- scipy/ndimage/_measurements.py +4 -2
- scipy/ndimage/_morphology.py +5 -0
- scipy/ndimage/_nd_image.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_docstrings.py +5 -1
- scipy/ndimage/_ni_label.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_support.py +1 -5
- scipy/ndimage/_rank_filter_1d.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_support_alternative_backends.py +18 -6
- scipy/ndimage/tests/test_filters.py +370 -259
- scipy/ndimage/tests/test_fourier.py +7 -9
- scipy/ndimage/tests/test_interpolation.py +68 -61
- scipy/ndimage/tests/test_measurements.py +18 -35
- scipy/ndimage/tests/test_morphology.py +143 -131
- scipy/ndimage/tests/test_splines.py +1 -3
- scipy/odr/__odrpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_basinhopping.py +13 -7
- scipy/optimize/_bglu_dense.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_bracket.py +17 -24
- scipy/optimize/_chandrupatla.py +9 -10
- scipy/optimize/_cobyla_py.py +104 -123
- scipy/optimize/_constraints.py +14 -10
- scipy/optimize/_differentiable_functions.py +371 -230
- scipy/optimize/_differentialevolution.py +4 -3
- scipy/optimize/_direct.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_dual_annealing.py +1 -1
- scipy/optimize/_elementwise.py +1 -4
- scipy/optimize/_group_columns.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_core.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_highs_options.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb_py.py +57 -16
- scipy/optimize/_linprog_doc.py +2 -2
- scipy/optimize/_linprog_highs.py +2 -2
- scipy/optimize/_linprog_ip.py +25 -10
- scipy/optimize/_linprog_util.py +14 -16
- scipy/optimize/_lsap.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/common.py +3 -3
- scipy/optimize/_lsq/dogbox.py +16 -2
- scipy/optimize/_lsq/givens_elimination.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/least_squares.py +198 -126
- scipy/optimize/_lsq/lsq_linear.py +6 -6
- scipy/optimize/_lsq/trf.py +35 -8
- scipy/optimize/_milp.py +3 -1
- scipy/optimize/_minimize.py +105 -36
- scipy/optimize/_minpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_minpack_py.py +21 -14
- scipy/optimize/_moduleTNC.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_nnls.py +20 -21
- scipy/optimize/_nonlin.py +34 -3
- scipy/optimize/_numdiff.py +288 -110
- scipy/optimize/_optimize.py +86 -48
- scipy/optimize/_pava_pybind.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_remove_redundancy.py +5 -5
- scipy/optimize/_root_scalar.py +1 -1
- scipy/optimize/_shgo.py +6 -0
- scipy/optimize/_shgo_lib/_complex.py +1 -1
- scipy/optimize/_slsqp_py.py +216 -124
- scipy/optimize/_slsqplib.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_spectral.py +1 -1
- scipy/optimize/_tnc.py +8 -1
- scipy/optimize/_trlib/_trlib.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_trustregion.py +20 -6
- scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
- scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
- scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
- scipy/optimize/_trustregion_constr/projections.py +12 -8
- scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
- scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
- scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
- scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
- scipy/optimize/_trustregion_exact.py +0 -1
- scipy/optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_zeros_py.py +97 -17
- scipy/optimize/cython_optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/slsqp.py +0 -1
- scipy/optimize/tests/test__basinhopping.py +1 -1
- scipy/optimize/tests/test__differential_evolution.py +4 -4
- scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
- scipy/optimize/tests/test__numdiff.py +66 -22
- scipy/optimize/tests/test__remove_redundancy.py +2 -2
- scipy/optimize/tests/test__shgo.py +9 -1
- scipy/optimize/tests/test_bracket.py +36 -46
- scipy/optimize/tests/test_chandrupatla.py +133 -135
- scipy/optimize/tests/test_cobyla.py +74 -45
- scipy/optimize/tests/test_constraints.py +1 -1
- scipy/optimize/tests/test_differentiable_functions.py +226 -6
- scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
- scipy/optimize/tests/test_least_squares.py +125 -13
- scipy/optimize/tests/test_linear_assignment.py +3 -3
- scipy/optimize/tests/test_linprog.py +3 -3
- scipy/optimize/tests/test_lsq_linear.py +6 -6
- scipy/optimize/tests/test_minimize_constrained.py +2 -2
- scipy/optimize/tests/test_minpack.py +4 -4
- scipy/optimize/tests/test_nnls.py +43 -3
- scipy/optimize/tests/test_nonlin.py +36 -0
- scipy/optimize/tests/test_optimize.py +95 -17
- scipy/optimize/tests/test_slsqp.py +36 -4
- scipy/optimize/tests/test_zeros.py +34 -1
- scipy/signal/__init__.py +12 -23
- scipy/signal/_delegators.py +568 -0
- scipy/signal/_filter_design.py +459 -241
- scipy/signal/_fir_filter_design.py +262 -90
- scipy/signal/_lti_conversion.py +3 -2
- scipy/signal/_ltisys.py +118 -91
- scipy/signal/_max_len_seq_inner.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_peak_finding_utils.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_polyutils.py +172 -0
- scipy/signal/_short_time_fft.py +519 -70
- scipy/signal/_signal_api.py +30 -0
- scipy/signal/_signaltools.py +719 -399
- scipy/signal/_sigtools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_sosfilt.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_spectral_py.py +230 -50
- scipy/signal/_spline.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_spline_filters.py +108 -68
- scipy/signal/_support_alternative_backends.py +73 -0
- scipy/signal/_upfirdn.py +4 -1
- scipy/signal/_upfirdn_apply.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/signal/_waveforms.py +2 -11
- scipy/signal/_wavelets.py +1 -1
- scipy/signal/fir_filter_design.py +1 -0
- scipy/signal/spline.py +4 -11
- scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
- scipy/signal/tests/test_bsplines.py +114 -79
- scipy/signal/tests/test_cont2discrete.py +9 -2
- scipy/signal/tests/test_filter_design.py +721 -481
- scipy/signal/tests/test_fir_filter_design.py +332 -140
- scipy/signal/tests/test_savitzky_golay.py +4 -3
- scipy/signal/tests/test_short_time_fft.py +221 -3
- scipy/signal/tests/test_signaltools.py +2144 -1348
- scipy/signal/tests/test_spectral.py +50 -6
- scipy/signal/tests/test_splines.py +161 -96
- scipy/signal/tests/test_upfirdn.py +84 -50
- scipy/signal/tests/test_waveforms.py +20 -0
- scipy/signal/tests/test_windows.py +607 -466
- scipy/signal/windows/_windows.py +287 -148
- scipy/sparse/__init__.py +23 -4
- scipy/sparse/_base.py +270 -108
- scipy/sparse/_bsr.py +7 -4
- scipy/sparse/_compressed.py +59 -231
- scipy/sparse/_construct.py +90 -38
- scipy/sparse/_coo.py +115 -181
- scipy/sparse/_csc.py +4 -4
- scipy/sparse/_csparsetools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/_csr.py +2 -2
- scipy/sparse/_data.py +48 -48
- scipy/sparse/_dia.py +105 -18
- scipy/sparse/_dok.py +0 -23
- scipy/sparse/_index.py +4 -4
- scipy/sparse/_matrix.py +23 -0
- scipy/sparse/_sparsetools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/_sputils.py +37 -22
- scipy/sparse/base.py +0 -9
- scipy/sparse/bsr.py +0 -14
- scipy/sparse/compressed.py +0 -23
- scipy/sparse/construct.py +0 -6
- scipy/sparse/coo.py +0 -14
- scipy/sparse/csc.py +0 -3
- scipy/sparse/csgraph/_flow.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_matching.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_reordering.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_shortest_path.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_tools.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_traversal.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/tests/test_matching.py +14 -2
- scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
- scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
- scipy/sparse/csr.py +0 -5
- scipy/sparse/data.py +1 -6
- scipy/sparse/dia.py +0 -7
- scipy/sparse/dok.py +0 -10
- scipy/sparse/linalg/_dsolve/_superlu.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
- scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
- scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
- scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
- scipy/sparse/linalg/_interface.py +17 -18
- scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
- scipy/sparse/linalg/_isolve/iterative.py +51 -45
- scipy/sparse/linalg/_isolve/lgmres.py +6 -6
- scipy/sparse/linalg/_isolve/minres.py +5 -5
- scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
- scipy/sparse/linalg/_isolve/utils.py +2 -8
- scipy/sparse/linalg/_matfuncs.py +1 -1
- scipy/sparse/linalg/_norm.py +1 -1
- scipy/sparse/linalg/_propack/_cpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_spropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
- scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
- scipy/sparse/tests/test_arithmetic1d.py +5 -2
- scipy/sparse/tests/test_base.py +214 -42
- scipy/sparse/tests/test_common1d.py +7 -7
- scipy/sparse/tests/test_construct.py +1 -1
- scipy/sparse/tests/test_coo.py +272 -4
- scipy/sparse/tests/test_sparsetools.py +5 -0
- scipy/sparse/tests/test_sputils.py +36 -7
- scipy/spatial/_ckdtree.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_pybind.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_wrap.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_hausdorff.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_qhull.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/_voronoi.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/distance.py +49 -42
- scipy/spatial/tests/test_distance.py +15 -1
- scipy/spatial/tests/test_kdtree.py +1 -0
- scipy/spatial/tests/test_qhull.py +7 -2
- scipy/spatial/transform/__init__.py +5 -3
- scipy/spatial/transform/_rigid_transform.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/_rotation.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
- scipy/spatial/transform/tests/test_rotation.py +1213 -832
- scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
- scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
- scipy/special/__init__.py +1 -47
- scipy/special/_add_newdocs.py +34 -772
- scipy/special/_basic.py +22 -25
- scipy/special/_comb.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ellip_harm_2.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_gufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_logsumexp.py +67 -58
- scipy/special/_orthogonal.pyi +1 -1
- scipy/special/_specfun.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_special_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_spherical_bessel.py +4 -4
- scipy/special/_support_alternative_backends.py +212 -119
- scipy/special/_test_internal.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_testutils.py +4 -4
- scipy/special/_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs.pyi +1 -0
- scipy/special/_ufuncs.pyx +215 -1400
- scipy/special/_ufuncs_cxx.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs_cxx.pxd +2 -15
- scipy/special/_ufuncs_cxx.pyx +5 -44
- scipy/special/_ufuncs_cxx_defs.h +2 -16
- scipy/special/_ufuncs_defs.h +0 -8
- scipy/special/cython_special.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/special/cython_special.pxd +1 -1
- scipy/special/tests/_cython_examples/meson.build +10 -1
- scipy/special/tests/test_basic.py +153 -20
- scipy/special/tests/test_boost_ufuncs.py +3 -0
- scipy/special/tests/test_cdflib.py +35 -11
- scipy/special/tests/test_gammainc.py +16 -0
- scipy/special/tests/test_hyp2f1.py +2 -2
- scipy/special/tests/test_log1mexp.py +85 -0
- scipy/special/tests/test_logsumexp.py +206 -64
- scipy/special/tests/test_mpmath.py +1 -0
- scipy/special/tests/test_nan_inputs.py +1 -1
- scipy/special/tests/test_orthogonal.py +17 -18
- scipy/special/tests/test_sf_error.py +3 -2
- scipy/special/tests/test_sph_harm.py +6 -7
- scipy/special/tests/test_support_alternative_backends.py +211 -76
- scipy/stats/__init__.py +4 -1
- scipy/stats/_ansari_swilk_statistics.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_axis_nan_policy.py +5 -12
- scipy/stats/_biasedurn.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_continued_fraction.py +387 -0
- scipy/stats/_continuous_distns.py +277 -310
- scipy/stats/_correlation.py +1 -1
- scipy/stats/_covariance.py +6 -3
- scipy/stats/_discrete_distns.py +39 -32
- scipy/stats/_distn_infrastructure.py +39 -12
- scipy/stats/_distribution_infrastructure.py +900 -238
- scipy/stats/_entropy.py +9 -10
- scipy/{_lib → stats}/_finite_differences.py +1 -1
- scipy/stats/_hypotests.py +83 -50
- scipy/stats/_kde.py +53 -49
- scipy/stats/_ksstats.py +1 -1
- scipy/stats/_levy_stable/__init__.py +7 -15
- scipy/stats/_levy_stable/levyst.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_morestats.py +118 -73
- scipy/stats/_mstats_basic.py +13 -17
- scipy/stats/_mstats_extras.py +8 -8
- scipy/stats/_multivariate.py +89 -113
- scipy/stats/_new_distributions.py +97 -20
- scipy/stats/_page_trend_test.py +12 -5
- scipy/stats/_probability_distribution.py +265 -43
- scipy/stats/_qmc.py +14 -9
- scipy/stats/_qmc_cy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_qmvnt.py +16 -95
- scipy/stats/_qmvnt_cy.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/rcont.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_resampling.py +4 -29
- scipy/stats/_sampling.py +1 -1
- scipy/stats/_sobol.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats_mstats_common.py +21 -2
- scipy/stats/_stats_py.py +550 -476
- scipy/stats/_stats_pythran.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
- scipy/stats/_variation.py +6 -8
- scipy/stats/_wilcoxon.py +13 -7
- scipy/stats/tests/common_tests.py +6 -4
- scipy/stats/tests/test_axis_nan_policy.py +62 -24
- scipy/stats/tests/test_continued_fraction.py +173 -0
- scipy/stats/tests/test_continuous.py +379 -60
- scipy/stats/tests/test_continuous_basic.py +18 -12
- scipy/stats/tests/test_discrete_basic.py +14 -8
- scipy/stats/tests/test_discrete_distns.py +16 -16
- scipy/stats/tests/test_distributions.py +95 -75
- scipy/stats/tests/test_entropy.py +40 -48
- scipy/stats/tests/test_fit.py +4 -3
- scipy/stats/tests/test_hypotests.py +153 -24
- scipy/stats/tests/test_kdeoth.py +109 -41
- scipy/stats/tests/test_marray.py +289 -0
- scipy/stats/tests/test_morestats.py +79 -47
- scipy/stats/tests/test_mstats_basic.py +3 -3
- scipy/stats/tests/test_multivariate.py +434 -83
- scipy/stats/tests/test_qmc.py +13 -10
- scipy/stats/tests/test_quantile.py +199 -0
- scipy/stats/tests/test_rank.py +119 -112
- scipy/stats/tests/test_resampling.py +47 -56
- scipy/stats/tests/test_sampling.py +9 -4
- scipy/stats/tests/test_stats.py +799 -939
- scipy/stats/tests/test_variation.py +8 -6
- scipy/version.py +2 -2
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +1262 -1269
- scipy.libs/libgcc_s-69c45f16.so.1 +0 -0
- scipy.libs/libgfortran-db0b6589.so.5.0.0 +0 -0
- scipy.libs/{libstdc++-1b614e01.so.6.0.32 → libstdc++-1f1a71be.so.6.0.33} +0 -0
- scipy/_lib/array_api_extra/_funcs.py +0 -484
- scipy/_lib/array_api_extra/_typing.py +0 -8
- scipy/interpolate/_bspl.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cobyla.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cython_nnls.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/optimize/_slsqp.cpython-312-aarch64-linux-musl.so +0 -0
- scipy/spatial/qhull_src/COPYING.txt +0 -38
- scipy/special/libsf_error_state.so +0 -0
- scipy/special/tests/test_log_softmax.py +0 -109
- scipy/special/tests/test_xsf_cuda.py +0 -114
- scipy/special/xsf/binom.h +0 -89
- scipy/special/xsf/cdflib.h +0 -100
- scipy/special/xsf/cephes/airy.h +0 -307
- scipy/special/xsf/cephes/besselpoly.h +0 -51
- scipy/special/xsf/cephes/beta.h +0 -257
- scipy/special/xsf/cephes/cbrt.h +0 -131
- scipy/special/xsf/cephes/chbevl.h +0 -85
- scipy/special/xsf/cephes/chdtr.h +0 -193
- scipy/special/xsf/cephes/const.h +0 -87
- scipy/special/xsf/cephes/ellie.h +0 -293
- scipy/special/xsf/cephes/ellik.h +0 -251
- scipy/special/xsf/cephes/ellpe.h +0 -107
- scipy/special/xsf/cephes/ellpk.h +0 -117
- scipy/special/xsf/cephes/expn.h +0 -260
- scipy/special/xsf/cephes/gamma.h +0 -398
- scipy/special/xsf/cephes/hyp2f1.h +0 -596
- scipy/special/xsf/cephes/hyperg.h +0 -361
- scipy/special/xsf/cephes/i0.h +0 -149
- scipy/special/xsf/cephes/i1.h +0 -158
- scipy/special/xsf/cephes/igam.h +0 -421
- scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
- scipy/special/xsf/cephes/igami.h +0 -313
- scipy/special/xsf/cephes/j0.h +0 -225
- scipy/special/xsf/cephes/j1.h +0 -198
- scipy/special/xsf/cephes/jv.h +0 -715
- scipy/special/xsf/cephes/k0.h +0 -164
- scipy/special/xsf/cephes/k1.h +0 -163
- scipy/special/xsf/cephes/kn.h +0 -243
- scipy/special/xsf/cephes/lanczos.h +0 -112
- scipy/special/xsf/cephes/ndtr.h +0 -275
- scipy/special/xsf/cephes/poch.h +0 -85
- scipy/special/xsf/cephes/polevl.h +0 -167
- scipy/special/xsf/cephes/psi.h +0 -194
- scipy/special/xsf/cephes/rgamma.h +0 -111
- scipy/special/xsf/cephes/scipy_iv.h +0 -811
- scipy/special/xsf/cephes/shichi.h +0 -248
- scipy/special/xsf/cephes/sici.h +0 -224
- scipy/special/xsf/cephes/sindg.h +0 -221
- scipy/special/xsf/cephes/tandg.h +0 -139
- scipy/special/xsf/cephes/trig.h +0 -58
- scipy/special/xsf/cephes/unity.h +0 -186
- scipy/special/xsf/cephes/zeta.h +0 -172
- scipy/special/xsf/config.h +0 -304
- scipy/special/xsf/digamma.h +0 -205
- scipy/special/xsf/error.h +0 -57
- scipy/special/xsf/evalpoly.h +0 -47
- scipy/special/xsf/expint.h +0 -266
- scipy/special/xsf/hyp2f1.h +0 -694
- scipy/special/xsf/iv_ratio.h +0 -173
- scipy/special/xsf/lambertw.h +0 -150
- scipy/special/xsf/loggamma.h +0 -163
- scipy/special/xsf/sici.h +0 -200
- scipy/special/xsf/tools.h +0 -427
- scipy/special/xsf/trig.h +0 -164
- scipy/special/xsf/wright_bessel.h +0 -843
- scipy/special/xsf/zlog1.h +0 -35
- scipy/stats/_mvn.cpython-312-aarch64-linux-musl.so +0 -0
- scipy.libs/libgcc_s-7393e603.so.1 +0 -0
- scipy.libs/libgfortran-eb933d8e.so.5.0.0 +0 -0
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
scipy/_lib/_array_api.py
CHANGED
@@ -6,8 +6,15 @@ https://data-apis.org/array-api/latest/purpose_and_scope.html
|
|
6
6
|
The SciPy use case of the Array API is described on the following page:
|
7
7
|
https://data-apis.org/array-api/latest/use_cases.html#use-case-scipy
|
8
8
|
"""
|
9
|
+
import contextlib
|
10
|
+
import dataclasses
|
11
|
+
import functools
|
9
12
|
import os
|
13
|
+
import textwrap
|
10
14
|
|
15
|
+
from collections.abc import Generator, Iterable, Iterator
|
16
|
+
from contextlib import contextmanager
|
17
|
+
from contextvars import ContextVar
|
11
18
|
from types import ModuleType
|
12
19
|
from typing import Any, Literal, TypeAlias
|
13
20
|
|
@@ -17,6 +24,7 @@ import numpy.typing as npt
|
|
17
24
|
from scipy._lib import array_api_compat
|
18
25
|
from scipy._lib.array_api_compat import (
|
19
26
|
is_array_api_obj,
|
27
|
+
is_lazy_array,
|
20
28
|
size as xp_size,
|
21
29
|
numpy as np_compat,
|
22
30
|
device as xp_device,
|
@@ -24,18 +32,21 @@ from scipy._lib.array_api_compat import (
|
|
24
32
|
is_cupy_namespace as is_cupy,
|
25
33
|
is_torch_namespace as is_torch,
|
26
34
|
is_jax_namespace as is_jax,
|
35
|
+
is_dask_namespace as is_dask,
|
27
36
|
is_array_api_strict_namespace as is_array_api_strict
|
28
37
|
)
|
38
|
+
from scipy._lib._sparse import issparse
|
39
|
+
from scipy._lib._docscrape import FunctionDoc
|
29
40
|
|
30
41
|
__all__ = [
|
31
42
|
'_asarray', 'array_namespace', 'assert_almost_equal', 'assert_array_almost_equal',
|
32
|
-
'
|
33
|
-
'is_array_api_strict', 'is_complex', 'is_cupy', 'is_jax', 'is_numpy', 'is_torch',
|
43
|
+
'default_xp', 'eager_warns', 'is_lazy_array', 'is_marray',
|
44
|
+
'is_array_api_strict', 'is_complex', 'is_cupy', 'is_jax', 'is_numpy', 'is_torch',
|
34
45
|
'SCIPY_ARRAY_API', 'SCIPY_DEVICE', 'scipy_namespace_for',
|
35
46
|
'xp_assert_close', 'xp_assert_equal', 'xp_assert_less',
|
36
|
-
'xp_copy', '
|
37
|
-
'
|
38
|
-
'
|
47
|
+
'xp_copy', 'xp_device', 'xp_ravel', 'xp_size',
|
48
|
+
'xp_unsupported_param_msg', 'xp_vector_norm', 'xp_capabilities',
|
49
|
+
'xp_result_type', 'xp_promote'
|
39
50
|
]
|
40
51
|
|
41
52
|
|
@@ -54,8 +65,9 @@ Array: TypeAlias = Any # To be changed to a Protocol later (see array-api#589)
|
|
54
65
|
ArrayLike: TypeAlias = Array | npt.ArrayLike
|
55
66
|
|
56
67
|
|
57
|
-
def _compliance_scipy(arrays):
|
58
|
-
"""Raise exceptions on known-bad subclasses.
|
68
|
+
def _compliance_scipy(arrays: Iterable[ArrayLike]) -> Iterator[Array]:
|
69
|
+
"""Raise exceptions on known-bad subclasses. Discard 0-dimensional ArrayLikes
|
70
|
+
and convert 1+-dimensional ArrayLikes to numpy.
|
59
71
|
|
60
72
|
The following subclasses are not supported and raise and error:
|
61
73
|
- `numpy.ma.MaskedArray`
|
@@ -64,10 +76,10 @@ def _compliance_scipy(arrays):
|
|
64
76
|
- Any array-like which is neither array API compatible nor coercible by NumPy
|
65
77
|
- Any array-like which is coerced by NumPy to an unsupported dtype
|
66
78
|
"""
|
67
|
-
for
|
68
|
-
array
|
79
|
+
for array in arrays:
|
80
|
+
if array is None:
|
81
|
+
continue
|
69
82
|
|
70
|
-
from scipy.sparse import issparse
|
71
83
|
# this comes from `_util._asarray_validated`
|
72
84
|
if issparse(array):
|
73
85
|
msg = ('Sparse arrays/matrices are not supported by this function. '
|
@@ -77,14 +89,19 @@ def _compliance_scipy(arrays):
|
|
77
89
|
|
78
90
|
if isinstance(array, np.ma.MaskedArray):
|
79
91
|
raise TypeError("Inputs of type `numpy.ma.MaskedArray` are not supported.")
|
80
|
-
|
92
|
+
|
93
|
+
if isinstance(array, np.matrix):
|
81
94
|
raise TypeError("Inputs of type `numpy.matrix` are not supported.")
|
95
|
+
|
82
96
|
if isinstance(array, np.ndarray | np.generic):
|
83
97
|
dtype = array.dtype
|
84
98
|
if not (np.issubdtype(dtype, np.number) or np.issubdtype(dtype, np.bool_)):
|
85
99
|
raise TypeError(f"An argument has dtype `{dtype!r}`; "
|
86
100
|
f"only boolean and numerical dtypes are supported.")
|
87
|
-
|
101
|
+
|
102
|
+
if is_array_api_obj(array):
|
103
|
+
yield array
|
104
|
+
else:
|
88
105
|
try:
|
89
106
|
array = np.asanyarray(array)
|
90
107
|
except TypeError:
|
@@ -97,17 +114,17 @@ def _compliance_scipy(arrays):
|
|
97
114
|
f"only boolean and numerical dtypes are supported."
|
98
115
|
)
|
99
116
|
raise TypeError(message)
|
100
|
-
arrays
|
101
|
-
|
117
|
+
# Ignore 0-dimensional arrays, coherently with array-api-compat.
|
118
|
+
# Raise if there are 1+-dimensional array-likes mixed with non-numpy
|
119
|
+
# Array API objects.
|
120
|
+
if array.ndim:
|
121
|
+
yield array
|
102
122
|
|
103
123
|
|
104
124
|
def _check_finite(array: Array, xp: ModuleType) -> None:
|
105
125
|
"""Check for NaNs or Infs."""
|
106
|
-
|
107
|
-
|
108
|
-
if not xp.all(xp.isfinite(array)):
|
109
|
-
raise ValueError(msg)
|
110
|
-
except TypeError:
|
126
|
+
if not xp.all(xp.isfinite(array)):
|
127
|
+
msg = "array must not contain infs or NaNs"
|
111
128
|
raise ValueError(msg)
|
112
129
|
|
113
130
|
|
@@ -141,11 +158,13 @@ def array_namespace(*arrays: Array) -> ModuleType:
|
|
141
158
|
# here we could wrap the namespace if needed
|
142
159
|
return np_compat
|
143
160
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
161
|
+
api_arrays = list(_compliance_scipy(arrays))
|
162
|
+
# In case of a mix of array API compliant arrays and scalars, return
|
163
|
+
# the array API namespace. If there are only ArrayLikes (e.g. lists),
|
164
|
+
# return NumPy (wrapped by array-api-compat).
|
165
|
+
if api_arrays:
|
166
|
+
return array_api_compat.array_namespace(*api_arrays)
|
167
|
+
return np_compat
|
149
168
|
|
150
169
|
|
151
170
|
def _asarray(
|
@@ -223,12 +242,49 @@ def xp_copy(x: Array, *, xp: ModuleType | None = None) -> Array:
|
|
223
242
|
return _asarray(x, copy=True, xp=xp)
|
224
243
|
|
225
244
|
|
245
|
+
_default_xp_ctxvar: ContextVar[ModuleType] = ContextVar("_default_xp")
|
246
|
+
|
247
|
+
@contextmanager
|
248
|
+
def default_xp(xp: ModuleType) -> Generator[None, None, None]:
|
249
|
+
"""In all ``xp_assert_*`` and ``assert_*`` function calls executed within this
|
250
|
+
context manager, test by default that the array namespace is
|
251
|
+
the provided across all arrays, unless one explicitly passes the ``xp=``
|
252
|
+
parameter or ``check_namespace=False``.
|
253
|
+
|
254
|
+
Without this context manager, the default value for `xp` is the namespace
|
255
|
+
for the desired array (the second parameter of the tests).
|
256
|
+
"""
|
257
|
+
token = _default_xp_ctxvar.set(xp)
|
258
|
+
try:
|
259
|
+
yield
|
260
|
+
finally:
|
261
|
+
_default_xp_ctxvar.reset(token)
|
262
|
+
|
263
|
+
|
264
|
+
def eager_warns(x, warning_type, match=None):
|
265
|
+
"""pytest.warns context manager, but only if x is not a lazy array."""
|
266
|
+
import pytest
|
267
|
+
# This attribute is interpreted by pytest-run-parallel, ensuring that tests that use
|
268
|
+
# `eager_warns` aren't run in parallel (since pytest.warns isn't thread-safe).
|
269
|
+
__thread_safe__ = False # noqa: F841
|
270
|
+
if is_lazy_array(x):
|
271
|
+
return contextlib.nullcontext()
|
272
|
+
return pytest.warns(warning_type, match=match)
|
273
|
+
|
274
|
+
|
226
275
|
def _strict_check(actual, desired, xp, *,
|
227
276
|
check_namespace=True, check_dtype=True, check_shape=True,
|
228
277
|
check_0d=True):
|
229
278
|
__tracebackhide__ = True # Hide traceback for py.test
|
279
|
+
|
280
|
+
if xp is None:
|
281
|
+
try:
|
282
|
+
xp = _default_xp_ctxvar.get()
|
283
|
+
except LookupError:
|
284
|
+
xp = array_namespace(desired)
|
285
|
+
|
230
286
|
if check_namespace:
|
231
|
-
_assert_matching_namespace(actual, desired)
|
287
|
+
_assert_matching_namespace(actual, desired, xp)
|
232
288
|
|
233
289
|
# only NumPy distinguishes between scalars and arrays; we do if check_0d=True.
|
234
290
|
# do this first so we can then cast to array (and thus use the array API) below.
|
@@ -246,32 +302,39 @@ def _strict_check(actual, desired, xp, *,
|
|
246
302
|
assert actual.dtype == desired.dtype, _msg
|
247
303
|
|
248
304
|
if check_shape:
|
305
|
+
if is_dask(xp):
|
306
|
+
actual.compute_chunk_sizes()
|
307
|
+
desired.compute_chunk_sizes()
|
249
308
|
_msg = f"Shapes do not match.\nActual: {actual.shape}\nDesired: {desired.shape}"
|
250
309
|
assert actual.shape == desired.shape, _msg
|
251
310
|
|
252
311
|
desired = xp.broadcast_to(desired, actual.shape)
|
253
|
-
return actual, desired
|
312
|
+
return actual, desired, xp
|
254
313
|
|
255
314
|
|
256
|
-
def _assert_matching_namespace(actual, desired):
|
315
|
+
def _assert_matching_namespace(actual, desired, xp):
|
257
316
|
__tracebackhide__ = True # Hide traceback for py.test
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
317
|
+
|
318
|
+
desired_arr_space = array_namespace(desired)
|
319
|
+
_msg = ("Namespace of desired array does not match expectations "
|
320
|
+
"set by the `default_xp` context manager or by the `xp`"
|
321
|
+
"pytest fixture.\n"
|
322
|
+
f"Desired array's space: {desired_arr_space.__name__}\n"
|
323
|
+
f"Expected namespace: {xp.__name__}")
|
324
|
+
assert desired_arr_space == xp, _msg
|
325
|
+
|
326
|
+
actual_arr_space = array_namespace(actual)
|
327
|
+
_msg = ("Namespace of actual and desired arrays do not match.\n"
|
328
|
+
f"Actual: {actual_arr_space.__name__}\n"
|
329
|
+
f"Desired: {xp.__name__}")
|
330
|
+
assert actual_arr_space == xp, _msg
|
266
331
|
|
267
332
|
|
268
333
|
def xp_assert_equal(actual, desired, *, check_namespace=True, check_dtype=True,
|
269
334
|
check_shape=True, check_0d=True, err_msg='', xp=None):
|
270
335
|
__tracebackhide__ = True # Hide traceback for py.test
|
271
|
-
if xp is None:
|
272
|
-
xp = array_namespace(actual)
|
273
336
|
|
274
|
-
actual, desired = _strict_check(
|
337
|
+
actual, desired, xp = _strict_check(
|
275
338
|
actual, desired, xp, check_namespace=check_namespace,
|
276
339
|
check_dtype=check_dtype, check_shape=check_shape,
|
277
340
|
check_0d=check_0d
|
@@ -293,10 +356,8 @@ def xp_assert_close(actual, desired, *, rtol=None, atol=0, check_namespace=True,
|
|
293
356
|
check_dtype=True, check_shape=True, check_0d=True,
|
294
357
|
err_msg='', xp=None):
|
295
358
|
__tracebackhide__ = True # Hide traceback for py.test
|
296
|
-
if xp is None:
|
297
|
-
xp = array_namespace(actual)
|
298
359
|
|
299
|
-
actual, desired = _strict_check(
|
360
|
+
actual, desired, xp = _strict_check(
|
300
361
|
actual, desired, xp,
|
301
362
|
check_namespace=check_namespace, check_dtype=check_dtype,
|
302
363
|
check_shape=check_shape, check_0d=check_0d
|
@@ -326,10 +387,8 @@ def xp_assert_close(actual, desired, *, rtol=None, atol=0, check_namespace=True,
|
|
326
387
|
def xp_assert_less(actual, desired, *, check_namespace=True, check_dtype=True,
|
327
388
|
check_shape=True, check_0d=True, err_msg='', verbose=True, xp=None):
|
328
389
|
__tracebackhide__ = True # Hide traceback for py.test
|
329
|
-
if xp is None:
|
330
|
-
xp = array_namespace(actual)
|
331
390
|
|
332
|
-
actual, desired = _strict_check(
|
391
|
+
actual, desired, xp = _strict_check(
|
333
392
|
actual, desired, xp, check_namespace=check_namespace,
|
334
393
|
check_dtype=check_dtype, check_shape=check_shape,
|
335
394
|
check_0d=check_0d
|
@@ -374,42 +433,6 @@ def is_complex(x: Array, xp: ModuleType) -> bool:
|
|
374
433
|
return xp.isdtype(x.dtype, 'complex floating')
|
375
434
|
|
376
435
|
|
377
|
-
def get_xp_devices(xp: ModuleType) -> list[str] | list[None]:
|
378
|
-
"""Returns a list of available devices for the given namespace."""
|
379
|
-
devices: list[str] = []
|
380
|
-
if is_torch(xp):
|
381
|
-
devices += ['cpu']
|
382
|
-
import torch # type: ignore[import]
|
383
|
-
num_cuda = torch.cuda.device_count()
|
384
|
-
for i in range(0, num_cuda):
|
385
|
-
devices += [f'cuda:{i}']
|
386
|
-
if torch.backends.mps.is_available():
|
387
|
-
devices += ['mps']
|
388
|
-
return devices
|
389
|
-
elif is_cupy(xp):
|
390
|
-
import cupy # type: ignore[import]
|
391
|
-
num_cuda = cupy.cuda.runtime.getDeviceCount()
|
392
|
-
for i in range(0, num_cuda):
|
393
|
-
devices += [f'cuda:{i}']
|
394
|
-
return devices
|
395
|
-
elif is_jax(xp):
|
396
|
-
import jax # type: ignore[import]
|
397
|
-
num_cpu = jax.device_count(backend='cpu')
|
398
|
-
for i in range(0, num_cpu):
|
399
|
-
devices += [f'cpu:{i}']
|
400
|
-
num_gpu = jax.device_count(backend='gpu')
|
401
|
-
for i in range(0, num_gpu):
|
402
|
-
devices += [f'gpu:{i}']
|
403
|
-
num_tpu = jax.device_count(backend='tpu')
|
404
|
-
for i in range(0, num_tpu):
|
405
|
-
devices += [f'tpu:{i}']
|
406
|
-
return devices
|
407
|
-
|
408
|
-
# given namespace is not known to have a list of available devices;
|
409
|
-
# return `[None]` so that one can use this in tests for `device=None`.
|
410
|
-
return [None]
|
411
|
-
|
412
|
-
|
413
436
|
def scipy_namespace_for(xp: ModuleType) -> ModuleType | None:
|
414
437
|
"""Return the `scipy`-like namespace of a non-NumPy backend
|
415
438
|
|
@@ -432,42 +455,6 @@ def scipy_namespace_for(xp: ModuleType) -> ModuleType | None:
|
|
432
455
|
return None
|
433
456
|
|
434
457
|
|
435
|
-
# temporary substitute for xp.moveaxis, which is not yet in all backends
|
436
|
-
# or covered by array_api_compat.
|
437
|
-
def xp_moveaxis_to_end(
|
438
|
-
x: Array,
|
439
|
-
source: int,
|
440
|
-
/, *,
|
441
|
-
xp: ModuleType | None = None) -> Array:
|
442
|
-
xp = array_namespace(xp) if xp is None else xp
|
443
|
-
axes = list(range(x.ndim))
|
444
|
-
temp = axes.pop(source)
|
445
|
-
axes = axes + [temp]
|
446
|
-
return xp.permute_dims(x, axes)
|
447
|
-
|
448
|
-
|
449
|
-
# temporary substitute for xp.copysign, which is not yet in all backends
|
450
|
-
# or covered by array_api_compat.
|
451
|
-
def xp_copysign(x1: Array, x2: Array, /, *, xp: ModuleType | None = None) -> Array:
|
452
|
-
# no attempt to account for special cases
|
453
|
-
xp = array_namespace(x1, x2) if xp is None else xp
|
454
|
-
abs_x1 = xp.abs(x1)
|
455
|
-
return xp.where(x2 >= 0, abs_x1, -abs_x1)
|
456
|
-
|
457
|
-
|
458
|
-
# partial substitute for xp.sign, which does not cover the NaN special case
|
459
|
-
# that I need. (https://github.com/data-apis/array-api-compat/issues/136)
|
460
|
-
def xp_sign(x: Array, /, *, xp: ModuleType | None = None) -> Array:
|
461
|
-
xp = array_namespace(x) if xp is None else xp
|
462
|
-
if is_numpy(xp): # only NumPy implements the special cases correctly
|
463
|
-
return xp.sign(x)
|
464
|
-
sign = xp.zeros_like(x)
|
465
|
-
one = xp.asarray(1, dtype=x.dtype)
|
466
|
-
sign = xp.where(x > 0, one, sign)
|
467
|
-
sign = xp.where(x < 0, -one, sign)
|
468
|
-
sign = xp.where(xp.isnan(x), xp.nan*one, sign)
|
469
|
-
return sign
|
470
|
-
|
471
458
|
# maybe use `scipy.linalg` if/when array API support is added
|
472
459
|
def xp_vector_norm(x: Array, /, *,
|
473
460
|
axis: int | tuple[int] | None = None,
|
@@ -503,53 +490,96 @@ def xp_ravel(x: Array, /, *, xp: ModuleType | None = None) -> Array:
|
|
503
490
|
return xp.reshape(x, (-1,))
|
504
491
|
|
505
492
|
|
506
|
-
def
|
507
|
-
#
|
508
|
-
|
509
|
-
|
510
|
-
|
493
|
+
def xp_swapaxes(a, axis1, axis2, xp=None):
|
494
|
+
# Equivalent of np.swapaxes written in terms of array API
|
495
|
+
xp = array_namespace(a) if xp is None else xp
|
496
|
+
axes = list(range(a.ndim))
|
497
|
+
axes[axis1], axes[axis2] = axes[axis2], axes[axis1]
|
498
|
+
a = xp.permute_dims(a, axes)
|
499
|
+
return a
|
511
500
|
|
512
501
|
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
502
|
+
# utility to find common dtype with option to force floating
|
503
|
+
def xp_result_type(*args, force_floating=False, xp):
|
504
|
+
"""
|
505
|
+
Returns the dtype that results from applying type promotion rules
|
506
|
+
(see Array API Standard Type Promotion Rules) to the arguments. Augments
|
507
|
+
standard `result_type` in a few ways:
|
508
|
+
|
509
|
+
- There is a `force_floating` argument that ensures that the result type
|
510
|
+
is floating point, even when all args are integer.
|
511
|
+
- When a TypeError is raised (e.g. due to an unsupported promotion)
|
512
|
+
and `force_floating=True`, we define a custom rule: use the result type
|
513
|
+
of the default float and any other floats passed. See
|
514
|
+
https://github.com/scipy/scipy/pull/22695/files#r1997905891
|
515
|
+
for rationale.
|
516
|
+
- This function accepts array-like iterables, which are immediately converted
|
517
|
+
to the namespace's arrays before result type calculation. Consequently, the
|
518
|
+
result dtype may be different when an argument is `1.` vs `[1.]`.
|
519
|
+
|
520
|
+
Typically, this function will be called shortly after `array_namespace`
|
521
|
+
on a subset of the arguments passed to `array_namespace`.
|
522
|
+
"""
|
523
|
+
args = [(_asarray(arg, subok=True, xp=xp) if np.iterable(arg) else arg)
|
524
|
+
for arg in args]
|
525
|
+
args_not_none = [arg for arg in args if arg is not None]
|
526
|
+
if force_floating:
|
527
|
+
args_not_none.append(1.0)
|
526
528
|
|
529
|
+
if is_numpy(xp) and xp.__version__ < '2.0':
|
530
|
+
# Follow NEP 50 promotion rules anyway
|
531
|
+
args_not_none = [arg.dtype if getattr(arg, 'size', 0) == 1 else arg
|
532
|
+
for arg in args_not_none]
|
533
|
+
return xp.result_type(*args_not_none)
|
527
534
|
|
528
|
-
#
|
529
|
-
|
530
|
-
|
535
|
+
try: # follow library's preferred promotion rules
|
536
|
+
return xp.result_type(*args_not_none)
|
537
|
+
except TypeError: # mixed type promotion isn't defined
|
538
|
+
if not force_floating:
|
539
|
+
raise
|
540
|
+
# use `result_type` of default floating point type and any floats present
|
541
|
+
# This can be revisited, but right now, the only backends that get here
|
542
|
+
# are array-api-strict (which is not for production use) and PyTorch
|
543
|
+
# (due to data-apis/array-api-compat#279).
|
544
|
+
float_args = []
|
545
|
+
for arg in args_not_none:
|
546
|
+
arg_array = xp.asarray(arg) if np.isscalar(arg) else arg
|
547
|
+
dtype = getattr(arg_array, 'dtype', arg)
|
548
|
+
if xp.isdtype(dtype, ('real floating', 'complex floating')):
|
549
|
+
float_args.append(arg)
|
550
|
+
return xp.result_type(*float_args, xp_default_dtype(xp))
|
551
|
+
|
552
|
+
|
553
|
+
def xp_promote(*args, broadcast=False, force_floating=False, xp):
|
554
|
+
"""
|
555
|
+
Promotes elements of *args to result dtype, ignoring `None`s.
|
556
|
+
Includes options for forcing promotion to floating point and
|
557
|
+
broadcasting the arrays, again ignoring `None`s.
|
558
|
+
Type promotion rules follow `xp_result_type` instead of `xp.result_type`.
|
531
559
|
|
532
|
-
|
533
|
-
|
560
|
+
Typically, this function will be called shortly after `array_namespace`
|
561
|
+
on a subset of the arguments passed to `array_namespace`.
|
534
562
|
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
563
|
+
This function accepts array-like iterables, which are immediately converted
|
564
|
+
to the namespace's arrays before result type calculation. Consequently, the
|
565
|
+
result dtype may be different when an argument is `1.` vs `[1.]`.
|
566
|
+
|
567
|
+
See Also
|
568
|
+
--------
|
569
|
+
xp_result_type
|
570
|
+
"""
|
571
|
+
args = [(_asarray(arg, subok=True, xp=xp) if np.iterable(arg) else arg)
|
572
|
+
for arg in args] # solely to prevent double conversion of iterable to array
|
573
|
+
|
574
|
+
dtype = xp_result_type(*args, force_floating=force_floating, xp=xp)
|
575
|
+
|
576
|
+
args = [(_asarray(arg, dtype=dtype, subok=True, xp=xp) if arg is not None else arg)
|
577
|
+
for arg in args]
|
578
|
+
|
579
|
+
if not broadcast:
|
580
|
+
return args[0] if len(args)==1 else tuple(args)
|
581
|
+
|
582
|
+
args_not_none = [arg for arg in args if arg is not None]
|
553
583
|
|
554
584
|
# determine result shape
|
555
585
|
shapes = {arg.shape for arg in args_not_none}
|
@@ -573,12 +603,13 @@ def xp_broadcast_promote(*args, ensure_writeable=False, force_floating=False, xp
|
|
573
603
|
kwargs = {'subok': True} if is_numpy(xp) else {}
|
574
604
|
arg = xp.broadcast_to(arg, shape, **kwargs)
|
575
605
|
|
576
|
-
#
|
577
|
-
if
|
578
|
-
arg = xp.astype(arg, dtype
|
606
|
+
# This is much faster than xp.astype(arg, dtype, copy=False)
|
607
|
+
if arg.dtype != dtype:
|
608
|
+
arg = xp.astype(arg, dtype)
|
609
|
+
|
579
610
|
out.append(arg)
|
580
611
|
|
581
|
-
return out
|
612
|
+
return out[0] if len(out)==1 else tuple(out)
|
582
613
|
|
583
614
|
|
584
615
|
def xp_float_to_complex(arr: Array, xp: ModuleType | None = None) -> Array:
|
@@ -604,3 +635,297 @@ def xp_default_dtype(xp):
|
|
604
635
|
else:
|
605
636
|
# we default to float64
|
606
637
|
return xp.float64
|
638
|
+
|
639
|
+
|
640
|
+
def xp_result_device(*args):
|
641
|
+
"""Return the device of an array in `args`, for the purpose of
|
642
|
+
input-output device propagation.
|
643
|
+
If there are multiple devices, return an arbitrary one.
|
644
|
+
If there are no arrays, return None (this typically happens only on NumPy).
|
645
|
+
"""
|
646
|
+
for arg in args:
|
647
|
+
# Do not do a duck-type test for the .device attribute, as many backends today
|
648
|
+
# don't have it yet. See workarouunds in array_api_compat.device().
|
649
|
+
if is_array_api_obj(arg):
|
650
|
+
return xp_device(arg)
|
651
|
+
return None
|
652
|
+
|
653
|
+
|
654
|
+
def is_marray(xp):
|
655
|
+
"""Returns True if `xp` is an MArray namespace; False otherwise."""
|
656
|
+
return "marray" in xp.__name__
|
657
|
+
|
658
|
+
|
659
|
+
@dataclasses.dataclass(repr=False)
|
660
|
+
class _XPSphinxCapability:
|
661
|
+
cpu: bool | None # None if not applicable
|
662
|
+
gpu: bool | None
|
663
|
+
warnings: list[str] = dataclasses.field(default_factory=list)
|
664
|
+
|
665
|
+
def _render(self, value):
|
666
|
+
if value is None:
|
667
|
+
return "n/a"
|
668
|
+
if not value:
|
669
|
+
return "⛔"
|
670
|
+
if self.warnings:
|
671
|
+
res = "⚠️ " + '; '.join(self.warnings)
|
672
|
+
assert len(res) <= 20, "Warnings too long"
|
673
|
+
return res
|
674
|
+
return "✅"
|
675
|
+
|
676
|
+
def __str__(self):
|
677
|
+
cpu = self._render(self.cpu)
|
678
|
+
gpu = self._render(self.gpu)
|
679
|
+
return f"{cpu:20} {gpu:20}"
|
680
|
+
|
681
|
+
|
682
|
+
def _make_sphinx_capabilities(
|
683
|
+
# lists of tuples [(module name, reason), ...]
|
684
|
+
skip_backends=(), xfail_backends=(),
|
685
|
+
# @pytest.mark.skip/xfail_xp_backends kwargs
|
686
|
+
cpu_only=False, np_only=False, exceptions=(),
|
687
|
+
# xpx.lazy_xp_backends kwargs
|
688
|
+
allow_dask_compute=False, jax_jit=True,
|
689
|
+
# list of tuples [(module name, reason), ...]
|
690
|
+
warnings = (),
|
691
|
+
# unused in documentation
|
692
|
+
reason=None,
|
693
|
+
):
|
694
|
+
exceptions = set(exceptions)
|
695
|
+
|
696
|
+
# Default capabilities
|
697
|
+
capabilities = {
|
698
|
+
"numpy": _XPSphinxCapability(cpu=True, gpu=None),
|
699
|
+
"array_api_strict": _XPSphinxCapability(cpu=True, gpu=None),
|
700
|
+
"cupy": _XPSphinxCapability(cpu=None, gpu=True),
|
701
|
+
"torch": _XPSphinxCapability(cpu=True, gpu=True),
|
702
|
+
"jax.numpy": _XPSphinxCapability(cpu=True, gpu=True,
|
703
|
+
warnings=[] if jax_jit else ["no JIT"]),
|
704
|
+
# Note: Dask+CuPy is currently untested and unsupported
|
705
|
+
"dask.array": _XPSphinxCapability(cpu=True, gpu=None,
|
706
|
+
warnings=["computes graph"] if allow_dask_compute else []),
|
707
|
+
}
|
708
|
+
|
709
|
+
# documentation doesn't display the reason
|
710
|
+
for module, _ in list(skip_backends) + list(xfail_backends):
|
711
|
+
backend = capabilities[module]
|
712
|
+
if backend.cpu is not None:
|
713
|
+
backend.cpu = False
|
714
|
+
if backend.gpu is not None:
|
715
|
+
backend.gpu = False
|
716
|
+
|
717
|
+
for module, backend in capabilities.items():
|
718
|
+
if np_only and module not in exceptions | {"numpy"}:
|
719
|
+
if backend.cpu is not None:
|
720
|
+
backend.cpu = False
|
721
|
+
if backend.gpu is not None:
|
722
|
+
backend.gpu = False
|
723
|
+
elif cpu_only and module not in exceptions and backend.gpu is not None:
|
724
|
+
backend.gpu = False
|
725
|
+
|
726
|
+
for module, warning in warnings:
|
727
|
+
backend = capabilities[module]
|
728
|
+
backend.warnings.append(warning)
|
729
|
+
|
730
|
+
return capabilities
|
731
|
+
|
732
|
+
|
733
|
+
def _make_capabilities_note(fun_name, capabilities):
|
734
|
+
# Note: deliberately not documenting array-api-strict
|
735
|
+
note = f"""
|
736
|
+
`{fun_name}` has experimental support for Python Array API Standard compatible
|
737
|
+
backends in addition to NumPy. Please consider testing these features
|
738
|
+
by setting an environment variable ``SCIPY_ARRAY_API=1`` and providing
|
739
|
+
CuPy, PyTorch, JAX, or Dask arrays as array arguments. The following
|
740
|
+
combinations of backend and device (or other capability) are supported.
|
741
|
+
|
742
|
+
==================== ==================== ====================
|
743
|
+
Library CPU GPU
|
744
|
+
==================== ==================== ====================
|
745
|
+
NumPy {capabilities['numpy'] }
|
746
|
+
CuPy {capabilities['cupy'] }
|
747
|
+
PyTorch {capabilities['torch'] }
|
748
|
+
JAX {capabilities['jax.numpy'] }
|
749
|
+
Dask {capabilities['dask.array'] }
|
750
|
+
==================== ==================== ====================
|
751
|
+
|
752
|
+
See :ref:`dev-arrayapi` for more information.
|
753
|
+
"""
|
754
|
+
return textwrap.dedent(note)
|
755
|
+
|
756
|
+
|
757
|
+
def xp_capabilities(
|
758
|
+
*,
|
759
|
+
# Alternative capabilities table.
|
760
|
+
# Used only for testing this decorator.
|
761
|
+
capabilities_table=None,
|
762
|
+
# Generate pytest.mark.skip/xfail_xp_backends.
|
763
|
+
# See documentation in conftest.py.
|
764
|
+
# lists of tuples [(module name, reason), ...]
|
765
|
+
skip_backends=(), xfail_backends=(),
|
766
|
+
cpu_only=False, np_only=False, reason=None, exceptions=(),
|
767
|
+
# lists of tuples [(module name, reason), ...]
|
768
|
+
warnings=(),
|
769
|
+
# xpx.testing.lazy_xp_function kwargs.
|
770
|
+
# Refer to array-api-extra documentation.
|
771
|
+
allow_dask_compute=False, jax_jit=True,
|
772
|
+
):
|
773
|
+
"""Decorator for a function that states its support among various
|
774
|
+
Array API compatible backends.
|
775
|
+
|
776
|
+
This decorator has two effects:
|
777
|
+
1. It allows tagging tests with ``@make_xp_test_case`` or
|
778
|
+
``make_xp_pytest_param`` (see below) to automatically generate
|
779
|
+
SKIP/XFAIL markers and perform additional backend-specific
|
780
|
+
testing, such as extra validation for Dask and JAX;
|
781
|
+
2. It automatically adds a note to the function's docstring, containing
|
782
|
+
a table matching what has been tested.
|
783
|
+
|
784
|
+
See Also
|
785
|
+
--------
|
786
|
+
make_xp_test_case
|
787
|
+
make_xp_pytest_param
|
788
|
+
array_api_extra.testing.lazy_xp_function
|
789
|
+
"""
|
790
|
+
capabilities_table = (xp_capabilities_table if capabilities_table is None
|
791
|
+
else capabilities_table)
|
792
|
+
|
793
|
+
capabilities = dict(
|
794
|
+
skip_backends=skip_backends,
|
795
|
+
xfail_backends=xfail_backends,
|
796
|
+
cpu_only=cpu_only,
|
797
|
+
np_only=np_only,
|
798
|
+
reason=reason,
|
799
|
+
exceptions=exceptions,
|
800
|
+
allow_dask_compute=allow_dask_compute,
|
801
|
+
jax_jit=jax_jit,
|
802
|
+
warnings=warnings,
|
803
|
+
)
|
804
|
+
sphinx_capabilities = _make_sphinx_capabilities(**capabilities)
|
805
|
+
|
806
|
+
def decorator(f):
|
807
|
+
# Don't use a wrapper, as in some cases @xp_capabilities is
|
808
|
+
# applied to a ufunc
|
809
|
+
capabilities_table[f] = capabilities
|
810
|
+
note = _make_capabilities_note(f.__name__, sphinx_capabilities)
|
811
|
+
doc = FunctionDoc(f)
|
812
|
+
doc['Notes'].append(note)
|
813
|
+
doc = str(doc).split("\n", 1)[1] # remove signature
|
814
|
+
try:
|
815
|
+
f.__doc__ = doc
|
816
|
+
except AttributeError:
|
817
|
+
# Can't update __doc__ on ufuncs if SciPy
|
818
|
+
# was compiled against NumPy < 2.2.
|
819
|
+
pass
|
820
|
+
|
821
|
+
return f
|
822
|
+
return decorator
|
823
|
+
|
824
|
+
|
825
|
+
def _make_xp_pytest_marks(*funcs, capabilities_table=None):
|
826
|
+
capabilities_table = (xp_capabilities_table if capabilities_table is None
|
827
|
+
else capabilities_table)
|
828
|
+
import pytest
|
829
|
+
from scipy._lib.array_api_extra.testing import lazy_xp_function
|
830
|
+
|
831
|
+
marks = []
|
832
|
+
for func in funcs:
|
833
|
+
capabilities = capabilities_table[func]
|
834
|
+
exceptions = capabilities['exceptions']
|
835
|
+
reason = capabilities['reason']
|
836
|
+
|
837
|
+
if capabilities['cpu_only']:
|
838
|
+
marks.append(pytest.mark.skip_xp_backends(
|
839
|
+
cpu_only=True, exceptions=exceptions, reason=reason))
|
840
|
+
if capabilities['np_only']:
|
841
|
+
marks.append(pytest.mark.skip_xp_backends(
|
842
|
+
np_only=True, exceptions=exceptions, reason=reason))
|
843
|
+
|
844
|
+
for mod_name, reason in capabilities['skip_backends']:
|
845
|
+
marks.append(pytest.mark.skip_xp_backends(mod_name, reason=reason))
|
846
|
+
for mod_name, reason in capabilities['xfail_backends']:
|
847
|
+
marks.append(pytest.mark.xfail_xp_backends(mod_name, reason=reason))
|
848
|
+
|
849
|
+
lazy_kwargs = {k: capabilities[k]
|
850
|
+
for k in ('allow_dask_compute', 'jax_jit')}
|
851
|
+
lazy_xp_function(func, **lazy_kwargs)
|
852
|
+
|
853
|
+
return marks
|
854
|
+
|
855
|
+
|
856
|
+
def make_xp_test_case(*funcs, capabilities_table=None):
|
857
|
+
capabilities_table = (xp_capabilities_table if capabilities_table is None
|
858
|
+
else capabilities_table)
|
859
|
+
"""Generate pytest decorator for a test function that tests functionality
|
860
|
+
of one or more Array API compatible functions.
|
861
|
+
|
862
|
+
Read the parameters of the ``@xp_capabilities`` decorator applied to the
|
863
|
+
listed functions and:
|
864
|
+
|
865
|
+
- Generate the ``@pytest.mark.skip_xp_backends`` and
|
866
|
+
``@pytest.mark.xfail_xp_backends`` decorators
|
867
|
+
for the decorated test function
|
868
|
+
- Tag the function with `xpx.testing.lazy_xp_function`
|
869
|
+
|
870
|
+
See Also
|
871
|
+
--------
|
872
|
+
xp_capabilities
|
873
|
+
make_xp_pytest_param
|
874
|
+
array_api_extra.testing.lazy_xp_function
|
875
|
+
"""
|
876
|
+
marks = _make_xp_pytest_marks(*funcs, capabilities_table=capabilities_table)
|
877
|
+
return lambda func: functools.reduce(lambda f, g: g(f), marks, func)
|
878
|
+
|
879
|
+
|
880
|
+
def make_xp_pytest_param(func, *args, capabilities_table=None):
|
881
|
+
"""Variant of ``make_xp_test_case`` that returns a pytest.param for a function,
|
882
|
+
with all necessary skip_xp_backends and xfail_xp_backends marks applied::
|
883
|
+
|
884
|
+
@pytest.mark.parametrize(
|
885
|
+
"func", [make_xp_pytest_param(f1), make_xp_pytest_param(f2)]
|
886
|
+
)
|
887
|
+
def test(func, xp):
|
888
|
+
...
|
889
|
+
|
890
|
+
The above is equivalent to::
|
891
|
+
|
892
|
+
@pytest.mark.parametrize(
|
893
|
+
"func", [
|
894
|
+
pytest.param(f1, marks=[
|
895
|
+
pytest.mark.skip_xp_backends(...),
|
896
|
+
pytest.mark.xfail_xp_backends(...), ...]),
|
897
|
+
pytest.param(f2, marks=[
|
898
|
+
pytest.mark.skip_xp_backends(...),
|
899
|
+
pytest.mark.xfail_xp_backends(...), ...]),
|
900
|
+
)
|
901
|
+
def test(func, xp):
|
902
|
+
...
|
903
|
+
|
904
|
+
Parameters
|
905
|
+
----------
|
906
|
+
func : Callable
|
907
|
+
Function to be tested. It must be decorated with ``@xp_capabilities``.
|
908
|
+
*args : Any, optional
|
909
|
+
Extra pytest parameters for the use case, e.g.::
|
910
|
+
|
911
|
+
@pytest.mark.parametrize("func,verb", [
|
912
|
+
make_xp_pytest_param(f1, "hello"),
|
913
|
+
make_xp_pytest_param(f2, "world")])
|
914
|
+
def test(func, verb, xp):
|
915
|
+
# iterates on (func=f1, verb="hello")
|
916
|
+
# and (func=f2, verb="world")
|
917
|
+
|
918
|
+
See Also
|
919
|
+
--------
|
920
|
+
xp_capabilities
|
921
|
+
make_xp_test_case
|
922
|
+
array_api_extra.testing.lazy_xp_function
|
923
|
+
"""
|
924
|
+
import pytest
|
925
|
+
|
926
|
+
marks = _make_xp_pytest_marks(func, capabilities_table=capabilities_table)
|
927
|
+
return pytest.param(func, *args, marks=marks, id=func.__name__)
|
928
|
+
|
929
|
+
|
930
|
+
# Is it OK to have a dictionary that is mutated (once upon import) in many places?
|
931
|
+
xp_capabilities_table = {} # type: ignore[var-annotated]
|