scipy 1.15.3__cp313-cp313t-win_amd64.whl → 1.16.0rc2__cp313-cp313t-win_amd64.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 (759) hide show
  1. scipy/__config__.py +7 -7
  2. scipy/__init__.py +3 -6
  3. scipy/_cyutility.cp313t-win_amd64.dll.a +0 -0
  4. scipy/_cyutility.cp313t-win_amd64.pyd +0 -0
  5. scipy/_lib/_array_api.py +486 -161
  6. scipy/_lib/_array_api_compat_vendor.py +9 -0
  7. scipy/_lib/_bunch.py +4 -0
  8. scipy/_lib/_ccallback_c.cp313t-win_amd64.dll.a +0 -0
  9. scipy/_lib/_ccallback_c.cp313t-win_amd64.pyd +0 -0
  10. scipy/_lib/_docscrape.py +1 -1
  11. scipy/_lib/_elementwise_iterative_method.py +15 -26
  12. scipy/_lib/_fpumode.cp313t-win_amd64.dll.a +0 -0
  13. scipy/_lib/_fpumode.cp313t-win_amd64.pyd +0 -0
  14. scipy/_lib/_sparse.py +41 -0
  15. scipy/_lib/_test_ccallback.cp313t-win_amd64.dll.a +0 -0
  16. scipy/_lib/_test_ccallback.cp313t-win_amd64.pyd +0 -0
  17. scipy/_lib/_test_deprecation_call.cp313t-win_amd64.dll.a +0 -0
  18. scipy/_lib/_test_deprecation_call.cp313t-win_amd64.pyd +0 -0
  19. scipy/_lib/_test_deprecation_def.cp313t-win_amd64.dll.a +0 -0
  20. scipy/_lib/_test_deprecation_def.cp313t-win_amd64.pyd +0 -0
  21. scipy/_lib/_testutils.py +6 -2
  22. scipy/_lib/_uarray/_uarray.cp313t-win_amd64.dll.a +0 -0
  23. scipy/_lib/_uarray/_uarray.cp313t-win_amd64.pyd +0 -0
  24. scipy/_lib/_util.py +222 -125
  25. scipy/_lib/array_api_compat/__init__.py +4 -4
  26. scipy/_lib/array_api_compat/_internal.py +19 -6
  27. scipy/_lib/array_api_compat/common/__init__.py +1 -1
  28. scipy/_lib/array_api_compat/common/_aliases.py +365 -193
  29. scipy/_lib/array_api_compat/common/_fft.py +94 -64
  30. scipy/_lib/array_api_compat/common/_helpers.py +413 -180
  31. scipy/_lib/array_api_compat/common/_linalg.py +116 -40
  32. scipy/_lib/array_api_compat/common/_typing.py +179 -10
  33. scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
  34. scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
  35. scipy/_lib/array_api_compat/cupy/_info.py +16 -6
  36. scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
  37. scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
  38. scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
  39. scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
  40. scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
  41. scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
  42. scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
  43. scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
  44. scipy/_lib/array_api_compat/numpy/_info.py +36 -16
  45. scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
  46. scipy/_lib/array_api_compat/numpy/fft.py +11 -5
  47. scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
  48. scipy/_lib/array_api_compat/torch/__init__.py +3 -5
  49. scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
  50. scipy/_lib/array_api_compat/torch/_info.py +27 -16
  51. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  52. scipy/_lib/array_api_compat/torch/fft.py +17 -18
  53. scipy/_lib/array_api_compat/torch/linalg.py +16 -16
  54. scipy/_lib/array_api_extra/__init__.py +26 -3
  55. scipy/_lib/array_api_extra/_delegation.py +171 -0
  56. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  57. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  58. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  59. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  60. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  61. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  62. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  63. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  64. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  65. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  66. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  67. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  68. scipy/_lib/array_api_extra/testing.py +359 -0
  69. scipy/_lib/decorator.py +2 -2
  70. scipy/_lib/doccer.py +1 -7
  71. scipy/_lib/messagestream.cp313t-win_amd64.dll.a +0 -0
  72. scipy/_lib/messagestream.cp313t-win_amd64.pyd +0 -0
  73. scipy/_lib/pyprima/__init__.py +212 -0
  74. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  75. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  76. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  77. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  78. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  79. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  80. scipy/_lib/pyprima/cobyla/update.py +289 -0
  81. scipy/_lib/pyprima/common/__init__.py +0 -0
  82. scipy/_lib/pyprima/common/_bounds.py +34 -0
  83. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  84. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  85. scipy/_lib/pyprima/common/_project.py +173 -0
  86. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  87. scipy/_lib/pyprima/common/consts.py +47 -0
  88. scipy/_lib/pyprima/common/evaluate.py +99 -0
  89. scipy/_lib/pyprima/common/history.py +38 -0
  90. scipy/_lib/pyprima/common/infos.py +30 -0
  91. scipy/_lib/pyprima/common/linalg.py +435 -0
  92. scipy/_lib/pyprima/common/message.py +290 -0
  93. scipy/_lib/pyprima/common/powalg.py +131 -0
  94. scipy/_lib/pyprima/common/preproc.py +277 -0
  95. scipy/_lib/pyprima/common/present.py +5 -0
  96. scipy/_lib/pyprima/common/ratio.py +54 -0
  97. scipy/_lib/pyprima/common/redrho.py +47 -0
  98. scipy/_lib/pyprima/common/selectx.py +296 -0
  99. scipy/_lib/tests/test__util.py +105 -121
  100. scipy/_lib/tests/test_array_api.py +166 -35
  101. scipy/_lib/tests/test_bunch.py +7 -0
  102. scipy/_lib/tests/test_ccallback.py +2 -10
  103. scipy/_lib/tests/test_public_api.py +13 -0
  104. scipy/cluster/_hierarchy.cp313t-win_amd64.dll.a +0 -0
  105. scipy/cluster/_hierarchy.cp313t-win_amd64.pyd +0 -0
  106. scipy/cluster/_optimal_leaf_ordering.cp313t-win_amd64.dll.a +0 -0
  107. scipy/cluster/_optimal_leaf_ordering.cp313t-win_amd64.pyd +0 -0
  108. scipy/cluster/_vq.cp313t-win_amd64.dll.a +0 -0
  109. scipy/cluster/_vq.cp313t-win_amd64.pyd +0 -0
  110. scipy/cluster/hierarchy.py +393 -223
  111. scipy/cluster/tests/test_hierarchy.py +273 -335
  112. scipy/cluster/tests/test_vq.py +45 -61
  113. scipy/cluster/vq.py +39 -35
  114. scipy/conftest.py +263 -157
  115. scipy/constants/_constants.py +4 -1
  116. scipy/constants/tests/test_codata.py +2 -2
  117. scipy/constants/tests/test_constants.py +11 -18
  118. scipy/datasets/_download_all.py +15 -1
  119. scipy/datasets/_fetchers.py +7 -1
  120. scipy/datasets/_utils.py +1 -1
  121. scipy/differentiate/_differentiate.py +25 -25
  122. scipy/differentiate/tests/test_differentiate.py +24 -25
  123. scipy/fft/_basic.py +20 -0
  124. scipy/fft/_helper.py +3 -34
  125. scipy/fft/_pocketfft/helper.py +29 -1
  126. scipy/fft/_pocketfft/pypocketfft.cp313t-win_amd64.dll.a +0 -0
  127. scipy/fft/_pocketfft/pypocketfft.cp313t-win_amd64.pyd +0 -0
  128. scipy/fft/_pocketfft/tests/test_basic.py +2 -4
  129. scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
  130. scipy/fft/_realtransforms.py +13 -0
  131. scipy/fft/tests/test_basic.py +27 -25
  132. scipy/fft/tests/test_fftlog.py +16 -7
  133. scipy/fft/tests/test_helper.py +18 -34
  134. scipy/fft/tests/test_real_transforms.py +8 -10
  135. scipy/fftpack/convolve.cp313t-win_amd64.dll.a +0 -0
  136. scipy/fftpack/convolve.cp313t-win_amd64.pyd +0 -0
  137. scipy/fftpack/tests/test_basic.py +2 -4
  138. scipy/fftpack/tests/test_real_transforms.py +8 -9
  139. scipy/integrate/_bvp.py +9 -3
  140. scipy/integrate/_cubature.py +3 -2
  141. scipy/integrate/_dop.cp313t-win_amd64.dll.a +0 -0
  142. scipy/integrate/_dop.cp313t-win_amd64.pyd +0 -0
  143. scipy/integrate/_lsoda.cp313t-win_amd64.dll.a +0 -0
  144. scipy/integrate/_lsoda.cp313t-win_amd64.pyd +0 -0
  145. scipy/integrate/_ode.py +9 -2
  146. scipy/integrate/_odepack.cp313t-win_amd64.dll.a +0 -0
  147. scipy/integrate/_odepack.cp313t-win_amd64.pyd +0 -0
  148. scipy/integrate/_quad_vec.py +21 -29
  149. scipy/integrate/_quadpack.cp313t-win_amd64.dll.a +0 -0
  150. scipy/integrate/_quadpack.cp313t-win_amd64.pyd +0 -0
  151. scipy/integrate/_quadpack_py.py +11 -7
  152. scipy/integrate/_quadrature.py +3 -3
  153. scipy/integrate/_rules/_base.py +2 -2
  154. scipy/integrate/_tanhsinh.py +48 -47
  155. scipy/integrate/_test_multivariate.cp313t-win_amd64.dll.a +0 -0
  156. scipy/integrate/_test_multivariate.cp313t-win_amd64.pyd +0 -0
  157. scipy/integrate/_test_odeint_banded.cp313t-win_amd64.dll.a +0 -0
  158. scipy/integrate/_test_odeint_banded.cp313t-win_amd64.pyd +0 -0
  159. scipy/integrate/_vode.cp313t-win_amd64.dll.a +0 -0
  160. scipy/integrate/_vode.cp313t-win_amd64.pyd +0 -0
  161. scipy/integrate/tests/test__quad_vec.py +0 -6
  162. scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
  163. scipy/integrate/tests/test_cubature.py +21 -35
  164. scipy/integrate/tests/test_quadrature.py +6 -8
  165. scipy/integrate/tests/test_tanhsinh.py +56 -48
  166. scipy/interpolate/__init__.py +70 -58
  167. scipy/interpolate/_bary_rational.py +22 -22
  168. scipy/interpolate/_bsplines.py +119 -66
  169. scipy/interpolate/_cubic.py +65 -50
  170. scipy/interpolate/_dfitpack.cp313t-win_amd64.dll.a +0 -0
  171. scipy/interpolate/_dfitpack.cp313t-win_amd64.pyd +0 -0
  172. scipy/interpolate/_dierckx.cp313t-win_amd64.dll.a +0 -0
  173. scipy/interpolate/_dierckx.cp313t-win_amd64.pyd +0 -0
  174. scipy/interpolate/_fitpack.cp313t-win_amd64.dll.a +0 -0
  175. scipy/interpolate/_fitpack.cp313t-win_amd64.pyd +0 -0
  176. scipy/interpolate/_fitpack2.py +9 -6
  177. scipy/interpolate/_fitpack_impl.py +32 -26
  178. scipy/interpolate/_fitpack_repro.py +23 -19
  179. scipy/interpolate/_interpnd.cp313t-win_amd64.dll.a +0 -0
  180. scipy/interpolate/_interpnd.cp313t-win_amd64.pyd +0 -0
  181. scipy/interpolate/_interpolate.py +30 -12
  182. scipy/interpolate/_ndbspline.py +13 -18
  183. scipy/interpolate/_ndgriddata.py +5 -8
  184. scipy/interpolate/_polyint.py +95 -31
  185. scipy/interpolate/_ppoly.cp313t-win_amd64.dll.a +0 -0
  186. scipy/interpolate/_ppoly.cp313t-win_amd64.pyd +0 -0
  187. scipy/interpolate/_rbf.py +2 -2
  188. scipy/interpolate/_rbfinterp.py +1 -1
  189. scipy/interpolate/_rbfinterp_pythran.cp313t-win_amd64.dll.a +0 -0
  190. scipy/interpolate/_rbfinterp_pythran.cp313t-win_amd64.pyd +0 -0
  191. scipy/interpolate/_rgi.py +31 -26
  192. scipy/interpolate/_rgi_cython.cp313t-win_amd64.dll.a +0 -0
  193. scipy/interpolate/_rgi_cython.cp313t-win_amd64.pyd +0 -0
  194. scipy/interpolate/dfitpack.py +0 -20
  195. scipy/interpolate/interpnd.py +1 -2
  196. scipy/interpolate/tests/test_bary_rational.py +2 -2
  197. scipy/interpolate/tests/test_bsplines.py +97 -1
  198. scipy/interpolate/tests/test_fitpack2.py +39 -1
  199. scipy/interpolate/tests/test_interpnd.py +32 -20
  200. scipy/interpolate/tests/test_interpolate.py +48 -4
  201. scipy/interpolate/tests/test_rgi.py +2 -1
  202. scipy/io/_fast_matrix_market/__init__.py +2 -0
  203. scipy/io/_fast_matrix_market/_fmm_core.cp313t-win_amd64.dll.a +0 -0
  204. scipy/io/_fast_matrix_market/_fmm_core.cp313t-win_amd64.pyd +0 -0
  205. scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
  206. scipy/io/_harwell_boeing/hb.py +7 -11
  207. scipy/io/_idl.py +5 -7
  208. scipy/io/_netcdf.py +15 -5
  209. scipy/io/_test_fortran.cp313t-win_amd64.dll.a +0 -0
  210. scipy/io/_test_fortran.cp313t-win_amd64.pyd +0 -0
  211. scipy/io/arff/tests/test_arffread.py +3 -3
  212. scipy/io/matlab/__init__.py +5 -3
  213. scipy/io/matlab/_mio.py +4 -1
  214. scipy/io/matlab/_mio5.py +19 -13
  215. scipy/io/matlab/_mio5_utils.cp313t-win_amd64.dll.a +0 -0
  216. scipy/io/matlab/_mio5_utils.cp313t-win_amd64.pyd +0 -0
  217. scipy/io/matlab/_mio_utils.cp313t-win_amd64.dll.a +0 -0
  218. scipy/io/matlab/_mio_utils.cp313t-win_amd64.pyd +0 -0
  219. scipy/io/matlab/_miobase.py +4 -1
  220. scipy/io/matlab/_streams.cp313t-win_amd64.dll.a +0 -0
  221. scipy/io/matlab/_streams.cp313t-win_amd64.pyd +0 -0
  222. scipy/io/matlab/tests/test_mio.py +46 -18
  223. scipy/io/matlab/tests/test_mio_funcs.py +1 -1
  224. scipy/io/tests/test_mmio.py +7 -1
  225. scipy/io/tests/test_wavfile.py +41 -0
  226. scipy/io/wavfile.py +57 -10
  227. scipy/linalg/_basic.py +113 -86
  228. scipy/linalg/_cythonized_array_utils.cp313t-win_amd64.dll.a +0 -0
  229. scipy/linalg/_cythonized_array_utils.cp313t-win_amd64.pyd +0 -0
  230. scipy/linalg/_decomp.py +22 -9
  231. scipy/linalg/_decomp_cholesky.py +28 -13
  232. scipy/linalg/_decomp_cossin.py +45 -30
  233. scipy/linalg/_decomp_interpolative.cp313t-win_amd64.dll.a +0 -0
  234. scipy/linalg/_decomp_interpolative.cp313t-win_amd64.pyd +0 -0
  235. scipy/linalg/_decomp_ldl.py +4 -1
  236. scipy/linalg/_decomp_lu.py +18 -6
  237. scipy/linalg/_decomp_lu_cython.cp313t-win_amd64.dll.a +0 -0
  238. scipy/linalg/_decomp_lu_cython.cp313t-win_amd64.pyd +0 -0
  239. scipy/linalg/_decomp_polar.py +2 -0
  240. scipy/linalg/_decomp_qr.py +6 -2
  241. scipy/linalg/_decomp_qz.py +3 -0
  242. scipy/linalg/_decomp_schur.py +3 -1
  243. scipy/linalg/_decomp_svd.py +13 -2
  244. scipy/linalg/_decomp_update.cp313t-win_amd64.dll.a +0 -0
  245. scipy/linalg/_decomp_update.cp313t-win_amd64.pyd +0 -0
  246. scipy/linalg/_expm_frechet.py +4 -0
  247. scipy/linalg/_fblas.cp313t-win_amd64.dll.a +0 -0
  248. scipy/linalg/_fblas.cp313t-win_amd64.pyd +0 -0
  249. scipy/linalg/_flapack.cp313t-win_amd64.dll.a +0 -0
  250. scipy/linalg/_flapack.cp313t-win_amd64.pyd +0 -0
  251. scipy/linalg/_linalg_pythran.cp313t-win_amd64.dll.a +0 -0
  252. scipy/linalg/_linalg_pythran.cp313t-win_amd64.pyd +0 -0
  253. scipy/linalg/_matfuncs.py +187 -4
  254. scipy/linalg/_matfuncs_expm.cp313t-win_amd64.dll.a +0 -0
  255. scipy/linalg/_matfuncs_expm.cp313t-win_amd64.pyd +0 -0
  256. scipy/linalg/_matfuncs_schur_sqrtm.cp313t-win_amd64.dll.a +0 -0
  257. scipy/linalg/_matfuncs_schur_sqrtm.cp313t-win_amd64.pyd +0 -0
  258. scipy/linalg/_matfuncs_sqrtm.py +1 -99
  259. scipy/linalg/_matfuncs_sqrtm_triu.cp313t-win_amd64.dll.a +0 -0
  260. scipy/linalg/_matfuncs_sqrtm_triu.cp313t-win_amd64.pyd +0 -0
  261. scipy/linalg/_procrustes.py +2 -0
  262. scipy/linalg/_sketches.py +17 -6
  263. scipy/linalg/_solve_toeplitz.cp313t-win_amd64.dll.a +0 -0
  264. scipy/linalg/_solve_toeplitz.cp313t-win_amd64.pyd +0 -0
  265. scipy/linalg/_solvers.py +7 -2
  266. scipy/linalg/_special_matrices.py +26 -36
  267. scipy/linalg/cython_blas.cp313t-win_amd64.dll.a +0 -0
  268. scipy/linalg/cython_blas.cp313t-win_amd64.pyd +0 -0
  269. scipy/linalg/cython_lapack.cp313t-win_amd64.dll.a +0 -0
  270. scipy/linalg/cython_lapack.cp313t-win_amd64.pyd +0 -0
  271. scipy/linalg/lapack.py +22 -2
  272. scipy/linalg/tests/_cython_examples/meson.build +7 -0
  273. scipy/linalg/tests/test_basic.py +31 -16
  274. scipy/linalg/tests/test_batch.py +588 -0
  275. scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
  276. scipy/linalg/tests/test_decomp.py +40 -3
  277. scipy/linalg/tests/test_decomp_cossin.py +14 -0
  278. scipy/linalg/tests/test_decomp_ldl.py +1 -1
  279. scipy/linalg/tests/test_lapack.py +115 -7
  280. scipy/linalg/tests/test_matfuncs.py +157 -102
  281. scipy/linalg/tests/test_procrustes.py +0 -7
  282. scipy/linalg/tests/test_solve_toeplitz.py +1 -1
  283. scipy/linalg/tests/test_special_matrices.py +1 -5
  284. scipy/ndimage/__init__.py +1 -0
  285. scipy/ndimage/_ctest.cp313t-win_amd64.dll.a +0 -0
  286. scipy/ndimage/_ctest.cp313t-win_amd64.pyd +0 -0
  287. scipy/ndimage/_cytest.cp313t-win_amd64.dll.a +0 -0
  288. scipy/ndimage/_cytest.cp313t-win_amd64.pyd +0 -0
  289. scipy/ndimage/_delegators.py +8 -2
  290. scipy/ndimage/_filters.py +453 -5
  291. scipy/ndimage/_interpolation.py +36 -6
  292. scipy/ndimage/_measurements.py +4 -2
  293. scipy/ndimage/_morphology.py +5 -0
  294. scipy/ndimage/_nd_image.cp313t-win_amd64.dll.a +0 -0
  295. scipy/ndimage/_nd_image.cp313t-win_amd64.pyd +0 -0
  296. scipy/ndimage/_ni_docstrings.py +5 -1
  297. scipy/ndimage/_ni_label.cp313t-win_amd64.dll.a +0 -0
  298. scipy/ndimage/_ni_label.cp313t-win_amd64.pyd +0 -0
  299. scipy/ndimage/_ni_support.py +1 -5
  300. scipy/ndimage/_rank_filter_1d.cp313t-win_amd64.dll.a +0 -0
  301. scipy/ndimage/_rank_filter_1d.cp313t-win_amd64.pyd +0 -0
  302. scipy/ndimage/_support_alternative_backends.py +18 -6
  303. scipy/ndimage/tests/test_filters.py +370 -259
  304. scipy/ndimage/tests/test_fourier.py +7 -9
  305. scipy/ndimage/tests/test_interpolation.py +68 -61
  306. scipy/ndimage/tests/test_measurements.py +18 -35
  307. scipy/ndimage/tests/test_morphology.py +143 -131
  308. scipy/ndimage/tests/test_splines.py +1 -3
  309. scipy/odr/__odrpack.cp313t-win_amd64.dll.a +0 -0
  310. scipy/odr/__odrpack.cp313t-win_amd64.pyd +0 -0
  311. scipy/optimize/_basinhopping.py +13 -7
  312. scipy/optimize/_bglu_dense.cp313t-win_amd64.dll.a +0 -0
  313. scipy/optimize/_bglu_dense.cp313t-win_amd64.pyd +0 -0
  314. scipy/optimize/_bracket.py +17 -24
  315. scipy/optimize/_chandrupatla.py +9 -10
  316. scipy/optimize/_cobyla_py.py +104 -123
  317. scipy/optimize/_constraints.py +14 -10
  318. scipy/optimize/_differentiable_functions.py +371 -230
  319. scipy/optimize/_differentialevolution.py +4 -3
  320. scipy/optimize/_direct.cp313t-win_amd64.dll.a +0 -0
  321. scipy/optimize/_direct.cp313t-win_amd64.pyd +0 -0
  322. scipy/optimize/_dual_annealing.py +1 -1
  323. scipy/optimize/_elementwise.py +1 -4
  324. scipy/optimize/_group_columns.cp313t-win_amd64.dll.a +0 -0
  325. scipy/optimize/_group_columns.cp313t-win_amd64.pyd +0 -0
  326. scipy/optimize/_highspy/_core.cp313t-win_amd64.dll.a +0 -0
  327. scipy/optimize/_highspy/_core.cp313t-win_amd64.pyd +0 -0
  328. scipy/optimize/_highspy/_highs_options.cp313t-win_amd64.dll.a +0 -0
  329. scipy/optimize/_highspy/_highs_options.cp313t-win_amd64.pyd +0 -0
  330. scipy/optimize/_lbfgsb.cp313t-win_amd64.dll.a +0 -0
  331. scipy/optimize/_lbfgsb.cp313t-win_amd64.pyd +0 -0
  332. scipy/optimize/_lbfgsb_py.py +57 -16
  333. scipy/optimize/_linprog_doc.py +2 -2
  334. scipy/optimize/_linprog_highs.py +2 -2
  335. scipy/optimize/_linprog_ip.py +25 -10
  336. scipy/optimize/_linprog_util.py +14 -16
  337. scipy/optimize/_lsap.cp313t-win_amd64.dll.a +0 -0
  338. scipy/optimize/_lsap.cp313t-win_amd64.pyd +0 -0
  339. scipy/optimize/_lsq/common.py +3 -3
  340. scipy/optimize/_lsq/dogbox.py +16 -2
  341. scipy/optimize/_lsq/givens_elimination.cp313t-win_amd64.dll.a +0 -0
  342. scipy/optimize/_lsq/givens_elimination.cp313t-win_amd64.pyd +0 -0
  343. scipy/optimize/_lsq/least_squares.py +198 -126
  344. scipy/optimize/_lsq/lsq_linear.py +6 -6
  345. scipy/optimize/_lsq/trf.py +35 -8
  346. scipy/optimize/_milp.py +3 -1
  347. scipy/optimize/_minimize.py +105 -36
  348. scipy/optimize/_minpack.cp313t-win_amd64.dll.a +0 -0
  349. scipy/optimize/_minpack.cp313t-win_amd64.pyd +0 -0
  350. scipy/optimize/_minpack_py.py +21 -14
  351. scipy/optimize/_moduleTNC.cp313t-win_amd64.dll.a +0 -0
  352. scipy/optimize/_moduleTNC.cp313t-win_amd64.pyd +0 -0
  353. scipy/optimize/_nnls.py +20 -21
  354. scipy/optimize/_nonlin.py +34 -3
  355. scipy/optimize/_numdiff.py +288 -110
  356. scipy/optimize/_optimize.py +86 -48
  357. scipy/optimize/_pava_pybind.cp313t-win_amd64.dll.a +0 -0
  358. scipy/optimize/_pava_pybind.cp313t-win_amd64.pyd +0 -0
  359. scipy/optimize/_remove_redundancy.py +5 -5
  360. scipy/optimize/_root_scalar.py +1 -1
  361. scipy/optimize/_shgo.py +6 -0
  362. scipy/optimize/_shgo_lib/_complex.py +1 -1
  363. scipy/optimize/_slsqp_py.py +216 -124
  364. scipy/optimize/_slsqplib.cp313t-win_amd64.dll.a +0 -0
  365. scipy/optimize/_slsqplib.cp313t-win_amd64.pyd +0 -0
  366. scipy/optimize/_spectral.py +1 -1
  367. scipy/optimize/_tnc.py +8 -1
  368. scipy/optimize/_trlib/_trlib.cp313t-win_amd64.dll.a +0 -0
  369. scipy/optimize/_trlib/_trlib.cp313t-win_amd64.pyd +0 -0
  370. scipy/optimize/_trustregion.py +20 -6
  371. scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
  372. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
  373. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
  374. scipy/optimize/_trustregion_constr/projections.py +12 -8
  375. scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
  376. scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
  377. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
  378. scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
  379. scipy/optimize/_trustregion_exact.py +0 -1
  380. scipy/optimize/_zeros.cp313t-win_amd64.dll.a +0 -0
  381. scipy/optimize/_zeros.cp313t-win_amd64.pyd +0 -0
  382. scipy/optimize/_zeros_py.py +97 -17
  383. scipy/optimize/cython_optimize/_zeros.cp313t-win_amd64.dll.a +0 -0
  384. scipy/optimize/cython_optimize/_zeros.cp313t-win_amd64.pyd +0 -0
  385. scipy/optimize/slsqp.py +0 -1
  386. scipy/optimize/tests/test__basinhopping.py +1 -1
  387. scipy/optimize/tests/test__differential_evolution.py +4 -4
  388. scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
  389. scipy/optimize/tests/test__numdiff.py +66 -22
  390. scipy/optimize/tests/test__remove_redundancy.py +2 -2
  391. scipy/optimize/tests/test__shgo.py +9 -1
  392. scipy/optimize/tests/test_bracket.py +36 -46
  393. scipy/optimize/tests/test_chandrupatla.py +133 -135
  394. scipy/optimize/tests/test_cobyla.py +74 -45
  395. scipy/optimize/tests/test_constraints.py +1 -1
  396. scipy/optimize/tests/test_differentiable_functions.py +226 -6
  397. scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
  398. scipy/optimize/tests/test_least_squares.py +125 -13
  399. scipy/optimize/tests/test_linear_assignment.py +3 -3
  400. scipy/optimize/tests/test_linprog.py +3 -3
  401. scipy/optimize/tests/test_lsq_linear.py +6 -6
  402. scipy/optimize/tests/test_minimize_constrained.py +2 -2
  403. scipy/optimize/tests/test_minpack.py +4 -4
  404. scipy/optimize/tests/test_nnls.py +43 -3
  405. scipy/optimize/tests/test_nonlin.py +36 -0
  406. scipy/optimize/tests/test_optimize.py +95 -17
  407. scipy/optimize/tests/test_slsqp.py +36 -4
  408. scipy/optimize/tests/test_zeros.py +34 -1
  409. scipy/signal/__init__.py +12 -23
  410. scipy/signal/_delegators.py +568 -0
  411. scipy/signal/_filter_design.py +459 -241
  412. scipy/signal/_fir_filter_design.py +262 -90
  413. scipy/signal/_lti_conversion.py +3 -2
  414. scipy/signal/_ltisys.py +118 -91
  415. scipy/signal/_max_len_seq_inner.cp313t-win_amd64.dll.a +0 -0
  416. scipy/signal/_max_len_seq_inner.cp313t-win_amd64.pyd +0 -0
  417. scipy/signal/_peak_finding_utils.cp313t-win_amd64.dll.a +0 -0
  418. scipy/signal/_peak_finding_utils.cp313t-win_amd64.pyd +0 -0
  419. scipy/signal/_polyutils.py +172 -0
  420. scipy/signal/_short_time_fft.py +519 -70
  421. scipy/signal/_signal_api.py +30 -0
  422. scipy/signal/_signaltools.py +719 -399
  423. scipy/signal/_sigtools.cp313t-win_amd64.dll.a +0 -0
  424. scipy/signal/_sigtools.cp313t-win_amd64.pyd +0 -0
  425. scipy/signal/_sosfilt.cp313t-win_amd64.dll.a +0 -0
  426. scipy/signal/_sosfilt.cp313t-win_amd64.pyd +0 -0
  427. scipy/signal/_spectral_py.py +230 -50
  428. scipy/signal/_spline.cp313t-win_amd64.dll.a +0 -0
  429. scipy/signal/_spline.cp313t-win_amd64.pyd +0 -0
  430. scipy/signal/_spline_filters.py +108 -68
  431. scipy/signal/_support_alternative_backends.py +73 -0
  432. scipy/signal/_upfirdn.py +4 -1
  433. scipy/signal/_upfirdn_apply.cp313t-win_amd64.dll.a +0 -0
  434. scipy/signal/_upfirdn_apply.cp313t-win_amd64.pyd +0 -0
  435. scipy/signal/_waveforms.py +2 -11
  436. scipy/signal/_wavelets.py +1 -1
  437. scipy/signal/fir_filter_design.py +1 -0
  438. scipy/signal/spline.py +4 -11
  439. scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
  440. scipy/signal/tests/test_bsplines.py +114 -79
  441. scipy/signal/tests/test_cont2discrete.py +9 -2
  442. scipy/signal/tests/test_filter_design.py +721 -481
  443. scipy/signal/tests/test_fir_filter_design.py +332 -140
  444. scipy/signal/tests/test_savitzky_golay.py +4 -3
  445. scipy/signal/tests/test_short_time_fft.py +221 -3
  446. scipy/signal/tests/test_signaltools.py +2144 -1348
  447. scipy/signal/tests/test_spectral.py +50 -6
  448. scipy/signal/tests/test_splines.py +161 -96
  449. scipy/signal/tests/test_upfirdn.py +84 -50
  450. scipy/signal/tests/test_waveforms.py +20 -0
  451. scipy/signal/tests/test_windows.py +607 -466
  452. scipy/signal/windows/_windows.py +287 -148
  453. scipy/sparse/__init__.py +23 -4
  454. scipy/sparse/_base.py +270 -108
  455. scipy/sparse/_bsr.py +7 -4
  456. scipy/sparse/_compressed.py +59 -231
  457. scipy/sparse/_construct.py +90 -38
  458. scipy/sparse/_coo.py +115 -181
  459. scipy/sparse/_csc.py +4 -4
  460. scipy/sparse/_csparsetools.cp313t-win_amd64.dll.a +0 -0
  461. scipy/sparse/_csparsetools.cp313t-win_amd64.pyd +0 -0
  462. scipy/sparse/_csr.py +2 -2
  463. scipy/sparse/_data.py +48 -48
  464. scipy/sparse/_dia.py +105 -18
  465. scipy/sparse/_dok.py +0 -23
  466. scipy/sparse/_index.py +4 -4
  467. scipy/sparse/_matrix.py +23 -0
  468. scipy/sparse/_sparsetools.cp313t-win_amd64.dll.a +0 -0
  469. scipy/sparse/_sparsetools.cp313t-win_amd64.pyd +0 -0
  470. scipy/sparse/_sputils.py +37 -22
  471. scipy/sparse/base.py +0 -9
  472. scipy/sparse/bsr.py +0 -14
  473. scipy/sparse/compressed.py +0 -23
  474. scipy/sparse/construct.py +0 -6
  475. scipy/sparse/coo.py +0 -14
  476. scipy/sparse/csc.py +0 -3
  477. scipy/sparse/csgraph/_flow.cp313t-win_amd64.dll.a +0 -0
  478. scipy/sparse/csgraph/_flow.cp313t-win_amd64.pyd +0 -0
  479. scipy/sparse/csgraph/_matching.cp313t-win_amd64.dll.a +0 -0
  480. scipy/sparse/csgraph/_matching.cp313t-win_amd64.pyd +0 -0
  481. scipy/sparse/csgraph/_min_spanning_tree.cp313t-win_amd64.dll.a +0 -0
  482. scipy/sparse/csgraph/_min_spanning_tree.cp313t-win_amd64.pyd +0 -0
  483. scipy/sparse/csgraph/_reordering.cp313t-win_amd64.dll.a +0 -0
  484. scipy/sparse/csgraph/_reordering.cp313t-win_amd64.pyd +0 -0
  485. scipy/sparse/csgraph/_shortest_path.cp313t-win_amd64.dll.a +0 -0
  486. scipy/sparse/csgraph/_shortest_path.cp313t-win_amd64.pyd +0 -0
  487. scipy/sparse/csgraph/_tools.cp313t-win_amd64.dll.a +0 -0
  488. scipy/sparse/csgraph/_tools.cp313t-win_amd64.pyd +0 -0
  489. scipy/sparse/csgraph/_traversal.cp313t-win_amd64.dll.a +0 -0
  490. scipy/sparse/csgraph/_traversal.cp313t-win_amd64.pyd +0 -0
  491. scipy/sparse/csgraph/tests/test_matching.py +14 -2
  492. scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
  493. scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
  494. scipy/sparse/csr.py +0 -5
  495. scipy/sparse/data.py +1 -6
  496. scipy/sparse/dia.py +0 -7
  497. scipy/sparse/dok.py +0 -10
  498. scipy/sparse/linalg/_dsolve/_superlu.cp313t-win_amd64.dll.a +0 -0
  499. scipy/sparse/linalg/_dsolve/_superlu.cp313t-win_amd64.pyd +0 -0
  500. scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
  501. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
  502. scipy/sparse/linalg/_eigen/arpack/_arpack.cp313t-win_amd64.dll.a +0 -0
  503. scipy/sparse/linalg/_eigen/arpack/_arpack.cp313t-win_amd64.pyd +0 -0
  504. scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
  505. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
  506. scipy/sparse/linalg/_interface.py +17 -18
  507. scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
  508. scipy/sparse/linalg/_isolve/iterative.py +51 -45
  509. scipy/sparse/linalg/_isolve/lgmres.py +6 -6
  510. scipy/sparse/linalg/_isolve/minres.py +5 -5
  511. scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
  512. scipy/sparse/linalg/_isolve/utils.py +2 -8
  513. scipy/sparse/linalg/_matfuncs.py +1 -1
  514. scipy/sparse/linalg/_norm.py +1 -1
  515. scipy/sparse/linalg/_propack/_cpropack.cp313t-win_amd64.dll.a +0 -0
  516. scipy/sparse/linalg/_propack/_cpropack.cp313t-win_amd64.pyd +0 -0
  517. scipy/sparse/linalg/_propack/_dpropack.cp313t-win_amd64.dll.a +0 -0
  518. scipy/sparse/linalg/_propack/_dpropack.cp313t-win_amd64.pyd +0 -0
  519. scipy/sparse/linalg/_propack/_spropack.cp313t-win_amd64.dll.a +0 -0
  520. scipy/sparse/linalg/_propack/_spropack.cp313t-win_amd64.pyd +0 -0
  521. scipy/sparse/linalg/_propack/_zpropack.cp313t-win_amd64.dll.a +0 -0
  522. scipy/sparse/linalg/_propack/_zpropack.cp313t-win_amd64.pyd +0 -0
  523. scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
  524. scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
  525. scipy/sparse/tests/test_arithmetic1d.py +5 -2
  526. scipy/sparse/tests/test_base.py +214 -42
  527. scipy/sparse/tests/test_common1d.py +7 -7
  528. scipy/sparse/tests/test_construct.py +1 -1
  529. scipy/sparse/tests/test_coo.py +272 -4
  530. scipy/sparse/tests/test_sparsetools.py +5 -0
  531. scipy/sparse/tests/test_sputils.py +36 -7
  532. scipy/spatial/_ckdtree.cp313t-win_amd64.dll.a +0 -0
  533. scipy/spatial/_ckdtree.cp313t-win_amd64.pyd +0 -0
  534. scipy/spatial/_distance_pybind.cp313t-win_amd64.dll.a +0 -0
  535. scipy/spatial/_distance_pybind.cp313t-win_amd64.pyd +0 -0
  536. scipy/spatial/_distance_wrap.cp313t-win_amd64.dll.a +0 -0
  537. scipy/spatial/_distance_wrap.cp313t-win_amd64.pyd +0 -0
  538. scipy/spatial/_hausdorff.cp313t-win_amd64.dll.a +0 -0
  539. scipy/spatial/_hausdorff.cp313t-win_amd64.pyd +0 -0
  540. scipy/spatial/_qhull.cp313t-win_amd64.dll.a +0 -0
  541. scipy/spatial/_qhull.cp313t-win_amd64.pyd +0 -0
  542. scipy/spatial/_voronoi.cp313t-win_amd64.dll.a +0 -0
  543. scipy/spatial/_voronoi.cp313t-win_amd64.pyd +0 -0
  544. scipy/spatial/distance.py +49 -42
  545. scipy/spatial/tests/test_distance.py +15 -1
  546. scipy/spatial/tests/test_kdtree.py +1 -0
  547. scipy/spatial/tests/test_qhull.py +7 -2
  548. scipy/spatial/transform/__init__.py +5 -3
  549. scipy/spatial/transform/_rigid_transform.cp313t-win_amd64.dll.a +0 -0
  550. scipy/spatial/transform/_rigid_transform.cp313t-win_amd64.pyd +0 -0
  551. scipy/spatial/transform/_rotation.cp313t-win_amd64.dll.a +0 -0
  552. scipy/spatial/transform/_rotation.cp313t-win_amd64.pyd +0 -0
  553. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  554. scipy/spatial/transform/tests/test_rotation.py +1213 -832
  555. scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
  556. scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
  557. scipy/special/__init__.py +1 -47
  558. scipy/special/_add_newdocs.py +34 -772
  559. scipy/special/_basic.py +22 -25
  560. scipy/special/_comb.cp313t-win_amd64.dll.a +0 -0
  561. scipy/special/_comb.cp313t-win_amd64.pyd +0 -0
  562. scipy/special/_ellip_harm_2.cp313t-win_amd64.dll.a +0 -0
  563. scipy/special/_ellip_harm_2.cp313t-win_amd64.pyd +0 -0
  564. scipy/special/_gufuncs.cp313t-win_amd64.dll.a +0 -0
  565. scipy/special/_gufuncs.cp313t-win_amd64.pyd +0 -0
  566. scipy/special/_logsumexp.py +67 -58
  567. scipy/special/_orthogonal.pyi +1 -1
  568. scipy/special/_specfun.cp313t-win_amd64.dll.a +0 -0
  569. scipy/special/_specfun.cp313t-win_amd64.pyd +0 -0
  570. scipy/special/_special_ufuncs.cp313t-win_amd64.dll.a +0 -0
  571. scipy/special/_special_ufuncs.cp313t-win_amd64.pyd +0 -0
  572. scipy/special/_spherical_bessel.py +4 -4
  573. scipy/special/_support_alternative_backends.py +212 -119
  574. scipy/special/_test_internal.cp313t-win_amd64.dll.a +0 -0
  575. scipy/special/_test_internal.cp313t-win_amd64.pyd +0 -0
  576. scipy/special/_testutils.py +4 -4
  577. scipy/special/_ufuncs.cp313t-win_amd64.dll.a +0 -0
  578. scipy/special/_ufuncs.cp313t-win_amd64.pyd +0 -0
  579. scipy/special/_ufuncs.pyi +1 -0
  580. scipy/special/_ufuncs.pyx +215 -1400
  581. scipy/special/_ufuncs_cxx.cp313t-win_amd64.dll.a +0 -0
  582. scipy/special/_ufuncs_cxx.cp313t-win_amd64.pyd +0 -0
  583. scipy/special/_ufuncs_cxx.pxd +2 -15
  584. scipy/special/_ufuncs_cxx.pyx +5 -44
  585. scipy/special/_ufuncs_cxx_defs.h +2 -16
  586. scipy/special/_ufuncs_defs.h +0 -8
  587. scipy/special/cython_special.cp313t-win_amd64.dll.a +0 -0
  588. scipy/special/cython_special.cp313t-win_amd64.pyd +0 -0
  589. scipy/special/cython_special.pxd +1 -1
  590. scipy/special/tests/_cython_examples/meson.build +10 -1
  591. scipy/special/tests/test_basic.py +153 -20
  592. scipy/special/tests/test_boost_ufuncs.py +3 -0
  593. scipy/special/tests/test_cdflib.py +35 -11
  594. scipy/special/tests/test_gammainc.py +16 -0
  595. scipy/special/tests/test_hyp2f1.py +2 -2
  596. scipy/special/tests/test_log1mexp.py +85 -0
  597. scipy/special/tests/test_logsumexp.py +206 -64
  598. scipy/special/tests/test_mpmath.py +1 -0
  599. scipy/special/tests/test_nan_inputs.py +1 -1
  600. scipy/special/tests/test_orthogonal.py +17 -18
  601. scipy/special/tests/test_sf_error.py +3 -2
  602. scipy/special/tests/test_sph_harm.py +6 -7
  603. scipy/special/tests/test_support_alternative_backends.py +211 -76
  604. scipy/stats/__init__.py +4 -1
  605. scipy/stats/_ansari_swilk_statistics.cp313t-win_amd64.dll.a +0 -0
  606. scipy/stats/_ansari_swilk_statistics.cp313t-win_amd64.pyd +0 -0
  607. scipy/stats/_axis_nan_policy.py +5 -12
  608. scipy/stats/_biasedurn.cp313t-win_amd64.dll.a +0 -0
  609. scipy/stats/_biasedurn.cp313t-win_amd64.pyd +0 -0
  610. scipy/stats/_continued_fraction.py +387 -0
  611. scipy/stats/_continuous_distns.py +277 -310
  612. scipy/stats/_correlation.py +1 -1
  613. scipy/stats/_covariance.py +6 -3
  614. scipy/stats/_discrete_distns.py +39 -32
  615. scipy/stats/_distn_infrastructure.py +39 -12
  616. scipy/stats/_distribution_infrastructure.py +900 -238
  617. scipy/stats/_entropy.py +9 -10
  618. scipy/{_lib → stats}/_finite_differences.py +1 -1
  619. scipy/stats/_hypotests.py +83 -50
  620. scipy/stats/_kde.py +53 -49
  621. scipy/stats/_ksstats.py +1 -1
  622. scipy/stats/_levy_stable/__init__.py +7 -15
  623. scipy/stats/_levy_stable/levyst.cp313t-win_amd64.dll.a +0 -0
  624. scipy/stats/_levy_stable/levyst.cp313t-win_amd64.pyd +0 -0
  625. scipy/stats/_morestats.py +118 -73
  626. scipy/stats/_mstats_basic.py +13 -17
  627. scipy/stats/_mstats_extras.py +8 -8
  628. scipy/stats/_multivariate.py +89 -113
  629. scipy/stats/_new_distributions.py +97 -20
  630. scipy/stats/_page_trend_test.py +12 -5
  631. scipy/stats/_probability_distribution.py +265 -43
  632. scipy/stats/_qmc.py +14 -9
  633. scipy/stats/_qmc_cy.cp313t-win_amd64.dll.a +0 -0
  634. scipy/stats/_qmc_cy.cp313t-win_amd64.pyd +0 -0
  635. scipy/stats/_qmvnt.py +16 -95
  636. scipy/stats/_qmvnt_cy.cp313t-win_amd64.dll.a +0 -0
  637. scipy/stats/_qmvnt_cy.cp313t-win_amd64.pyd +0 -0
  638. scipy/stats/_quantile.py +335 -0
  639. scipy/stats/_rcont/rcont.cp313t-win_amd64.dll.a +0 -0
  640. scipy/stats/_rcont/rcont.cp313t-win_amd64.pyd +0 -0
  641. scipy/stats/_resampling.py +4 -29
  642. scipy/stats/_sampling.py +1 -1
  643. scipy/stats/_sobol.cp313t-win_amd64.dll.a +0 -0
  644. scipy/stats/_sobol.cp313t-win_amd64.pyd +0 -0
  645. scipy/stats/_stats.cp313t-win_amd64.dll.a +0 -0
  646. scipy/stats/_stats.cp313t-win_amd64.pyd +0 -0
  647. scipy/stats/_stats_mstats_common.py +21 -2
  648. scipy/stats/_stats_py.py +550 -476
  649. scipy/stats/_stats_pythran.cp313t-win_amd64.dll.a +0 -0
  650. scipy/stats/_stats_pythran.cp313t-win_amd64.pyd +0 -0
  651. scipy/stats/_unuran/unuran_wrapper.cp313t-win_amd64.dll.a +0 -0
  652. scipy/stats/_unuran/unuran_wrapper.cp313t-win_amd64.pyd +0 -0
  653. scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
  654. scipy/stats/_variation.py +6 -8
  655. scipy/stats/_wilcoxon.py +13 -7
  656. scipy/stats/tests/common_tests.py +6 -4
  657. scipy/stats/tests/test_axis_nan_policy.py +62 -24
  658. scipy/stats/tests/test_continued_fraction.py +173 -0
  659. scipy/stats/tests/test_continuous.py +379 -60
  660. scipy/stats/tests/test_continuous_basic.py +18 -12
  661. scipy/stats/tests/test_discrete_basic.py +14 -8
  662. scipy/stats/tests/test_discrete_distns.py +16 -16
  663. scipy/stats/tests/test_distributions.py +95 -75
  664. scipy/stats/tests/test_entropy.py +40 -48
  665. scipy/stats/tests/test_fit.py +4 -3
  666. scipy/stats/tests/test_hypotests.py +153 -24
  667. scipy/stats/tests/test_kdeoth.py +109 -41
  668. scipy/stats/tests/test_marray.py +289 -0
  669. scipy/stats/tests/test_morestats.py +79 -47
  670. scipy/stats/tests/test_mstats_basic.py +3 -3
  671. scipy/stats/tests/test_multivariate.py +434 -83
  672. scipy/stats/tests/test_qmc.py +13 -10
  673. scipy/stats/tests/test_quantile.py +199 -0
  674. scipy/stats/tests/test_rank.py +119 -112
  675. scipy/stats/tests/test_resampling.py +47 -56
  676. scipy/stats/tests/test_sampling.py +9 -4
  677. scipy/stats/tests/test_stats.py +799 -939
  678. scipy/stats/tests/test_variation.py +8 -6
  679. scipy/version.py +2 -2
  680. scipy-1.16.0rc2.dist-info/DELVEWHEEL +2 -0
  681. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
  682. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
  683. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +685 -693
  684. scipy/_lib/array_api_extra/_funcs.py +0 -484
  685. scipy/_lib/array_api_extra/_typing.py +0 -8
  686. scipy/interpolate/_bspl.cp313t-win_amd64.dll.a +0 -0
  687. scipy/interpolate/_bspl.cp313t-win_amd64.pyd +0 -0
  688. scipy/optimize/_cobyla.cp313t-win_amd64.dll.a +0 -0
  689. scipy/optimize/_cobyla.cp313t-win_amd64.pyd +0 -0
  690. scipy/optimize/_cython_nnls.cp313t-win_amd64.dll.a +0 -0
  691. scipy/optimize/_cython_nnls.cp313t-win_amd64.pyd +0 -0
  692. scipy/optimize/_slsqp.cp313t-win_amd64.dll.a +0 -0
  693. scipy/optimize/_slsqp.cp313t-win_amd64.pyd +0 -0
  694. scipy/spatial/qhull_src/COPYING.txt +0 -38
  695. scipy/special/libsf_error_state.dll +0 -0
  696. scipy/special/libsf_error_state.dll.a +0 -0
  697. scipy/special/tests/test_log_softmax.py +0 -109
  698. scipy/special/tests/test_xsf_cuda.py +0 -114
  699. scipy/special/xsf/binom.h +0 -89
  700. scipy/special/xsf/cdflib.h +0 -100
  701. scipy/special/xsf/cephes/airy.h +0 -307
  702. scipy/special/xsf/cephes/besselpoly.h +0 -51
  703. scipy/special/xsf/cephes/beta.h +0 -257
  704. scipy/special/xsf/cephes/cbrt.h +0 -131
  705. scipy/special/xsf/cephes/chbevl.h +0 -85
  706. scipy/special/xsf/cephes/chdtr.h +0 -193
  707. scipy/special/xsf/cephes/const.h +0 -87
  708. scipy/special/xsf/cephes/ellie.h +0 -293
  709. scipy/special/xsf/cephes/ellik.h +0 -251
  710. scipy/special/xsf/cephes/ellpe.h +0 -107
  711. scipy/special/xsf/cephes/ellpk.h +0 -117
  712. scipy/special/xsf/cephes/expn.h +0 -260
  713. scipy/special/xsf/cephes/gamma.h +0 -398
  714. scipy/special/xsf/cephes/hyp2f1.h +0 -596
  715. scipy/special/xsf/cephes/hyperg.h +0 -361
  716. scipy/special/xsf/cephes/i0.h +0 -149
  717. scipy/special/xsf/cephes/i1.h +0 -158
  718. scipy/special/xsf/cephes/igam.h +0 -421
  719. scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
  720. scipy/special/xsf/cephes/igami.h +0 -313
  721. scipy/special/xsf/cephes/j0.h +0 -225
  722. scipy/special/xsf/cephes/j1.h +0 -198
  723. scipy/special/xsf/cephes/jv.h +0 -715
  724. scipy/special/xsf/cephes/k0.h +0 -164
  725. scipy/special/xsf/cephes/k1.h +0 -163
  726. scipy/special/xsf/cephes/kn.h +0 -243
  727. scipy/special/xsf/cephes/lanczos.h +0 -112
  728. scipy/special/xsf/cephes/ndtr.h +0 -275
  729. scipy/special/xsf/cephes/poch.h +0 -85
  730. scipy/special/xsf/cephes/polevl.h +0 -167
  731. scipy/special/xsf/cephes/psi.h +0 -194
  732. scipy/special/xsf/cephes/rgamma.h +0 -111
  733. scipy/special/xsf/cephes/scipy_iv.h +0 -811
  734. scipy/special/xsf/cephes/shichi.h +0 -248
  735. scipy/special/xsf/cephes/sici.h +0 -224
  736. scipy/special/xsf/cephes/sindg.h +0 -221
  737. scipy/special/xsf/cephes/tandg.h +0 -139
  738. scipy/special/xsf/cephes/trig.h +0 -58
  739. scipy/special/xsf/cephes/unity.h +0 -186
  740. scipy/special/xsf/cephes/zeta.h +0 -172
  741. scipy/special/xsf/config.h +0 -304
  742. scipy/special/xsf/digamma.h +0 -205
  743. scipy/special/xsf/error.h +0 -57
  744. scipy/special/xsf/evalpoly.h +0 -47
  745. scipy/special/xsf/expint.h +0 -266
  746. scipy/special/xsf/hyp2f1.h +0 -694
  747. scipy/special/xsf/iv_ratio.h +0 -173
  748. scipy/special/xsf/lambertw.h +0 -150
  749. scipy/special/xsf/loggamma.h +0 -163
  750. scipy/special/xsf/sici.h +0 -200
  751. scipy/special/xsf/tools.h +0 -427
  752. scipy/special/xsf/trig.h +0 -164
  753. scipy/special/xsf/wright_bessel.h +0 -843
  754. scipy/special/xsf/zlog1.h +0 -35
  755. scipy/stats/_mvn.cp313t-win_amd64.dll.a +0 -0
  756. scipy/stats/_mvn.cp313t-win_amd64.pyd +0 -0
  757. scipy-1.15.3.dist-info/DELVEWHEEL +0 -2
  758. /scipy-1.15.3-cp313-cp313t-win_amd64.whl → /scipy-1.16.0rc2-cp313-cp313t-win_amd64.whl +0 -0
  759. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
@@ -32,29 +32,34 @@
32
32
  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33
33
  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34
34
  import numpy as np
35
- from numpy.testing import (assert_allclose, assert_equal, assert_array_equal, assert_,
36
- assert_warns)
35
+ from numpy.testing import assert_allclose, assert_equal, assert_array_equal, assert_
37
36
  import pytest
38
37
  from pytest import raises as assert_raises
39
38
 
40
- import scipy.cluster.hierarchy
41
39
  from scipy.cluster.hierarchy import (
42
40
  ClusterWarning, linkage, from_mlab_linkage, to_mlab_linkage,
43
41
  num_obs_linkage, inconsistent, cophenet, fclusterdata, fcluster,
44
- is_isomorphic, single, leaders,
42
+ is_isomorphic, single, ward, leaders,
45
43
  correspond, is_monotonic, maxdists, maxinconsts, maxRstat,
46
44
  is_valid_linkage, is_valid_im, to_tree, leaves_list, dendrogram,
47
45
  set_link_color_palette, cut_tree, optimal_leaf_ordering,
48
- _order_cluster_tree, _hierarchy, _LINKAGE_METHODS)
49
- from scipy.spatial.distance import pdist
46
+ _order_cluster_tree, _hierarchy, _EUCLIDEAN_METHODS, _LINKAGE_METHODS)
50
47
  from scipy.cluster._hierarchy import Heap
51
- from scipy.conftest import array_api_compatible
52
- from scipy._lib._array_api import xp_assert_close, xp_assert_equal
48
+ from scipy.spatial.distance import pdist
49
+ from scipy._lib._array_api import (eager_warns, make_xp_test_case,
50
+ xp_assert_close, xp_assert_equal)
51
+ import scipy._lib.array_api_extra as xpx
53
52
 
54
53
  from threading import Lock
55
54
 
56
55
  from . import hierarchy_test_data
57
56
 
57
+ class eager:
58
+ # Bypass xpx.testing.lazy_xp_function when calling
59
+ # these functions from this namespace
60
+ is_valid_im = is_valid_im
61
+ is_valid_linkage = is_valid_linkage
62
+
58
63
 
59
64
  # Matplotlib is not a scipy dependency but is optionally used in dendrogram, so
60
65
  # check if it's available
@@ -68,27 +73,24 @@ try:
68
73
  except Exception:
69
74
  have_matplotlib = False
70
75
 
71
-
72
- pytestmark = [array_api_compatible, pytest.mark.usefixtures("skip_xp_backends")]
73
76
  skip_xp_backends = pytest.mark.skip_xp_backends
74
77
 
75
78
 
79
+ @make_xp_test_case(linkage)
76
80
  class TestLinkage:
77
81
 
78
- @skip_xp_backends(cpu_only=True)
82
+ @skip_xp_backends("jax.numpy", reason="Can't raise inside jax.pure_callback")
79
83
  def test_linkage_non_finite_elements_in_distance_matrix(self, xp):
80
84
  # Tests linkage(Y) where Y contains a non-finite element (e.g. NaN or Inf).
81
85
  # Exception expected.
82
86
  y = xp.asarray([xp.nan] + [0.0]*5)
83
87
  assert_raises(ValueError, linkage, y)
84
88
 
85
- @skip_xp_backends(cpu_only=True)
86
89
  def test_linkage_empty_distance_matrix(self, xp):
87
90
  # Tests linkage(Y) where Y is a 0x4 linkage matrix. Exception expected.
88
91
  y = xp.zeros((0,))
89
92
  assert_raises(ValueError, linkage, y)
90
93
 
91
- @skip_xp_backends(cpu_only=True)
92
94
  def test_linkage_tdist(self, xp):
93
95
  for method in ['single', 'complete', 'average', 'weighted']:
94
96
  self.check_linkage_tdist(method, xp)
@@ -99,7 +101,6 @@ class TestLinkage:
99
101
  expectedZ = getattr(hierarchy_test_data, 'linkage_ytdist_' + method)
100
102
  xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
101
103
 
102
- @skip_xp_backends(cpu_only=True)
103
104
  def test_linkage_X(self, xp):
104
105
  for method in ['centroid', 'median', 'ward']:
105
106
  self.check_linkage_q(method, xp)
@@ -110,12 +111,11 @@ class TestLinkage:
110
111
  expectedZ = getattr(hierarchy_test_data, 'linkage_X_' + method)
111
112
  xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
112
113
 
113
- y = scipy.spatial.distance.pdist(hierarchy_test_data.X,
114
- metric="euclidean")
115
- Z = linkage(xp.asarray(y), method)
114
+ X = xp.asarray(hierarchy_test_data.X)
115
+ y = pdist(X, metric="euclidean")
116
+ Z = linkage(y, method)
116
117
  xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
117
118
 
118
- @skip_xp_backends(cpu_only=True)
119
119
  def test_compare_with_trivial(self, xp):
120
120
  rng = np.random.RandomState(0)
121
121
  n = 20
@@ -127,46 +127,59 @@ class TestLinkage:
127
127
  Z = linkage(xp.asarray(d), method)
128
128
  xp_assert_close(Z, xp.asarray(Z_trivial), rtol=1e-14, atol=1e-15)
129
129
 
130
- @skip_xp_backends(cpu_only=True)
131
130
  def test_optimal_leaf_ordering(self, xp):
132
131
  Z = linkage(xp.asarray(hierarchy_test_data.ytdist), optimal_ordering=True)
133
132
  expectedZ = getattr(hierarchy_test_data, 'linkage_ytdist_single_olo')
134
133
  xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
135
134
 
136
-
137
- @skip_xp_backends(cpu_only=True)
138
- class TestLinkageTies:
139
-
140
- _expectations = {
141
- 'single': np.array([[0, 1, 1.41421356, 2],
142
- [2, 3, 1.41421356, 3]]),
143
- 'complete': np.array([[0, 1, 1.41421356, 2],
144
- [2, 3, 2.82842712, 3]]),
145
- 'average': np.array([[0, 1, 1.41421356, 2],
146
- [2, 3, 2.12132034, 3]]),
147
- 'weighted': np.array([[0, 1, 1.41421356, 2],
148
- [2, 3, 2.12132034, 3]]),
149
- 'centroid': np.array([[0, 1, 1.41421356, 2],
150
- [2, 3, 2.12132034, 3]]),
151
- 'median': np.array([[0, 1, 1.41421356, 2],
152
- [2, 3, 2.12132034, 3]]),
153
- 'ward': np.array([[0, 1, 1.41421356, 2],
154
- [2, 3, 2.44948974, 3]]),
155
- }
156
-
157
- def test_linkage_ties(self, xp):
158
- for method in ['single', 'complete', 'average', 'weighted',
159
- 'centroid', 'median', 'ward']:
160
- self.check_linkage_ties(method, xp)
161
-
162
- def check_linkage_ties(self, method, xp):
135
+ @pytest.mark.parametrize("method,expect", [
136
+ ('single', [[0, 1, 1.41421356, 2],
137
+ [2, 3, 1.41421356, 3]]),
138
+ ('complete', [[0, 1, 1.41421356, 2],
139
+ [2, 3, 2.82842712, 3]]),
140
+ ('average', [[0, 1, 1.41421356, 2],
141
+ [2, 3, 2.12132034, 3]]),
142
+ ('weighted', [[0, 1, 1.41421356, 2],
143
+ [2, 3, 2.12132034, 3]]),
144
+ ('centroid', [[0, 1, 1.41421356, 2],
145
+ [2, 3, 2.12132034, 3]]),
146
+ ('median', [[0, 1, 1.41421356, 2],
147
+ [2, 3, 2.12132034, 3]]),
148
+ ('ward', [[0, 1, 1.41421356, 2],
149
+ [2, 3, 2.44948974, 3]]),
150
+ ])
151
+ def test_linkage_ties(self, method, expect, xp):
163
152
  X = xp.asarray([[-1, -1], [0, 0], [1, 1]])
164
153
  Z = linkage(X, method=method)
165
- expectedZ = self._expectations[method]
166
- xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
167
-
168
-
169
- @skip_xp_backends(cpu_only=True)
154
+ expect = xp.asarray(expect, dtype=xp.float64)
155
+ xp_assert_close(Z, expect, atol=1e-06)
156
+
157
+ def test_unsupported_uncondensed_distance_matrix_linkage_warning(self, xp):
158
+ X = xp.asarray([[0, 1], [1, 0]])
159
+ with eager_warns(X, ClusterWarning):
160
+ linkage(X)
161
+
162
+ @pytest.mark.parametrize("method", _EUCLIDEAN_METHODS)
163
+ def test_euclidean_linkage_value_error(self, method, xp):
164
+ X = xp.asarray([[1, 1], [1, 1]])
165
+ with pytest.raises(ValueError):
166
+ linkage(X, method=method, metric='cityblock')
167
+
168
+ def test_2x2_linkage(self, xp):
169
+ Z1 = linkage(xp.asarray([1]), method='single', metric='euclidean')
170
+ Z2 = linkage(xp.asarray([[0, 1], [0, 0]]), method='single', metric='euclidean')
171
+ xp_assert_close(Z1, Z2, rtol=1e-15)
172
+
173
+ @skip_xp_backends("jax.numpy", reason="Can't raise inside jax.pure_callback")
174
+ def test_centroid_neg_distance(self, xp):
175
+ # gh-21011
176
+ values = xp.asarray([0, 0, -1])
177
+ with pytest.raises(ValueError):
178
+ # This is just checking that this doesn't crash
179
+ linkage(values, method='centroid')
180
+
181
+
182
+ @make_xp_test_case(inconsistent)
170
183
  class TestInconsistent:
171
184
 
172
185
  def test_inconsistent_tdist(self, xp):
@@ -179,7 +192,7 @@ class TestInconsistent:
179
192
  xp.asarray(hierarchy_test_data.inconsistent_ytdist[depth]))
180
193
 
181
194
 
182
- @skip_xp_backends(cpu_only=True)
195
+ @make_xp_test_case(cophenet)
183
196
  class TestCopheneticDistance:
184
197
 
185
198
  def test_linkage_cophenet_tdist_Z(self, xp):
@@ -200,6 +213,7 @@ class TestCopheneticDistance:
200
213
  xp_assert_close(c, expectedc, atol=1e-10)
201
214
  xp_assert_close(M, expectedM, atol=1e-10)
202
215
 
216
+ @skip_xp_backends("jax.numpy", reason="Can't raise inside jax.pure_callback")
203
217
  def test_gh_22183(self, xp):
204
218
  # check for lack of segfault
205
219
  # (out of bounds memory access)
@@ -220,6 +234,7 @@ class TestCopheneticDistance:
220
234
  cophenet(xp.asarray(arr))
221
235
 
222
236
 
237
+ @make_xp_test_case(from_mlab_linkage, to_mlab_linkage)
223
238
  class TestMLabLinkageConversion:
224
239
 
225
240
  def test_mlab_linkage_conversion_empty(self, xp):
@@ -228,7 +243,6 @@ class TestMLabLinkageConversion:
228
243
  xp_assert_equal(from_mlab_linkage(X), X)
229
244
  xp_assert_equal(to_mlab_linkage(X), X)
230
245
 
231
- @skip_xp_backends(cpu_only=True)
232
246
  def test_mlab_linkage_conversion_single_row(self, xp):
233
247
  # Tests from/to_mlab_linkage on linkage array with single row.
234
248
  Z = xp.asarray([[0., 1., 3., 2.]])
@@ -238,7 +252,6 @@ class TestMLabLinkageConversion:
238
252
  xp_assert_close(to_mlab_linkage(Z), xp.asarray(Zm, dtype=xp.float64),
239
253
  rtol=1e-15)
240
254
 
241
- @skip_xp_backends(cpu_only=True)
242
255
  def test_mlab_linkage_conversion_multiple_rows(self, xp):
243
256
  # Tests from/to_mlab_linkage on linkage array with multiple rows.
244
257
  Zm = xp.asarray([[3, 6, 138], [4, 5, 219],
@@ -254,57 +267,56 @@ class TestMLabLinkageConversion:
254
267
  rtol=1e-15)
255
268
 
256
269
 
257
- @skip_xp_backends(cpu_only=True)
258
- class TestFcluster:
270
+ @make_xp_test_case(fclusterdata)
271
+ class TestFclusterData:
259
272
 
260
- def test_fclusterdata(self, xp):
261
- for t in hierarchy_test_data.fcluster_inconsistent:
262
- self.check_fclusterdata(t, 'inconsistent', xp)
263
- for t in hierarchy_test_data.fcluster_distance:
264
- self.check_fclusterdata(t, 'distance', xp)
265
- for t in hierarchy_test_data.fcluster_maxclust:
266
- self.check_fclusterdata(t, 'maxclust', xp)
267
-
268
- def check_fclusterdata(self, t, criterion, xp):
273
+ @make_xp_test_case(is_isomorphic)
274
+ @pytest.mark.parametrize("criterion,t",
275
+ [("inconsistent", t) for t in hierarchy_test_data.fcluster_inconsistent]
276
+ + [("distance", t) for t in hierarchy_test_data.fcluster_distance]
277
+ + [("maxclust", t) for t in hierarchy_test_data.fcluster_maxclust]
278
+ )
279
+ def test_fclusterdata(self, t, criterion, xp):
269
280
  # Tests fclusterdata(X, criterion=criterion, t=t) on a random 3-cluster data set
270
281
  expectedT = xp.asarray(getattr(hierarchy_test_data, 'fcluster_' + criterion)[t])
271
282
  X = xp.asarray(hierarchy_test_data.Q_X)
272
283
  T = fclusterdata(X, criterion=criterion, t=t)
273
- assert_(is_isomorphic(T, expectedT))
284
+ assert is_isomorphic(T, expectedT)
285
+
274
286
 
275
- def test_fcluster(self, xp):
276
- for t in hierarchy_test_data.fcluster_inconsistent:
277
- self.check_fcluster(t, 'inconsistent', xp)
278
- for t in hierarchy_test_data.fcluster_distance:
279
- self.check_fcluster(t, 'distance', xp)
280
- for t in hierarchy_test_data.fcluster_maxclust:
281
- self.check_fcluster(t, 'maxclust', xp)
287
+ @make_xp_test_case(fcluster)
288
+ class TestFcluster:
282
289
 
283
- def check_fcluster(self, t, criterion, xp):
290
+ @make_xp_test_case(single, is_isomorphic)
291
+ @pytest.mark.parametrize("criterion,t",
292
+ [("inconsistent", t) for t in hierarchy_test_data.fcluster_inconsistent]
293
+ + [("distance", t) for t in hierarchy_test_data.fcluster_distance]
294
+ + [("maxclust", t) for t in hierarchy_test_data.fcluster_maxclust]
295
+ )
296
+ def test_fcluster(self, t, criterion, xp):
284
297
  # Tests fcluster(Z, criterion=criterion, t=t) on a random 3-cluster data set.
285
298
  expectedT = xp.asarray(getattr(hierarchy_test_data, 'fcluster_' + criterion)[t])
286
299
  Z = single(xp.asarray(hierarchy_test_data.Q_X))
287
300
  T = fcluster(Z, criterion=criterion, t=t)
288
301
  assert_(is_isomorphic(T, expectedT))
289
302
 
290
- def test_fcluster_monocrit(self, xp):
291
- for t in hierarchy_test_data.fcluster_distance:
292
- self.check_fcluster_monocrit(t, xp)
293
- for t in hierarchy_test_data.fcluster_maxclust:
294
- self.check_fcluster_maxclust_monocrit(t, xp)
295
-
296
- def check_fcluster_monocrit(self, t, xp):
303
+ @make_xp_test_case(single, is_isomorphic, maxdists)
304
+ @pytest.mark.parametrize("t", hierarchy_test_data.fcluster_distance)
305
+ def test_fcluster_monocrit(self, t, xp):
297
306
  expectedT = xp.asarray(hierarchy_test_data.fcluster_distance[t])
298
307
  Z = single(xp.asarray(hierarchy_test_data.Q_X))
299
308
  T = fcluster(Z, t, criterion='monocrit', monocrit=maxdists(Z))
300
309
  assert_(is_isomorphic(T, expectedT))
301
310
 
302
- def check_fcluster_maxclust_monocrit(self, t, xp):
311
+ @make_xp_test_case(single, is_isomorphic, maxdists)
312
+ @pytest.mark.parametrize("t", hierarchy_test_data.fcluster_maxclust)
313
+ def test_fcluster_maxclust_monocrit(self, t, xp):
303
314
  expectedT = xp.asarray(hierarchy_test_data.fcluster_maxclust[t])
304
315
  Z = single(xp.asarray(hierarchy_test_data.Q_X))
305
316
  T = fcluster(Z, t, criterion='maxclust_monocrit', monocrit=maxdists(Z))
306
317
  assert_(is_isomorphic(T, expectedT))
307
318
 
319
+ @make_xp_test_case(single)
308
320
  def test_fcluster_maxclust_gh_12651(self, xp):
309
321
  y = xp.asarray([[1], [4], [5]])
310
322
  Z = single(y)
@@ -318,29 +330,26 @@ class TestFcluster:
318
330
  xp.asarray([1, 2, 3]))
319
331
 
320
332
 
321
- @skip_xp_backends(cpu_only=True)
333
+ @make_xp_test_case(leaders)
322
334
  class TestLeaders:
323
335
 
324
336
  def test_leaders_single(self, xp):
325
337
  # Tests leaders using a flat clustering generated by single linkage.
326
338
  X = hierarchy_test_data.Q_X
327
339
  Y = pdist(X)
328
- Y = xp.asarray(Y)
329
340
  Z = linkage(Y)
330
341
  T = fcluster(Z, criterion='maxclust', t=3)
331
- Lright = (xp.asarray([53, 55, 56]), xp.asarray([2, 3, 1]))
342
+ Z = xp.asarray(Z)
332
343
  T = xp.asarray(T, dtype=xp.int32)
333
344
  L = leaders(Z, T)
334
- assert_allclose(np.concatenate(L), np.concatenate(Lright), rtol=1e-15)
345
+ expect = xp.asarray([53, 55, 56, 2, 3, 1], dtype=xp.int32)
346
+ xp_assert_close(xp.concat(L), expect, rtol=1e-15)
335
347
 
336
348
 
337
- @skip_xp_backends(np_only=True,
338
- reason='`is_isomorphic` only supports NumPy backend')
349
+ @make_xp_test_case(is_isomorphic)
339
350
  class TestIsIsomorphic:
340
351
 
341
- @skip_xp_backends(np_only=True,
342
- reason='array-likes only supported for NumPy backend')
343
- def test_array_like(self, xp):
352
+ def test_array_like(self):
344
353
  assert is_isomorphic([1, 1, 1], [2, 2, 2])
345
354
  assert is_isomorphic([], [])
346
355
 
@@ -377,8 +386,8 @@ class TestIsIsomorphic:
377
386
  # (3 flat clusters, different labelings, nonisomorphic)
378
387
  a = xp.asarray([1, 2, 3, 3])
379
388
  b = xp.asarray([1, 3, 2, 3])
380
- assert is_isomorphic(a, b) is False
381
- assert is_isomorphic(b, a) is False
389
+ assert not is_isomorphic(a, b)
390
+ assert not is_isomorphic(b, a)
382
391
 
383
392
  def test_is_isomorphic_4C(self, xp):
384
393
  # Tests is_isomorphic on test case #4C
@@ -388,18 +397,18 @@ class TestIsIsomorphic:
388
397
  assert is_isomorphic(a, b)
389
398
  assert is_isomorphic(b, a)
390
399
 
391
- def test_is_isomorphic_5(self, xp):
400
+ @pytest.mark.parametrize("nclusters", [2, 3, 5])
401
+ def test_is_isomorphic_5(self, nclusters, xp):
392
402
  # Tests is_isomorphic on test case #5 (1000 observations, 2/3/5 random
393
403
  # clusters, random permutation of the labeling).
394
- for nc in [2, 3, 5]:
395
- self.help_is_isomorphic_randperm(1000, nc, xp=xp)
404
+ self.is_isomorphic_randperm(1000, nclusters, xp=xp)
396
405
 
397
- def test_is_isomorphic_6(self, xp):
406
+ @pytest.mark.parametrize("nclusters", [2, 3, 5])
407
+ def test_is_isomorphic_6(self, nclusters, xp):
398
408
  # Tests is_isomorphic on test case #5A (1000 observations, 2/3/5 random
399
409
  # clusters, random permutation of the labeling, slightly
400
410
  # nonisomorphic.)
401
- for nc in [2, 3, 5]:
402
- self.help_is_isomorphic_randperm(1000, nc, True, 5, xp=xp)
411
+ self.is_isomorphic_randperm(1000, nclusters, True, 5, xp=xp)
403
412
 
404
413
  def test_is_isomorphic_7(self, xp):
405
414
  # Regression test for gh-6271
@@ -407,9 +416,8 @@ class TestIsIsomorphic:
407
416
  b = xp.asarray([1, 1, 1])
408
417
  assert not is_isomorphic(a, b)
409
418
 
410
- def help_is_isomorphic_randperm(self, nobs, nclusters, noniso=False, nerrors=0,
411
- *, xp):
412
- for k in range(3):
419
+ def is_isomorphic_randperm(self, nobs, nclusters, noniso=False, nerrors=0, *, xp):
420
+ for _ in range(3):
413
421
  a = (np.random.rand(nobs) * nclusters).astype(int)
414
422
  b = np.zeros(a.size, dtype=int)
415
423
  P = np.random.permutation(nclusters)
@@ -425,20 +433,17 @@ class TestIsIsomorphic:
425
433
  assert is_isomorphic(b, a) == (not noniso)
426
434
 
427
435
 
428
- @skip_xp_backends(cpu_only=True)
436
+ @make_xp_test_case(is_valid_linkage)
429
437
  class TestIsValidLinkage:
430
438
 
431
- def test_is_valid_linkage_various_size(self, xp):
432
- for nrow, ncol, valid in [(2, 5, False), (2, 3, False),
433
- (1, 4, True), (2, 4, True)]:
434
- self.check_is_valid_linkage_various_size(nrow, ncol, valid, xp)
435
-
436
- def check_is_valid_linkage_various_size(self, nrow, ncol, valid, xp):
439
+ @pytest.mark.parametrize("nrow, ncol, valid", [(2, 5, False), (2, 3, False),
440
+ (1, 4, True), (2, 4, True)])
441
+ def test_is_valid_linkage_various_size(self, nrow, ncol, valid, xp):
437
442
  # Tests is_valid_linkage(Z) with linkage matrices of various sizes
438
443
  Z = xp.asarray([[0, 1, 3.0, 2, 5],
439
444
  [3, 2, 4.0, 3, 3]], dtype=xp.float64)
440
445
  Z = Z[:nrow, :ncol]
441
- assert_(is_valid_linkage(Z) == valid)
446
+ xp_assert_equal(is_valid_linkage(Z), valid, check_namespace=False)
442
447
  if not valid:
443
448
  assert_raises(ValueError, is_valid_linkage, Z, throw=True)
444
449
 
@@ -446,13 +451,13 @@ class TestIsValidLinkage:
446
451
  # Tests is_valid_linkage(Z) with integer type.
447
452
  Z = xp.asarray([[0, 1, 3.0, 2],
448
453
  [3, 2, 4.0, 3]], dtype=xp.int64)
449
- assert_(is_valid_linkage(Z) is False)
454
+ xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
450
455
  assert_raises(TypeError, is_valid_linkage, Z, throw=True)
451
456
 
452
457
  def test_is_valid_linkage_empty(self, xp):
453
458
  # Tests is_valid_linkage(Z) with empty linkage.
454
459
  Z = xp.zeros((0, 4), dtype=xp.float64)
455
- assert_(is_valid_linkage(Z) is False)
460
+ xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
456
461
  assert_raises(ValueError, is_valid_linkage, Z, throw=True)
457
462
 
458
463
  def test_is_valid_linkage_4_and_up(self, xp):
@@ -460,91 +465,84 @@ class TestIsValidLinkage:
460
465
  # sizes 4 and 15 (step size 3).
461
466
  for i in range(4, 15, 3):
462
467
  y = np.random.rand(i*(i-1)//2)
468
+ Z = xp.asarray(linkage(y))
463
469
  y = xp.asarray(y)
464
- Z = linkage(y)
465
- assert_(is_valid_linkage(Z) is True)
470
+ xp_assert_equal(is_valid_linkage(Z), True, check_namespace=False)
466
471
 
467
- @skip_xp_backends('jax.numpy',
468
- reason='jax arrays do not support item assignment')
469
472
  def test_is_valid_linkage_4_and_up_neg_index_left(self, xp):
470
473
  # Tests is_valid_linkage(Z) on linkage on observation sets between
471
474
  # sizes 4 and 15 (step size 3) with negative indices (left).
472
475
  for i in range(4, 15, 3):
473
476
  y = np.random.rand(i*(i-1)//2)
477
+ Z = xp.asarray(linkage(y))
474
478
  y = xp.asarray(y)
475
- Z = linkage(y)
476
- Z[i//2,0] = -2
477
- assert_(is_valid_linkage(Z) is False)
478
- assert_raises(ValueError, is_valid_linkage, Z, throw=True)
479
+ Z = xpx.at(Z)[i//2, 0].set(-2)
480
+ xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
481
+ with pytest.raises(ValueError):
482
+ eager.is_valid_linkage(Z, throw=True)
479
483
 
480
- @skip_xp_backends('jax.numpy',
481
- reason='jax arrays do not support item assignment')
482
484
  def test_is_valid_linkage_4_and_up_neg_index_right(self, xp):
483
485
  # Tests is_valid_linkage(Z) on linkage on observation sets between
484
486
  # sizes 4 and 15 (step size 3) with negative indices (right).
485
487
  for i in range(4, 15, 3):
486
488
  y = np.random.rand(i*(i-1)//2)
489
+ Z = xp.asarray(linkage(y))
487
490
  y = xp.asarray(y)
488
- Z = linkage(y)
489
- Z[i//2,1] = -2
490
- assert_(is_valid_linkage(Z) is False)
491
- assert_raises(ValueError, is_valid_linkage, Z, throw=True)
491
+ Z = xpx.at(Z)[i//2, 1].set(-2)
492
+ xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
493
+ with pytest.raises(ValueError):
494
+ eager.is_valid_linkage(Z, throw=True)
492
495
 
493
- @skip_xp_backends('jax.numpy',
494
- reason='jax arrays do not support item assignment')
495
496
  def test_is_valid_linkage_4_and_up_neg_dist(self, xp):
496
497
  # Tests is_valid_linkage(Z) on linkage on observation sets between
497
498
  # sizes 4 and 15 (step size 3) with negative distances.
498
499
  for i in range(4, 15, 3):
499
500
  y = np.random.rand(i*(i-1)//2)
501
+ Z = xp.asarray(linkage(y))
500
502
  y = xp.asarray(y)
501
- Z = linkage(y)
502
- Z[i//2,2] = -0.5
503
- assert_(is_valid_linkage(Z) is False)
504
- assert_raises(ValueError, is_valid_linkage, Z, throw=True)
503
+ Z = xpx.at(Z)[i//2, 2].set(-0.5)
504
+ xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
505
+ with pytest.raises(ValueError):
506
+ eager.is_valid_linkage(Z, throw=True)
505
507
 
506
- @skip_xp_backends('jax.numpy',
507
- reason='jax arrays do not support item assignment')
508
508
  def test_is_valid_linkage_4_and_up_neg_counts(self, xp):
509
509
  # Tests is_valid_linkage(Z) on linkage on observation sets between
510
510
  # sizes 4 and 15 (step size 3) with negative counts.
511
511
  for i in range(4, 15, 3):
512
512
  y = np.random.rand(i*(i-1)//2)
513
+ Z = xp.asarray(linkage(y))
513
514
  y = xp.asarray(y)
514
- Z = linkage(y)
515
- Z[i//2,3] = -2
516
- assert_(is_valid_linkage(Z) is False)
517
- assert_raises(ValueError, is_valid_linkage, Z, throw=True)
515
+ Z = xpx.at(Z)[i//2, 3].set(-2)
516
+ xp_assert_equal(is_valid_linkage(Z), False, check_namespace=False)
517
+ with pytest.raises(ValueError):
518
+ eager.is_valid_linkage(Z, throw=True)
518
519
 
519
520
 
520
- @skip_xp_backends(cpu_only=True)
521
+ @make_xp_test_case(is_valid_im)
521
522
  class TestIsValidInconsistent:
522
523
 
523
524
  def test_is_valid_im_int_type(self, xp):
524
525
  # Tests is_valid_im(R) with integer type.
525
526
  R = xp.asarray([[0, 1, 3.0, 2],
526
527
  [3, 2, 4.0, 3]], dtype=xp.int64)
527
- assert_(is_valid_im(R) is False)
528
+ xp_assert_equal(is_valid_im(R), False, check_namespace=False)
528
529
  assert_raises(TypeError, is_valid_im, R, throw=True)
529
530
 
530
- def test_is_valid_im_various_size(self, xp):
531
- for nrow, ncol, valid in [(2, 5, False), (2, 3, False),
532
- (1, 4, True), (2, 4, True)]:
533
- self.check_is_valid_im_various_size(nrow, ncol, valid, xp)
534
-
535
- def check_is_valid_im_various_size(self, nrow, ncol, valid, xp):
531
+ @pytest.mark.parametrize("nrow, ncol, valid", [(2, 5, False), (2, 3, False),
532
+ (1, 4, True), (2, 4, True)])
533
+ def test_is_valid_im_various_size(self, nrow, ncol, valid, xp):
536
534
  # Tests is_valid_im(R) with linkage matrices of various sizes
537
535
  R = xp.asarray([[0, 1, 3.0, 2, 5],
538
536
  [3, 2, 4.0, 3, 3]], dtype=xp.float64)
539
537
  R = R[:nrow, :ncol]
540
- assert_(is_valid_im(R) == valid)
538
+ xp_assert_equal(is_valid_im(R), valid, check_namespace=False)
541
539
  if not valid:
542
540
  assert_raises(ValueError, is_valid_im, R, throw=True)
543
541
 
544
542
  def test_is_valid_im_empty(self, xp):
545
543
  # Tests is_valid_im(R) with empty inconsistency matrix.
546
544
  R = xp.zeros((0, 4), dtype=xp.float64)
547
- assert_(is_valid_im(R) is False)
545
+ xp_assert_equal(is_valid_im(R), False, check_namespace=False)
548
546
  assert_raises(ValueError, is_valid_im, R, throw=True)
549
547
 
550
548
  def test_is_valid_im_4_and_up(self, xp):
@@ -552,54 +550,53 @@ class TestIsValidInconsistent:
552
550
  # (step size 3).
553
551
  for i in range(4, 15, 3):
554
552
  y = np.random.rand(i*(i-1)//2)
555
- y = xp.asarray(y)
556
553
  Z = linkage(y)
557
554
  R = inconsistent(Z)
558
- assert_(is_valid_im(R) is True)
555
+ R = xp.asarray(R)
556
+ xp_assert_equal(is_valid_im(R), True, check_namespace=False)
559
557
 
560
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
561
558
  def test_is_valid_im_4_and_up_neg_index_left(self, xp):
562
559
  # Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
563
560
  # (step size 3) with negative link height means.
564
561
  for i in range(4, 15, 3):
565
562
  y = np.random.rand(i*(i-1)//2)
566
- y = xp.asarray(y)
567
563
  Z = linkage(y)
568
564
  R = inconsistent(Z)
569
- R[i//2,0] = -2.0
570
- assert_(is_valid_im(R) is False)
571
- assert_raises(ValueError, is_valid_im, R, throw=True)
565
+ R = xpx.at(R)[i//2 , 0].set(-2.0)
566
+ R = xp.asarray(R)
567
+ xp_assert_equal(is_valid_im(R), False, check_namespace=False)
568
+ with pytest.raises(ValueError):
569
+ eager.is_valid_im(R, throw=True)
572
570
 
573
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
574
571
  def test_is_valid_im_4_and_up_neg_index_right(self, xp):
575
572
  # Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
576
573
  # (step size 3) with negative link height standard deviations.
577
574
  for i in range(4, 15, 3):
578
575
  y = np.random.rand(i*(i-1)//2)
579
- y = xp.asarray(y)
580
576
  Z = linkage(y)
581
577
  R = inconsistent(Z)
582
- R[i//2,1] = -2.0
583
- assert_(is_valid_im(R) is False)
584
- assert_raises(ValueError, is_valid_im, R, throw=True)
578
+ R = xpx.at(R)[i//2 , 1].set(-2.0)
579
+ R = xp.asarray(R)
580
+ xp_assert_equal(is_valid_im(R), False, check_namespace=False)
581
+ with pytest.raises(ValueError):
582
+ eager.is_valid_im(R, throw=True)
585
583
 
586
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
587
584
  def test_is_valid_im_4_and_up_neg_dist(self, xp):
588
585
  # Tests is_valid_im(R) on im on observation sets between sizes 4 and 15
589
586
  # (step size 3) with negative link counts.
590
587
  for i in range(4, 15, 3):
591
588
  y = np.random.rand(i*(i-1)//2)
592
- y = xp.asarray(y)
593
589
  Z = linkage(y)
594
590
  R = inconsistent(Z)
595
- R[i//2,2] = -0.5
596
- assert_(is_valid_im(R) is False)
597
- assert_raises(ValueError, is_valid_im, R, throw=True)
591
+ R = xpx.at(R)[i//2, 2].set(-0.5)
592
+ R = xp.asarray(R)
593
+ xp_assert_equal(is_valid_im(R), False, check_namespace=False)
594
+ with pytest.raises(ValueError):
595
+ eager.is_valid_im(R, throw=True)
598
596
 
599
597
 
600
598
  class TestNumObsLinkage:
601
599
 
602
- @skip_xp_backends(cpu_only=True)
603
600
  def test_num_obs_linkage_empty(self, xp):
604
601
  # Tests num_obs_linkage(Z) with empty linkage.
605
602
  Z = xp.zeros((0, 4), dtype=xp.float64)
@@ -608,26 +605,32 @@ class TestNumObsLinkage:
608
605
  def test_num_obs_linkage_1x4(self, xp):
609
606
  # Tests num_obs_linkage(Z) on linkage over 2 observations.
610
607
  Z = xp.asarray([[0, 1, 3.0, 2]], dtype=xp.float64)
611
- assert_equal(num_obs_linkage(Z), 2)
608
+ assert num_obs_linkage(Z) == 2
612
609
 
613
610
  def test_num_obs_linkage_2x4(self, xp):
614
611
  # Tests num_obs_linkage(Z) on linkage over 3 observations.
615
612
  Z = xp.asarray([[0, 1, 3.0, 2],
616
613
  [3, 2, 4.0, 3]], dtype=xp.float64)
617
- assert_equal(num_obs_linkage(Z), 3)
614
+ assert num_obs_linkage(Z) == 3
618
615
 
619
- @skip_xp_backends(cpu_only=True)
620
616
  def test_num_obs_linkage_4_and_up(self, xp):
621
617
  # Tests num_obs_linkage(Z) on linkage on observation sets between sizes
622
618
  # 4 and 15 (step size 3).
623
619
  for i in range(4, 15, 3):
624
620
  y = np.random.rand(i*(i-1)//2)
625
- y = xp.asarray(y)
626
- Z = linkage(y)
627
- assert_equal(num_obs_linkage(Z), i)
621
+ Z = xp.asarray(linkage(y))
622
+ assert num_obs_linkage(Z) == i
628
623
 
624
+ def test_num_obs_linkage_multi_matrix(self, xp):
625
+ # Tests num_obs_linkage with observation matrices of multiple sizes.
626
+ for n in range(2, 10):
627
+ X = np.random.rand(n, 4)
628
+ Y = pdist(X)
629
+ Z = xp.asarray(linkage(Y))
630
+ assert num_obs_linkage(Z) == n
629
631
 
630
- @skip_xp_backends(cpu_only=True)
632
+
633
+ @make_xp_test_case(leaves_list, to_tree)
631
634
  class TestLeavesList:
632
635
 
633
636
  def test_leaves_list_1x4(self, xp):
@@ -643,29 +646,26 @@ class TestLeavesList:
643
646
  to_tree(Z)
644
647
  assert_allclose(leaves_list(Z), [0, 1, 2], rtol=1e-15)
645
648
 
646
- def test_leaves_list_Q(self, xp):
647
- for method in ['single', 'complete', 'average', 'weighted', 'centroid',
648
- 'median', 'ward']:
649
- self.check_leaves_list_Q(method, xp)
650
-
651
- def check_leaves_list_Q(self, method, xp):
649
+ @pytest.mark.parametrize("method",
650
+ ['single', 'complete', 'average', 'weighted', 'centroid', 'median', 'ward'])
651
+ def test_leaves_list_Q(self, method, xp):
652
652
  # Tests leaves_list(Z) on the Q data set
653
- X = xp.asarray(hierarchy_test_data.Q_X)
654
- Z = linkage(X, method)
653
+ X = hierarchy_test_data.Q_X
654
+ Z = xp.asarray(linkage(X, method))
655
655
  node = to_tree(Z)
656
656
  assert_allclose(node.pre_order(), leaves_list(Z), rtol=1e-15)
657
657
 
658
658
  def test_Q_subtree_pre_order(self, xp):
659
659
  # Tests that pre_order() works when called on sub-trees.
660
- X = xp.asarray(hierarchy_test_data.Q_X)
661
- Z = linkage(X, 'single')
660
+ X = hierarchy_test_data.Q_X
661
+ Z = xp.asarray(linkage(X, 'single'))
662
662
  node = to_tree(Z)
663
- assert_allclose(node.pre_order(), (node.get_left().pre_order()
664
- + node.get_right().pre_order()),
663
+ assert_allclose(node.pre_order(),
664
+ (node.get_left().pre_order() + node.get_right().pre_order()),
665
665
  rtol=1e-15)
666
666
 
667
667
 
668
- @skip_xp_backends(cpu_only=True)
668
+ @make_xp_test_case(correspond)
669
669
  class TestCorrespond:
670
670
 
671
671
  def test_correspond_empty(self, xp):
@@ -679,13 +679,13 @@ class TestCorrespond:
679
679
  # different sizes.
680
680
  for i in range(2, 4):
681
681
  y = np.random.rand(i*(i-1)//2)
682
+ Z = xp.asarray(linkage(y))
682
683
  y = xp.asarray(y)
683
- Z = linkage(y)
684
684
  assert_(correspond(Z, y))
685
685
  for i in range(4, 15, 3):
686
686
  y = np.random.rand(i*(i-1)//2)
687
+ Z = xp.asarray(linkage(y))
687
688
  y = xp.asarray(y)
688
- Z = linkage(y)
689
689
  assert_(correspond(Z, y))
690
690
 
691
691
  def test_correspond_4_and_up(self, xp):
@@ -695,10 +695,10 @@ class TestCorrespond:
695
695
  list(zip(list(range(3, 5)), list(range(2, 4))))):
696
696
  y = np.random.rand(i*(i-1)//2)
697
697
  y2 = np.random.rand(j*(j-1)//2)
698
+ Z = xp.asarray(linkage(y))
699
+ Z2 = xp.asarray(linkage(y2))
698
700
  y = xp.asarray(y)
699
701
  y2 = xp.asarray(y2)
700
- Z = linkage(y)
701
- Z2 = linkage(y2)
702
702
  assert not correspond(Z, y2)
703
703
  assert not correspond(Z2, y)
704
704
 
@@ -709,24 +709,15 @@ class TestCorrespond:
709
709
  list(zip(list(range(2, 7)), list(range(16, 21))))):
710
710
  y = np.random.rand(i*(i-1)//2)
711
711
  y2 = np.random.rand(j*(j-1)//2)
712
+ Z = xp.asarray(linkage(y))
713
+ Z2 = xp.asarray(linkage(y2))
712
714
  y = xp.asarray(y)
713
715
  y2 = xp.asarray(y2)
714
- Z = linkage(y)
715
- Z2 = linkage(y2)
716
716
  assert not correspond(Z, y2)
717
717
  assert not correspond(Z2, y)
718
718
 
719
- def test_num_obs_linkage_multi_matrix(self, xp):
720
- # Tests num_obs_linkage with observation matrices of multiple sizes.
721
- for n in range(2, 10):
722
- X = np.random.rand(n, 4)
723
- Y = pdist(X)
724
- Y = xp.asarray(Y)
725
- Z = linkage(Y)
726
- assert_equal(num_obs_linkage(Z), n)
727
-
728
719
 
729
- @skip_xp_backends(cpu_only=True)
720
+ @make_xp_test_case(is_monotonic)
730
721
  class TestIsMonotonic:
731
722
 
732
723
  def test_is_monotonic_empty(self, xp):
@@ -782,26 +773,25 @@ class TestIsMonotonic:
782
773
  def test_is_monotonic_tdist_linkage1(self, xp):
783
774
  # Tests is_monotonic(Z) on clustering generated by single linkage on
784
775
  # tdist data set. Expecting True.
785
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
776
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
786
777
  assert is_monotonic(Z)
787
778
 
788
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
789
779
  def test_is_monotonic_tdist_linkage2(self, xp):
790
780
  # Tests is_monotonic(Z) on clustering generated by single linkage on
791
781
  # tdist data set. Perturbing. Expecting False.
792
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
793
- Z[2,2] = 0.0
782
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
783
+ Z = xpx.at(Z)[2, 2].set(0.0)
794
784
  assert not is_monotonic(Z)
795
785
 
796
786
  def test_is_monotonic_Q_linkage(self, xp):
797
787
  # Tests is_monotonic(Z) on clustering generated by single linkage on
798
788
  # Q data set. Expecting True.
799
- X = xp.asarray(hierarchy_test_data.Q_X)
800
- Z = linkage(X, 'single')
789
+ X = hierarchy_test_data.Q_X
790
+ Z = xp.asarray(linkage(X, 'single'))
801
791
  assert is_monotonic(Z)
802
792
 
803
793
 
804
- @skip_xp_backends(cpu_only=True)
794
+ @make_xp_test_case(maxdists)
805
795
  class TestMaxDists:
806
796
 
807
797
  def test_maxdists_empty_linkage(self, xp):
@@ -809,7 +799,6 @@ class TestMaxDists:
809
799
  Z = xp.zeros((0, 4), dtype=xp.float64)
810
800
  assert_raises(ValueError, maxdists, Z)
811
801
 
812
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
813
802
  def test_maxdists_one_cluster_linkage(self, xp):
814
803
  # Tests maxdists(Z) on linkage with one cluster.
815
804
  Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
@@ -817,23 +806,20 @@ class TestMaxDists:
817
806
  expectedMD = calculate_maximum_distances(Z, xp)
818
807
  xp_assert_close(MD, expectedMD, atol=1e-15)
819
808
 
820
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment')
821
- def test_maxdists_Q_linkage(self, xp):
822
- for method in ['single', 'complete', 'ward', 'centroid', 'median']:
823
- self.check_maxdists_Q_linkage(method, xp)
824
-
825
- def check_maxdists_Q_linkage(self, method, xp):
809
+ @pytest.mark.parametrize(
810
+ "method", ['single', 'complete', 'ward', 'centroid', 'median'])
811
+ def test_maxdists_Q_linkage(self, method, xp):
826
812
  # Tests maxdists(Z) on the Q data set
827
- X = xp.asarray(hierarchy_test_data.Q_X)
828
- Z = linkage(X, method)
813
+ X = hierarchy_test_data.Q_X
814
+ Z = xp.asarray(linkage(X, method))
829
815
  MD = maxdists(Z)
830
816
  expectedMD = calculate_maximum_distances(Z, xp)
831
817
  xp_assert_close(MD, expectedMD, atol=1e-15)
832
818
 
833
819
 
820
+ @make_xp_test_case(maxinconsts)
834
821
  class TestMaxInconsts:
835
822
 
836
- @skip_xp_backends(cpu_only=True)
837
823
  def test_maxinconsts_empty_linkage(self, xp):
838
824
  # Tests maxinconsts(Z, R) on empty linkage. Expecting exception.
839
825
  Z = xp.zeros((0, 4), dtype=xp.float64)
@@ -848,8 +834,6 @@ class TestMaxInconsts:
848
834
  R = xp.asarray(R)
849
835
  assert_raises(ValueError, maxinconsts, Z, R)
850
836
 
851
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment',
852
- cpu_only=True)
853
837
  def test_maxinconsts_one_cluster_linkage(self, xp):
854
838
  # Tests maxinconsts(Z, R) on linkage with one cluster.
855
839
  Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
@@ -858,53 +842,42 @@ class TestMaxInconsts:
858
842
  expectedMD = calculate_maximum_inconsistencies(Z, R, xp=xp)
859
843
  xp_assert_close(MD, expectedMD, atol=1e-15)
860
844
 
861
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment',
862
- cpu_only=True)
863
- def test_maxinconsts_Q_linkage(self, xp):
864
- for method in ['single', 'complete', 'ward', 'centroid', 'median']:
865
- self.check_maxinconsts_Q_linkage(method, xp)
866
-
867
- def check_maxinconsts_Q_linkage(self, method, xp):
845
+ @pytest.mark.parametrize(
846
+ "method", ['single', 'complete', 'ward', 'centroid', 'median'])
847
+ def test_maxinconsts_Q_linkage(self, method, xp):
868
848
  # Tests maxinconsts(Z, R) on the Q data set
869
- X = xp.asarray(hierarchy_test_data.Q_X)
849
+ X = hierarchy_test_data.Q_X
870
850
  Z = linkage(X, method)
871
- R = inconsistent(Z)
851
+ R = xp.asarray(inconsistent(Z))
852
+ Z = xp.asarray(Z)
872
853
  MD = maxinconsts(Z, R)
873
854
  expectedMD = calculate_maximum_inconsistencies(Z, R, xp=xp)
874
855
  xp_assert_close(MD, expectedMD, atol=1e-15)
875
856
 
876
857
 
858
+ @make_xp_test_case(maxRstat)
877
859
  class TestMaxRStat:
878
860
 
879
861
  def test_maxRstat_invalid_index(self, xp):
880
- for i in [3.3, -1, 4]:
881
- self.check_maxRstat_invalid_index(i, xp)
882
-
883
- def check_maxRstat_invalid_index(self, i, xp):
884
862
  # Tests maxRstat(Z, R, i). Expecting exception.
885
863
  Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
886
864
  R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
887
- if isinstance(i, int):
888
- assert_raises(ValueError, maxRstat, Z, R, i)
889
- else:
890
- assert_raises(TypeError, maxRstat, Z, R, i)
891
-
892
- @skip_xp_backends(cpu_only=True)
893
- def test_maxRstat_empty_linkage(self, xp):
894
- for i in range(4):
895
- self.check_maxRstat_empty_linkage(i, xp)
896
-
897
- def check_maxRstat_empty_linkage(self, i, xp):
865
+ with pytest.raises(TypeError):
866
+ maxRstat(Z, R, 3.3)
867
+ with pytest.raises(ValueError):
868
+ maxRstat(Z, R, -1)
869
+ with pytest.raises(ValueError):
870
+ maxRstat(Z, R, 4)
871
+
872
+ @pytest.mark.parametrize("i", range(4))
873
+ def test_maxRstat_empty_linkage(self, i, xp):
898
874
  # Tests maxRstat(Z, R, i) on empty linkage. Expecting exception.
899
875
  Z = xp.zeros((0, 4), dtype=xp.float64)
900
876
  R = xp.zeros((0, 4), dtype=xp.float64)
901
877
  assert_raises(ValueError, maxRstat, Z, R, i)
902
878
 
903
- def test_maxRstat_difrow_linkage(self, xp):
904
- for i in range(4):
905
- self.check_maxRstat_difrow_linkage(i, xp)
906
-
907
- def check_maxRstat_difrow_linkage(self, i, xp):
879
+ @pytest.mark.parametrize("i", range(4))
880
+ def test_maxRstat_difrow_linkage(self, i, xp):
908
881
  # Tests maxRstat(Z, R, i) on linkage and inconsistency matrices with
909
882
  # different numbers of clusters. Expecting exception.
910
883
  Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
@@ -912,13 +885,7 @@ class TestMaxRStat:
912
885
  R = xp.asarray(R)
913
886
  assert_raises(ValueError, maxRstat, Z, R, i)
914
887
 
915
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment',
916
- cpu_only=True)
917
888
  def test_maxRstat_one_cluster_linkage(self, xp):
918
- for i in range(4):
919
- self.check_maxRstat_one_cluster_linkage(i, xp)
920
-
921
- def check_maxRstat_one_cluster_linkage(self, i, xp):
922
889
  # Tests maxRstat(Z, R, i) on linkage with one cluster.
923
890
  Z = xp.asarray([[0, 1, 0.3, 4]], dtype=xp.float64)
924
891
  R = xp.asarray([[0, 0, 0, 0.3]], dtype=xp.float64)
@@ -926,40 +893,36 @@ class TestMaxRStat:
926
893
  expectedMD = calculate_maximum_inconsistencies(Z, R, 1, xp)
927
894
  xp_assert_close(MD, expectedMD, atol=1e-15)
928
895
 
929
- @skip_xp_backends('jax.numpy', reason='jax arrays do not support item assignment',
930
- cpu_only=True)
931
- def test_maxRstat_Q_linkage(self, xp):
932
- for method in ['single', 'complete', 'ward', 'centroid', 'median']:
933
- for i in range(4):
934
- self.check_maxRstat_Q_linkage(method, i, xp)
935
-
936
- def check_maxRstat_Q_linkage(self, method, i, xp):
937
- # Tests maxRstat(Z, R, i) on the Q data set
938
- X = xp.asarray(hierarchy_test_data.Q_X)
896
+ @pytest.mark.parametrize(
897
+ "method", ['single', 'complete', 'ward', 'centroid', 'median'])
898
+ def test_maxRstat_Q_linkage(self, method, xp):
899
+ # Tests maxRstat(Z, R, 1) on the Q data set
900
+ X = hierarchy_test_data.Q_X
939
901
  Z = linkage(X, method)
940
- R = inconsistent(Z)
902
+ R = xp.asarray(inconsistent(Z))
903
+ Z = xp.asarray(Z)
941
904
  MD = maxRstat(Z, R, 1)
942
905
  expectedMD = calculate_maximum_inconsistencies(Z, R, 1, xp)
943
906
  xp_assert_close(MD, expectedMD, atol=1e-15)
944
907
 
945
908
 
946
- @skip_xp_backends(cpu_only=True)
909
+ @make_xp_test_case(dendrogram)
947
910
  class TestDendrogram:
948
911
 
949
912
  def test_dendrogram_single_linkage_tdist(self, xp):
950
913
  # Tests dendrogram calculation on single linkage of the tdist data set.
951
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
914
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
952
915
  R = dendrogram(Z, no_plot=True)
953
916
  leaves = R["leaves"]
954
917
  assert_equal(leaves, [2, 5, 1, 0, 3, 4])
955
918
 
956
919
  def test_valid_orientation(self, xp):
957
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
920
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
958
921
  assert_raises(ValueError, dendrogram, Z, orientation="foo")
959
922
 
960
923
  def test_labels_as_array_or_list(self, xp):
961
924
  # test for gh-12418
962
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
925
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
963
926
  labels = [1, 3, 2, 6, 4, 5]
964
927
  result1 = dendrogram(Z, labels=xp.asarray(labels), no_plot=True)
965
928
  result2 = dendrogram(Z, labels=labels, no_plot=True)
@@ -989,14 +952,14 @@ class TestDendrogram:
989
952
  reason='MPL 3.9.2 & torch DeprecationWarning from __array_wrap__'
990
953
  ' and NumPy 2.0'
991
954
  )
955
+ @skip_xp_backends('dask.array',
956
+ reason='dask.array has bad interaction with matplotlib'
957
+ )
992
958
  @pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
993
- def test_dendrogram_plot(self, xp):
994
- for orientation in ['top', 'bottom', 'left', 'right']:
995
- self.check_dendrogram_plot(orientation, xp)
996
-
997
- def check_dendrogram_plot(self, orientation, xp):
959
+ @pytest.mark.parametrize("orientation", ['top', 'bottom', 'left', 'right'])
960
+ def test_dendrogram_plot(self, orientation, xp):
998
961
  # Tests dendrogram plotting.
999
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
962
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
1000
963
  expected = {'color_list': ['C1', 'C0', 'C0', 'C0', 'C0'],
1001
964
  'dcoord': [[0.0, 138.0, 138.0, 0.0],
1002
965
  [0.0, 219.0, 219.0, 0.0],
@@ -1060,9 +1023,12 @@ class TestDendrogram:
1060
1023
  reason='MPL 3.9.2 & torch DeprecationWarning from __array_wrap__'
1061
1024
  ' and NumPy 2.0'
1062
1025
  )
1026
+ @skip_xp_backends('dask.array',
1027
+ reason='dask.array has bad interaction with matplotlib'
1028
+ )
1063
1029
  @pytest.mark.skipif(not have_matplotlib, reason="no matplotlib")
1064
1030
  def test_dendrogram_truncate_mode(self, xp):
1065
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
1031
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
1066
1032
 
1067
1033
  R = dendrogram(Z, 2, 'lastp', show_contracted=True)
1068
1034
  plt.close()
@@ -1098,13 +1064,13 @@ class TestDendrogram:
1098
1064
 
1099
1065
  def test_dendrogram_colors(self, xp, dendrogram_lock):
1100
1066
  # Tests dendrogram plots with alternate colors
1101
- Z = linkage(xp.asarray(hierarchy_test_data.ytdist), 'single')
1067
+ Z = xp.asarray(linkage(hierarchy_test_data.ytdist, 'single'))
1102
1068
 
1103
1069
  with dendrogram_lock:
1104
1070
  # Global color palette might be changed concurrently
1105
1071
  set_link_color_palette(['c', 'm', 'y', 'k'])
1106
1072
  R = dendrogram(Z, no_plot=True,
1107
- above_threshold_color='g', color_threshold=250)
1073
+ above_threshold_color='g', color_threshold=250)
1108
1074
  set_link_color_palette(['g', 'r', 'c', 'm', 'y', 'k'])
1109
1075
 
1110
1076
  color_list = R['color_list']
@@ -1116,14 +1082,14 @@ class TestDendrogram:
1116
1082
  def test_dendrogram_leaf_colors_zero_dist(self, xp):
1117
1083
  # tests that the colors of leafs are correct for tree
1118
1084
  # with two identical points
1119
- x = xp.asarray([[1, 0, 0],
1085
+ X = np.asarray([[1, 0, 0],
1120
1086
  [0, 0, 1],
1121
1087
  [0, 2, 0],
1122
1088
  [0, 0, 1],
1123
1089
  [0, 1, 0],
1124
1090
  [0, 1, 0]])
1125
- z = linkage(x, "single")
1126
- d = dendrogram(z, no_plot=True)
1091
+ Z = xp.asarray(linkage(X, "single"))
1092
+ d = dendrogram(Z, no_plot=True)
1127
1093
  exp_colors = ['C0', 'C1', 'C1', 'C0', 'C2', 'C2']
1128
1094
  colors = d["leaves_color_list"]
1129
1095
  assert_equal(colors, exp_colors)
@@ -1131,14 +1097,14 @@ class TestDendrogram:
1131
1097
  def test_dendrogram_leaf_colors(self, xp):
1132
1098
  # tests that the colors are correct for a tree
1133
1099
  # with two near points ((0, 0, 1.1) and (0, 0, 1))
1134
- x = xp.asarray([[1, 0, 0],
1100
+ X = np.asarray([[1, 0, 0],
1135
1101
  [0, 0, 1.1],
1136
1102
  [0, 2, 0],
1137
1103
  [0, 0, 1],
1138
1104
  [0, 1, 0],
1139
1105
  [0, 1, 0]])
1140
- z = linkage(x, "single")
1141
- d = dendrogram(z, no_plot=True)
1106
+ Z = xp.asarray(linkage(X, "single"))
1107
+ d = dendrogram(Z, no_plot=True)
1142
1108
  exp_colors = ['C0', 'C1', 'C1', 'C0', 'C2', 'C2']
1143
1109
  colors = d["leaves_color_list"]
1144
1110
  assert_equal(colors, exp_colors)
@@ -1148,17 +1114,18 @@ def calculate_maximum_distances(Z, xp):
1148
1114
  # Used for testing correctness of maxdists.
1149
1115
  n = Z.shape[0] + 1
1150
1116
  B = xp.zeros((n-1,), dtype=Z.dtype)
1151
- q = xp.zeros((3,))
1152
1117
  for i in range(0, n - 1):
1153
- q[:] = 0.0
1118
+ q = xp.zeros((3,))
1154
1119
  left = Z[i, 0]
1155
1120
  right = Z[i, 1]
1156
1121
  if left >= n:
1157
- q[0] = B[xp.asarray(left, dtype=xp.int64) - n]
1122
+ b_left = B[xp.asarray(left, dtype=xp.int64) - n]
1123
+ q = xpx.at(q, 0).set(b_left)
1158
1124
  if right >= n:
1159
- q[1] = B[xp.asarray(right, dtype=xp.int64) - n]
1160
- q[2] = Z[i, 2]
1161
- B[i] = xp.max(q)
1125
+ b_right = B[xp.asarray(right, dtype=xp.int64) - n]
1126
+ q = xpx.at(q, 1).set(b_right)
1127
+ q = xpx.at(q, 2).set(Z[i, 2])
1128
+ B = xpx.at(B, i).set(xp.max(q))
1162
1129
  return B
1163
1130
 
1164
1131
 
@@ -1167,46 +1134,27 @@ def calculate_maximum_inconsistencies(Z, R, k=3, xp=np):
1167
1134
  n = Z.shape[0] + 1
1168
1135
  dtype = xp.result_type(Z, R)
1169
1136
  B = xp.zeros((n-1,), dtype=dtype)
1170
- q = xp.zeros((3,))
1171
1137
  for i in range(0, n - 1):
1172
- q[:] = 0.0
1138
+ q = xp.zeros((3,))
1173
1139
  left = Z[i, 0]
1174
1140
  right = Z[i, 1]
1175
1141
  if left >= n:
1176
- q[0] = B[xp.asarray(left, dtype=xp.int64) - n]
1142
+ b_left = B[xp.asarray(left, dtype=xp.int64) - n]
1143
+ q = xpx.at(q, 0).set(b_left)
1177
1144
  if right >= n:
1178
- q[1] = B[xp.asarray(right, dtype=xp.int64) - n]
1179
- q[2] = R[i, k]
1180
- B[i] = xp.max(q)
1145
+ b_right = B[xp.asarray(right, dtype=xp.int64) - n]
1146
+ q = xpx.at(q, 1).set(b_right)
1147
+ q = xpx.at(q, 2).set(R[i, k])
1148
+ B = xpx.at(B, i).set(xp.max(q))
1181
1149
  return B
1182
1150
 
1183
1151
 
1184
- @pytest.mark.thread_unsafe
1185
- @skip_xp_backends(cpu_only=True)
1186
- def test_unsupported_uncondensed_distance_matrix_linkage_warning(xp):
1187
- assert_warns(ClusterWarning, linkage, xp.asarray([[0, 1], [1, 0]]))
1188
-
1189
-
1190
- def test_euclidean_linkage_value_error(xp):
1191
- for method in scipy.cluster.hierarchy._EUCLIDEAN_METHODS:
1192
- assert_raises(ValueError, linkage, xp.asarray([[1, 1], [1, 1]]),
1193
- method=method, metric='cityblock')
1194
-
1195
-
1196
- @skip_xp_backends(cpu_only=True)
1197
- def test_2x2_linkage(xp):
1198
- Z1 = linkage(xp.asarray([1]), method='single', metric='euclidean')
1199
- Z2 = linkage(xp.asarray([[0, 1], [0, 0]]), method='single', metric='euclidean')
1200
- xp_assert_close(Z1, Z2, rtol=1e-15)
1201
-
1202
-
1203
- @skip_xp_backends(cpu_only=True)
1152
+ @make_xp_test_case(to_tree)
1204
1153
  def test_node_compare(xp):
1205
1154
  np.random.seed(23)
1206
1155
  nobs = 50
1207
1156
  X = np.random.randn(nobs, 4)
1208
- X = xp.asarray(X)
1209
- Z = scipy.cluster.hierarchy.ward(X)
1157
+ Z = xp.asarray(ward(X))
1210
1158
  tree = to_tree(Z)
1211
1159
  assert_(tree > tree.get_left())
1212
1160
  assert_(tree.get_right() > tree.get_left())
@@ -1214,13 +1162,12 @@ def test_node_compare(xp):
1214
1162
  assert_(tree.get_right() != tree.get_left())
1215
1163
 
1216
1164
 
1217
- @skip_xp_backends(np_only=True, reason='`cut_tree` uses non-standard indexing')
1165
+ @make_xp_test_case(cut_tree)
1218
1166
  def test_cut_tree(xp):
1219
1167
  np.random.seed(23)
1220
1168
  nobs = 50
1221
1169
  X = np.random.randn(nobs, 4)
1222
- X = xp.asarray(X)
1223
- Z = scipy.cluster.hierarchy.ward(X)
1170
+ Z = xp.asarray(ward(X))
1224
1171
  cutree = cut_tree(Z)
1225
1172
 
1226
1173
  # cutree.dtype varies between int32 and int64 over platforms
@@ -1243,16 +1190,16 @@ def test_cut_tree(xp):
1243
1190
  cut_tree(Z, height=[10, 5]), rtol=1e-15)
1244
1191
 
1245
1192
 
1246
- @skip_xp_backends(cpu_only=True)
1193
+ @make_xp_test_case(optimal_leaf_ordering)
1247
1194
  def test_optimal_leaf_ordering(xp):
1248
1195
  # test with the distance vector y
1249
- Z = optimal_leaf_ordering(linkage(xp.asarray(hierarchy_test_data.ytdist)),
1196
+ Z = optimal_leaf_ordering(xp.asarray(linkage(hierarchy_test_data.ytdist)),
1250
1197
  xp.asarray(hierarchy_test_data.ytdist))
1251
1198
  expectedZ = hierarchy_test_data.linkage_ytdist_single_olo
1252
1199
  xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-10)
1253
1200
 
1254
1201
  # test with the observation matrix X
1255
- Z = optimal_leaf_ordering(linkage(xp.asarray(hierarchy_test_data.X), 'ward'),
1202
+ Z = optimal_leaf_ordering(xp.asarray(linkage(hierarchy_test_data.X, 'ward')),
1256
1203
  xp.asarray(hierarchy_test_data.X))
1257
1204
  expectedZ = hierarchy_test_data.linkage_X_ward_olo
1258
1205
  xp_assert_close(Z, xp.asarray(expectedZ), atol=1e-06)
@@ -1289,12 +1236,3 @@ def test_Heap(xp):
1289
1236
  pair = heap.get_min()
1290
1237
  assert_equal(pair['key'], 1)
1291
1238
  assert_equal(pair['value'], 10)
1292
-
1293
-
1294
- @skip_xp_backends(cpu_only=True)
1295
- def test_centroid_neg_distance(xp):
1296
- # gh-21011
1297
- values = xp.asarray([0, 0, -1])
1298
- with pytest.raises(ValueError):
1299
- # This is just checking that this doesn't crash
1300
- linkage(values, method='centroid')