scipy 1.15.3__cp313-cp313-musllinux_1_2_aarch64.whl → 1.16.0rc2__cp313-cp313-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.
Files changed (641) hide show
  1. scipy/__config__.py +10 -10
  2. scipy/__init__.py +3 -6
  3. scipy/_cyutility.cpython-313-aarch64-linux-musl.so +0 -0
  4. scipy/_lib/_array_api.py +486 -161
  5. scipy/_lib/_array_api_compat_vendor.py +9 -0
  6. scipy/_lib/_bunch.py +4 -0
  7. scipy/_lib/_ccallback_c.cpython-313-aarch64-linux-musl.so +0 -0
  8. scipy/_lib/_docscrape.py +1 -1
  9. scipy/_lib/_elementwise_iterative_method.py +15 -26
  10. scipy/_lib/_fpumode.cpython-313-aarch64-linux-musl.so +0 -0
  11. scipy/_lib/_sparse.py +41 -0
  12. scipy/_lib/_test_ccallback.cpython-313-aarch64-linux-musl.so +0 -0
  13. scipy/_lib/_test_deprecation_call.cpython-313-aarch64-linux-musl.so +0 -0
  14. scipy/_lib/_test_deprecation_def.cpython-313-aarch64-linux-musl.so +0 -0
  15. scipy/_lib/_testutils.py +6 -2
  16. scipy/_lib/_uarray/_uarray.cpython-313-aarch64-linux-musl.so +0 -0
  17. scipy/_lib/_util.py +222 -125
  18. scipy/_lib/array_api_compat/__init__.py +4 -4
  19. scipy/_lib/array_api_compat/_internal.py +19 -6
  20. scipy/_lib/array_api_compat/common/__init__.py +1 -1
  21. scipy/_lib/array_api_compat/common/_aliases.py +365 -193
  22. scipy/_lib/array_api_compat/common/_fft.py +94 -64
  23. scipy/_lib/array_api_compat/common/_helpers.py +413 -180
  24. scipy/_lib/array_api_compat/common/_linalg.py +116 -40
  25. scipy/_lib/array_api_compat/common/_typing.py +179 -10
  26. scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
  27. scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
  28. scipy/_lib/array_api_compat/cupy/_info.py +16 -6
  29. scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
  30. scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
  31. scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
  32. scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
  33. scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
  34. scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
  35. scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
  36. scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
  37. scipy/_lib/array_api_compat/numpy/_info.py +36 -16
  38. scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
  39. scipy/_lib/array_api_compat/numpy/fft.py +11 -5
  40. scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
  41. scipy/_lib/array_api_compat/torch/__init__.py +3 -5
  42. scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
  43. scipy/_lib/array_api_compat/torch/_info.py +27 -16
  44. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  45. scipy/_lib/array_api_compat/torch/fft.py +17 -18
  46. scipy/_lib/array_api_compat/torch/linalg.py +16 -16
  47. scipy/_lib/array_api_extra/__init__.py +26 -3
  48. scipy/_lib/array_api_extra/_delegation.py +171 -0
  49. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  50. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  51. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  52. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  53. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  54. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  55. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  56. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  57. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  58. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  59. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  60. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  61. scipy/_lib/array_api_extra/testing.py +359 -0
  62. scipy/_lib/decorator.py +2 -2
  63. scipy/_lib/doccer.py +1 -7
  64. scipy/_lib/messagestream.cpython-313-aarch64-linux-musl.so +0 -0
  65. scipy/_lib/pyprima/__init__.py +212 -0
  66. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  67. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  68. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  69. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  70. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  71. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  72. scipy/_lib/pyprima/cobyla/update.py +289 -0
  73. scipy/_lib/pyprima/common/__init__.py +0 -0
  74. scipy/_lib/pyprima/common/_bounds.py +34 -0
  75. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  76. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  77. scipy/_lib/pyprima/common/_project.py +173 -0
  78. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  79. scipy/_lib/pyprima/common/consts.py +47 -0
  80. scipy/_lib/pyprima/common/evaluate.py +99 -0
  81. scipy/_lib/pyprima/common/history.py +38 -0
  82. scipy/_lib/pyprima/common/infos.py +30 -0
  83. scipy/_lib/pyprima/common/linalg.py +435 -0
  84. scipy/_lib/pyprima/common/message.py +290 -0
  85. scipy/_lib/pyprima/common/powalg.py +131 -0
  86. scipy/_lib/pyprima/common/preproc.py +277 -0
  87. scipy/_lib/pyprima/common/present.py +5 -0
  88. scipy/_lib/pyprima/common/ratio.py +54 -0
  89. scipy/_lib/pyprima/common/redrho.py +47 -0
  90. scipy/_lib/pyprima/common/selectx.py +296 -0
  91. scipy/_lib/tests/test__util.py +105 -121
  92. scipy/_lib/tests/test_array_api.py +166 -35
  93. scipy/_lib/tests/test_bunch.py +7 -0
  94. scipy/_lib/tests/test_ccallback.py +2 -10
  95. scipy/_lib/tests/test_public_api.py +13 -0
  96. scipy/cluster/_hierarchy.cpython-313-aarch64-linux-musl.so +0 -0
  97. scipy/cluster/_optimal_leaf_ordering.cpython-313-aarch64-linux-musl.so +0 -0
  98. scipy/cluster/_vq.cpython-313-aarch64-linux-musl.so +0 -0
  99. scipy/cluster/hierarchy.py +393 -223
  100. scipy/cluster/tests/test_hierarchy.py +273 -335
  101. scipy/cluster/tests/test_vq.py +45 -61
  102. scipy/cluster/vq.py +39 -35
  103. scipy/conftest.py +263 -157
  104. scipy/constants/_constants.py +4 -1
  105. scipy/constants/tests/test_codata.py +2 -2
  106. scipy/constants/tests/test_constants.py +11 -18
  107. scipy/datasets/_download_all.py +15 -1
  108. scipy/datasets/_fetchers.py +7 -1
  109. scipy/datasets/_utils.py +1 -1
  110. scipy/differentiate/_differentiate.py +25 -25
  111. scipy/differentiate/tests/test_differentiate.py +24 -25
  112. scipy/fft/_basic.py +20 -0
  113. scipy/fft/_helper.py +3 -34
  114. scipy/fft/_pocketfft/helper.py +29 -1
  115. scipy/fft/_pocketfft/pypocketfft.cpython-313-aarch64-linux-musl.so +0 -0
  116. scipy/fft/_pocketfft/tests/test_basic.py +2 -4
  117. scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
  118. scipy/fft/_realtransforms.py +13 -0
  119. scipy/fft/tests/test_basic.py +27 -25
  120. scipy/fft/tests/test_fftlog.py +16 -7
  121. scipy/fft/tests/test_helper.py +18 -34
  122. scipy/fft/tests/test_real_transforms.py +8 -10
  123. scipy/fftpack/convolve.cpython-313-aarch64-linux-musl.so +0 -0
  124. scipy/fftpack/tests/test_basic.py +2 -4
  125. scipy/fftpack/tests/test_real_transforms.py +8 -9
  126. scipy/integrate/_bvp.py +9 -3
  127. scipy/integrate/_cubature.py +3 -2
  128. scipy/integrate/_dop.cpython-313-aarch64-linux-musl.so +0 -0
  129. scipy/integrate/_lsoda.cpython-313-aarch64-linux-musl.so +0 -0
  130. scipy/integrate/_ode.py +9 -2
  131. scipy/integrate/_odepack.cpython-313-aarch64-linux-musl.so +0 -0
  132. scipy/integrate/_quad_vec.py +21 -29
  133. scipy/integrate/_quadpack.cpython-313-aarch64-linux-musl.so +0 -0
  134. scipy/integrate/_quadpack_py.py +11 -7
  135. scipy/integrate/_quadrature.py +3 -3
  136. scipy/integrate/_rules/_base.py +2 -2
  137. scipy/integrate/_tanhsinh.py +48 -47
  138. scipy/integrate/_test_multivariate.cpython-313-aarch64-linux-musl.so +0 -0
  139. scipy/integrate/_test_odeint_banded.cpython-313-aarch64-linux-musl.so +0 -0
  140. scipy/integrate/_vode.cpython-313-aarch64-linux-musl.so +0 -0
  141. scipy/integrate/tests/test__quad_vec.py +0 -6
  142. scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
  143. scipy/integrate/tests/test_cubature.py +21 -35
  144. scipy/integrate/tests/test_quadrature.py +6 -8
  145. scipy/integrate/tests/test_tanhsinh.py +56 -48
  146. scipy/interpolate/__init__.py +70 -58
  147. scipy/interpolate/_bary_rational.py +22 -22
  148. scipy/interpolate/_bsplines.py +119 -66
  149. scipy/interpolate/_cubic.py +65 -50
  150. scipy/interpolate/_dfitpack.cpython-313-aarch64-linux-musl.so +0 -0
  151. scipy/interpolate/_dierckx.cpython-313-aarch64-linux-musl.so +0 -0
  152. scipy/interpolate/_fitpack.cpython-313-aarch64-linux-musl.so +0 -0
  153. scipy/interpolate/_fitpack2.py +9 -6
  154. scipy/interpolate/_fitpack_impl.py +32 -26
  155. scipy/interpolate/_fitpack_repro.py +23 -19
  156. scipy/interpolate/_interpnd.cpython-313-aarch64-linux-musl.so +0 -0
  157. scipy/interpolate/_interpolate.py +30 -12
  158. scipy/interpolate/_ndbspline.py +13 -18
  159. scipy/interpolate/_ndgriddata.py +5 -8
  160. scipy/interpolate/_polyint.py +95 -31
  161. scipy/interpolate/_ppoly.cpython-313-aarch64-linux-musl.so +0 -0
  162. scipy/interpolate/_rbf.py +2 -2
  163. scipy/interpolate/_rbfinterp.py +1 -1
  164. scipy/interpolate/_rbfinterp_pythran.cpython-313-aarch64-linux-musl.so +0 -0
  165. scipy/interpolate/_rgi.py +31 -26
  166. scipy/interpolate/_rgi_cython.cpython-313-aarch64-linux-musl.so +0 -0
  167. scipy/interpolate/dfitpack.py +0 -20
  168. scipy/interpolate/interpnd.py +1 -2
  169. scipy/interpolate/tests/test_bary_rational.py +2 -2
  170. scipy/interpolate/tests/test_bsplines.py +97 -1
  171. scipy/interpolate/tests/test_fitpack2.py +39 -1
  172. scipy/interpolate/tests/test_interpnd.py +32 -20
  173. scipy/interpolate/tests/test_interpolate.py +48 -4
  174. scipy/interpolate/tests/test_rgi.py +2 -1
  175. scipy/io/_fast_matrix_market/__init__.py +2 -0
  176. scipy/io/_fast_matrix_market/_fmm_core.cpython-313-aarch64-linux-musl.so +0 -0
  177. scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
  178. scipy/io/_harwell_boeing/hb.py +7 -11
  179. scipy/io/_idl.py +5 -7
  180. scipy/io/_netcdf.py +15 -5
  181. scipy/io/_test_fortran.cpython-313-aarch64-linux-musl.so +0 -0
  182. scipy/io/arff/tests/test_arffread.py +3 -3
  183. scipy/io/matlab/__init__.py +5 -3
  184. scipy/io/matlab/_mio.py +4 -1
  185. scipy/io/matlab/_mio5.py +19 -13
  186. scipy/io/matlab/_mio5_utils.cpython-313-aarch64-linux-musl.so +0 -0
  187. scipy/io/matlab/_mio_utils.cpython-313-aarch64-linux-musl.so +0 -0
  188. scipy/io/matlab/_miobase.py +4 -1
  189. scipy/io/matlab/_streams.cpython-313-aarch64-linux-musl.so +0 -0
  190. scipy/io/matlab/tests/test_mio.py +46 -18
  191. scipy/io/matlab/tests/test_mio_funcs.py +1 -1
  192. scipy/io/tests/test_mmio.py +7 -1
  193. scipy/io/tests/test_wavfile.py +41 -0
  194. scipy/io/wavfile.py +57 -10
  195. scipy/linalg/_basic.py +113 -86
  196. scipy/linalg/_cythonized_array_utils.cpython-313-aarch64-linux-musl.so +0 -0
  197. scipy/linalg/_decomp.py +22 -9
  198. scipy/linalg/_decomp_cholesky.py +28 -13
  199. scipy/linalg/_decomp_cossin.py +45 -30
  200. scipy/linalg/_decomp_interpolative.cpython-313-aarch64-linux-musl.so +0 -0
  201. scipy/linalg/_decomp_ldl.py +4 -1
  202. scipy/linalg/_decomp_lu.py +18 -6
  203. scipy/linalg/_decomp_lu_cython.cpython-313-aarch64-linux-musl.so +0 -0
  204. scipy/linalg/_decomp_polar.py +2 -0
  205. scipy/linalg/_decomp_qr.py +6 -2
  206. scipy/linalg/_decomp_qz.py +3 -0
  207. scipy/linalg/_decomp_schur.py +3 -1
  208. scipy/linalg/_decomp_svd.py +13 -2
  209. scipy/linalg/_decomp_update.cpython-313-aarch64-linux-musl.so +0 -0
  210. scipy/linalg/_expm_frechet.py +4 -0
  211. scipy/linalg/_fblas.cpython-313-aarch64-linux-musl.so +0 -0
  212. scipy/linalg/_flapack.cpython-313-aarch64-linux-musl.so +0 -0
  213. scipy/linalg/_linalg_pythran.cpython-313-aarch64-linux-musl.so +0 -0
  214. scipy/linalg/_matfuncs.py +187 -4
  215. scipy/linalg/_matfuncs_expm.cpython-313-aarch64-linux-musl.so +0 -0
  216. scipy/linalg/_matfuncs_schur_sqrtm.cpython-313-aarch64-linux-musl.so +0 -0
  217. scipy/linalg/_matfuncs_sqrtm.py +1 -99
  218. scipy/linalg/_matfuncs_sqrtm_triu.cpython-313-aarch64-linux-musl.so +0 -0
  219. scipy/linalg/_procrustes.py +2 -0
  220. scipy/linalg/_sketches.py +17 -6
  221. scipy/linalg/_solve_toeplitz.cpython-313-aarch64-linux-musl.so +0 -0
  222. scipy/linalg/_solvers.py +7 -2
  223. scipy/linalg/_special_matrices.py +26 -36
  224. scipy/linalg/cython_blas.cpython-313-aarch64-linux-musl.so +0 -0
  225. scipy/linalg/cython_lapack.cpython-313-aarch64-linux-musl.so +0 -0
  226. scipy/linalg/lapack.py +22 -2
  227. scipy/linalg/tests/_cython_examples/meson.build +7 -0
  228. scipy/linalg/tests/test_basic.py +31 -16
  229. scipy/linalg/tests/test_batch.py +588 -0
  230. scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
  231. scipy/linalg/tests/test_decomp.py +40 -3
  232. scipy/linalg/tests/test_decomp_cossin.py +14 -0
  233. scipy/linalg/tests/test_decomp_ldl.py +1 -1
  234. scipy/linalg/tests/test_lapack.py +115 -7
  235. scipy/linalg/tests/test_matfuncs.py +157 -102
  236. scipy/linalg/tests/test_procrustes.py +0 -7
  237. scipy/linalg/tests/test_solve_toeplitz.py +1 -1
  238. scipy/linalg/tests/test_special_matrices.py +1 -5
  239. scipy/ndimage/__init__.py +1 -0
  240. scipy/ndimage/_ctest.cpython-313-aarch64-linux-musl.so +0 -0
  241. scipy/ndimage/_cytest.cpython-313-aarch64-linux-musl.so +0 -0
  242. scipy/ndimage/_delegators.py +8 -2
  243. scipy/ndimage/_filters.py +453 -5
  244. scipy/ndimage/_interpolation.py +36 -6
  245. scipy/ndimage/_measurements.py +4 -2
  246. scipy/ndimage/_morphology.py +5 -0
  247. scipy/ndimage/_nd_image.cpython-313-aarch64-linux-musl.so +0 -0
  248. scipy/ndimage/_ni_docstrings.py +5 -1
  249. scipy/ndimage/_ni_label.cpython-313-aarch64-linux-musl.so +0 -0
  250. scipy/ndimage/_ni_support.py +1 -5
  251. scipy/ndimage/_rank_filter_1d.cpython-313-aarch64-linux-musl.so +0 -0
  252. scipy/ndimage/_support_alternative_backends.py +18 -6
  253. scipy/ndimage/tests/test_filters.py +370 -259
  254. scipy/ndimage/tests/test_fourier.py +7 -9
  255. scipy/ndimage/tests/test_interpolation.py +68 -61
  256. scipy/ndimage/tests/test_measurements.py +18 -35
  257. scipy/ndimage/tests/test_morphology.py +143 -131
  258. scipy/ndimage/tests/test_splines.py +1 -3
  259. scipy/odr/__odrpack.cpython-313-aarch64-linux-musl.so +0 -0
  260. scipy/optimize/_basinhopping.py +13 -7
  261. scipy/optimize/_bglu_dense.cpython-313-aarch64-linux-musl.so +0 -0
  262. scipy/optimize/_bracket.py +17 -24
  263. scipy/optimize/_chandrupatla.py +9 -10
  264. scipy/optimize/_cobyla_py.py +104 -123
  265. scipy/optimize/_constraints.py +14 -10
  266. scipy/optimize/_differentiable_functions.py +371 -230
  267. scipy/optimize/_differentialevolution.py +4 -3
  268. scipy/optimize/_direct.cpython-313-aarch64-linux-musl.so +0 -0
  269. scipy/optimize/_dual_annealing.py +1 -1
  270. scipy/optimize/_elementwise.py +1 -4
  271. scipy/optimize/_group_columns.cpython-313-aarch64-linux-musl.so +0 -0
  272. scipy/optimize/_highspy/_core.cpython-313-aarch64-linux-musl.so +0 -0
  273. scipy/optimize/_highspy/_highs_options.cpython-313-aarch64-linux-musl.so +0 -0
  274. scipy/optimize/_lbfgsb.cpython-313-aarch64-linux-musl.so +0 -0
  275. scipy/optimize/_lbfgsb_py.py +57 -16
  276. scipy/optimize/_linprog_doc.py +2 -2
  277. scipy/optimize/_linprog_highs.py +2 -2
  278. scipy/optimize/_linprog_ip.py +25 -10
  279. scipy/optimize/_linprog_util.py +14 -16
  280. scipy/optimize/_lsap.cpython-313-aarch64-linux-musl.so +0 -0
  281. scipy/optimize/_lsq/common.py +3 -3
  282. scipy/optimize/_lsq/dogbox.py +16 -2
  283. scipy/optimize/_lsq/givens_elimination.cpython-313-aarch64-linux-musl.so +0 -0
  284. scipy/optimize/_lsq/least_squares.py +198 -126
  285. scipy/optimize/_lsq/lsq_linear.py +6 -6
  286. scipy/optimize/_lsq/trf.py +35 -8
  287. scipy/optimize/_milp.py +3 -1
  288. scipy/optimize/_minimize.py +105 -36
  289. scipy/optimize/_minpack.cpython-313-aarch64-linux-musl.so +0 -0
  290. scipy/optimize/_minpack_py.py +21 -14
  291. scipy/optimize/_moduleTNC.cpython-313-aarch64-linux-musl.so +0 -0
  292. scipy/optimize/_nnls.py +20 -21
  293. scipy/optimize/_nonlin.py +34 -3
  294. scipy/optimize/_numdiff.py +288 -110
  295. scipy/optimize/_optimize.py +86 -48
  296. scipy/optimize/_pava_pybind.cpython-313-aarch64-linux-musl.so +0 -0
  297. scipy/optimize/_remove_redundancy.py +5 -5
  298. scipy/optimize/_root_scalar.py +1 -1
  299. scipy/optimize/_shgo.py +6 -0
  300. scipy/optimize/_shgo_lib/_complex.py +1 -1
  301. scipy/optimize/_slsqp_py.py +216 -124
  302. scipy/optimize/_slsqplib.cpython-313-aarch64-linux-musl.so +0 -0
  303. scipy/optimize/_spectral.py +1 -1
  304. scipy/optimize/_tnc.py +8 -1
  305. scipy/optimize/_trlib/_trlib.cpython-313-aarch64-linux-musl.so +0 -0
  306. scipy/optimize/_trustregion.py +20 -6
  307. scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
  308. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
  309. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
  310. scipy/optimize/_trustregion_constr/projections.py +12 -8
  311. scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
  312. scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
  313. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
  314. scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
  315. scipy/optimize/_trustregion_exact.py +0 -1
  316. scipy/optimize/_zeros.cpython-313-aarch64-linux-musl.so +0 -0
  317. scipy/optimize/_zeros_py.py +97 -17
  318. scipy/optimize/cython_optimize/_zeros.cpython-313-aarch64-linux-musl.so +0 -0
  319. scipy/optimize/slsqp.py +0 -1
  320. scipy/optimize/tests/test__basinhopping.py +1 -1
  321. scipy/optimize/tests/test__differential_evolution.py +4 -4
  322. scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
  323. scipy/optimize/tests/test__numdiff.py +66 -22
  324. scipy/optimize/tests/test__remove_redundancy.py +2 -2
  325. scipy/optimize/tests/test__shgo.py +9 -1
  326. scipy/optimize/tests/test_bracket.py +36 -46
  327. scipy/optimize/tests/test_chandrupatla.py +133 -135
  328. scipy/optimize/tests/test_cobyla.py +74 -45
  329. scipy/optimize/tests/test_constraints.py +1 -1
  330. scipy/optimize/tests/test_differentiable_functions.py +226 -6
  331. scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
  332. scipy/optimize/tests/test_least_squares.py +125 -13
  333. scipy/optimize/tests/test_linear_assignment.py +3 -3
  334. scipy/optimize/tests/test_linprog.py +3 -3
  335. scipy/optimize/tests/test_lsq_linear.py +6 -6
  336. scipy/optimize/tests/test_minimize_constrained.py +2 -2
  337. scipy/optimize/tests/test_minpack.py +4 -4
  338. scipy/optimize/tests/test_nnls.py +43 -3
  339. scipy/optimize/tests/test_nonlin.py +36 -0
  340. scipy/optimize/tests/test_optimize.py +95 -17
  341. scipy/optimize/tests/test_slsqp.py +36 -4
  342. scipy/optimize/tests/test_zeros.py +34 -1
  343. scipy/signal/__init__.py +12 -23
  344. scipy/signal/_delegators.py +568 -0
  345. scipy/signal/_filter_design.py +459 -241
  346. scipy/signal/_fir_filter_design.py +262 -90
  347. scipy/signal/_lti_conversion.py +3 -2
  348. scipy/signal/_ltisys.py +118 -91
  349. scipy/signal/_max_len_seq_inner.cpython-313-aarch64-linux-musl.so +0 -0
  350. scipy/signal/_peak_finding_utils.cpython-313-aarch64-linux-musl.so +0 -0
  351. scipy/signal/_polyutils.py +172 -0
  352. scipy/signal/_short_time_fft.py +519 -70
  353. scipy/signal/_signal_api.py +30 -0
  354. scipy/signal/_signaltools.py +719 -399
  355. scipy/signal/_sigtools.cpython-313-aarch64-linux-musl.so +0 -0
  356. scipy/signal/_sosfilt.cpython-313-aarch64-linux-musl.so +0 -0
  357. scipy/signal/_spectral_py.py +230 -50
  358. scipy/signal/_spline.cpython-313-aarch64-linux-musl.so +0 -0
  359. scipy/signal/_spline_filters.py +108 -68
  360. scipy/signal/_support_alternative_backends.py +73 -0
  361. scipy/signal/_upfirdn.py +4 -1
  362. scipy/signal/_upfirdn_apply.cpython-313-aarch64-linux-musl.so +0 -0
  363. scipy/signal/_waveforms.py +2 -11
  364. scipy/signal/_wavelets.py +1 -1
  365. scipy/signal/fir_filter_design.py +1 -0
  366. scipy/signal/spline.py +4 -11
  367. scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
  368. scipy/signal/tests/test_bsplines.py +114 -79
  369. scipy/signal/tests/test_cont2discrete.py +9 -2
  370. scipy/signal/tests/test_filter_design.py +721 -481
  371. scipy/signal/tests/test_fir_filter_design.py +332 -140
  372. scipy/signal/tests/test_savitzky_golay.py +4 -3
  373. scipy/signal/tests/test_short_time_fft.py +221 -3
  374. scipy/signal/tests/test_signaltools.py +2144 -1348
  375. scipy/signal/tests/test_spectral.py +50 -6
  376. scipy/signal/tests/test_splines.py +161 -96
  377. scipy/signal/tests/test_upfirdn.py +84 -50
  378. scipy/signal/tests/test_waveforms.py +20 -0
  379. scipy/signal/tests/test_windows.py +607 -466
  380. scipy/signal/windows/_windows.py +287 -148
  381. scipy/sparse/__init__.py +23 -4
  382. scipy/sparse/_base.py +270 -108
  383. scipy/sparse/_bsr.py +7 -4
  384. scipy/sparse/_compressed.py +59 -231
  385. scipy/sparse/_construct.py +90 -38
  386. scipy/sparse/_coo.py +115 -181
  387. scipy/sparse/_csc.py +4 -4
  388. scipy/sparse/_csparsetools.cpython-313-aarch64-linux-musl.so +0 -0
  389. scipy/sparse/_csr.py +2 -2
  390. scipy/sparse/_data.py +48 -48
  391. scipy/sparse/_dia.py +105 -18
  392. scipy/sparse/_dok.py +0 -23
  393. scipy/sparse/_index.py +4 -4
  394. scipy/sparse/_matrix.py +23 -0
  395. scipy/sparse/_sparsetools.cpython-313-aarch64-linux-musl.so +0 -0
  396. scipy/sparse/_sputils.py +37 -22
  397. scipy/sparse/base.py +0 -9
  398. scipy/sparse/bsr.py +0 -14
  399. scipy/sparse/compressed.py +0 -23
  400. scipy/sparse/construct.py +0 -6
  401. scipy/sparse/coo.py +0 -14
  402. scipy/sparse/csc.py +0 -3
  403. scipy/sparse/csgraph/_flow.cpython-313-aarch64-linux-musl.so +0 -0
  404. scipy/sparse/csgraph/_matching.cpython-313-aarch64-linux-musl.so +0 -0
  405. scipy/sparse/csgraph/_min_spanning_tree.cpython-313-aarch64-linux-musl.so +0 -0
  406. scipy/sparse/csgraph/_reordering.cpython-313-aarch64-linux-musl.so +0 -0
  407. scipy/sparse/csgraph/_shortest_path.cpython-313-aarch64-linux-musl.so +0 -0
  408. scipy/sparse/csgraph/_tools.cpython-313-aarch64-linux-musl.so +0 -0
  409. scipy/sparse/csgraph/_traversal.cpython-313-aarch64-linux-musl.so +0 -0
  410. scipy/sparse/csgraph/tests/test_matching.py +14 -2
  411. scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
  412. scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
  413. scipy/sparse/csr.py +0 -5
  414. scipy/sparse/data.py +1 -6
  415. scipy/sparse/dia.py +0 -7
  416. scipy/sparse/dok.py +0 -10
  417. scipy/sparse/linalg/_dsolve/_superlu.cpython-313-aarch64-linux-musl.so +0 -0
  418. scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
  419. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
  420. scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-313-aarch64-linux-musl.so +0 -0
  421. scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
  422. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
  423. scipy/sparse/linalg/_interface.py +17 -18
  424. scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
  425. scipy/sparse/linalg/_isolve/iterative.py +51 -45
  426. scipy/sparse/linalg/_isolve/lgmres.py +6 -6
  427. scipy/sparse/linalg/_isolve/minres.py +5 -5
  428. scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
  429. scipy/sparse/linalg/_isolve/utils.py +2 -8
  430. scipy/sparse/linalg/_matfuncs.py +1 -1
  431. scipy/sparse/linalg/_norm.py +1 -1
  432. scipy/sparse/linalg/_propack/_cpropack.cpython-313-aarch64-linux-musl.so +0 -0
  433. scipy/sparse/linalg/_propack/_dpropack.cpython-313-aarch64-linux-musl.so +0 -0
  434. scipy/sparse/linalg/_propack/_spropack.cpython-313-aarch64-linux-musl.so +0 -0
  435. scipy/sparse/linalg/_propack/_zpropack.cpython-313-aarch64-linux-musl.so +0 -0
  436. scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
  437. scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
  438. scipy/sparse/tests/test_arithmetic1d.py +5 -2
  439. scipy/sparse/tests/test_base.py +214 -42
  440. scipy/sparse/tests/test_common1d.py +7 -7
  441. scipy/sparse/tests/test_construct.py +1 -1
  442. scipy/sparse/tests/test_coo.py +272 -4
  443. scipy/sparse/tests/test_sparsetools.py +5 -0
  444. scipy/sparse/tests/test_sputils.py +36 -7
  445. scipy/spatial/_ckdtree.cpython-313-aarch64-linux-musl.so +0 -0
  446. scipy/spatial/_distance_pybind.cpython-313-aarch64-linux-musl.so +0 -0
  447. scipy/spatial/_distance_wrap.cpython-313-aarch64-linux-musl.so +0 -0
  448. scipy/spatial/_hausdorff.cpython-313-aarch64-linux-musl.so +0 -0
  449. scipy/spatial/_qhull.cpython-313-aarch64-linux-musl.so +0 -0
  450. scipy/spatial/_voronoi.cpython-313-aarch64-linux-musl.so +0 -0
  451. scipy/spatial/distance.py +49 -42
  452. scipy/spatial/tests/test_distance.py +15 -1
  453. scipy/spatial/tests/test_kdtree.py +1 -0
  454. scipy/spatial/tests/test_qhull.py +7 -2
  455. scipy/spatial/transform/__init__.py +5 -3
  456. scipy/spatial/transform/_rigid_transform.cpython-313-aarch64-linux-musl.so +0 -0
  457. scipy/spatial/transform/_rotation.cpython-313-aarch64-linux-musl.so +0 -0
  458. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  459. scipy/spatial/transform/tests/test_rotation.py +1213 -832
  460. scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
  461. scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
  462. scipy/special/__init__.py +1 -47
  463. scipy/special/_add_newdocs.py +34 -772
  464. scipy/special/_basic.py +22 -25
  465. scipy/special/_comb.cpython-313-aarch64-linux-musl.so +0 -0
  466. scipy/special/_ellip_harm_2.cpython-313-aarch64-linux-musl.so +0 -0
  467. scipy/special/_gufuncs.cpython-313-aarch64-linux-musl.so +0 -0
  468. scipy/special/_logsumexp.py +67 -58
  469. scipy/special/_orthogonal.pyi +1 -1
  470. scipy/special/_specfun.cpython-313-aarch64-linux-musl.so +0 -0
  471. scipy/special/_special_ufuncs.cpython-313-aarch64-linux-musl.so +0 -0
  472. scipy/special/_spherical_bessel.py +4 -4
  473. scipy/special/_support_alternative_backends.py +212 -119
  474. scipy/special/_test_internal.cpython-313-aarch64-linux-musl.so +0 -0
  475. scipy/special/_testutils.py +4 -4
  476. scipy/special/_ufuncs.cpython-313-aarch64-linux-musl.so +0 -0
  477. scipy/special/_ufuncs.pyi +1 -0
  478. scipy/special/_ufuncs.pyx +215 -1400
  479. scipy/special/_ufuncs_cxx.cpython-313-aarch64-linux-musl.so +0 -0
  480. scipy/special/_ufuncs_cxx.pxd +2 -15
  481. scipy/special/_ufuncs_cxx.pyx +5 -44
  482. scipy/special/_ufuncs_cxx_defs.h +2 -16
  483. scipy/special/_ufuncs_defs.h +0 -8
  484. scipy/special/cython_special.cpython-313-aarch64-linux-musl.so +0 -0
  485. scipy/special/cython_special.pxd +1 -1
  486. scipy/special/tests/_cython_examples/meson.build +10 -1
  487. scipy/special/tests/test_basic.py +153 -20
  488. scipy/special/tests/test_boost_ufuncs.py +3 -0
  489. scipy/special/tests/test_cdflib.py +35 -11
  490. scipy/special/tests/test_gammainc.py +16 -0
  491. scipy/special/tests/test_hyp2f1.py +2 -2
  492. scipy/special/tests/test_log1mexp.py +85 -0
  493. scipy/special/tests/test_logsumexp.py +206 -64
  494. scipy/special/tests/test_mpmath.py +1 -0
  495. scipy/special/tests/test_nan_inputs.py +1 -1
  496. scipy/special/tests/test_orthogonal.py +17 -18
  497. scipy/special/tests/test_sf_error.py +3 -2
  498. scipy/special/tests/test_sph_harm.py +6 -7
  499. scipy/special/tests/test_support_alternative_backends.py +211 -76
  500. scipy/stats/__init__.py +4 -1
  501. scipy/stats/_ansari_swilk_statistics.cpython-313-aarch64-linux-musl.so +0 -0
  502. scipy/stats/_axis_nan_policy.py +5 -12
  503. scipy/stats/_biasedurn.cpython-313-aarch64-linux-musl.so +0 -0
  504. scipy/stats/_continued_fraction.py +387 -0
  505. scipy/stats/_continuous_distns.py +277 -310
  506. scipy/stats/_correlation.py +1 -1
  507. scipy/stats/_covariance.py +6 -3
  508. scipy/stats/_discrete_distns.py +39 -32
  509. scipy/stats/_distn_infrastructure.py +39 -12
  510. scipy/stats/_distribution_infrastructure.py +900 -238
  511. scipy/stats/_entropy.py +9 -10
  512. scipy/{_lib → stats}/_finite_differences.py +1 -1
  513. scipy/stats/_hypotests.py +83 -50
  514. scipy/stats/_kde.py +53 -49
  515. scipy/stats/_ksstats.py +1 -1
  516. scipy/stats/_levy_stable/__init__.py +7 -15
  517. scipy/stats/_levy_stable/levyst.cpython-313-aarch64-linux-musl.so +0 -0
  518. scipy/stats/_morestats.py +118 -73
  519. scipy/stats/_mstats_basic.py +13 -17
  520. scipy/stats/_mstats_extras.py +8 -8
  521. scipy/stats/_multivariate.py +89 -113
  522. scipy/stats/_new_distributions.py +97 -20
  523. scipy/stats/_page_trend_test.py +12 -5
  524. scipy/stats/_probability_distribution.py +265 -43
  525. scipy/stats/_qmc.py +14 -9
  526. scipy/stats/_qmc_cy.cpython-313-aarch64-linux-musl.so +0 -0
  527. scipy/stats/_qmvnt.py +16 -95
  528. scipy/stats/_qmvnt_cy.cpython-313-aarch64-linux-musl.so +0 -0
  529. scipy/stats/_quantile.py +335 -0
  530. scipy/stats/_rcont/rcont.cpython-313-aarch64-linux-musl.so +0 -0
  531. scipy/stats/_resampling.py +4 -29
  532. scipy/stats/_sampling.py +1 -1
  533. scipy/stats/_sobol.cpython-313-aarch64-linux-musl.so +0 -0
  534. scipy/stats/_stats.cpython-313-aarch64-linux-musl.so +0 -0
  535. scipy/stats/_stats_mstats_common.py +21 -2
  536. scipy/stats/_stats_py.py +550 -476
  537. scipy/stats/_stats_pythran.cpython-313-aarch64-linux-musl.so +0 -0
  538. scipy/stats/_unuran/unuran_wrapper.cpython-313-aarch64-linux-musl.so +0 -0
  539. scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
  540. scipy/stats/_variation.py +6 -8
  541. scipy/stats/_wilcoxon.py +13 -7
  542. scipy/stats/tests/common_tests.py +6 -4
  543. scipy/stats/tests/test_axis_nan_policy.py +62 -24
  544. scipy/stats/tests/test_continued_fraction.py +173 -0
  545. scipy/stats/tests/test_continuous.py +379 -60
  546. scipy/stats/tests/test_continuous_basic.py +18 -12
  547. scipy/stats/tests/test_discrete_basic.py +14 -8
  548. scipy/stats/tests/test_discrete_distns.py +16 -16
  549. scipy/stats/tests/test_distributions.py +95 -75
  550. scipy/stats/tests/test_entropy.py +40 -48
  551. scipy/stats/tests/test_fit.py +4 -3
  552. scipy/stats/tests/test_hypotests.py +153 -24
  553. scipy/stats/tests/test_kdeoth.py +109 -41
  554. scipy/stats/tests/test_marray.py +289 -0
  555. scipy/stats/tests/test_morestats.py +79 -47
  556. scipy/stats/tests/test_mstats_basic.py +3 -3
  557. scipy/stats/tests/test_multivariate.py +434 -83
  558. scipy/stats/tests/test_qmc.py +13 -10
  559. scipy/stats/tests/test_quantile.py +199 -0
  560. scipy/stats/tests/test_rank.py +119 -112
  561. scipy/stats/tests/test_resampling.py +47 -56
  562. scipy/stats/tests/test_sampling.py +9 -4
  563. scipy/stats/tests/test_stats.py +799 -939
  564. scipy/stats/tests/test_variation.py +8 -6
  565. scipy/version.py +2 -2
  566. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
  567. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
  568. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +1262 -1269
  569. scipy.libs/libgcc_s-69c45f16.so.1 +0 -0
  570. scipy.libs/libgfortran-db0b6589.so.5.0.0 +0 -0
  571. scipy.libs/{libstdc++-1b614e01.so.6.0.32 → libstdc++-1f1a71be.so.6.0.33} +0 -0
  572. scipy/_lib/array_api_extra/_funcs.py +0 -484
  573. scipy/_lib/array_api_extra/_typing.py +0 -8
  574. scipy/interpolate/_bspl.cpython-313-aarch64-linux-musl.so +0 -0
  575. scipy/optimize/_cobyla.cpython-313-aarch64-linux-musl.so +0 -0
  576. scipy/optimize/_cython_nnls.cpython-313-aarch64-linux-musl.so +0 -0
  577. scipy/optimize/_slsqp.cpython-313-aarch64-linux-musl.so +0 -0
  578. scipy/spatial/qhull_src/COPYING.txt +0 -38
  579. scipy/special/libsf_error_state.so +0 -0
  580. scipy/special/tests/test_log_softmax.py +0 -109
  581. scipy/special/tests/test_xsf_cuda.py +0 -114
  582. scipy/special/xsf/binom.h +0 -89
  583. scipy/special/xsf/cdflib.h +0 -100
  584. scipy/special/xsf/cephes/airy.h +0 -307
  585. scipy/special/xsf/cephes/besselpoly.h +0 -51
  586. scipy/special/xsf/cephes/beta.h +0 -257
  587. scipy/special/xsf/cephes/cbrt.h +0 -131
  588. scipy/special/xsf/cephes/chbevl.h +0 -85
  589. scipy/special/xsf/cephes/chdtr.h +0 -193
  590. scipy/special/xsf/cephes/const.h +0 -87
  591. scipy/special/xsf/cephes/ellie.h +0 -293
  592. scipy/special/xsf/cephes/ellik.h +0 -251
  593. scipy/special/xsf/cephes/ellpe.h +0 -107
  594. scipy/special/xsf/cephes/ellpk.h +0 -117
  595. scipy/special/xsf/cephes/expn.h +0 -260
  596. scipy/special/xsf/cephes/gamma.h +0 -398
  597. scipy/special/xsf/cephes/hyp2f1.h +0 -596
  598. scipy/special/xsf/cephes/hyperg.h +0 -361
  599. scipy/special/xsf/cephes/i0.h +0 -149
  600. scipy/special/xsf/cephes/i1.h +0 -158
  601. scipy/special/xsf/cephes/igam.h +0 -421
  602. scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
  603. scipy/special/xsf/cephes/igami.h +0 -313
  604. scipy/special/xsf/cephes/j0.h +0 -225
  605. scipy/special/xsf/cephes/j1.h +0 -198
  606. scipy/special/xsf/cephes/jv.h +0 -715
  607. scipy/special/xsf/cephes/k0.h +0 -164
  608. scipy/special/xsf/cephes/k1.h +0 -163
  609. scipy/special/xsf/cephes/kn.h +0 -243
  610. scipy/special/xsf/cephes/lanczos.h +0 -112
  611. scipy/special/xsf/cephes/ndtr.h +0 -275
  612. scipy/special/xsf/cephes/poch.h +0 -85
  613. scipy/special/xsf/cephes/polevl.h +0 -167
  614. scipy/special/xsf/cephes/psi.h +0 -194
  615. scipy/special/xsf/cephes/rgamma.h +0 -111
  616. scipy/special/xsf/cephes/scipy_iv.h +0 -811
  617. scipy/special/xsf/cephes/shichi.h +0 -248
  618. scipy/special/xsf/cephes/sici.h +0 -224
  619. scipy/special/xsf/cephes/sindg.h +0 -221
  620. scipy/special/xsf/cephes/tandg.h +0 -139
  621. scipy/special/xsf/cephes/trig.h +0 -58
  622. scipy/special/xsf/cephes/unity.h +0 -186
  623. scipy/special/xsf/cephes/zeta.h +0 -172
  624. scipy/special/xsf/config.h +0 -304
  625. scipy/special/xsf/digamma.h +0 -205
  626. scipy/special/xsf/error.h +0 -57
  627. scipy/special/xsf/evalpoly.h +0 -47
  628. scipy/special/xsf/expint.h +0 -266
  629. scipy/special/xsf/hyp2f1.h +0 -694
  630. scipy/special/xsf/iv_ratio.h +0 -173
  631. scipy/special/xsf/lambertw.h +0 -150
  632. scipy/special/xsf/loggamma.h +0 -163
  633. scipy/special/xsf/sici.h +0 -200
  634. scipy/special/xsf/tools.h +0 -427
  635. scipy/special/xsf/trig.h +0 -164
  636. scipy/special/xsf/wright_bessel.h +0 -843
  637. scipy/special/xsf/zlog1.h +0 -35
  638. scipy/stats/_mvn.cpython-313-aarch64-linux-musl.so +0 -0
  639. scipy.libs/libgcc_s-7393e603.so.1 +0 -0
  640. scipy.libs/libgfortran-eb933d8e.so.5.0.0 +0 -0
  641. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
scipy/conftest.py CHANGED
@@ -4,6 +4,7 @@ import os
4
4
  import warnings
5
5
  import tempfile
6
6
  from contextlib import contextmanager
7
+ from typing import Literal
7
8
 
8
9
  import numpy as np
9
10
  import numpy.testing as npt
@@ -11,8 +12,12 @@ import pytest
11
12
  import hypothesis
12
13
 
13
14
  from scipy._lib._fpumode import get_fpu_mode
15
+ from scipy._lib._array_api import (
16
+ SCIPY_ARRAY_API, SCIPY_DEVICE, array_namespace, default_xp,
17
+ is_cupy, is_dask, is_jax,
18
+ )
14
19
  from scipy._lib._testutils import FPUModeChangeWarning
15
- from scipy._lib._array_api import SCIPY_ARRAY_API, SCIPY_DEVICE
20
+ from scipy._lib.array_api_extra.testing import patch_lazy_xp_functions
16
21
  from scipy._lib import _pep440
17
22
 
18
23
  try:
@@ -29,12 +34,6 @@ except Exception:
29
34
 
30
35
 
31
36
  def pytest_configure(config):
32
- config.addinivalue_line("markers",
33
- "slow: Tests that are very slow.")
34
- config.addinivalue_line("markers",
35
- "xslow: mark test as extremely slow (not run unless explicitly requested)")
36
- config.addinivalue_line("markers",
37
- "xfail_on_32bit: mark test as failing on 32-bit platforms")
38
37
  try:
39
38
  import pytest_timeout # noqa:F401
40
39
  except Exception:
@@ -47,14 +46,7 @@ def pytest_configure(config):
47
46
  except Exception:
48
47
  config.addinivalue_line(
49
48
  "markers", 'fail_slow: mark a test for a non-default timeout failure')
50
- config.addinivalue_line("markers",
51
- "skip_xp_backends(backends, reason=None, np_only=False, cpu_only=False, "
52
- "exceptions=None): "
53
- "mark the desired skip configuration for the `skip_xp_backends` fixture.")
54
- config.addinivalue_line("markers",
55
- "xfail_xp_backends(backends, reason=None, np_only=False, cpu_only=False, "
56
- "exceptions=None): "
57
- "mark the desired xfail configuration for the `xfail_xp_backends` fixture.")
49
+
58
50
  if not PARALLEL_RUN_AVAILABLE:
59
51
  config.addinivalue_line(
60
52
  'markers',
@@ -141,17 +133,21 @@ if not PARALLEL_RUN_AVAILABLE:
141
133
 
142
134
 
143
135
  # Array API backend handling
136
+ xp_known_backends = {'numpy', 'array_api_strict', 'torch', 'cupy', 'jax.numpy',
137
+ 'dask.array'}
144
138
  xp_available_backends = {'numpy': np}
139
+ xp_skip_cpu_only_backends = set()
140
+ xp_skip_eager_only_backends = set()
145
141
 
146
- if SCIPY_ARRAY_API and isinstance(SCIPY_ARRAY_API, str):
142
+ if SCIPY_ARRAY_API:
147
143
  # fill the dict of backends with available libraries
148
144
  try:
149
145
  import array_api_strict
150
146
  xp_available_backends.update({'array_api_strict': array_api_strict})
151
- if _pep440.parse(array_api_strict.__version__) < _pep440.Version('2.0'):
152
- raise ImportError("array-api-strict must be >= version 2.0")
147
+ if _pep440.parse(array_api_strict.__version__) < _pep440.Version('2.3'):
148
+ raise ImportError("array-api-strict must be >= version 2.3")
153
149
  array_api_strict.set_array_api_strict_flags(
154
- api_version='2023.12'
150
+ api_version='2024.12'
155
151
  )
156
152
  except ImportError:
157
153
  pass
@@ -159,14 +155,35 @@ if SCIPY_ARRAY_API and isinstance(SCIPY_ARRAY_API, str):
159
155
  try:
160
156
  import torch # type: ignore[import-not-found]
161
157
  xp_available_backends.update({'torch': torch})
162
- # can use `mps` or `cpu`
163
158
  torch.set_default_device(SCIPY_DEVICE)
159
+ if SCIPY_DEVICE != "cpu":
160
+ xp_skip_cpu_only_backends.add('torch')
161
+
162
+ # default to float64 unless explicitly requested
163
+ default = os.getenv('SCIPY_DEFAULT_DTYPE', default='float64')
164
+ if default == 'float64':
165
+ torch.set_default_dtype(torch.float64)
166
+ elif default != "float32":
167
+ raise ValueError(
168
+ "SCIPY_DEFAULT_DTYPE env var, if set, can only be either 'float64' "
169
+ f"or 'float32'. Got '{default}' instead."
170
+ )
164
171
  except ImportError:
165
172
  pass
166
173
 
167
174
  try:
168
175
  import cupy # type: ignore[import-not-found]
176
+ # Note: cupy disregards SCIPY_DEVICE and always runs on cuda.
177
+ # It will fail to import if you don't have CUDA hardware and drivers.
169
178
  xp_available_backends.update({'cupy': cupy})
179
+ xp_skip_cpu_only_backends.add('cupy')
180
+
181
+ # this is annoying in CuPy 13.x
182
+ warnings.filterwarnings(
183
+ 'ignore', 'cupyx.jit.rawkernel is experimental', category=FutureWarning
184
+ )
185
+ from cupyx.scipy import signal
186
+ del signal
170
187
  except ImportError:
171
188
  pass
172
189
 
@@ -175,16 +192,35 @@ if SCIPY_ARRAY_API and isinstance(SCIPY_ARRAY_API, str):
175
192
  xp_available_backends.update({'jax.numpy': jax.numpy})
176
193
  jax.config.update("jax_enable_x64", True)
177
194
  jax.config.update("jax_default_device", jax.devices(SCIPY_DEVICE)[0])
195
+ if SCIPY_DEVICE != "cpu":
196
+ xp_skip_cpu_only_backends.add('jax.numpy')
197
+ # JAX can be eager or lazy (when wrapped in jax.jit). However it is
198
+ # recommended by upstream devs to assume it's always lazy.
199
+ xp_skip_eager_only_backends.add('jax.numpy')
200
+ except ImportError:
201
+ pass
202
+
203
+ try:
204
+ import dask.array as da
205
+ xp_available_backends.update({'dask.array': da})
206
+ # Dask can wrap around cupy. However, this is untested in scipy
207
+ # (and will almost surely not work as delegation will misbehave).
208
+
209
+ # Dask, strictly speaking, can be eager, in the sense that
210
+ # __array__, __bool__ etc. are implemented and do not raise.
211
+ # However, calling them triggers an extra computation of the whole graph
212
+ # until that point, which is highly destructive for performance.
213
+ xp_skip_eager_only_backends.add('dask.array')
178
214
  except ImportError:
179
215
  pass
180
216
 
181
217
  # by default, use all available backends
182
- if SCIPY_ARRAY_API.lower() not in ("1", "true"):
218
+ if (
219
+ isinstance(SCIPY_ARRAY_API, str)
220
+ and SCIPY_ARRAY_API.lower() not in ("1", "true", "all")
221
+ ):
183
222
  SCIPY_ARRAY_API_ = json.loads(SCIPY_ARRAY_API)
184
-
185
- if 'all' in SCIPY_ARRAY_API_:
186
- pass # same as True
187
- else:
223
+ if SCIPY_ARRAY_API_ != ['all']:
188
224
  # only select a subset of backend by filtering out the dict
189
225
  try:
190
226
  xp_available_backends = {
@@ -192,166 +228,220 @@ if SCIPY_ARRAY_API and isinstance(SCIPY_ARRAY_API, str):
192
228
  for backend in SCIPY_ARRAY_API_
193
229
  }
194
230
  except KeyError:
195
- msg = f"'--array-api-backend' must be in {xp_available_backends.keys()}"
231
+ msg = ("'--array-api-backend' must be in "
232
+ f"{list(xp_available_backends)}; got {SCIPY_ARRAY_API_}")
196
233
  raise ValueError(msg)
197
234
 
198
- if 'cupy' in xp_available_backends:
199
- SCIPY_DEVICE = 'cuda'
235
+ assert not set(xp_available_backends) - xp_known_backends
236
+ xp_skip_np_only_backends = set(xp_available_backends) - {"numpy"}
200
237
 
201
- array_api_compatible = pytest.mark.parametrize("xp", xp_available_backends.values())
202
238
 
203
- skip_xp_invalid_arg = pytest.mark.skipif(SCIPY_ARRAY_API,
204
- reason = ('Test involves masked arrays, object arrays, or other types '
205
- 'that are not valid input when `SCIPY_ARRAY_API` is used.'))
239
+ @pytest.fixture(params=[
240
+ pytest.param(v, id=k, marks=pytest.mark.array_api_backends)
241
+ for k, v in xp_available_backends.items()
242
+ ])
243
+ def xp(request, monkeypatch):
244
+ """Run the test that uses this fixture on each available array API library.
206
245
 
246
+ You can select all and only the tests that use the `xp` fixture by
247
+ passing `-m array_api_backends` to pytest.
207
248
 
208
- def _backends_kwargs_from_request(request, skip_or_xfail):
209
- """A helper for {skip,xfail}_xp_backends"""
210
- # do not allow multiple backends
211
- args_ = request.keywords[f'{skip_or_xfail}_xp_backends'].args
212
- if len(args_) > 1:
213
- # np_only / cpu_only has args=(), otherwise it's ('numpy',)
214
- # and we do not allow ('numpy', 'cupy')
215
- raise ValueError(f"multiple backends: {args_}")
216
-
217
- markers = list(request.node.iter_markers(f'{skip_or_xfail}_xp_backends'))
218
- backends = []
219
- kwargs = {}
220
- for marker in markers:
221
- if marker.kwargs.get('np_only'):
222
- kwargs['np_only'] = True
223
- kwargs['exceptions'] = marker.kwargs.get('exceptions', [])
224
- elif marker.kwargs.get('cpu_only'):
225
- if not kwargs.get('np_only'):
226
- # if np_only is given, it is certainly cpu only
227
- kwargs['cpu_only'] = True
228
- kwargs['exceptions'] = marker.kwargs.get('exceptions', [])
229
-
230
- # add backends, if any
231
- if len(marker.args) > 0:
232
- backend = marker.args[0] # was a tuple, ('numpy',) etc
233
- backends.append(backend)
234
- kwargs.update(**{backend: marker.kwargs})
249
+ You can select where individual tests run through the `@skip_xp_backends`,
250
+ `@xfail_xp_backends`, and `@skip_xp_invalid_arg` pytest markers.
235
251
 
236
- return backends, kwargs
252
+ Please read: https://docs.scipy.org/doc/scipy/dev/api-dev/array_api.html#adding-tests
253
+ """
254
+ # Read all @pytest.marks.skip_xp_backends markers that decorate to the test,
255
+ # if any, and raise pytest.skip() if the current xp is in the list.
256
+ skip_or_xfail_xp_backends(request, "skip")
257
+ # Read all @pytest.marks.xfail_xp_backends markers that decorate the test,
258
+ # if any, and raise pytest.xfail() if the current xp is in the list.
259
+ skip_or_xfail_xp_backends(request, "xfail")
260
+
261
+ xp = request.param
262
+ # Potentially wrap namespace with array_api_compat
263
+ xp = array_namespace(xp.empty(0))
264
+
265
+ if SCIPY_ARRAY_API:
266
+ # If request.param==jax.numpy, wrap tested functions in jax.jit
267
+ patch_lazy_xp_functions(
268
+ xp=request.param, request=request, monkeypatch=monkeypatch
269
+ )
237
270
 
271
+ # Throughout all calls to assert_almost_equal, assert_array_almost_equal, and
272
+ # xp_assert_* functions, test that the array namespace is xp in both the
273
+ # expected and actual arrays. This is to detect the case where both arrays are
274
+ # erroneously just plain numpy while xp is something else.
275
+ with default_xp(xp):
276
+ yield xp
277
+ else:
278
+ yield xp
238
279
 
239
- @pytest.fixture
240
- def skip_xp_backends(xp, request):
241
- """skip_xp_backends(backend=None, reason=None, np_only=False, cpu_only=False, exceptions=None)
242
280
 
243
- Skip a decorated test for the provided backend, or skip a category of backends.
281
+ skip_xp_invalid_arg = pytest.mark.skipif(SCIPY_ARRAY_API,
282
+ reason = ('Test involves masked arrays, object arrays, or other types '
283
+ 'that are not valid input when `SCIPY_ARRAY_API` is used.'))
244
284
 
245
- See ``skip_or_xfail_backends`` docstring for details. Note that, contrary to
246
- ``skip_or_xfail_backends``, the ``backend`` and ``reason`` arguments are optional
247
- single strings: this function only skips a single backend at a time.
248
- To skip multiple backends, provide multiple decorators.
249
- """ # noqa: E501
250
- if "skip_xp_backends" not in request.keywords:
251
- return
252
285
 
253
- backends, kwargs = _backends_kwargs_from_request(request, skip_or_xfail='skip')
254
- skip_or_xfail_xp_backends(xp, backends, kwargs, skip_or_xfail='skip')
286
+ def _backends_kwargs_from_request(request, skip_or_xfail):
287
+ """A helper for {skip,xfail}_xp_backends.
255
288
 
289
+ Return dict of {backend to skip/xfail: top reason to skip/xfail it}
290
+ """
291
+ markers = list(request.node.iter_markers(f'{skip_or_xfail}_xp_backends'))
292
+ reasons = {backend: [] for backend in xp_known_backends}
256
293
 
257
- @pytest.fixture
258
- def xfail_xp_backends(xp, request):
259
- """xfail_xp_backends(backend=None, reason=None, np_only=False, cpu_only=False, exceptions=None)
294
+ for marker in markers:
295
+ invalid_kwargs = set(marker.kwargs) - {
296
+ "cpu_only", "np_only", "eager_only", "reason", "exceptions"}
297
+ if invalid_kwargs:
298
+ raise TypeError(f"Invalid kwargs: {invalid_kwargs}")
299
+
300
+ exceptions = set(marker.kwargs.get('exceptions', []))
301
+ invalid_exceptions = exceptions - xp_known_backends
302
+ if (invalid_exceptions := list(exceptions - xp_known_backends)):
303
+ raise ValueError(f"Unknown backend(s): {invalid_exceptions}; "
304
+ f"must be a subset of {list(xp_known_backends)}")
305
+
306
+ if marker.kwargs.get('np_only', False):
307
+ reason = marker.kwargs.get("reason") or "do not run with non-NumPy backends"
308
+ for backend in xp_skip_np_only_backends - exceptions:
309
+ reasons[backend].append(reason)
310
+
311
+ elif marker.kwargs.get('cpu_only', False):
312
+ reason = marker.kwargs.get("reason") or (
313
+ "no array-agnostic implementation or delegation available "
314
+ "for this backend and device")
315
+ for backend in xp_skip_cpu_only_backends - exceptions:
316
+ reasons[backend].append(reason)
317
+
318
+ elif marker.kwargs.get('eager_only', False):
319
+ reason = marker.kwargs.get("reason") or (
320
+ "eager checks not executed on lazy backends")
321
+ for backend in xp_skip_eager_only_backends - exceptions:
322
+ reasons[backend].append(reason)
260
323
 
261
- xfail a decorated test for the provided backend, or xfail a category of backends.
324
+ # add backends, if any
325
+ if len(marker.args) == 1:
326
+ backend = marker.args[0]
327
+ if backend not in xp_known_backends:
328
+ raise ValueError(f"Unknown backend: {backend}; "
329
+ f"must be one of {list(xp_known_backends)}")
330
+ reason = marker.kwargs.get("reason") or (
331
+ f"do not run with array API backend: {backend}")
332
+ # reason overrides the ones from cpu_only, np_only, and eager_only.
333
+ # This is regardless of order of appearence of the markers.
334
+ reasons[backend].insert(0, reason)
335
+
336
+ for kwarg in ("cpu_only", "np_only", "eager_only", "exceptions"):
337
+ if kwarg in marker.kwargs:
338
+ raise ValueError(f"{kwarg} is mutually exclusive with {backend}")
339
+
340
+ elif len(marker.args) > 1:
341
+ raise ValueError(
342
+ f"Please specify only one backend per marker: {marker.args}"
343
+ )
344
+
345
+ return {backend: backend_reasons[0]
346
+ for backend, backend_reasons in reasons.items()
347
+ if backend_reasons}
348
+
349
+
350
+ def skip_or_xfail_xp_backends(request: pytest.FixtureRequest,
351
+ skip_or_xfail: Literal['skip', 'xfail']) -> None:
352
+ """
353
+ Helper of the `xp` fixture.
354
+ Skip or xfail based on the ``skip_xp_backends`` or ``xfail_xp_backends`` markers.
262
355
 
263
- See ``skip_or_xfail_backends`` docstring for details. Note that, contrary to
264
- ``skip_or_xfail_backends``, the ``backend`` and ``reason`` arguments are optional
265
- single strings: this function only xfails a single backend at a time.
266
- To xfail multiple backends, provide multiple decorators.
267
- """ # noqa: E501
268
- if "xfail_xp_backends" not in request.keywords:
269
- return
270
- backends, kwargs = _backends_kwargs_from_request(request, skip_or_xfail='xfail')
271
- skip_or_xfail_xp_backends(xp, backends, kwargs, skip_or_xfail='xfail')
356
+ See the "Support for the array API standard" docs page for usage examples.
272
357
 
358
+ Usage
359
+ -----
360
+ ::
361
+ skip_xp_backends = pytest.mark.skip_xp_backends
362
+ xfail_xp_backends = pytest.mark.xfail_xp_backends
363
+ ...
273
364
 
274
- def skip_or_xfail_xp_backends(xp, backends, kwargs, skip_or_xfail='skip'):
275
- """
276
- Skip based on the ``skip_xp_backends`` or ``xfail_xp_backends`` marker.
365
+ @skip_xp_backends(backend, *, reason=None)
366
+ @skip_xp_backends(*, cpu_only=True, exceptions=(), reason=None)
367
+ @skip_xp_backends(*, eager_only=True, exceptions=(), reason=None)
368
+ @skip_xp_backends(*, np_only=True, exceptions=(), reason=None)
277
369
 
278
- See the "Support for the array API standard" docs page for usage examples.
370
+ @xfail_xp_backends(backend, *, reason=None)
371
+ @xfail_xp_backends(*, cpu_only=True, exceptions=(), reason=None)
372
+ @xfail_xp_backends(*, eager_only=True, exceptions=(), reason=None)
373
+ @xfail_xp_backends(*, np_only=True, exceptions=(), reason=None)
279
374
 
280
375
  Parameters
281
376
  ----------
282
- backends : tuple
283
- Backends to skip/xfail, e.g. ``("array_api_strict", "torch")``.
284
- These are overriden when ``np_only`` is ``True``, and are not
285
- necessary to provide for non-CPU backends when ``cpu_only`` is ``True``.
286
- For a custom reason to apply, you should pass a dict ``{'reason': '...'}``
287
- to a keyword matching the name of the backend.
288
- reason : str, optional
289
- A reason for the skip/xfail in the case of ``np_only=True``.
290
- If unprovided, a default reason is used. Note that it is not possible
291
- to specify a custom reason with ``cpu_only``.
377
+ backend : str, optional
378
+ Backend to skip/xfail, e.g. ``"torch"``.
379
+ Mutually exclusive with ``cpu_only``, ``eager_only``, and ``np_only``.
380
+ cpu_only : bool, optional
381
+ When ``True``, the test is skipped/xfailed on non-CPU devices,
382
+ minus exceptions. Mutually exclusive with ``backend``.
383
+ eager_only : bool, optional
384
+ When ``True``, the test is skipped/xfailed for lazy backends, e.g. those
385
+ with major caveats when invoking ``__array__``, ``__bool__``, ``__float__``,
386
+ or ``__complex__``, minus exceptions. Mutually exclusive with ``backend``.
292
387
  np_only : bool, optional
293
388
  When ``True``, the test is skipped/xfailed for all backends other
294
- than the default NumPy backend. There is no need to provide
295
- any ``backends`` in this case. To specify a reason, pass a
296
- value to ``reason``. Default: ``False``.
297
- cpu_only : bool, optional
298
- When ``True``, the test is skipped/xfailed on non-CPU devices.
299
- There is no need to provide any ``backends`` in this case,
300
- but any ``backends`` will also be skipped on the CPU.
301
- Default: ``False``.
302
- exceptions : list, optional
303
- A list of exceptions for use with ``cpu_only`` or ``np_only``.
389
+ than the default NumPy backend and the exceptions.
390
+ Mutually exclusive with ``backend``. Implies ``cpu_only`` and ``eager_only``.
391
+ reason : str, optional
392
+ A reason for the skip/xfail. If omitted, a default reason is used.
393
+ exceptions : list[str], optional
394
+ A list of exceptions for use with ``cpu_only``, ``eager_only``, or ``np_only``.
304
395
  This should be provided when delegation is implemented for some,
305
396
  but not all, non-CPU/non-NumPy backends.
306
- skip_or_xfail : str
307
- ``'skip'`` to skip, ``'xfail'`` to xfail.
308
397
  """
309
- skip_or_xfail = getattr(pytest, skip_or_xfail)
310
- np_only = kwargs.get("np_only", False)
311
- cpu_only = kwargs.get("cpu_only", False)
312
- exceptions = kwargs.get("exceptions", [])
313
-
314
- if reasons := kwargs.get("reasons"):
315
- raise ValueError(f"provide a single `reason=` kwarg; got {reasons=} instead")
316
-
317
- # input validation
318
- if np_only and cpu_only:
319
- # np_only is a stricter subset of cpu_only
320
- cpu_only = False
321
- if exceptions and not (cpu_only or np_only):
322
- raise ValueError("`exceptions` is only valid alongside `cpu_only` or `np_only`")
323
-
324
- if np_only:
325
- reason = kwargs.get("reason", "do not run with non-NumPy backends.")
326
- if not isinstance(reason, str) and len(reason) > 1:
327
- raise ValueError("please provide a singleton `reason` "
328
- "when using `np_only`")
329
- if xp.__name__ != 'numpy' and xp.__name__ not in exceptions:
330
- skip_or_xfail(reason=reason)
398
+ if f"{skip_or_xfail}_xp_backends" not in request.keywords:
331
399
  return
332
- if cpu_only:
333
- reason = ("no array-agnostic implementation or delegation available "
334
- "for this backend and device")
335
- exceptions = [] if exceptions is None else exceptions
336
- if SCIPY_ARRAY_API and SCIPY_DEVICE != 'cpu':
337
- if xp.__name__ == 'cupy' and 'cupy' not in exceptions:
338
- skip_or_xfail(reason=reason)
339
- elif xp.__name__ == 'torch' and 'torch' not in exceptions:
340
- if 'cpu' not in xp.empty(0).device.type:
341
- skip_or_xfail(reason=reason)
342
- elif xp.__name__ == 'jax.numpy' and 'jax.numpy' not in exceptions:
343
- for d in xp.empty(0).devices():
344
- if 'cpu' not in d.device_kind:
345
- skip_or_xfail(reason=reason)
346
-
347
- if backends is not None:
348
- for i, backend in enumerate(backends):
349
- if xp.__name__ == backend:
350
- reason = kwargs[backend].get('reason')
351
- if not reason:
352
- reason = f"do not run with array API backend: {backend}"
353
-
354
- skip_or_xfail(reason=reason)
400
+
401
+ skip_xfail_reasons = _backends_kwargs_from_request(
402
+ request, skip_or_xfail=skip_or_xfail
403
+ )
404
+ xp = request.param
405
+ if xp.__name__ in skip_xfail_reasons:
406
+ reason = skip_xfail_reasons[xp.__name__]
407
+ assert reason # Default reason applied above
408
+ skip_or_xfail = getattr(pytest, skip_or_xfail)
409
+ skip_or_xfail(reason=reason)
410
+
411
+
412
+ @pytest.fixture
413
+ def devices(xp):
414
+ """Fixture that returns a list of all devices for the backend, plus None.
415
+ Used to test input->output device propagation.
416
+
417
+ Usage
418
+ -----
419
+ from scipy._lib._array_api import xp_device
420
+
421
+ def test_device(xp, devices):
422
+ for d in devices:
423
+ x = xp.asarray(..., device=d)
424
+ y = f(x)
425
+ assert xp_device(y) == xp_device(x)
426
+ """
427
+ if is_cupy(xp):
428
+ # CuPy does not support devices other than the current one
429
+ # data-apis/array-api-compat#293
430
+ pytest.xfail(reason="data-apis/array-api-compat#293")
431
+ if is_dask(xp):
432
+ # Skip dummy DASK_DEVICE from array-api-compat, which does not propagate
433
+ return ["cpu", None]
434
+ if is_jax(xp):
435
+ # The .device attribute is not accessible inside jax.jit; the consequence
436
+ # (downstream of array-api-compat hacks) is that a non-default device in
437
+ # input is not guaranteed to propagate to the output even if the scipy code
438
+ # states `device=xp_device(arg)`` in all array creation functions.
439
+ # While this issue is specific to jax.jit, it would be unnecessarily
440
+ # verbose to skip the test for each jit-capable function and run it for
441
+ # those that only support eager mode.
442
+ pytest.xfail(reason="jax-ml/jax#26000")
443
+
444
+ return xp.__array_namespace_info__().devices() + [None]
355
445
 
356
446
 
357
447
  # Following the approach of NumPy's conftest.py...
@@ -521,12 +611,25 @@ if HAVE_SCPDT:
521
611
  # equivalent to "pytest --ignore=path/to/file"
522
612
  "scipy/special/_precompute",
523
613
  "scipy/interpolate/_interpnd_info.py",
614
+ "scipy/interpolate/_rbfinterp_pythran.py",
615
+ "scipy/_build_utils/tempita.py",
524
616
  "scipy/_lib/array_api_compat",
525
617
  "scipy/_lib/highs",
526
618
  "scipy/_lib/unuran",
527
619
  "scipy/_lib/_gcutils.py",
528
620
  "scipy/_lib/doccer.py",
529
621
  "scipy/_lib/_uarray",
622
+ "scipy/linalg/_cython_signature_generator.py",
623
+ "scipy/linalg/_generate_pyx.py",
624
+ "scipy/linalg/_linalg_pythran.py",
625
+ "scipy/linalg/_matfuncs_sqrtm_triu.py",
626
+ "scipy/ndimage/utils/generate_label_testvectors.py",
627
+ "scipy/optimize/_group_columns.py",
628
+ "scipy/optimize/_max_len_seq_inner.py",
629
+ "scipy/signal/_max_len_seq_inner.py",
630
+ "scipy/sparse/_generate_sparsetools.py",
631
+ "scipy/special/_generate_pyx.py",
632
+ "scipy/stats/_stats_pythran.py",
530
633
  ]
531
634
 
532
635
  dt_config.pytest_extra_xfail = {
@@ -549,4 +652,7 @@ if HAVE_SCPDT:
549
652
  }
550
653
 
551
654
  dt_config.strict_check = True
655
+
656
+ # ignore Matplotlib's `ax.text`:
657
+ dt_config.stopwords.add('.text(')
552
658
  ############################################################################
@@ -15,7 +15,7 @@ from ._codata import value as _cd
15
15
  if TYPE_CHECKING:
16
16
  import numpy.typing as npt
17
17
 
18
- from scipy._lib._array_api import array_namespace, _asarray
18
+ from scipy._lib._array_api import array_namespace, _asarray, xp_capabilities
19
19
 
20
20
 
21
21
  """
@@ -225,6 +225,7 @@ kgf = kilogram_force = g # * 1 kg
225
225
  # functions for conversions that are not linear
226
226
 
227
227
 
228
+ @xp_capabilities()
228
229
  def convert_temperature(
229
230
  val: "npt.ArrayLike",
230
231
  old_scale: str,
@@ -304,6 +305,7 @@ def convert_temperature(
304
305
  # optics
305
306
 
306
307
 
308
+ @xp_capabilities()
307
309
  def lambda2nu(lambda_: "npt.ArrayLike") -> Any:
308
310
  """
309
311
  Convert wavelength to optical frequency
@@ -335,6 +337,7 @@ def lambda2nu(lambda_: "npt.ArrayLike") -> Any:
335
337
  return c / _asarray(lambda_, xp=xp, subok=True)
336
338
 
337
339
 
340
+ @xp_capabilities()
338
341
  def nu2lambda(nu: "npt.ArrayLike") -> Any:
339
342
  """
340
343
  Convert optical frequency to wavelength.
@@ -31,8 +31,8 @@ def test_basic_table_parse():
31
31
 
32
32
 
33
33
  def test_basic_lookup():
34
- assert_equal('%d %s' % (_cd.value('speed of light in vacuum'),
35
- _cd.unit('speed of light in vacuum')),
34
+ assert_equal('{} {}'.format(int(_cd.value('speed of light in vacuum')),
35
+ _cd.unit('speed of light in vacuum')),
36
36
  '299792458 m s^-1')
37
37
 
38
38
 
@@ -1,15 +1,13 @@
1
1
  import pytest
2
2
 
3
3
  import scipy.constants as sc
4
- from scipy.conftest import array_api_compatible
5
4
  from scipy._lib._array_api_no_0d import xp_assert_equal, xp_assert_close
6
- from numpy.testing import assert_allclose
5
+ from scipy._lib._array_api import make_xp_test_case
7
6
 
8
-
9
- pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_xp_backends")]
10
- skip_xp_backends = pytest.mark.skip_xp_backends
7
+ lazy_xp_modules = [sc]
11
8
 
12
9
 
10
+ @make_xp_test_case(sc.convert_temperature)
13
11
  class TestConvertTemperature:
14
12
  def test_convert_temperature(self, xp):
15
13
  xp_assert_equal(sc.convert_temperature(xp.asarray(32.), 'f', 'Celsius'),
@@ -52,39 +50,34 @@ class TestConvertTemperature:
52
50
  'rankine', 'kelvin'),
53
51
  xp.asarray([273.15, 0.], dtype=xp.float64), rtol=0., atol=1e-13)
54
52
 
55
- @skip_xp_backends(np_only=True, reason='Python list input uses NumPy backend')
56
53
  def test_convert_temperature_array_like(self):
57
- assert_allclose(sc.convert_temperature([491.67, 0.], 'rankine', 'kelvin'),
54
+ xp_assert_close(sc.convert_temperature([491.67, 0.], 'rankine', 'kelvin'),
58
55
  [273.15, 0.], rtol=0., atol=1e-13)
59
56
 
60
57
 
61
- @skip_xp_backends(np_only=True, reason='Python int input uses NumPy backend')
62
- def test_convert_temperature_errors(self, xp):
58
+ def test_convert_temperature_errors(self):
63
59
  with pytest.raises(NotImplementedError, match="old_scale="):
64
60
  sc.convert_temperature(1, old_scale="cheddar", new_scale="kelvin")
65
61
  with pytest.raises(NotImplementedError, match="new_scale="):
66
62
  sc.convert_temperature(1, old_scale="kelvin", new_scale="brie")
67
63
 
68
64
 
65
+ @make_xp_test_case(sc.lambda2nu)
69
66
  class TestLambdaToNu:
70
67
  def test_lambda_to_nu(self, xp):
71
68
  xp_assert_equal(sc.lambda2nu(xp.asarray([sc.speed_of_light, 1])),
72
69
  xp.asarray([1, sc.speed_of_light]))
73
70
 
74
71
 
75
- @skip_xp_backends(np_only=True, reason='Python list input uses NumPy backend')
76
- def test_lambda_to_nu_array_like(self, xp):
77
- assert_allclose(sc.lambda2nu([sc.speed_of_light, 1]),
78
- [1, sc.speed_of_light])
72
+ def test_lambda_to_nu_array_like(self):
73
+ xp_assert_close(sc.lambda2nu([sc.speed_of_light, 1]), [1, sc.speed_of_light])
79
74
 
80
75
 
76
+ @make_xp_test_case(sc.nu2lambda)
81
77
  class TestNuToLambda:
82
78
  def test_nu_to_lambda(self, xp):
83
79
  xp_assert_equal(sc.nu2lambda(xp.asarray([sc.speed_of_light, 1])),
84
80
  xp.asarray([1, sc.speed_of_light]))
85
81
 
86
- @skip_xp_backends(np_only=True, reason='Python list input uses NumPy backend')
87
- def test_nu_to_lambda_array_like(self, xp):
88
- assert_allclose(sc.nu2lambda([sc.speed_of_light, 1]),
89
- [1, sc.speed_of_light])
90
-
82
+ def test_nu_to_lambda_array_like(self):
83
+ xp_assert_close(sc.nu2lambda([sc.speed_of_light, 1]), [1, sc.speed_of_light])