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.
Files changed (641) hide show
  1. scipy/__config__.py +10 -10
  2. scipy/__init__.py +3 -6
  3. scipy/_cyutility.cpython-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  11. scipy/_lib/_sparse.py +41 -0
  12. scipy/_lib/_test_ccallback.cpython-312-aarch64-linux-musl.so +0 -0
  13. scipy/_lib/_test_deprecation_call.cpython-312-aarch64-linux-musl.so +0 -0
  14. scipy/_lib/_test_deprecation_def.cpython-312-aarch64-linux-musl.so +0 -0
  15. scipy/_lib/_testutils.py +6 -2
  16. scipy/_lib/_uarray/_uarray.cpython-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  97. scipy/cluster/_optimal_leaf_ordering.cpython-312-aarch64-linux-musl.so +0 -0
  98. scipy/cluster/_vq.cpython-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  129. scipy/integrate/_lsoda.cpython-312-aarch64-linux-musl.so +0 -0
  130. scipy/integrate/_ode.py +9 -2
  131. scipy/integrate/_odepack.cpython-312-aarch64-linux-musl.so +0 -0
  132. scipy/integrate/_quad_vec.py +21 -29
  133. scipy/integrate/_quadpack.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  139. scipy/integrate/_test_odeint_banded.cpython-312-aarch64-linux-musl.so +0 -0
  140. scipy/integrate/_vode.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  151. scipy/interpolate/_dierckx.cpython-312-aarch64-linux-musl.so +0 -0
  152. scipy/interpolate/_fitpack.cpython-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  165. scipy/interpolate/_rgi.py +31 -26
  166. scipy/interpolate/_rgi_cython.cpython-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  187. scipy/io/matlab/_mio_utils.cpython-312-aarch64-linux-musl.so +0 -0
  188. scipy/io/matlab/_miobase.py +4 -1
  189. scipy/io/matlab/_streams.cpython-312-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-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  210. scipy/linalg/_expm_frechet.py +4 -0
  211. scipy/linalg/_fblas.cpython-312-aarch64-linux-musl.so +0 -0
  212. scipy/linalg/_flapack.cpython-312-aarch64-linux-musl.so +0 -0
  213. scipy/linalg/_linalg_pythran.cpython-312-aarch64-linux-musl.so +0 -0
  214. scipy/linalg/_matfuncs.py +187 -4
  215. scipy/linalg/_matfuncs_expm.cpython-312-aarch64-linux-musl.so +0 -0
  216. scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-aarch64-linux-musl.so +0 -0
  217. scipy/linalg/_matfuncs_sqrtm.py +1 -99
  218. scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  225. scipy/linalg/cython_lapack.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  241. scipy/ndimage/_cytest.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  248. scipy/ndimage/_ni_docstrings.py +5 -1
  249. scipy/ndimage/_ni_label.cpython-312-aarch64-linux-musl.so +0 -0
  250. scipy/ndimage/_ni_support.py +1 -5
  251. scipy/ndimage/_rank_filter_1d.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  260. scipy/optimize/_basinhopping.py +13 -7
  261. scipy/optimize/_bglu_dense.cpython-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  272. scipy/optimize/_highspy/_core.cpython-312-aarch64-linux-musl.so +0 -0
  273. scipy/optimize/_highspy/_highs_options.cpython-312-aarch64-linux-musl.so +0 -0
  274. scipy/optimize/_lbfgsb.cpython-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  290. scipy/optimize/_minpack_py.py +21 -14
  291. scipy/optimize/_moduleTNC.cpython-312-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-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  317. scipy/optimize/_zeros_py.py +97 -17
  318. scipy/optimize/cython_optimize/_zeros.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  350. scipy/signal/_peak_finding_utils.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  356. scipy/signal/_sosfilt.cpython-312-aarch64-linux-musl.so +0 -0
  357. scipy/signal/_spectral_py.py +230 -50
  358. scipy/signal/_spline.cpython-312-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-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  404. scipy/sparse/csgraph/_matching.cpython-312-aarch64-linux-musl.so +0 -0
  405. scipy/sparse/csgraph/_min_spanning_tree.cpython-312-aarch64-linux-musl.so +0 -0
  406. scipy/sparse/csgraph/_reordering.cpython-312-aarch64-linux-musl.so +0 -0
  407. scipy/sparse/csgraph/_shortest_path.cpython-312-aarch64-linux-musl.so +0 -0
  408. scipy/sparse/csgraph/_tools.cpython-312-aarch64-linux-musl.so +0 -0
  409. scipy/sparse/csgraph/_traversal.cpython-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  433. scipy/sparse/linalg/_propack/_dpropack.cpython-312-aarch64-linux-musl.so +0 -0
  434. scipy/sparse/linalg/_propack/_spropack.cpython-312-aarch64-linux-musl.so +0 -0
  435. scipy/sparse/linalg/_propack/_zpropack.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  446. scipy/spatial/_distance_pybind.cpython-312-aarch64-linux-musl.so +0 -0
  447. scipy/spatial/_distance_wrap.cpython-312-aarch64-linux-musl.so +0 -0
  448. scipy/spatial/_hausdorff.cpython-312-aarch64-linux-musl.so +0 -0
  449. scipy/spatial/_qhull.cpython-312-aarch64-linux-musl.so +0 -0
  450. scipy/spatial/_voronoi.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  457. scipy/spatial/transform/_rotation.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  466. scipy/special/_ellip_harm_2.cpython-312-aarch64-linux-musl.so +0 -0
  467. scipy/special/_gufuncs.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  471. scipy/special/_special_ufuncs.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  475. scipy/special/_testutils.py +4 -4
  476. scipy/special/_ufuncs.cpython-312-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-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  502. scipy/stats/_axis_nan_policy.py +5 -12
  503. scipy/stats/_biasedurn.cpython-312-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-312-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-312-aarch64-linux-musl.so +0 -0
  527. scipy/stats/_qmvnt.py +16 -95
  528. scipy/stats/_qmvnt_cy.cpython-312-aarch64-linux-musl.so +0 -0
  529. scipy/stats/_quantile.py +335 -0
  530. scipy/stats/_rcont/rcont.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  534. scipy/stats/_stats.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  538. scipy/stats/_unuran/unuran_wrapper.cpython-312-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-312-aarch64-linux-musl.so +0 -0
  575. scipy/optimize/_cobyla.cpython-312-aarch64-linux-musl.so +0 -0
  576. scipy/optimize/_cython_nnls.cpython-312-aarch64-linux-musl.so +0 -0
  577. scipy/optimize/_slsqp.cpython-312-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-312-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
@@ -2,6 +2,7 @@
2
2
  import functools
3
3
  import itertools
4
4
  import re
5
+ import contextlib
5
6
 
6
7
  import numpy as np
7
8
  import pytest
@@ -17,8 +18,8 @@ from scipy._lib._array_api import (
17
18
  xp_assert_close,
18
19
  xp_assert_equal,
19
20
  )
20
- from scipy._lib._array_api import is_cupy, is_numpy, is_torch, array_namespace
21
- from scipy.conftest import array_api_compatible
21
+ from scipy._lib._array_api import (is_cupy, is_torch, is_dask, is_jax, array_namespace,
22
+ is_array_api_strict, xp_copy)
22
23
  from scipy.ndimage._filters import _gaussian_kernel1d
23
24
 
24
25
  from . import types, float_types, complex_types
@@ -26,9 +27,18 @@ from . import types, float_types, complex_types
26
27
 
27
28
  skip_xp_backends = pytest.mark.skip_xp_backends
28
29
  xfail_xp_backends = pytest.mark.xfail_xp_backends
29
- pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_xp_backends"),
30
- pytest.mark.usefixtures("xfail_xp_backends"),
31
- skip_xp_backends(cpu_only=True, exceptions=['cupy', 'jax.numpy']),]
30
+ pytestmark = [skip_xp_backends(cpu_only=True, exceptions=['cupy', 'jax.numpy'])]
31
+
32
+ uses_output_dtype = skip_xp_backends(
33
+ np_only=True, exceptions=["cupy"],
34
+ reason="output=dtype is numpy-specific"
35
+ )
36
+
37
+
38
+ def uses_output_array(f):
39
+ return skip_xp_backends("dask.array", reason="output=array requires buffer view")(
40
+ skip_xp_backends("jax.numpy", reason="output=array requires buffer view")(f))
41
+
32
42
 
33
43
 
34
44
  def sumsq(a, b, xp=None):
@@ -44,9 +54,8 @@ def _complex_correlate(xp, array, kernel, real_dtype, convolve=False,
44
54
  """
45
55
  array = xp.asarray(array)
46
56
  kernel = xp.asarray(kernel)
47
- isdtype = array_namespace(array, kernel).isdtype
48
- complex_array = isdtype(array.dtype, 'complex floating')
49
- complex_kernel = isdtype(kernel.dtype, 'complex floating')
57
+ complex_array = xp.isdtype(array.dtype, 'complex floating')
58
+ complex_kernel = xp.isdtype(kernel.dtype, 'complex floating')
50
59
  if array.ndim == 1:
51
60
  func = ndimage.convolve1d if convolve else ndimage.correlate1d
52
61
  else:
@@ -197,7 +206,7 @@ class TestNdimageFilters:
197
206
  assert_array_almost_equal(output, expected)
198
207
 
199
208
  @xfail_xp_backends('cupy', reason="Differs by a factor of two?")
200
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
209
+ @uses_output_array
201
210
  def test_correlate01_overlap(self, xp):
202
211
  array = xp.reshape(xp.arange(256), (16, 16))
203
212
  weights = xp.asarray([2])
@@ -348,9 +357,7 @@ class TestNdimageFilters:
348
357
  output = ndimage.convolve(array, kernel)
349
358
  assert_array_almost_equal(xp.asarray([[6, 8, 9], [9, 11, 12]]), output)
350
359
 
351
- @xfail_xp_backends(np_only=True,
352
- reason="output=dtype is numpy-specific",
353
- exceptions=['cupy'],)
360
+ @uses_output_dtype
354
361
  @pytest.mark.parametrize('dtype_array', types)
355
362
  @pytest.mark.parametrize('dtype_kernel', types)
356
363
  def test_correlate13(self, dtype_array, dtype_kernel, xp):
@@ -370,9 +377,7 @@ class TestNdimageFilters:
370
377
  assert_array_almost_equal(xp.asarray([[6, 8, 9], [9, 11, 12]]), output)
371
378
  assert output.dtype.type == dtype_kernel
372
379
 
373
- @xfail_xp_backends(np_only=True,
374
- reason="output=dtype is numpy-specific",
375
- exceptions=['cupy'],)
380
+ @uses_output_array
376
381
  @pytest.mark.parametrize('dtype_array', types)
377
382
  @pytest.mark.parametrize('dtype_output', types)
378
383
  def test_correlate14(self, dtype_array, dtype_output, xp):
@@ -386,15 +391,13 @@ class TestNdimageFilters:
386
391
  output = xp.zeros(array.shape, dtype=dtype_output)
387
392
  ndimage.correlate(array, kernel, output=output)
388
393
  assert_array_almost_equal(xp.asarray([[2, 3, 5], [5, 6, 8]]), output)
389
- assert output.dtype.type == dtype_output
394
+ assert output.dtype == dtype_output
390
395
 
391
396
  ndimage.convolve(array, kernel, output=output)
392
397
  assert_array_almost_equal(xp.asarray([[6, 8, 9], [9, 11, 12]]), output)
393
- assert output.dtype.type == dtype_output
398
+ assert output.dtype == dtype_output
394
399
 
395
- @xfail_xp_backends(np_only=True,
396
- reason="output=dtype is numpy-specific",
397
- exceptions=['cupy'],)
400
+ @uses_output_dtype
398
401
  @pytest.mark.parametrize('dtype_array', types)
399
402
  def test_correlate15(self, dtype_array, xp):
400
403
  dtype_array = getattr(xp, dtype_array)
@@ -411,9 +414,7 @@ class TestNdimageFilters:
411
414
  assert_array_almost_equal(xp.asarray([[6, 8, 9], [9, 11, 12]]), output)
412
415
  assert output.dtype.type == xp.float32
413
416
 
414
- @xfail_xp_backends(np_only=True,
415
- reason="output=dtype is numpy-specific",
416
- exceptions=['cupy'],)
417
+ @uses_output_dtype
417
418
  @pytest.mark.parametrize('dtype_array', types)
418
419
  def test_correlate16(self, dtype_array, xp):
419
420
  dtype_array = getattr(xp, dtype_array)
@@ -443,9 +444,7 @@ class TestNdimageFilters:
443
444
  output = ndimage.convolve1d(array, kernel, origin=-1)
444
445
  assert_array_almost_equal(tcov, output)
445
446
 
446
- @xfail_xp_backends(np_only=True,
447
- reason="output=dtype is numpy-specific",
448
- exceptions=['cupy'],)
447
+ @uses_output_dtype
449
448
  @pytest.mark.parametrize('dtype_array', types)
450
449
  def test_correlate18(self, dtype_array, xp):
451
450
  dtype_array = getattr(xp, dtype_array)
@@ -474,9 +473,7 @@ class TestNdimageFilters:
474
473
  with assert_raises(RuntimeError):
475
474
  ndimage.convolve(array, kernel, mode=['nearest', 'reflect'])
476
475
 
477
- @xfail_xp_backends(np_only=True,
478
- reason="output=dtype is numpy-specific",
479
- exceptions=['cupy'],)
476
+ @uses_output_dtype
480
477
  @pytest.mark.parametrize('dtype_array', types)
481
478
  def test_correlate19(self, dtype_array, xp):
482
479
  dtype_array = getattr(xp, dtype_array)
@@ -497,9 +494,7 @@ class TestNdimageFilters:
497
494
  assert_array_almost_equal(xp.asarray([[3, 5, 6], [6, 8, 9]]), output)
498
495
  assert output.dtype.type == xp.float32
499
496
 
500
- @xfail_xp_backends(np_only=True,
501
- reason="output=dtype is numpy-specific",
502
- exceptions=['cupy'],)
497
+ @uses_output_array
503
498
  @pytest.mark.parametrize('dtype_array', types)
504
499
  @pytest.mark.parametrize('dtype_output', types)
505
500
  def test_correlate20(self, dtype_array, dtype_output, xp):
@@ -526,9 +521,7 @@ class TestNdimageFilters:
526
521
  output = ndimage.convolve1d(array, weights, axis=0)
527
522
  assert_array_almost_equal(output, expected)
528
523
 
529
- @xfail_xp_backends(np_only=True,
530
- reason="output=dtype is numpy-specific",
531
- exceptions=['cupy'],)
524
+ @uses_output_array
532
525
  @pytest.mark.parametrize('dtype_array', types)
533
526
  @pytest.mark.parametrize('dtype_output', types)
534
527
  def test_correlate22(self, dtype_array, dtype_output, xp):
@@ -547,7 +540,7 @@ class TestNdimageFilters:
547
540
  mode='wrap', output=output)
548
541
  assert_array_almost_equal(output, expected)
549
542
 
550
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
543
+ @uses_output_array
551
544
  @pytest.mark.parametrize('dtype_array', types)
552
545
  @pytest.mark.parametrize('dtype_output', types)
553
546
  def test_correlate23(self, dtype_array, dtype_output, xp):
@@ -566,7 +559,7 @@ class TestNdimageFilters:
566
559
  mode='nearest', output=output)
567
560
  assert_array_almost_equal(output, expected)
568
561
 
569
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
562
+ @uses_output_array
570
563
  @pytest.mark.parametrize('dtype_array', types)
571
564
  @pytest.mark.parametrize('dtype_output', types)
572
565
  def test_correlate24(self, dtype_array, dtype_output, xp):
@@ -586,7 +579,7 @@ class TestNdimageFilters:
586
579
  mode='nearest', output=output, origin=-1)
587
580
  assert_array_almost_equal(output, tcov)
588
581
 
589
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
582
+ @uses_output_array
590
583
  @pytest.mark.parametrize('dtype_array', types)
591
584
  @pytest.mark.parametrize('dtype_output', types)
592
585
  def test_correlate25(self, dtype_array, dtype_output, xp):
@@ -614,9 +607,7 @@ class TestNdimageFilters:
614
607
  y = ndimage.correlate1d(xp.ones(1), xp.ones(5), mode='mirror')
615
608
  xp_assert_equal(y, xp.asarray([5.]))
616
609
 
617
- @xfail_xp_backends(np_only=True,
618
- reason="output=dtype is numpy-specific",
619
- exceptions=['cupy'],)
610
+ @uses_output_dtype
620
611
  @pytest.mark.parametrize('dtype_kernel', complex_types)
621
612
  @pytest.mark.parametrize('dtype_input', types)
622
613
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -633,9 +624,7 @@ class TestNdimageFilters:
633
624
  self._validate_complex(xp, array, kernel, dtype_output,
634
625
  check_warnings=num_parallel_threads == 1)
635
626
 
636
- @xfail_xp_backends(np_only=True,
637
- reason="output=dtype is numpy-specific",
638
- exceptions=['cupy'],)
627
+ @uses_output_dtype
639
628
  @pytest.mark.parametrize('dtype_kernel', complex_types)
640
629
  @pytest.mark.parametrize('dtype_input', types)
641
630
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -648,7 +637,7 @@ class TestNdimageFilters:
648
637
  dtype_output = getattr(xp, dtype_output)
649
638
 
650
639
  if is_cupy(xp) and mode == 'grid-constant':
651
- pytest.xfail('https://github.com/cupy/cupy/issues/8404')
640
+ pytest.xfail('cupy/cupy#8404')
652
641
 
653
642
  # test use of non-zero cval with complex inputs
654
643
  # also verifies that mode 'grid-constant' does not segfault
@@ -680,7 +669,7 @@ class TestNdimageFilters:
680
669
  func(array, kernel, mode='constant', cval=5.0 + 1.0j,
681
670
  output=xp.complex64)
682
671
 
683
- @skip_xp_backends(np_only=True, reason='output=dtype is numpy-specific')
672
+ @uses_output_dtype
684
673
  @pytest.mark.parametrize('dtype_kernel', complex_types)
685
674
  @pytest.mark.parametrize('dtype_input', types)
686
675
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -695,7 +684,7 @@ class TestNdimageFilters:
695
684
  self._validate_complex(xp, array, kernel, dtype_output,
696
685
  check_warnings=num_parallel_threads == 1)
697
686
 
698
- @skip_xp_backends(np_only=True, reason='output=dtype is numpy-specific')
687
+ @uses_output_dtype
699
688
  @pytest.mark.parametrize('dtype_kernel', complex_types)
700
689
  @pytest.mark.parametrize('dtype_input', types)
701
690
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -712,7 +701,7 @@ class TestNdimageFilters:
712
701
  cval=5.0,
713
702
  check_warnings=num_parallel_threads == 1)
714
703
 
715
- @skip_xp_backends(np_only=True, reason='output=dtype is numpy-specific')
704
+ @uses_output_dtype
716
705
  @pytest.mark.parametrize('dtype_kernel', types)
717
706
  @pytest.mark.parametrize('dtype_input', complex_types)
718
707
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -729,7 +718,7 @@ class TestNdimageFilters:
729
718
  self._validate_complex(xp, array, kernel, dtype_output,
730
719
  check_warnings=num_parallel_threads == 1)
731
720
 
732
- @skip_xp_backends(np_only=True, reason='output=dtype is numpy-specific')
721
+ @uses_output_dtype
733
722
  @pytest.mark.parametrize('dtype_kernel', types)
734
723
  @pytest.mark.parametrize('dtype_input', complex_types)
735
724
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -744,10 +733,8 @@ class TestNdimageFilters:
744
733
  self._validate_complex(xp, array, kernel, dtype_output,
745
734
  check_warnings=num_parallel_threads == 1)
746
735
 
747
- @xfail_xp_backends('cupy', reason="cupy/cupy#8405")
748
- @skip_xp_backends(np_only=True,
749
- reason='output=dtype is numpy-specific',
750
- exceptions=['cupy'])
736
+ @uses_output_dtype
737
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8405")
751
738
  @pytest.mark.parametrize('dtype_kernel', types)
752
739
  @pytest.mark.parametrize('dtype_input', complex_types)
753
740
  @pytest.mark.parametrize('dtype_output', complex_types)
@@ -764,7 +751,8 @@ class TestNdimageFilters:
764
751
  cval=5 - 3j,
765
752
  check_warnings=num_parallel_threads == 1)
766
753
 
767
- @skip_xp_backends(np_only=True, reason='output=dtype is numpy-specific')
754
+ @uses_output_dtype
755
+ @xfail_xp_backends("cupy", reason="unhashable type: 'ndarray'")
768
756
  @pytest.mark.parametrize('dtype', complex_types)
769
757
  @pytest.mark.parametrize('dtype_output', complex_types)
770
758
  def test_correlate_complex_input_and_kernel(self, dtype, dtype_output, xp,
@@ -779,10 +767,8 @@ class TestNdimageFilters:
779
767
  self._validate_complex(xp, array, kernel, dtype_output,
780
768
  check_warnings=num_parallel_threads == 1)
781
769
 
782
- @xfail_xp_backends('cupy', reason="cupy/cupy#8405")
783
- @skip_xp_backends(np_only=True,
784
- reason="output=dtype is numpy-specific",
785
- exceptions=['cupy'],)
770
+ @uses_output_dtype
771
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8405")
786
772
  @pytest.mark.parametrize('dtype', complex_types)
787
773
  @pytest.mark.parametrize('dtype_output', complex_types)
788
774
  def test_correlate_complex_input_and_kernel_cval(self, dtype,
@@ -799,7 +785,8 @@ class TestNdimageFilters:
799
785
  cval=5.0 + 2.0j,
800
786
  check_warnings=num_parallel_threads == 1)
801
787
 
802
- @skip_xp_backends(np_only=True, reason="output=dtype is numpy-specific")
788
+ @uses_output_dtype
789
+ @xfail_xp_backends("cupy", reason="unhashable type: 'ndarray'")
803
790
  @pytest.mark.parametrize('dtype', complex_types)
804
791
  @pytest.mark.parametrize('dtype_output', complex_types)
805
792
  @pytest.mark.thread_unsafe
@@ -813,20 +800,17 @@ class TestNdimageFilters:
813
800
  self._validate_complex(xp, array, kernel, dtype_output,
814
801
  check_warnings=num_parallel_threads == 1)
815
802
 
803
+ @uses_output_dtype
804
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8405")
816
805
  @pytest.mark.parametrize('dtype', complex_types)
817
806
  @pytest.mark.parametrize('dtype_output', complex_types)
818
807
  def test_correlate1d_complex_input_and_kernel_cval(self, dtype,
819
808
  dtype_output, xp,
820
809
  num_parallel_threads):
821
- if not (is_numpy(xp) or is_cupy(xp)):
822
- pytest.xfail("output=dtype is numpy-specific")
823
810
 
824
811
  dtype = getattr(xp, dtype)
825
812
  dtype_output = getattr(xp, dtype_output)
826
813
 
827
- if is_cupy(xp):
828
- pytest.xfail("https://github.com/cupy/cupy/issues/8405")
829
-
830
814
  kernel = xp.asarray([1, 1 + 1j], dtype=dtype)
831
815
  array = xp.asarray([1, 2j, 3, 1 + 4j, 5, 6j], dtype=dtype)
832
816
  self._validate_complex(xp, array, kernel, dtype_output, mode='constant',
@@ -846,10 +830,8 @@ class TestNdimageFilters:
846
830
  assert input.dtype == output.dtype
847
831
  assert input.shape == output.shape
848
832
 
833
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8403")
849
834
  def test_gauss03(self, xp):
850
- if is_cupy(xp):
851
- pytest.xfail("https://github.com/cupy/cupy/issues/8403")
852
-
853
835
  # single precision data
854
836
  input = xp.arange(100 * 100, dtype=xp.float32)
855
837
  input = xp.reshape(input, (100, 100))
@@ -865,10 +847,8 @@ class TestNdimageFilters:
865
847
  assert_almost_equal(o_sum, i_sum, decimal=0)
866
848
  assert sumsq(input, output) > 1.0
867
849
 
850
+ @uses_output_dtype
868
851
  def test_gauss04(self, xp):
869
- if not (is_numpy(xp) or is_cupy(xp)):
870
- pytest.xfail("output=dtype is numpy-specific")
871
-
872
852
  input = xp.arange(100 * 100, dtype=xp.float32)
873
853
  input = xp.reshape(input, (100, 100))
874
854
  otype = xp.float64
@@ -877,10 +857,8 @@ class TestNdimageFilters:
877
857
  assert input.shape == output.shape
878
858
  assert sumsq(input, output) > 1.0
879
859
 
860
+ @uses_output_dtype
880
861
  def test_gauss05(self, xp):
881
- if not (is_numpy(xp) or is_cupy(xp)):
882
- pytest.xfail("output=dtype is numpy-specific")
883
-
884
862
  input = xp.arange(100 * 100, dtype=xp.float32)
885
863
  input = xp.reshape(input, (100, 100))
886
864
  otype = xp.float64
@@ -890,10 +868,8 @@ class TestNdimageFilters:
890
868
  assert input.shape == output.shape
891
869
  assert sumsq(input, output) > 1.0
892
870
 
871
+ @uses_output_dtype
893
872
  def test_gauss06(self, xp):
894
- if not (is_numpy(xp) or is_cupy(xp)):
895
- pytest.xfail("output=dtype is numpy-specific")
896
-
897
873
  input = xp.arange(100 * 100, dtype=xp.float32)
898
874
  input = xp.reshape(input, (100, 100))
899
875
  otype = xp.float64
@@ -901,7 +877,7 @@ class TestNdimageFilters:
901
877
  output2 = ndimage.gaussian_filter(input, 1.0, output=otype)
902
878
  assert_array_almost_equal(output1, output2)
903
879
 
904
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
880
+ @uses_output_array
905
881
  def test_gauss_memory_overlap(self, xp):
906
882
  input = xp.arange(100 * 100, dtype=xp.float32)
907
883
  input = xp.reshape(input, (100, 100))
@@ -909,6 +885,7 @@ class TestNdimageFilters:
909
885
  ndimage.gaussian_filter(input, 1.0, output=input)
910
886
  assert_array_almost_equal(output1, input)
911
887
 
888
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
912
889
  @pytest.mark.parametrize(('filter_func', 'extra_args', 'size0', 'size'),
913
890
  [(ndimage.gaussian_filter, (), 0, 1.0),
914
891
  (ndimage.uniform_filter, (), 1, 3),
@@ -923,9 +900,6 @@ class TestNdimageFilters:
923
900
  + tuple(itertools.combinations(range(-3, 3), 2))
924
901
  + ((0, 1, 2),))
925
902
  def test_filter_axes(self, filter_func, extra_args, size0, size, axes, xp):
926
- if is_cupy(xp):
927
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
928
-
929
903
  # Note: `size` is called `sigma` in `gaussian_filter`
930
904
  array = xp.arange(6 * 8 * 12, dtype=xp.float64)
931
905
  array = xp.reshape(array, (6, 8, 12))
@@ -945,8 +919,7 @@ class TestNdimageFilters:
945
919
  xp_assert_close(output, expected)
946
920
 
947
921
  @skip_xp_backends("cupy",
948
- reason="these filters do not yet have axes support",
949
- )
922
+ reason="these filters do not yet have axes support")
950
923
  @pytest.mark.parametrize(('filter_func', 'kwargs'),
951
924
  [(ndimage.laplace, {}),
952
925
  (ndimage.gaussian_gradient_magnitude,
@@ -987,8 +960,7 @@ class TestNdimageFilters:
987
960
  xp_assert_close(output, expected)
988
961
 
989
962
  @skip_xp_backends("cupy",
990
- reason="generic_filter does not yet have axes support",
991
- )
963
+ reason="generic_filter does not yet have axes support")
992
964
  @pytest.mark.parametrize(
993
965
  'axes',
994
966
  tuple(itertools.combinations(range(-3, 3), 1))
@@ -1010,8 +982,7 @@ class TestNdimageFilters:
1010
982
  xp_assert_close(output, expected)
1011
983
 
1012
984
  @skip_xp_backends("cupy",
1013
- reason="https://github.com/cupy/cupy/pull/8339",
1014
- )
985
+ reason="https://github.com/cupy/cupy/pull/8339")
1015
986
  @pytest.mark.parametrize('func', [ndimage.correlate, ndimage.convolve])
1016
987
  @pytest.mark.parametrize(
1017
988
  'dtype', [np.float32, np.float64, np.complex64, np.complex128]
@@ -1051,9 +1022,7 @@ class TestNdimageFilters:
1051
1022
  mode=['reflect', 'nearest', 'constant'])
1052
1023
  kwargs_rank = dict(origin=(-1, 0, 1))
1053
1024
 
1054
- @skip_xp_backends("array_api_strict",
1055
- reason="fancy indexing is only available in 2024 version",
1056
- )
1025
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
1057
1026
  @pytest.mark.parametrize("filter_func, size0, size, kwargs",
1058
1027
  [(ndimage.gaussian_filter, 0, 1.0, kwargs_gauss),
1059
1028
  (ndimage.uniform_filter, 1, 3, kwargs_other),
@@ -1064,10 +1033,6 @@ class TestNdimageFilters:
1064
1033
  (ndimage.percentile_filter, 1, 3, kwargs_rank)])
1065
1034
  @pytest.mark.parametrize('axes', itertools.combinations(range(-3, 3), 2))
1066
1035
  def test_filter_axes_kwargs(self, filter_func, size0, size, kwargs, axes, xp):
1067
-
1068
- if is_cupy(xp):
1069
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
1070
-
1071
1036
  array = xp.arange(6 * 8 * 12, dtype=xp.float64)
1072
1037
  array = xp.reshape(array, (6, 8, 12))
1073
1038
 
@@ -1108,6 +1073,7 @@ class TestNdimageFilters:
1108
1073
  xp_assert_close(output, expected)
1109
1074
 
1110
1075
 
1076
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
1111
1077
  @pytest.mark.parametrize("filter_func, kwargs",
1112
1078
  [(ndimage.convolve, {}),
1113
1079
  (ndimage.correlate, {}),
@@ -1117,9 +1083,6 @@ class TestNdimageFilters:
1117
1083
  (ndimage.rank_filter, {"rank": 1}),
1118
1084
  (ndimage.percentile_filter, {"percentile": 30})])
1119
1085
  def test_filter_weights_subset_axes_origins(self, filter_func, kwargs, xp):
1120
- if is_cupy(xp):
1121
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
1122
-
1123
1086
  axes = (-2, -1)
1124
1087
  origins = (0, 1)
1125
1088
  array = xp.arange(6 * 8 * 12, dtype=xp.float64)
@@ -1152,6 +1115,7 @@ class TestNdimageFilters:
1152
1115
  output[:, :, origins[1]:], output0[:, :, :-origins[1]])
1153
1116
 
1154
1117
 
1118
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
1155
1119
  @pytest.mark.parametrize(
1156
1120
  'filter_func, args',
1157
1121
  [(ndimage.convolve, (np.ones((3, 3, 3)),)), # args = (weights,)
@@ -1167,9 +1131,6 @@ class TestNdimageFilters:
1167
1131
  'axes', [(1.5,), (0, 1, 2, 3), (3,), (-4,)]
1168
1132
  )
1169
1133
  def test_filter_invalid_axes(self, filter_func, args, axes, xp):
1170
- if is_cupy(xp):
1171
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
1172
-
1173
1134
  array = xp.arange(6 * 8 * 12, dtype=xp.float64)
1174
1135
  array = xp.reshape(array, (6, 8, 12))
1175
1136
  args = [
@@ -1185,6 +1146,7 @@ class TestNdimageFilters:
1185
1146
  with pytest.raises(error_class, match=match):
1186
1147
  filter_func(array, *args, axes=axes)
1187
1148
 
1149
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
1188
1150
  @pytest.mark.parametrize(
1189
1151
  'filter_func, kwargs',
1190
1152
  [(ndimage.convolve, {}),
@@ -1200,9 +1162,6 @@ class TestNdimageFilters:
1200
1162
  @pytest.mark.parametrize('separable_footprint', [False, True])
1201
1163
  def test_filter_invalid_footprint_ndim(self, filter_func, kwargs, axes,
1202
1164
  separable_footprint, xp):
1203
- if is_cupy(xp):
1204
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
1205
-
1206
1165
  array = xp.arange(6 * 8 * 12, dtype=xp.float64)
1207
1166
  array = xp.reshape(array, (6, 8, 12))
1208
1167
  # create a footprint with one too many dimensions
@@ -1226,14 +1185,12 @@ class TestNdimageFilters:
1226
1185
  with pytest.raises(RuntimeError, match=match):
1227
1186
  filter_func(array, axes=axes, **kwargs)
1228
1187
 
1188
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
1229
1189
  @pytest.mark.parametrize('n_mismatch', [1, 3])
1230
1190
  @pytest.mark.parametrize('filter_func, kwargs, key, val',
1231
1191
  _cases_axes_tuple_length_mismatch())
1232
1192
  def test_filter_tuple_length_mismatch(self, n_mismatch, filter_func,
1233
1193
  kwargs, key, val, xp):
1234
- if is_cupy(xp):
1235
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
1236
-
1237
1194
  # Test for the intended RuntimeError when a kwargs has an invalid size
1238
1195
  array = xp.arange(6 * 8 * 12, dtype=xp.float64)
1239
1196
  array = xp.reshape(array, (6, 8, 12))
@@ -1248,9 +1205,6 @@ class TestNdimageFilters:
1248
1205
 
1249
1206
  @pytest.mark.parametrize('dtype', types + complex_types)
1250
1207
  def test_prewitt01(self, dtype, xp):
1251
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1252
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1253
-
1254
1208
  dtype = getattr(xp, dtype)
1255
1209
  array = xp.asarray([[3, 2, 5, 1, 4],
1256
1210
  [5, 8, 3, 7, 1],
@@ -1260,12 +1214,9 @@ class TestNdimageFilters:
1260
1214
  output = ndimage.prewitt(array, 0)
1261
1215
  assert_array_almost_equal(t, output)
1262
1216
 
1263
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
1217
+ @uses_output_array
1264
1218
  @pytest.mark.parametrize('dtype', types + complex_types)
1265
1219
  def test_prewitt02(self, dtype, xp):
1266
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1267
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1268
-
1269
1220
  dtype = getattr(xp, dtype)
1270
1221
  array = xp.asarray([[3, 2, 5, 1, 4],
1271
1222
  [5, 8, 3, 7, 1],
@@ -1278,14 +1229,9 @@ class TestNdimageFilters:
1278
1229
 
1279
1230
  @pytest.mark.parametrize('dtype', types + complex_types)
1280
1231
  def test_prewitt03(self, dtype, xp):
1281
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1282
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1283
-
1284
1232
  dtype = getattr(xp, dtype)
1285
1233
  if is_cupy(xp) and dtype in [xp.uint32, xp.uint64]:
1286
1234
  pytest.xfail("uint UB? XXX")
1287
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1288
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1289
1235
 
1290
1236
  array = xp.asarray([[3, 2, 5, 1, 4],
1291
1237
  [5, 8, 3, 7, 1],
@@ -1297,9 +1243,6 @@ class TestNdimageFilters:
1297
1243
 
1298
1244
  @pytest.mark.parametrize('dtype', types + complex_types)
1299
1245
  def test_prewitt04(self, dtype, xp):
1300
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1301
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1302
-
1303
1246
  dtype = getattr(xp, dtype)
1304
1247
  array = xp.asarray([[3, 2, 5, 1, 4],
1305
1248
  [5, 8, 3, 7, 1],
@@ -1310,9 +1253,6 @@ class TestNdimageFilters:
1310
1253
 
1311
1254
  @pytest.mark.parametrize('dtype', types + complex_types)
1312
1255
  def test_sobel01(self, dtype, xp):
1313
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1314
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1315
-
1316
1256
  dtype = getattr(xp, dtype)
1317
1257
  array = xp.asarray([[3, 2, 5, 1, 4],
1318
1258
  [5, 8, 3, 7, 1],
@@ -1322,12 +1262,9 @@ class TestNdimageFilters:
1322
1262
  output = ndimage.sobel(array, 0)
1323
1263
  assert_array_almost_equal(t, output)
1324
1264
 
1325
- @skip_xp_backends("jax.numpy", reason="output array is read-only.",)
1265
+ @uses_output_array
1326
1266
  @pytest.mark.parametrize('dtype', types + complex_types)
1327
1267
  def test_sobel02(self, dtype, xp):
1328
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1329
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1330
-
1331
1268
  dtype = getattr(xp, dtype)
1332
1269
  array = xp.asarray([[3, 2, 5, 1, 4],
1333
1270
  [5, 8, 3, 7, 1],
@@ -1342,8 +1279,6 @@ class TestNdimageFilters:
1342
1279
  def test_sobel03(self, dtype, xp):
1343
1280
  if is_cupy(xp) and dtype in ["uint32", "uint64"]:
1344
1281
  pytest.xfail("uint UB? XXX")
1345
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1346
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1347
1282
 
1348
1283
  dtype = getattr(xp, dtype)
1349
1284
  array = xp.asarray([[3, 2, 5, 1, 4],
@@ -1357,9 +1292,6 @@ class TestNdimageFilters:
1357
1292
 
1358
1293
  @pytest.mark.parametrize('dtype', types + complex_types)
1359
1294
  def test_sobel04(self, dtype, xp):
1360
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1361
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1362
-
1363
1295
  dtype = getattr(xp, dtype)
1364
1296
  array = xp.asarray([[3, 2, 5, 1, 4],
1365
1297
  [5, 8, 3, 7, 1],
@@ -1382,7 +1314,7 @@ class TestNdimageFilters:
1382
1314
  output = ndimage.laplace(array)
1383
1315
  assert_array_almost_equal(tmp1 + tmp2, output)
1384
1316
 
1385
- @skip_xp_backends("jax.numpy", reason="output array is read-only",)
1317
+ @uses_output_array
1386
1318
  @pytest.mark.parametrize('dtype',
1387
1319
  ["int32", "float32", "float64",
1388
1320
  "complex64", "complex128"])
@@ -1412,7 +1344,7 @@ class TestNdimageFilters:
1412
1344
  output = ndimage.gaussian_laplace(array, 1.0)
1413
1345
  assert_array_almost_equal(tmp1 + tmp2, output)
1414
1346
 
1415
- @skip_xp_backends("jax.numpy", reason="output array is read-only")
1347
+ @uses_output_array
1416
1348
  @pytest.mark.parametrize('dtype',
1417
1349
  ["int32", "float32", "float64",
1418
1350
  "complex64", "complex128"])
@@ -1428,12 +1360,9 @@ class TestNdimageFilters:
1428
1360
  ndimage.gaussian_laplace(array, 1.0, output)
1429
1361
  assert_array_almost_equal(tmp1 + tmp2, output)
1430
1362
 
1431
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
1363
+ @uses_output_array
1432
1364
  @pytest.mark.parametrize('dtype', types + complex_types)
1433
1365
  def test_generic_laplace01(self, dtype, xp):
1434
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1435
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1436
-
1437
1366
  def derivative2(input, axis, output, mode, cval, a, b):
1438
1367
  sigma = np.asarray([a, b / 2.0])
1439
1368
  order = [0] * input.ndim
@@ -1453,7 +1382,6 @@ class TestNdimageFilters:
1453
1382
  ndimage.gaussian_laplace(array, 1.0, output)
1454
1383
  assert_array_almost_equal(tmp, output)
1455
1384
 
1456
- @skip_xp_backends("jax.numpy", reason="output array is read-only")
1457
1385
  @pytest.mark.parametrize('dtype',
1458
1386
  ["int32", "float32", "float64",
1459
1387
  "complex64", "complex128"])
@@ -1469,12 +1397,11 @@ class TestNdimageFilters:
1469
1397
  output = ndimage.gaussian_gradient_magnitude(array, 1.0)
1470
1398
  expected = tmp1 * tmp1 + tmp2 * tmp2
1471
1399
 
1472
- astype = array_namespace(expected).astype
1473
- expected_float = astype(expected, xp.float64) if is_int_dtype else expected
1474
- expected = astype(xp.sqrt(expected_float), dtype)
1400
+ expected_float = xp.astype(expected, xp.float64) if is_int_dtype else expected
1401
+ expected = xp.astype(xp.sqrt(expected_float), dtype)
1475
1402
  xp_assert_close(output, expected, rtol=1e-6, atol=1e-6)
1476
1403
 
1477
- @skip_xp_backends("jax.numpy", reason="output array is read-only")
1404
+ @uses_output_array
1478
1405
  @pytest.mark.parametrize('dtype',
1479
1406
  ["int32", "float32", "float64",
1480
1407
  "complex64", "complex128"])
@@ -1491,10 +1418,9 @@ class TestNdimageFilters:
1491
1418
  ndimage.gaussian_gradient_magnitude(array, 1.0, output)
1492
1419
  expected = tmp1 * tmp1 + tmp2 * tmp2
1493
1420
 
1494
- astype = array_namespace(expected).astype
1495
- fl_expected = astype(expected, xp.float64) if is_int_dtype else expected
1421
+ fl_expected = xp.astype(expected, xp.float64) if is_int_dtype else expected
1496
1422
 
1497
- expected = astype(xp.sqrt(fl_expected), dtype)
1423
+ expected = xp.astype(xp.sqrt(fl_expected), dtype)
1498
1424
  xp_assert_close(output, expected, rtol=1e-6, atol=1e-6)
1499
1425
 
1500
1426
  def test_generic_gradient_magnitude01(self, xp):
@@ -1514,18 +1440,12 @@ class TestNdimageFilters:
1514
1440
  extra_keywords={'b': 2.0})
1515
1441
  assert_array_almost_equal(tmp1, tmp2)
1516
1442
 
1517
- @skip_xp_backends("cupy",
1518
- reason="https://github.com/cupy/cupy/pull/8430",
1519
- )
1520
1443
  def test_uniform01(self, xp):
1521
1444
  array = xp.asarray([2, 4, 6])
1522
1445
  size = 2
1523
1446
  output = ndimage.uniform_filter1d(array, size, origin=-1)
1524
1447
  assert_array_almost_equal(xp.asarray([3, 5, 6]), output)
1525
1448
 
1526
- @skip_xp_backends("cupy",
1527
- reason="https://github.com/cupy/cupy/pull/8430",
1528
- )
1529
1449
  def test_uniform01_complex(self, xp):
1530
1450
  array = xp.asarray([2 + 1j, 4 + 2j, 6 + 3j], dtype=xp.complex128)
1531
1451
  size = 2
@@ -1545,9 +1465,6 @@ class TestNdimageFilters:
1545
1465
  output = ndimage.uniform_filter(array, filter_shape)
1546
1466
  assert_array_almost_equal(array, output)
1547
1467
 
1548
- @skip_xp_backends("cupy",
1549
- reason="https://github.com/cupy/cupy/pull/8430",
1550
- )
1551
1468
  def test_uniform04(self, xp):
1552
1469
  array = xp.asarray([2, 4, 6])
1553
1470
  filter_shape = [2]
@@ -1560,15 +1477,10 @@ class TestNdimageFilters:
1560
1477
  output = ndimage.uniform_filter(array, filter_shape)
1561
1478
  assert_array_almost_equal(xp.asarray([]), output)
1562
1479
 
1563
- @skip_xp_backends("cupy",
1564
- reason="https://github.com/cupy/cupy/pull/8430",
1565
- )
1480
+ @uses_output_dtype
1566
1481
  @pytest.mark.parametrize('dtype_array', types)
1567
1482
  @pytest.mark.parametrize('dtype_output', types)
1568
1483
  def test_uniform06(self, dtype_array, dtype_output, xp):
1569
- if not (is_numpy(xp) or is_cupy(xp)):
1570
- pytest.xfail("output=dtype is numpy-specific")
1571
-
1572
1484
  dtype_array = getattr(xp, dtype_array)
1573
1485
  dtype_output = getattr(xp, dtype_output)
1574
1486
 
@@ -1580,15 +1492,10 @@ class TestNdimageFilters:
1580
1492
  assert_array_almost_equal(xp.asarray([[4, 6, 10], [10, 12, 16]]), output)
1581
1493
  assert output.dtype.type == dtype_output
1582
1494
 
1583
- @skip_xp_backends("cupy",
1584
- reason="https://github.com/cupy/cupy/pull/8430",
1585
- )
1495
+ @uses_output_dtype
1586
1496
  @pytest.mark.parametrize('dtype_array', complex_types)
1587
1497
  @pytest.mark.parametrize('dtype_output', complex_types)
1588
1498
  def test_uniform06_complex(self, dtype_array, dtype_output, xp):
1589
- if not (is_numpy(xp) or is_cupy(xp)):
1590
- pytest.xfail("output=dtype is numpy-specific")
1591
-
1592
1499
  dtype_array = getattr(xp, dtype_array)
1593
1500
  dtype_output = getattr(xp, dtype_output)
1594
1501
 
@@ -1634,7 +1541,7 @@ class TestNdimageFilters:
1634
1541
  [2, 2, 1, 1, 1],
1635
1542
  [5, 3, 3, 1, 1]]), output)
1636
1543
 
1637
- @skip_xp_backends("jax.numpy", reason="assignment destination is read-only")
1544
+ @uses_output_array
1638
1545
  def test_minimum_filter05_overlap(self, xp):
1639
1546
  array = xp.asarray([[3, 2, 5, 1, 4],
1640
1547
  [7, 6, 9, 3, 5],
@@ -1776,6 +1683,7 @@ class TestNdimageFilters:
1776
1683
  [7, 9, 8, 9, 7],
1777
1684
  [8, 8, 8, 7, 7]]), output)
1778
1685
 
1686
+ @xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8339")
1779
1687
  @pytest.mark.parametrize(
1780
1688
  'axes', tuple(itertools.combinations(range(-3, 3), 2))
1781
1689
  )
@@ -1788,9 +1696,6 @@ class TestNdimageFilters:
1788
1696
  (ndimage.percentile_filter, dict(percentile=60))]
1789
1697
  )
1790
1698
  def test_minmax_nonseparable_axes(self, filter_func, axes, kwargs, xp):
1791
- if is_cupy(xp):
1792
- pytest.xfail("https://github.com/cupy/cupy/pull/8339")
1793
-
1794
1699
  array = xp.arange(6 * 8 * 12, dtype=xp.float32)
1795
1700
  array = xp.reshape(array, (6, 8, 12))
1796
1701
  # use 2D triangular footprint because it is non-separable
@@ -1806,8 +1711,7 @@ class TestNdimageFilters:
1806
1711
 
1807
1712
  missing_axis = tuple(set(range(3)) - set(axes % array.ndim))[0]
1808
1713
 
1809
- expand_dims = array_namespace(footprint).expand_dims
1810
- footprint_3d = expand_dims(footprint, axis=missing_axis)
1714
+ footprint_3d = xp.expand_dims(footprint, axis=missing_axis)
1811
1715
  expected = filter_func(array, footprint=footprint_3d, **kwargs)
1812
1716
  xp_assert_close(output, expected)
1813
1717
 
@@ -1865,18 +1769,14 @@ class TestNdimageFilters:
1865
1769
  output = ndimage.percentile_filter(array, 17, size=(2, 3))
1866
1770
  xp_assert_equal(expected, output)
1867
1771
 
1868
- @skip_xp_backends("jax.numpy",
1869
- reason="assignment destination is read-only",
1870
- )
1772
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8406")
1773
+ @uses_output_array
1871
1774
  def test_rank06_overlap(self, xp):
1872
- if is_cupy(xp):
1873
- pytest.xfail("https://github.com/cupy/cupy/issues/8406")
1874
1775
  array = xp.asarray([[3, 2, 5, 1, 4],
1875
1776
  [5, 8, 3, 7, 1],
1876
1777
  [5, 6, 9, 3, 5]])
1877
1778
 
1878
- asarray = array_namespace(array).asarray
1879
- array_copy = asarray(array, copy=True)
1779
+ array_copy = xp.asarray(array, copy=True)
1880
1780
  expected = [[2, 2, 1, 1, 1],
1881
1781
  [3, 3, 2, 1, 1],
1882
1782
  [5, 5, 3, 3, 1]]
@@ -1969,9 +1869,6 @@ class TestNdimageFilters:
1969
1869
 
1970
1870
  @pytest.mark.parametrize('dtype', types)
1971
1871
  def test_rank12(self, dtype, xp):
1972
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1973
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1974
-
1975
1872
  dtype = getattr(xp, dtype)
1976
1873
  expected = [[3, 3, 2, 4, 4],
1977
1874
  [3, 5, 2, 5, 1],
@@ -1991,9 +1888,6 @@ class TestNdimageFilters:
1991
1888
 
1992
1889
  @pytest.mark.parametrize('dtype', types)
1993
1890
  def test_rank13(self, dtype, xp):
1994
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
1995
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
1996
-
1997
1891
  dtype = getattr(xp, dtype)
1998
1892
  expected = [[5, 2, 5, 1, 1],
1999
1893
  [5, 8, 3, 5, 5],
@@ -2009,9 +1903,6 @@ class TestNdimageFilters:
2009
1903
 
2010
1904
  @pytest.mark.parametrize('dtype', types)
2011
1905
  def test_rank14(self, dtype, xp):
2012
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
2013
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
2014
-
2015
1906
  dtype = getattr(xp, dtype)
2016
1907
  expected = [[3, 5, 2, 5, 1],
2017
1908
  [5, 5, 8, 3, 5],
@@ -2027,9 +1918,6 @@ class TestNdimageFilters:
2027
1918
 
2028
1919
  @pytest.mark.parametrize('dtype', types)
2029
1920
  def test_rank15(self, dtype, xp):
2030
- if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
2031
- pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
2032
-
2033
1921
  dtype = getattr(xp, dtype)
2034
1922
  expected = [[2, 3, 1, 4, 1],
2035
1923
  [5, 3, 7, 1, 1],
@@ -2043,7 +1931,8 @@ class TestNdimageFilters:
2043
1931
  origin=[-1, 0])
2044
1932
  xp_assert_equal(expected, output)
2045
1933
 
2046
- def test_rank16(self, xp):
1934
+ # NumPy-only because test is for list input
1935
+ def test_rank16(self):
2047
1936
  # test that lists are accepted and interpreted as numpy arrays
2048
1937
  array = [3, 2, 5, 1, 4]
2049
1938
  # expected values are: median(3, 2, 5) = 3, median(2, 5, 1) = 2, etc
@@ -2080,20 +1969,20 @@ class TestNdimageFilters:
2080
1969
  y = ndimage.rank_filter(x, -2, size=3)
2081
1970
  assert y.dtype == x.dtype
2082
1971
 
2083
- @skip_xp_backends(np_only=True, reason="off-by-ones on alt backends")
1972
+ @skip_xp_backends(np_only=True, exceptions=["cupy"],
1973
+ reason="off-by-ones on alt backends")
1974
+ @xfail_xp_backends("cupy", reason="does not support extra_arguments")
2084
1975
  @pytest.mark.parametrize('dtype', types)
2085
1976
  def test_generic_filter1d01(self, dtype, xp):
2086
1977
  weights = xp.asarray([1.1, 2.2, 3.3])
2087
1978
 
2088
- if is_cupy(xp):
2089
- pytest.xfail("CuPy does not support extra_arguments")
2090
-
2091
1979
  def _filter_func(input, output, fltr, total):
2092
1980
  fltr = fltr / total
2093
1981
  for ii in range(input.shape[0] - 2):
2094
1982
  output[ii] = input[ii] * fltr[0]
2095
1983
  output[ii] += input[ii + 1] * fltr[1]
2096
1984
  output[ii] += input[ii + 2] * fltr[2]
1985
+
2097
1986
  a = np.arange(12, dtype=dtype).reshape(3, 4)
2098
1987
  a = xp.asarray(a)
2099
1988
  dtype = getattr(xp, dtype)
@@ -2105,10 +1994,9 @@ class TestNdimageFilters:
2105
1994
  extra_keywords={'total': xp.sum(weights)})
2106
1995
  assert_array_almost_equal(r1, r2)
2107
1996
 
1997
+ @xfail_xp_backends("cupy", reason="does not support extra_arguments")
2108
1998
  @pytest.mark.parametrize('dtype', types)
2109
1999
  def test_generic_filter01(self, dtype, xp):
2110
- if is_cupy(xp):
2111
- pytest.xfail("CuPy does not support extra_arguments")
2112
2000
  if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
2113
2001
  pytest.xfail("https://github.com/pytorch/pytorch/issues/58734")
2114
2002
 
@@ -2297,10 +2185,8 @@ class TestNdimageFilters:
2297
2185
  xp_assert_equal(output, expected_value)
2298
2186
 
2299
2187
 
2188
+ @xfail_xp_backends("cupy", reason="TypeError")
2300
2189
  def test_ticket_701(xp):
2301
- if is_cupy(xp):
2302
- pytest.xfail("CuPy raises a TypeError.")
2303
-
2304
2190
  # Test generic filter sizes
2305
2191
  arr = xp.asarray(np.arange(4).reshape(2, 2))
2306
2192
  def func(x):
@@ -2340,9 +2226,8 @@ def test_gh_5430():
2340
2226
  ndimage._ni_support._normalize_sequence(x, 0)
2341
2227
 
2342
2228
 
2229
+ @skip_xp_backends("cupy", reason="tests a private scipy utility")
2343
2230
  def test_gaussian_kernel1d(xp):
2344
- if is_cupy(xp):
2345
- pytest.skip("This test tests a private scipy utility.")
2346
2231
  radius = 10
2347
2232
  sigma = 2
2348
2233
  sigma2 = sigma * sigma
@@ -2373,13 +2258,12 @@ def test_orders_gauss(xp):
2373
2258
  assert_raises(ValueError, ndimage.gaussian_filter1d, arr, 1, -1, -1)
2374
2259
 
2375
2260
 
2261
+ @xfail_xp_backends("cupy", reason="TypeError")
2376
2262
  def test_valid_origins(xp):
2377
2263
  """Regression test for #1311."""
2378
- if is_cupy(xp):
2379
- pytest.xfail("CuPy raises a TypeError.")
2380
-
2381
2264
  def func(x):
2382
2265
  return xp.mean(x)
2266
+
2383
2267
  data = xp.asarray([1, 2, 3, 4, 5], dtype=xp.float64)
2384
2268
  assert_raises(ValueError, ndimage.generic_filter, data, func, size=3,
2385
2269
  origin=2)
@@ -2418,9 +2302,7 @@ def test_bad_convolve_and_correlate_origins(xp):
2418
2302
  assert_raises(ValueError, ndimage.convolve,
2419
2303
  xp.ones((3, 5)), xp.ones((2, 2)), origin=[0, -2])
2420
2304
 
2421
- @skip_xp_backends("cupy",
2422
- reason="https://github.com/cupy/cupy/pull/8430",
2423
- )
2305
+ @skip_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8430")
2424
2306
  def test_multiple_modes(xp):
2425
2307
  # Test that the filters with multiple mode capabilities for different
2426
2308
  # dimensions give the same result as applying a single mode.
@@ -2452,14 +2334,13 @@ def test_multiple_modes(xp):
2452
2334
 
2453
2335
 
2454
2336
  @skip_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8430")
2455
- @skip_xp_backends("jax.numpy", reason="output array is read-only.")
2456
2337
  def test_multiple_modes_sequentially(xp):
2457
2338
  # Test that the filters with multiple mode capabilities for different
2458
2339
  # dimensions give the same result as applying the filters with
2459
2340
  # different modes sequentially
2460
2341
  arr = xp.asarray([[1., 0., 0.],
2461
- [1., 1., 0.],
2462
- [0., 0., 0.]])
2342
+ [1., 1., 0.],
2343
+ [0., 0., 0.]])
2463
2344
 
2464
2345
  modes = ['reflect', 'wrap']
2465
2346
 
@@ -2567,9 +2448,7 @@ def test_multiple_modes_gaussian_gradient_magnitude(xp):
2567
2448
 
2568
2449
  assert_almost_equal(expected, calculated)
2569
2450
 
2570
- @skip_xp_backends("cupy",
2571
- reason="https://github.com/cupy/cupy/pull/8430",
2572
- )
2451
+ @skip_xp_backends("cupy", reason="https://github.com/cupy/cupy/pull/8430")
2573
2452
  def test_multiple_modes_uniform(xp):
2574
2453
  # Test uniform filter for multiple extrapolation modes
2575
2454
  arr = xp.asarray([[1., 0., 0.],
@@ -2610,8 +2489,6 @@ def test_gaussian_truncate(xp):
2610
2489
  )
2611
2490
  assert num_nonzeros_5 == 51**2
2612
2491
 
2613
- nnz_kw = {'as_tuple': True} if is_torch(xp) else {}
2614
-
2615
2492
  # Test truncate when sigma is a sequence.
2616
2493
  f = ndimage.gaussian_filter(arr, [0.5, 2.5], truncate=3.5)
2617
2494
  fpos = f > 0
@@ -2630,22 +2507,20 @@ def test_gaussian_truncate(xp):
2630
2507
 
2631
2508
  # Test gaussian_laplace
2632
2509
  y = ndimage.gaussian_laplace(x, sigma=2, truncate=3.5)
2633
- nonzero_indices = xp.nonzero(y != 0, **nnz_kw)[0]
2510
+ nonzero_indices = xp.nonzero(y != 0)[0]
2634
2511
 
2635
2512
  n = xp.max(nonzero_indices) - xp.min(nonzero_indices) + 1
2636
2513
  assert n == 15
2637
2514
 
2638
2515
  # Test gaussian_gradient_magnitude
2639
2516
  y = ndimage.gaussian_gradient_magnitude(x, sigma=2, truncate=3.5)
2640
- nonzero_indices = xp.nonzero(y != 0, **nnz_kw)[0]
2517
+ nonzero_indices = xp.nonzero(y != 0)[0]
2641
2518
  n = xp.max(nonzero_indices) - xp.min(nonzero_indices) + 1
2642
2519
  assert n == 15
2643
2520
 
2644
2521
 
2522
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8402")
2645
2523
  def test_gaussian_radius(xp):
2646
- if is_cupy(xp):
2647
- pytest.xfail("https://github.com/cupy/cupy/issues/8402")
2648
-
2649
2524
  # Test that Gaussian filters with radius argument produce the same
2650
2525
  # results as the filters with corresponding truncate argument.
2651
2526
  # radius = int(truncate * sigma + 0.5)
@@ -2674,10 +2549,8 @@ def test_gaussian_radius(xp):
2674
2549
  xp_assert_equal(f1, f2)
2675
2550
 
2676
2551
 
2552
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8402")
2677
2553
  def test_gaussian_radius_invalid(xp):
2678
- if is_cupy(xp):
2679
- pytest.xfail("https://github.com/cupy/cupy/issues/8402")
2680
-
2681
2554
  # radius must be a nonnegative integer
2682
2555
  with assert_raises(ValueError):
2683
2556
  ndimage.gaussian_filter1d(xp.zeros(8), sigma=1, radius=-1)
@@ -2685,7 +2558,7 @@ def test_gaussian_radius_invalid(xp):
2685
2558
  ndimage.gaussian_filter1d(xp.zeros(8), sigma=1, radius=1.1)
2686
2559
 
2687
2560
 
2688
- @skip_xp_backends("jax.numpy", reason="output array is read-only")
2561
+ @uses_output_array
2689
2562
  class TestThreading:
2690
2563
  def check_func_thread(self, n, fun, args, out):
2691
2564
  from threading import Thread
@@ -2698,10 +2571,9 @@ class TestThreading:
2698
2571
  for i in range(n):
2699
2572
  fun(*args, output=out[i, ...])
2700
2573
 
2574
+ @xfail_xp_backends("cupy",
2575
+ reason="XXX thread exception; cannot repro outside of pytest")
2701
2576
  def test_correlate1d(self, xp):
2702
- if is_cupy(xp):
2703
- pytest.xfail("XXX thread exception; cannot repro outside of pytest")
2704
-
2705
2577
  d = np.random.randn(5000)
2706
2578
  os = np.empty((4, d.size))
2707
2579
  ot = np.empty_like(os)
@@ -2713,10 +2585,9 @@ class TestThreading:
2713
2585
  self.check_func_thread(4, ndimage.correlate1d, (d, k), ot)
2714
2586
  xp_assert_equal(os, ot)
2715
2587
 
2588
+ @xfail_xp_backends("cupy",
2589
+ reason="XXX thread exception; cannot repro outside of pytest")
2716
2590
  def test_correlate(self, xp):
2717
- if is_cupy(xp):
2718
- pytest.xfail("XXX thread exception; cannot repro outside of pytest")
2719
-
2720
2591
  d = xp.asarray(np.random.randn(500, 500))
2721
2592
  k = xp.asarray(np.random.randn(10, 10))
2722
2593
  os = xp.empty([4] + list(d.shape))
@@ -2725,10 +2596,9 @@ class TestThreading:
2725
2596
  self.check_func_thread(4, ndimage.correlate, (d, k), ot)
2726
2597
  xp_assert_equal(os, ot)
2727
2598
 
2599
+ @xfail_xp_backends("cupy",
2600
+ reason="XXX thread exception; cannot repro outside of pytest")
2728
2601
  def test_median_filter(self, xp):
2729
- if is_cupy(xp):
2730
- pytest.xfail("XXX thread exception; cannot repro outside of pytest")
2731
-
2732
2602
  d = xp.asarray(np.random.randn(500, 500))
2733
2603
  os = xp.empty([4] + list(d.shape))
2734
2604
  ot = xp.empty_like(os)
@@ -2736,10 +2606,9 @@ class TestThreading:
2736
2606
  self.check_func_thread(4, ndimage.median_filter, (d, 3), ot)
2737
2607
  xp_assert_equal(os, ot)
2738
2608
 
2609
+ @xfail_xp_backends("cupy",
2610
+ reason="XXX thread exception; cannot repro outside of pytest")
2739
2611
  def test_uniform_filter1d(self, xp):
2740
- if is_cupy(xp):
2741
- pytest.xfail("XXX thread exception; cannot repro outside of pytest")
2742
-
2743
2612
  d = np.random.randn(5000)
2744
2613
  os = np.empty((4, d.size))
2745
2614
  ot = np.empty_like(os)
@@ -2750,10 +2619,9 @@ class TestThreading:
2750
2619
  self.check_func_thread(4, ndimage.uniform_filter1d, (d, 5), ot)
2751
2620
  xp_assert_equal(os, ot)
2752
2621
 
2622
+ @xfail_xp_backends("cupy",
2623
+ reason="XXX thread exception; cannot repro outside of pytest")
2753
2624
  def test_minmax_filter(self, xp):
2754
- if is_cupy(xp):
2755
- pytest.xfail("XXX thread exception; cannot repro outside of pytest")
2756
-
2757
2625
  d = xp.asarray(np.random.randn(500, 500))
2758
2626
  os = xp.empty([4] + list(d.shape))
2759
2627
  ot = xp.empty_like(os)
@@ -2794,9 +2662,8 @@ def test_minmaximum_filter1d(xp):
2794
2662
  xp_assert_equal(xp.asarray([9, 9, 4, 5, 6, 7, 8, 9, 9, 9]), out)
2795
2663
 
2796
2664
 
2665
+ @xfail_xp_backends("cupy", reason="cupy/cupy#8401")
2797
2666
  def test_uniform_filter1d_roundoff_errors(xp):
2798
- if is_cupy(xp):
2799
- pytest.xfail("https://github.com/cupy/cupy/issues/8401")
2800
2667
  # gh-6930
2801
2668
  in_ = np.repeat([0, 1, 0], [9, 9, 9])
2802
2669
  in_ = xp.asarray(in_)
@@ -2814,14 +2681,10 @@ def test_footprint_all_zeros(xp):
2814
2681
  ndimage.maximum_filter(arr, footprint=kernel)
2815
2682
 
2816
2683
 
2817
- def test_gaussian_filter(xp):
2818
- if is_cupy(xp):
2819
- pytest.xfail("CuPy does not raise")
2820
-
2821
- if not hasattr(xp, "float16"):
2822
- pytest.xfail(f"{xp} does not have float16")
2823
-
2824
- # Test gaussian filter with xp.float16
2684
+ @xfail_xp_backends("cupy", reason="does not raise")
2685
+ @skip_xp_backends("array_api_strict", reason="no float16")
2686
+ @skip_xp_backends("dask.array", reason="no float16")
2687
+ def test_gaussian_filter_float16(xp):
2825
2688
  # gh-8207
2826
2689
  data = xp.asarray([1], dtype=xp.float16)
2827
2690
  sigma = 1.0
@@ -2829,10 +2692,8 @@ def test_gaussian_filter(xp):
2829
2692
  ndimage.gaussian_filter(data, sigma)
2830
2693
 
2831
2694
 
2695
+ @xfail_xp_backends("cupy", reason="does not raise")
2832
2696
  def test_rank_filter_noninteger_rank(xp):
2833
- if is_cupy(xp):
2834
- pytest.xfail("CuPy does not raise")
2835
-
2836
2697
  # regression test for issue 9388: ValueError for
2837
2698
  # non integer rank when performing rank_filter
2838
2699
  arr = xp.asarray(np.random.random((10, 20, 30)))
@@ -2854,12 +2715,12 @@ def test_size_footprint_both_set(xp):
2854
2715
  )
2855
2716
 
2856
2717
 
2857
- @skip_xp_backends(np_only=True, reason='byteorder is numpy-specific')
2858
- def test_byte_order_median(xp):
2718
+ # NumPy-only because 'byteorder is numpy-specific'
2719
+ def test_byte_order_median():
2859
2720
  """Regression test for #413: median_filter does not handle bytes orders."""
2860
- a = xp.arange(9, dtype='<f4').reshape(3, 3)
2721
+ a = np.arange(9, dtype='<f4').reshape(3, 3)
2861
2722
  ref = ndimage.median_filter(a, (3, 3))
2862
- b = xp.arange(9, dtype='>f4').reshape(3, 3)
2723
+ b = np.arange(9, dtype='>f4').reshape(3, 3)
2863
2724
  t = ndimage.median_filter(b, (3, 3))
2864
2725
  assert_array_almost_equal(ref, t)
2865
2726
 
@@ -2909,6 +2770,256 @@ def test_gh_22333():
2909
2770
  assert_array_equal(actual, expected)
2910
2771
 
2911
2772
 
2773
+ @pytest.mark.filterwarnings("ignore:The given NumPy array is not writable:UserWarning")
2774
+ @pytest.mark.skip_xp_backends(cpu_only=True, exceptions=['cupy'])
2775
+ class TestVectorizedFilter:
2776
+ @pytest.mark.parametrize("axes, size",
2777
+ [(None, (3, 4, 5)), ((0, 2), (3, 4)), ((-1,), (5,))])
2778
+ @pytest.mark.parametrize("origin", [-1, 0, 1])
2779
+ @pytest.mark.parametrize("mode",
2780
+ ['reflect', 'nearest', 'mirror', 'wrap', 'constant'])
2781
+ @pytest.mark.parametrize("use_output", [False, True])
2782
+ def test_against_generic_filter(self, axes, size, origin, mode, use_output, xp):
2783
+ rng = np.random.default_rng(435982456983456987356)
2784
+
2785
+ if use_output and (is_dask(xp) or is_jax(xp)):
2786
+ pytest.skip("Requires mutable arrays.")
2787
+
2788
+ input = rng.random(size=(11, 12, 13))
2789
+ input_copy = input.copy() # check that it is not modified
2790
+ output = xp.zeros(input.shape) if use_output else None
2791
+
2792
+ kwargs = dict(axes=axes, size=size, origin=origin, mode=mode)
2793
+ ref = ndimage.generic_filter(input, np.mean, **kwargs)
2794
+ kwargs['output'] = output
2795
+ res = ndimage.vectorized_filter(xp.asarray(input.tolist()),
2796
+ xp.mean, **kwargs)
2797
+ xp_assert_close(res, xp.asarray(ref.tolist()), atol=1e-15)
2798
+ if use_output:
2799
+ xp_assert_equal(output, res)
2800
+
2801
+ if not (is_array_api_strict(xp) or is_dask(xp)):
2802
+ # currently requires support for [..., mask] indexing
2803
+ kwargs.pop('size')
2804
+ kwargs.pop('output')
2805
+ kwargs['footprint'] = rng.random(size=size or input.shape) > 0.5
2806
+ ref = ndimage.generic_filter(input, np.mean, **kwargs)
2807
+ kwargs['footprint'] = xp.asarray(kwargs['footprint'])
2808
+ kwargs['output'] = output
2809
+ res = ndimage.vectorized_filter(xp.asarray(input.tolist()),
2810
+ xp.mean, **kwargs)
2811
+ xp_assert_close(res, xp.asarray(ref.tolist()), atol=1e-15)
2812
+ if use_output:
2813
+ xp_assert_equal(output, res)
2814
+
2815
+ xp_assert_equal(xp.asarray(input), xp.asarray(input_copy))
2816
+
2817
+ @pytest.mark.parametrize("dtype",
2818
+ ["uint8", "uint16", "uint32", "uint64",
2819
+ "int8", "int16", "int32", "int64",
2820
+ "float32", "float64", "complex64", "complex128"])
2821
+ @pytest.mark.parametrize("batch_memory", [1, 16*3, np.inf])
2822
+ @pytest.mark.parametrize("use_footprint", [False, True])
2823
+ def test_dtype_batch_memory(self, dtype, batch_memory, use_footprint, xp):
2824
+ rng = np.random.default_rng(435982456983456987356)
2825
+ w = 3
2826
+
2827
+ if is_jax(xp) and not (batch_memory == 1):
2828
+ pytest.skip("Requires mutable array.")
2829
+ if is_torch(xp) and dtype in {'uint16', 'uint32', 'uint64'}:
2830
+ pytest.skip("Needs uint support.")
2831
+
2832
+ dtype = getattr(xp, dtype)
2833
+
2834
+ if use_footprint:
2835
+ if (is_dask(xp) or is_array_api_strict(xp)):
2836
+ pytest.skip("Requires [..., mask] indexing.")
2837
+ footprint = xp.asarray([True, False, True])
2838
+ kwargs = dict(footprint=footprint, batch_memory=batch_memory)
2839
+ else:
2840
+ footprint = xp.asarray([True, True, True])
2841
+ kwargs = dict(size=w, batch_memory=batch_memory)
2842
+
2843
+ # The intent here is to exercise all the code paths involved in `batch_memory`
2844
+ # and `output` handling. To test the limited-memory case, `batch_memory=16*3`
2845
+ # is chosen to be just large enough for a *single* window of `complex128` to
2846
+ # fit, and `n` is large enough that a whole sliding window view of `uint8`s
2847
+ # *won't* fit.
2848
+ n = 16*3 + 1
2849
+ input = rng.integers(0, 42, size=(n,))
2850
+ input = input + input*1j if xp.isdtype(dtype, 'complex floating') else input
2851
+ input_padded = xp.asarray(np.pad(input, [(1, 1)], mode='symmetric'),
2852
+ dtype=dtype)
2853
+ input = xp.asarray(input, dtype=dtype)
2854
+
2855
+ ref = [xp.sum(input_padded[i: i + w][footprint]) for i in range(n)]
2856
+ sum_dtype = xp.sum(input_padded).dtype
2857
+
2858
+ message = "`batch_memory` is insufficient for minimum chunk size."
2859
+ context = (pytest.raises(ValueError, match=message)
2860
+ if batch_memory == 1 else contextlib.nullcontext())
2861
+ with context:
2862
+ res = ndimage.vectorized_filter(input, xp.sum, **kwargs)
2863
+ xp_assert_close(res, xp.astype(xp.stack(ref), sum_dtype))
2864
+ assert res.dtype == sum_dtype
2865
+
2866
+ output = xp.empty_like(input)
2867
+ res = ndimage.vectorized_filter(input, xp.sum, output=output, **kwargs)
2868
+ xp_assert_close(res, xp.astype(xp.stack(ref), dtype))
2869
+ assert res.dtype == dtype
2870
+
2871
+ def test_mode_valid(self, xp):
2872
+ rng = np.random.default_rng(435982456983456987356)
2873
+ input = rng.random(size=(10, 11))
2874
+ input_xp = xp.asarray(input)
2875
+ input_xp_copy = xp_copy(input_xp) # check that it is not modified
2876
+ size = (3, 5)
2877
+
2878
+ res = ndimage.vectorized_filter(input_xp, xp.mean, size=size, mode='valid')
2879
+
2880
+ view = np.lib.stride_tricks.sliding_window_view(input, size)
2881
+ ref = np.mean(view, axis=(-2, -1))
2882
+
2883
+ xp_assert_close(res, xp.asarray(ref))
2884
+ assert res.shape == tuple(input.shape - np.asarray(size) + 1)
2885
+ xp_assert_equal(input_xp, input_xp_copy)
2886
+
2887
+ def test_input_validation(self, xp):
2888
+ input = xp.ones((10, 10))
2889
+ function = xp.mean
2890
+ size = 2
2891
+ footprint = xp.ones((2, 2))
2892
+
2893
+ message = "`function` must be a callable."
2894
+ with pytest.raises(ValueError, match=message):
2895
+ ndimage.vectorized_filter(input, "eggplant", size=size)
2896
+
2897
+ message = "Either `size` or `footprint` must be provided."
2898
+ with pytest.raises(ValueError, match=message):
2899
+ ndimage.vectorized_filter(input, function)
2900
+
2901
+ message = "Either `size` or `footprint` may be provided, not both."
2902
+ with pytest.raises(ValueError, match=message):
2903
+ ndimage.vectorized_filter(input, function, size=size, footprint=footprint)
2904
+
2905
+ message = "All elements of `size` must be positive integers."
2906
+ with pytest.raises(ValueError, match=message):
2907
+ ndimage.vectorized_filter(input, function, size=(1, -1))
2908
+ with pytest.raises(ValueError, match=message):
2909
+ ndimage.vectorized_filter(input, function, size=0)
2910
+
2911
+ message = "The length of `axes` may not exceed "
2912
+ axes = (0, 1, 2)
2913
+ with pytest.raises(ValueError, match=message):
2914
+ ndimage.vectorized_filter(input, function, size=(1, 2), axes=axes)
2915
+ with pytest.raises(ValueError, match=message):
2916
+ ndimage.vectorized_filter(input, function, footprint=xp.ones((2, 2)),
2917
+ axes=axes)
2918
+
2919
+ message = "`axes` must be compatible with the dimensionality..."
2920
+ with pytest.raises(ValueError, match=message):
2921
+ ndimage.vectorized_filter(input, function, size=(1,))
2922
+ with pytest.raises(ValueError, match=message):
2923
+ ndimage.vectorized_filter(input, function, size=(2,), axes=(0,1))
2924
+
2925
+ message = "All elements of `origin` must be integers"
2926
+ with pytest.raises(ValueError, match=message):
2927
+ ndimage.vectorized_filter(input, function, size=size, origin=(1, 1.5))
2928
+
2929
+ message = "`origin` must be an integer or tuple of integers with length..."
2930
+ with pytest.raises(ValueError, match=message):
2931
+ ndimage.vectorized_filter(input, function, size=size, origin=(1, 2, 3))
2932
+
2933
+ message = "`mode` must be one of..."
2934
+ with pytest.raises(ValueError, match=message):
2935
+ ndimage.vectorized_filter(input, function, size=size, mode='coconut')
2936
+
2937
+ message = "`mode='valid'` is incompatible with use of `origin`."
2938
+ with pytest.raises(ValueError, match=message):
2939
+ ndimage.vectorized_filter(input, function, size=size,
2940
+ mode='valid', origin=1)
2941
+
2942
+ message = "Use of `cval` is compatible only with `mode='constant'`."
2943
+ with pytest.raises(ValueError, match=message):
2944
+ ndimage.vectorized_filter(input, function, size=size, mode='valid', cval=1)
2945
+
2946
+ other_messages = "|Unsupported|The array_api_strict|new|Value 'a duck'"
2947
+ message = "`cval` must include only numbers." + other_messages
2948
+ with pytest.raises((ValueError, TypeError), match=message):
2949
+ ndimage.vectorized_filter(input, function, size=size,
2950
+ mode='constant', cval='a duck')
2951
+
2952
+ message = "`batch_memory` must be positive number." + other_messages
2953
+ with pytest.raises(ValueError, match=message):
2954
+ ndimage.vectorized_filter(input, function, size=size, batch_memory=0)
2955
+ with pytest.raises(ValueError, match=message):
2956
+ ndimage.vectorized_filter(input, function, size=size, batch_memory=(1, 2))
2957
+ with pytest.raises((ValueError, TypeError), match=message):
2958
+ ndimage.vectorized_filter(input, function, size=size, batch_memory="a duck")
2959
+
2960
+ @pytest.mark.parametrize('shape', [(0,), (1, 0), (0, 1, 0)])
2961
+ def test_zero_size(self, shape, xp):
2962
+ input = xp.empty(shape)
2963
+ res = ndimage.vectorized_filter(input, xp.mean, size=1)
2964
+ xp_assert_equal(res, input)
2965
+
2966
+ @pytest.mark.filterwarnings("ignore:Mean of empty slice.:RuntimeWarning")
2967
+ def test_edge_cases(self, xp):
2968
+ rng = np.random.default_rng(4835982345234982)
2969
+ function = xp.mean
2970
+
2971
+ # 0-D input
2972
+ input = xp.asarray(1.)
2973
+ res = ndimage.vectorized_filter(input, function, size=())
2974
+ xp_assert_equal(res, xp.asarray(function(input, axis=())))
2975
+
2976
+ if not (is_array_api_strict(xp) or is_dask(xp)):
2977
+ res = ndimage.vectorized_filter(input, function, footprint=True)
2978
+ xp_assert_equal(res, xp.asarray(function(input[True], axis=())))
2979
+
2980
+ res = ndimage.vectorized_filter(input, function, footprint=False)
2981
+ xp_assert_equal(res, xp.asarray(function(input[False], axis=())))
2982
+
2983
+ # 1x1 window
2984
+ input = xp.asarray(rng.random((5, 5)))
2985
+ res = ndimage.vectorized_filter(input, function, size=1)
2986
+ xp_assert_equal(res, input)
2987
+
2988
+ # window is bigger than input shouldn't be a problem
2989
+ res = ndimage.vectorized_filter(input, function, size=21)
2990
+ ref = ndimage.vectorized_filter(input, function, size=21)
2991
+ xp_assert_close(res, ref)
2992
+
2993
+ def test_gh23046_feature(self, xp):
2994
+ # The intent of gh-23046 was to always allow `size` to be a scalar.
2995
+ rng = np.random.default_rng(45982734597824)
2996
+ img = xp.asarray(rng.random((5, 5)))
2997
+
2998
+ ref = ndimage.vectorized_filter(img, xp.mean, size=2)
2999
+ res = ndimage.vectorized_filter(img, xp.mean, size=2, axes=(0, 1))
3000
+ xp_assert_close(res, ref)
3001
+
3002
+ ref = ndimage.vectorized_filter(img, xp.mean, size=(2,), axes=(0,))
3003
+ res = ndimage.vectorized_filter(img, xp.mean, size=2, axes=0)
3004
+ xp_assert_close(res, ref)
3005
+
3006
+ def test_gh23046_fix(self, xp):
3007
+ # While investigating the feasibility of gh-23046, I noticed a bug when the
3008
+ # length of an `axes` tuple equals the dimensionality of the image.
3009
+ rng = np.random.default_rng(45982734597824)
3010
+ img = xp.asarray(rng.random((5, 5)))
3011
+ size = (2, 3)
3012
+ ref = ndimage.vectorized_filter(img.T, xp.mean, size=size).T
3013
+ res = ndimage.vectorized_filter(img, xp.mean, size=size, axes=(1, 0))
3014
+ xp_assert_close(res, ref)
3015
+
3016
+ ref = ndimage.vectorized_filter(img, xp.mean, size=size, mode='constant')
3017
+ res = ndimage.vectorized_filter(img, xp.mean, size=size[::-1], axes=(1, 0),
3018
+ mode='constant')
3019
+ xp_assert_close(res, ref)
3020
+
3021
+
3022
+
2912
3023
  @given(x=npst.arrays(dtype=np.float64,
2913
3024
  shape=st.integers(min_value=1, max_value=1000)),
2914
3025
  size=st.integers(min_value=1, max_value=50),