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
@@ -0,0 +1,1221 @@
1
+ import pytest
2
+
3
+ import numpy as np
4
+ from scipy.spatial.transform import Rotation, RigidTransform
5
+ from scipy.spatial.transform._rigid_transform import normalize_dual_quaternion
6
+ from scipy._lib._array_api import (
7
+ is_lazy_array,
8
+ xp_vector_norm,
9
+ is_numpy,
10
+ xp_assert_close,
11
+ )
12
+ import scipy._lib.array_api_extra as xpx
13
+
14
+
15
+ pytestmark = pytest.mark.skip_xp_backends(np_only=True)
16
+
17
+
18
+ def rotation_to_xp(r: Rotation, xp):
19
+ return Rotation.from_quat(xp.asarray(r.as_quat()))
20
+
21
+
22
+ def rigid_transform_to_xp(r: RigidTransform, xp):
23
+ return RigidTransform.from_matrix(xp.asarray(r.as_matrix()))
24
+
25
+
26
+ def test_repr(xp):
27
+ actual = repr(RigidTransform.from_matrix(xp.eye(4)))
28
+ expected = """\
29
+ RigidTransform.from_matrix(array([[1., 0., 0., 0.],
30
+ [0., 1., 0., 0.],
31
+ [0., 0., 1., 0.],
32
+ [0., 0., 0., 1.]]))"""
33
+ assert actual == expected
34
+
35
+ tf = RigidTransform.from_matrix(xp.asarray(RigidTransform.identity(2).as_matrix()))
36
+ actual = repr(tf)
37
+ expected = """\
38
+ RigidTransform.from_matrix(array([[[1., 0., 0., 0.],
39
+ [0., 1., 0., 0.],
40
+ [0., 0., 1., 0.],
41
+ [0., 0., 0., 1.]],
42
+
43
+ [[1., 0., 0., 0.],
44
+ [0., 1., 0., 0.],
45
+ [0., 0., 1., 0.],
46
+ [0., 0., 0., 1.]]]))"""
47
+ assert actual == expected
48
+
49
+
50
+ def test_from_rotation(xp):
51
+ atol = 1e-12
52
+
53
+ # Test single rotation
54
+ r = Rotation.from_matrix(xp.eye(3))
55
+ tf = RigidTransform.from_rotation(r)
56
+ xp_assert_close(tf.as_matrix(), xp.eye(4), atol=atol)
57
+ assert tf.single
58
+
59
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
60
+ tf = RigidTransform.from_rotation(r)
61
+ xp_assert_close(tf.as_matrix()[:3, :3], r.as_matrix(), atol=atol)
62
+ xp_assert_close(tf.as_matrix()[:3, 3], xp.asarray([0.0, 0, 0]), atol=atol)
63
+ xp_assert_close(tf.as_matrix()[3, :], xp.asarray([0.0, 0, 0, 1]), atol=atol)
64
+ assert tf.single
65
+
66
+ # Test multiple rotations
67
+ r = Rotation.from_euler('zyx', xp.asarray([[90, 0, 0], [0, 90, 0]]), degrees=True)
68
+ tf = RigidTransform.from_rotation(r)
69
+ xp_assert_close(tf.as_matrix()[:, :3, :3], r.as_matrix(), atol=atol)
70
+ xp_assert_close(tf.as_matrix()[:, :3, 3], xp.asarray([[0.0, 0, 0], [0, 0, 0]]),
71
+ atol=atol)
72
+ xp_assert_close(tf.as_matrix()[:, 3, :], xp.asarray([[0.0, 0, 0, 1], [0, 0, 0, 1]]),
73
+ atol=atol)
74
+ assert not tf.single
75
+
76
+
77
+ def test_from_translation(xp):
78
+ # Test single translation
79
+ t = xp.asarray([1, 2, 3])
80
+ tf = RigidTransform.from_translation(t)
81
+ expected = xp.eye(4)
82
+ expected = xpx.at(expected)[..., :3, 3].set(t)
83
+ xp_assert_close(tf.as_matrix(), expected)
84
+ assert tf.single
85
+
86
+ # Test multiple translations
87
+ t = xp.asarray([[1, 2, 3], [4, 5, 6]])
88
+ tf = RigidTransform.from_translation(t)
89
+ for i in range(t.shape[0]):
90
+ expected = xp.eye(4)
91
+ expected = xpx.at(expected)[..., :3, 3].set(t[i, ...])
92
+ xp_assert_close(tf.as_matrix()[i, ...], expected)
93
+ assert not tf.single
94
+
95
+
96
+ def test_from_translation_array_like():
97
+ # Test single translation
98
+ t = [1, 2, 3]
99
+ tf = RigidTransform.from_translation(t)
100
+ tf_expected = RigidTransform.from_translation(np.array(t))
101
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix())
102
+ assert tf.single
103
+
104
+ # Test multiple translations
105
+ t = [[1, 2, 3], [4, 5, 6]]
106
+ tf = RigidTransform.from_translation(t)
107
+ tf_expected = RigidTransform.from_translation(np.array(t))
108
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix())
109
+ assert not tf.single
110
+
111
+
112
+ def test_from_matrix(xp):
113
+ atol = 1e-12
114
+
115
+ # Test single transform matrix
116
+ matrix = xp.eye(4)
117
+ matrix = xpx.at(matrix)[..., :3, 3].set(xp.asarray([1, 2, 3]))
118
+ tf = RigidTransform.from_matrix(matrix)
119
+ xp_assert_close(tf.as_matrix(), matrix, atol=atol)
120
+ assert tf.single
121
+
122
+ # Test multiple transform matrices
123
+ matrices = xp.repeat(xp.eye(4)[None, ...], 2, axis=0)
124
+ matrices = xpx.at(matrices)[0, :3, 3].set(xp.asarray([1, 2, 3]))
125
+ matrices = xpx.at(matrices)[1, :3, 3].set(xp.asarray([4, 5, 6]))
126
+ tf = RigidTransform.from_matrix(matrices)
127
+ xp_assert_close(tf.as_matrix(), matrices, atol=atol)
128
+ assert not tf.single
129
+
130
+ # Test non-1 determinant
131
+ matrix = xp.eye(4)
132
+ matrix = xpx.at(matrix)[..., :3, :3].set(xp.eye(3) * 2.0)
133
+ tf = RigidTransform.from_matrix(matrix)
134
+ xp_assert_close(tf.as_matrix(), xp.eye(4), atol=atol)
135
+
136
+ # Test non-orthogonal rotation matrix
137
+ matrix = xp.asarray([[1, 1, 0, 0],
138
+ [0, 1, 0, 0],
139
+ [0, 0, 1, 0],
140
+ [0, 0, 0, 1]])
141
+ tf = RigidTransform.from_matrix(matrix)
142
+ expected = xp.asarray([[0.894427, 0.447214, 0, 0],
143
+ [-0.447214, 0.894427, 0, 0],
144
+ [0, 0, 1, 0],
145
+ [0, 0, 0, 1]])
146
+ xp_assert_close(tf.as_matrix(), expected, atol=1e-6)
147
+
148
+ # Test invalid matrix
149
+ invalid = xp.eye(4)
150
+ invalid = xpx.at(invalid)[..., 3, 3].set(2) # Invalid last row
151
+ if is_lazy_array(invalid):
152
+ tf = RigidTransform.from_matrix(invalid)
153
+ assert xp.all(xp.isnan(tf.as_matrix()))
154
+ else:
155
+ with pytest.raises(ValueError):
156
+ RigidTransform.from_matrix(invalid)
157
+
158
+
159
+ def test_from_matrix_array_like():
160
+ # Test single transform matrix
161
+ matrix = [[1, 0, 0, 0],
162
+ [0, 1, 0, 0],
163
+ [0, 0, 1, 0],
164
+ [0, 0, 0, 1]]
165
+ expected = np.eye(4)
166
+ tf = RigidTransform.from_matrix(matrix)
167
+ xp_assert_close(tf.as_matrix(), expected)
168
+ assert tf.single
169
+
170
+ # Test multiple transform matrices
171
+ matrices = [matrix, matrix]
172
+ tf = RigidTransform.from_matrix(matrices)
173
+ for i in range(len(matrices)):
174
+ xp_assert_close(tf.as_matrix()[i, ...], expected)
175
+ assert not tf.single
176
+
177
+
178
+ def test_from_components(xp):
179
+ atol = 1e-12
180
+
181
+ # Test single rotation and translation
182
+ t = xp.asarray([1, 2, 3])
183
+ r = Rotation.from_euler("zyx", xp.asarray([90, 0, 0]), degrees=True)
184
+ tf = RigidTransform.from_components(t, r)
185
+
186
+ expected = xp.zeros((4, 4))
187
+ expected = xpx.at(expected)[..., :3, :3].set(r.as_matrix())
188
+ expected = xpx.at(expected)[..., :3, 3].set(t)
189
+ expected = xpx.at(expected)[..., 3, 3].set(1)
190
+ xp_assert_close(tf.as_matrix(), expected, atol=atol)
191
+ assert tf.single
192
+
193
+ # Test single rotation and multiple translations
194
+ t = xp.asarray([[1, 2, 3], [4, 5, 6]])
195
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
196
+ tf = RigidTransform.from_components(t, r)
197
+ assert not tf.single
198
+
199
+ for i in range(t.shape[0]):
200
+ expected = xp.zeros((4, 4))
201
+ expected = xpx.at(expected)[..., :3, :3].set(r.as_matrix())
202
+ expected = xpx.at(expected)[..., :3, 3].set(t[i, ...])
203
+ expected = xpx.at(expected)[..., 3, 3].set(1)
204
+ xp_assert_close(tf.as_matrix()[i, ...], expected, atol=atol)
205
+
206
+ # Test multiple rotations and translations
207
+ t = xp.asarray([[1, 2, 3], [4, 5, 6]])
208
+ r = Rotation.from_euler('zyx', xp.asarray([[90, 0, 0], [0, 90, 0]]), degrees=True)
209
+ tf = RigidTransform.from_components(t, r)
210
+ assert not tf.single
211
+
212
+ for i in range(t.shape[0]):
213
+ expected = xp.zeros((4, 4))
214
+ expected = xpx.at(expected)[..., :3, :3].set(r.as_matrix()[i, ...])
215
+ expected = xpx.at(expected)[..., :3, 3].set(t[i, ...])
216
+ expected = xpx.at(expected)[..., 3, 3].set(1)
217
+ xp_assert_close(tf.as_matrix()[i, ...], expected, atol=atol)
218
+
219
+
220
+ def test_from_components_array_like():
221
+ rng = np.random.default_rng(123)
222
+ # Test single rotation and translation
223
+ t = [1, 2, 3]
224
+ r = Rotation.random(rng=rng)
225
+ tf = RigidTransform.from_components(t, r)
226
+ tf_expected = RigidTransform.from_components(np.array(t), r)
227
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix(), atol=1e-12)
228
+ assert tf.single
229
+
230
+ # Test multiple rotations and translations
231
+ t = [[1, 2, 3], [4, 5, 6]]
232
+ r = Rotation.random(len(t), rng=rng)
233
+ tf = RigidTransform.from_components(t, r)
234
+ tf_expected = RigidTransform.from_components(np.array(t), r)
235
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix(), atol=1e-12)
236
+ assert not tf.single
237
+
238
+
239
+
240
+ def test_as_components(xp):
241
+ atol = 1e-12
242
+ n = 10
243
+ rng = np.random.default_rng(123)
244
+ t = xp.asarray(rng.normal(size=(n, 3)))
245
+ r = rotation_to_xp(Rotation.random(n, rng=rng), xp=xp)
246
+ tf = RigidTransform.from_components(t, r)
247
+ new_t, new_r = tf.as_components()
248
+ assert all(new_r.approx_equal(r, atol=atol))
249
+ xp_assert_close(new_t, t, atol=atol)
250
+
251
+
252
+ def test_from_exp_coords(xp):
253
+ # example from 3.3 of
254
+ # https://hades.mech.northwestern.edu/images/2/25/MR-v2.pdf
255
+ angle1 = np.deg2rad(30.0)
256
+ mat = xp.asarray([
257
+ [np.cos(angle1), -np.sin(angle1), 0.0, 1.0],
258
+ [np.sin(angle1), np.cos(angle1), 0.0, 2.0],
259
+ [0.0, 0.0, 1.0, 0.0],
260
+ [0.0, 0.0, 0.0, 1.0]
261
+ ])
262
+ tf1 = RigidTransform.from_matrix(mat)
263
+ angle2 = np.deg2rad(60.0)
264
+ mat = xp.asarray([
265
+ [np.cos(angle2), -np.sin(angle2), 0.0, 2.0],
266
+ [np.sin(angle2), np.cos(angle2), 0.0, 1.0],
267
+ [0.0, 0.0, 1.0, 0.0],
268
+ [0.0, 0.0, 0.0, 1.0]
269
+ ])
270
+ tf2 = RigidTransform.from_matrix(mat)
271
+ expected = tf2 * tf1.inv()
272
+ actual = RigidTransform.from_exp_coords(
273
+ np.deg2rad(30.0) * xp.asarray([0.0, 0.0, 1.0, 3.37, -3.37, 0.0]))
274
+ xp_assert_close(actual.as_matrix(), expected.as_matrix(), atol=1e-2)
275
+
276
+ # test cases generated by comparison to pytransform3d
277
+ exp_coords = xp.asarray([
278
+ [-2.01041204, -0.52983629, 0.65773501,
279
+ 0.10386614, 0.05855009, 0.54959179],
280
+ [-0.22537438, -0.24132627, -2.4747121,
281
+ -0.09158594, 1.88075832, -0.03197204]
282
+ ])
283
+ expected_matrix = xp.asarray([
284
+ [[0.76406621, 0.10504613, -0.63652819, -0.10209961],
285
+ [0.59956454, -0.47987325, 0.64050295, 0.40158789],
286
+ [-0.2381705, -0.87102639, -0.42963687, 0.19637636],
287
+ [0., 0., 0., 1.]],
288
+ [[-0.78446989, 0.61157488, 0.10287448, 1.33330055],
289
+ [-0.58017785, -0.78232107, 0.22664378, 0.52660831],
290
+ [0.21909052, 0.11810973, 0.96852952, -0.02968529],
291
+ [0., 0., 0., 1.]]
292
+ ])
293
+ xp_assert_close(
294
+ RigidTransform.from_exp_coords(exp_coords).as_matrix(),
295
+ expected_matrix, atol=1e-8)
296
+
297
+ # identity
298
+ xp_assert_close(
299
+ RigidTransform.from_exp_coords(xp.zeros(6)).as_matrix(),
300
+ xp.eye(4), atol=1e-12)
301
+
302
+ # only translation
303
+ expected_matrix = xp.asarray([
304
+ [[1.0, 0.0, 0.0, 3.0],
305
+ [0.0, 1.0, 0.0, -5.4],
306
+ [0.0, 0.0, 1.0, 100.2],
307
+ [0.0, 0.0, 0.0, 1.0]],
308
+ [[1.0, 0.0, 0.0, -3.0],
309
+ [0.0, 1.0, 0.0, 13.3],
310
+ [0.0, 0.0, 1.0, 1.3],
311
+ [0.0, 0.0, 0.0, 1.0]]
312
+ ])
313
+ actual = RigidTransform.from_exp_coords(xp.asarray([
314
+ [0.0, 0.0, 0.0, 3.0, -5.4, 100.2],
315
+ [0.0, 0.0, 0.0, -3.0, 13.3, 1.3],
316
+ ]))
317
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12)
318
+
319
+ # only rotation
320
+ rot = Rotation.from_euler(
321
+ 'zyx',
322
+ xp.asarray([[34, -12, 0.5],
323
+ [-102, -55, 30]]),
324
+ degrees=True)
325
+ rotvec = rot.as_rotvec()
326
+ expected_matrix = xp.repeat(xp.eye(4)[None, ...], 2, axis=0)
327
+ expected_matrix = xpx.at(expected_matrix)[..., :3, :3].set(rot.as_matrix())
328
+ actual = RigidTransform.from_exp_coords(
329
+ xp.concat((rotvec, xp.zeros((2, 3))), axis=-1))
330
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12)
331
+
332
+
333
+ def test_from_exp_coords_array_like():
334
+ rng = np.random.default_rng(123)
335
+ # Test single transform
336
+ t = np.array([1, 2, 3])
337
+ r = Rotation.random(rng=rng)
338
+ tf_expected = RigidTransform.from_components(t, r)
339
+ exp_coords = tf_expected.as_exp_coords().tolist()
340
+ assert isinstance(exp_coords, list)
341
+ tf = RigidTransform.from_exp_coords(exp_coords)
342
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix(), atol=1e-12)
343
+
344
+ # Test multiple transforms
345
+ t = [[1, 2, 3], [4, 5, 6]]
346
+ r = Rotation.random(len(t), rng=rng)
347
+ tf_expected = RigidTransform.from_components(t, r)
348
+ exp_coords = tf_expected.as_exp_coords().tolist()
349
+ assert isinstance(exp_coords, list)
350
+ tf = RigidTransform.from_exp_coords(exp_coords)
351
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix(), atol=1e-12)
352
+
353
+
354
+ def test_as_exp_coords(xp):
355
+ # identity
356
+ expected = xp.zeros(6)
357
+ actual = RigidTransform.from_exp_coords(expected).as_exp_coords()
358
+ xp_assert_close(actual, expected, atol=1e-12)
359
+
360
+ rng = np.random.default_rng(10)
361
+
362
+ # pure rotation
363
+ rot_vec = xp.asarray(rng.normal(scale=0.1, size=(1000, 3)))
364
+ tf = RigidTransform.from_rotation(Rotation.from_rotvec(rot_vec))
365
+ exp_coords = tf.as_exp_coords()
366
+ xp_assert_close(exp_coords[:, :3], rot_vec, rtol=1e-13)
367
+ expected = xp.zeros_like(rot_vec)
368
+ xp_assert_close(exp_coords[:, 3:], expected, atol=1e-16)
369
+
370
+ # pure translation
371
+ translation = xp.asarray(rng.normal(scale=100.0, size=(1000, 3)))
372
+ tf = RigidTransform.from_translation(translation)
373
+ exp_coords = tf.as_exp_coords()
374
+ xp_assert_close(exp_coords[:, :3], expected, atol=1e-16)
375
+ xp_assert_close(exp_coords[:, 3:], translation, rtol=1e-15)
376
+
377
+
378
+ def test_from_dual_quat(xp):
379
+ # identity
380
+ xp_assert_close(
381
+ RigidTransform.from_dual_quat(
382
+ xp.asarray([0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0])).as_matrix(),
383
+ xp.eye(4), atol=1e-12)
384
+ xp_assert_close(
385
+ RigidTransform.from_dual_quat(
386
+ xp.asarray([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
387
+ scalar_first=True).as_matrix(),
388
+ xp.eye(4), atol=1e-12)
389
+
390
+ # only translation
391
+ actual = RigidTransform.from_dual_quat(
392
+ xp.asarray([0, 0, 0, 1, 0.25, 0.15, -0.7, 0]))
393
+ expected_matrix = xp.asarray([
394
+ [1, 0, 0, 0.5],
395
+ [0, 1, 0, 0.3],
396
+ [0, 0, 1, -1.4],
397
+ [0, 0, 0, 1]
398
+ ])
399
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12)
400
+ actual = RigidTransform.from_dual_quat(
401
+ xp.asarray([1, 0, 0, 0, 0, 0.25, 0.15, -0.7]), scalar_first=True)
402
+ expected_matrix = xp.asarray([
403
+ [1, 0, 0, 0.5],
404
+ [0, 1, 0, 0.3],
405
+ [0, 0, 1, -1.4],
406
+ [0, 0, 0, 1]
407
+ ])
408
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12)
409
+
410
+ # only rotation
411
+ actual_rot = Rotation.from_euler("xyz", xp.asarray([65, -13, 90]), degrees=True)
412
+ actual = RigidTransform.from_dual_quat(
413
+ xp.concat((actual_rot.as_quat(), xp.zeros(4)), axis=-1))
414
+ expected_matrix = xp.eye(4)
415
+ expected_matrix = xpx.at(expected_matrix)[..., :3, :3].set(actual_rot.as_matrix())
416
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12)
417
+
418
+ actual = RigidTransform.from_dual_quat(
419
+ xp.concat((actual_rot.as_quat(scalar_first=True), xp.zeros(4)), axis=-1),
420
+ scalar_first=True)
421
+ expected_matrix = xp.eye(4)
422
+ expected_matrix = xpx.at(expected_matrix)[..., :3, :3].set(actual_rot.as_matrix())
423
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12)
424
+
425
+ # rotation and translation
426
+ # rtol is set to 1e-7 because xp_assert_close deviates from
427
+ # np.testing.assert_allclose in that it does not automatically default to 1e-7 for
428
+ # floating point inputs.
429
+ # See https://numpy.org/doc/2.2/reference/generated/numpy.testing.assert_allclose.html
430
+ actual = RigidTransform.from_dual_quat(
431
+ xp.asarray(
432
+ [[0.0617101, -0.06483886, 0.31432811, 0.94508498,
433
+ 0.04985168, -0.26119618, 0.1691491, -0.07743254],
434
+ [0.19507259, 0.49404931, -0.06091285, 0.8450749,
435
+ 0.65049656, -0.30782513, 0.16566752, 0.04174544]]))
436
+ expected_matrix = xp.asarray(
437
+ [[[0.79398752, -0.60213598, -0.08376202, 0.24605262],
438
+ [0.58613113, 0.79477941, -0.15740392, -0.4932833],
439
+ [0.16135089, 0.07588122, 0.98397557, 0.34262676],
440
+ [0., 0., 0., 1.]],
441
+ [[0.50440981, 0.2957028, 0.81125249, 1.20934468],
442
+ [0.08979911, 0.91647262, -0.3898898, -0.70540077],
443
+ [-0.8587822, 0.26951399, 0.43572393, -0.47776265],
444
+ [0., 0., 0., 1.]]])
445
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12, rtol=1e-7)
446
+
447
+ actual = RigidTransform.from_dual_quat(
448
+ xp.asarray(
449
+ [[0.94508498, 0.0617101, -0.06483886, 0.31432811,
450
+ -0.07743254, 0.04985168, -0.26119618, 0.1691491],
451
+ [0.8450749, 0.19507259, 0.49404931, -0.06091285,
452
+ 0.04174544, 0.65049656, -0.30782513, 0.16566752]]),
453
+ scalar_first=True)
454
+ xp_assert_close(actual.as_matrix(), expected_matrix, atol=1e-12, rtol=1e-7)
455
+
456
+ # unnormalized dual quaternions
457
+
458
+ # invalid real quaternion with norm 0
459
+ actual = RigidTransform.from_dual_quat(xp.zeros(8))
460
+ xp_assert_close(actual.as_matrix(), xp.eye(4), atol=1e-12)
461
+
462
+ # real quaternion with norm != 1
463
+ unnormalized_dual_quat = xp.asarray(
464
+ [-0.2547655, 1.23506123, 0.20230088, 0.24247194, # norm 1.3
465
+ 0.38559628, 0.08184063, 0.1755943, -0.1582222] # orthogonal
466
+ )
467
+ xp_assert_close(xp_vector_norm(unnormalized_dual_quat[:4]), xp.asarray(1.3)[()],
468
+ atol=1e-12)
469
+ xp_assert_close(xp.vecdot(unnormalized_dual_quat[:4],
470
+ unnormalized_dual_quat[4:])[()],
471
+ xp.asarray(0.0)[()], atol=1e-8)
472
+
473
+ dual_quat = RigidTransform.from_dual_quat(
474
+ unnormalized_dual_quat).as_dual_quat()
475
+ xp_assert_close(xp_vector_norm(dual_quat[:4]), xp.asarray(1.0)[()], atol=1e-12)
476
+ xp_assert_close(xp.vecdot(dual_quat[:4], dual_quat[4:])[()], xp.asarray(0.0)[()],
477
+ atol=1e-12)
478
+
479
+ # real and dual quaternion are not orthogonal
480
+ unnormalized_dual_quat = xp.asarray(
481
+ [0.20824458, 0.75098079, 0.54542913, -0.30849493, # unit norm
482
+ -0.16051025, 0.10742978, 0.21277201, 0.20596935] # not orthogonal
483
+ )
484
+ xp_assert_close(xp_vector_norm(unnormalized_dual_quat[:4]), xp.asarray(1.0)[()],
485
+ atol=1e-12)
486
+ assert xp.vecdot(unnormalized_dual_quat[:4], unnormalized_dual_quat[4:]) != 0.0
487
+ dual_quat = RigidTransform.from_dual_quat(
488
+ unnormalized_dual_quat).as_dual_quat()
489
+ xp_assert_close(xp_vector_norm(dual_quat[:4]), xp.asarray(1.0)[()], atol=1e-12)
490
+ xp_assert_close(xp.vecdot(dual_quat[:4], dual_quat[4:])[()], xp.asarray(0.0)[()],
491
+ atol=1e-12)
492
+
493
+ # invalid real quaternion with norm 0, non-orthogonal dual quaternion
494
+ unnormalized_dual_quat = xp.asarray(
495
+ [0.0, 0.0, 0.0, 0.0, -0.16051025, 0.10742978, 0.21277201, 0.20596935])
496
+ assert xp.vecdot(xp.asarray([0.0, 0, 0, 1]), unnormalized_dual_quat[4:]) != 0.0
497
+ dual_quat = RigidTransform.from_dual_quat(
498
+ unnormalized_dual_quat).as_dual_quat()
499
+ xp_assert_close(dual_quat[:4], xp.asarray([0.0, 0, 0, 1]), atol=1e-12)
500
+ xp_assert_close(xp.vecdot(dual_quat[:4], dual_quat[4:])[()], xp.asarray(0.0)[()],
501
+ atol=1e-12)
502
+
503
+ # compensation for precision loss in real quaternion
504
+ rng = np.random.default_rng(1000)
505
+ t = xp.asarray(rng.normal(size=(3,)))
506
+ r = rotation_to_xp(Rotation.random(10, rng=rng), xp=xp)
507
+ random_dual_quats = RigidTransform.from_components(t, r).as_dual_quat()
508
+
509
+ # ensure that random quaternions are not normalized
510
+ random_dual_quats = xpx.at(random_dual_quats)[:, :4].add(0.01)
511
+ assert not xp.any(xpx.isclose(xp_vector_norm(random_dual_quats[:, :4], axis=1),
512
+ 1.0, atol=0.0001))
513
+ dual_quat_norm = RigidTransform.from_dual_quat(
514
+ random_dual_quats).as_dual_quat()
515
+ expected = xp.ones(dual_quat_norm.shape[0])
516
+ xp_assert_close(xp_vector_norm(dual_quat_norm[:, :4], axis=1), expected, atol=1e-12)
517
+
518
+ # compensation for precision loss in dual quaternion, results in violation
519
+ # of orthogonality constraint
520
+ t = xp.asarray(rng.normal(size=(10, 3)))
521
+ r = rotation_to_xp(Rotation.random(10, rng=rng), xp=xp)
522
+ random_dual_quats = RigidTransform.from_components(t, r).as_dual_quat()
523
+
524
+ # ensure that random quaternions are not normalized
525
+ random_dual_quats = xpx.at(random_dual_quats)[:, 4:].add(0.01)
526
+ q_norm = xp.vecdot(random_dual_quats[:, :4], random_dual_quats[:, 4:])
527
+ assert not xp.any(xpx.isclose(q_norm, 0.0, atol=0.0001))
528
+ dual_quat_norm = RigidTransform.from_dual_quat(
529
+ random_dual_quats).as_dual_quat()
530
+ expected = xp.zeros(dual_quat_norm.shape[0])
531
+ xp_assert_close(xp.vecdot(dual_quat_norm[:, :4], dual_quat_norm[:, 4:]), expected,
532
+ atol=1e-12)
533
+ xp_assert_close(random_dual_quats[:, :4], dual_quat_norm[:, :4], atol=1e-12)
534
+
535
+
536
+ def test_from_dual_quat_array_like():
537
+ rng = np.random.default_rng(123)
538
+ # Test single transform
539
+ t = np.array([1, 2, 3])
540
+ r = Rotation.random(rng=rng)
541
+ tf_expected = RigidTransform.from_components(t, r)
542
+ dual_quat = tf_expected.as_dual_quat().tolist()
543
+ assert isinstance(dual_quat, list)
544
+ tf = RigidTransform.from_dual_quat(dual_quat)
545
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix(), atol=1e-12)
546
+
547
+ # Test multiple transforms
548
+ t = [[1, 2, 3], [4, 5, 6]]
549
+ r = Rotation.random(len(t), rng=rng)
550
+ tf_expected = RigidTransform.from_components(t, r)
551
+ dual_quat = tf_expected.as_dual_quat().tolist()
552
+ assert isinstance(dual_quat, list)
553
+ tf = RigidTransform.from_dual_quat(dual_quat)
554
+ xp_assert_close(tf.as_matrix(), tf_expected.as_matrix(), atol=1e-12)
555
+
556
+
557
+ def test_as_dual_quat(xp):
558
+ # identity
559
+ expected = xp.asarray([0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0])
560
+ actual = xp.asarray(RigidTransform.identity().as_dual_quat())
561
+ xp_assert_close(actual, expected, atol=1e-12)
562
+
563
+ expected = xp.asarray([1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
564
+ actual = xp.asarray(RigidTransform.identity().as_dual_quat(scalar_first=True))
565
+ xp_assert_close(actual, expected, atol=1e-12)
566
+
567
+ rng = np.random.default_rng(10)
568
+
569
+ # only rotation
570
+ for _ in range(10):
571
+ real_part = xp.asarray(Rotation.random(rng=rng).as_quat())
572
+ dual_part = xp.zeros(4)
573
+ expected = xp.concat((real_part, dual_part), axis=-1)
574
+ actual = RigidTransform.from_dual_quat(expected).as_dual_quat()
575
+ # because of double cover:
576
+ if xp.sign(expected[0]) != xp.sign(actual[0]):
577
+ actual = -actual
578
+ xp_assert_close(actual, expected, atol=1e-12)
579
+
580
+ # only translation
581
+ for _ in range(10):
582
+ tf = 0.5 * rng.normal(size=3)
583
+ expected = xp.asarray([0.0, 0, 0, 1, *tf.tolist(), 0])
584
+ actual = RigidTransform.from_dual_quat(expected).as_dual_quat()
585
+ # because of double cover:
586
+ if xp.sign(expected[0]) != xp.sign(actual[0]):
587
+ actual = -actual
588
+ xp_assert_close(actual, expected, atol=1e-12)
589
+
590
+ # rotation and translation
591
+ for _ in range(10):
592
+ t = xp.asarray(rng.normal(size=3))
593
+ r = rotation_to_xp(Rotation.random(rng=rng), xp=xp)
594
+ expected = RigidTransform.from_components(t, r).as_dual_quat()
595
+ actual = RigidTransform.from_dual_quat(expected).as_dual_quat()
596
+ # because of double cover:
597
+ if xp.sign(expected[0]) != xp.sign(actual[0]):
598
+ actual = -actual
599
+ xp_assert_close(actual, expected, atol=1e-12)
600
+
601
+
602
+ def test_from_as_internal_consistency(xp):
603
+ atol = 1e-12
604
+ n = 1000
605
+ rng = np.random.default_rng(10)
606
+ t = xp.asarray(rng.normal(size=(n, 3)))
607
+ r = rotation_to_xp(Rotation.random(n, rng=rng), xp=xp)
608
+ tf0 = RigidTransform.from_components(t, r)
609
+
610
+ tf1 = RigidTransform.from_components(*tf0.as_components())
611
+ xp_assert_close(tf0.as_matrix(), tf1.as_matrix(), atol=atol)
612
+
613
+ tf1 = RigidTransform.from_components(tf0.translation, tf0.rotation)
614
+ xp_assert_close(tf0.as_matrix(), tf1.as_matrix(), atol=atol)
615
+
616
+ tf1 = RigidTransform.from_exp_coords(tf0.as_exp_coords())
617
+ xp_assert_close(tf0.as_matrix(), tf1.as_matrix(), atol=atol)
618
+
619
+ tf1 = RigidTransform.from_matrix(tf0.as_matrix())
620
+ xp_assert_close(tf0.as_matrix(), tf1.as_matrix(), atol=atol)
621
+
622
+ tf1 = RigidTransform.from_dual_quat(tf0.as_dual_quat())
623
+ xp_assert_close(tf0.as_matrix(), tf1.as_matrix(), atol=atol)
624
+
625
+ # exp_coords small rotation
626
+ tf0 = RigidTransform.from_components(
627
+ xp.asarray(rng.normal(scale=1000.0, size=(1000, 3))),
628
+ Rotation.from_rotvec(xp.asarray(rng.normal(scale=1e-10, size=(1000, 3)))))
629
+ tf1 = RigidTransform.from_exp_coords(tf0.as_exp_coords())
630
+ xp_assert_close(tf0.as_matrix(), tf1.as_matrix(), atol=atol)
631
+
632
+
633
+ def test_identity():
634
+ # We do not use xp here because identity always returns numpy arrays
635
+ atol = 1e-12
636
+
637
+ # Test single identity
638
+ tf = RigidTransform.identity()
639
+ xp_assert_close(tf.as_matrix(), np.eye(4), atol=atol)
640
+
641
+ # Test multiple identities
642
+ tf = RigidTransform.identity(5)
643
+ xp_assert_close(tf.as_matrix(), np.array([np.eye(4)] * 5), atol=atol)
644
+
645
+
646
+ def test_apply(xp):
647
+ atol = 1e-12
648
+
649
+ ## Single transform
650
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
651
+ t = xp.asarray([2, 3, 4])
652
+ tf = RigidTransform.from_components(t, r)
653
+
654
+ # Single vector, single transform
655
+ vec = xp.asarray([1, 0, 0])
656
+ expected = t + r.apply(vec)
657
+ res = tf.apply(vec)
658
+ xp_assert_close(res, expected, atol=atol)
659
+
660
+ # Multiple vectors, single transform
661
+ vecs = xp.asarray([[1, 0, 0], [0, 1, 0]])
662
+ expected = t + r.apply(vecs)
663
+ xp_assert_close(tf.apply(vecs), expected, atol=atol)
664
+
665
+ ## Multiple transforms
666
+ r = Rotation.from_euler('z', xp.asarray([90, 0]), degrees=True)
667
+ t = xp.asarray([[2, 3, 4], [5, 6, 7]])
668
+ tf = RigidTransform.from_components(t, r)
669
+
670
+ # Single vector, multiple transforms
671
+ vec = xp.asarray([1, 0, 0])
672
+ expected = t + r.apply(vec)
673
+ xp_assert_close(tf.apply(vec), expected, atol=atol)
674
+
675
+ # Multiple vectors, multiple transforms
676
+ vecs = xp.asarray([[1, 0, 0], [0, 1, 0]])
677
+ expected = t + r.apply(vecs)
678
+ xp_assert_close(tf.apply(vecs), expected, atol=atol)
679
+
680
+
681
+ def test_apply_array_like():
682
+ rng = np.random.default_rng(123)
683
+ # Single vector
684
+ t = np.array([1, 2, 3])
685
+ r = Rotation.random(rng=rng)
686
+ tf = RigidTransform.from_components(t, r)
687
+ vec = [1, 0, 0]
688
+ expected = t + r.apply(vec)
689
+ xp_assert_close(tf.apply(vec), expected, atol=1e-12)
690
+
691
+ # Multiple vectors
692
+ t = np.array([[1, 2, 3], [4, 5, 6]])
693
+ r = Rotation.random(len(t), rng=rng)
694
+ tf = RigidTransform.from_components(t, r)
695
+ vec = [[1, 0, 0], [0, 1, 0]]
696
+ expected = t + r.apply(vec)
697
+ xp_assert_close(tf.apply(vec), expected, atol=1e-12)
698
+
699
+
700
+ def test_inverse_apply(xp):
701
+ atol = 1e-12
702
+
703
+ # Test applying inverse transform
704
+ t = xp.asarray([1, 2, 3])
705
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
706
+ tf = RigidTransform.from_components(t, r)
707
+
708
+ # Test single vector
709
+ vec = xp.asarray([1, 0, 0])
710
+ expected = tf.inv().apply(vec)
711
+ xp_assert_close(tf.apply(vec, inverse=True), expected, atol=atol)
712
+
713
+ # Test multiple vectors
714
+ vecs = xp.asarray([[1, 0, 0], [0, 1, 0]])
715
+ expected = tf.inv().apply(vecs)
716
+ xp_assert_close(tf.apply(vecs, inverse=True), expected, atol=atol)
717
+
718
+
719
+ def test_rotation_alone(xp):
720
+ atol = 1e-12
721
+
722
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
723
+ tf = RigidTransform.from_rotation(r)
724
+ vec = xp.asarray([1, 0, 0])
725
+ expected = r.apply(vec)
726
+ xp_assert_close(tf.apply(vec), expected, atol=atol)
727
+
728
+
729
+ def test_translation_alone(xp):
730
+ atol = 1e-12
731
+ t = xp.asarray([1.0, 2, 3])
732
+ tf = RigidTransform.from_translation(t)
733
+ vec = xp.asarray([5.0, 6, 7])
734
+ expected = t + vec
735
+ xp_assert_close(tf.apply(vec), expected, atol=atol)
736
+
737
+
738
+ def test_composition(xp):
739
+ atol = 1e-12
740
+
741
+ # Test composing single transforms
742
+ t1 = xp.asarray([1.0, 0, 0])
743
+ r1 = Rotation.from_euler('z', xp.asarray(90), degrees=True)
744
+ tf1 = RigidTransform.from_components(t1, r1)
745
+
746
+ t2 = xp.asarray([0, 1, 0])
747
+ r2 = Rotation.from_euler('x', xp.asarray(90), degrees=True)
748
+ tf2 = RigidTransform.from_components(t2, r2)
749
+
750
+ composed = tf2 * tf1
751
+ vec = xp.asarray([1, 0, 0])
752
+ expected = tf2.apply(tf1.apply(vec))
753
+ xp_assert_close(composed.apply(vec), expected, atol=atol)
754
+ assert composed.single
755
+
756
+ expected = t2 + r2.apply(t1 + r1.apply(vec))
757
+ xp_assert_close(composed.apply(vec), expected, atol=atol)
758
+
759
+ # Multiple transforms with single transform
760
+ t2 = np.array([[1, 2, 3], [4, 5, 6]])
761
+ tf2 = RigidTransform.from_components(t2, r2)
762
+
763
+ composed = tf2 * tf1
764
+ expected = tf2.apply(tf1.apply(vec))
765
+ xp_assert_close(composed.apply(vec), expected, atol=atol)
766
+ assert not composed.single
767
+
768
+ expected = t2 + r2.apply(t1 + r1.apply(vec))
769
+ xp_assert_close(composed.apply(vec), expected, atol=atol)
770
+
771
+ # Multiple transforms with multiple transforms
772
+ t1 = xp.asarray([[1, 0, 0], [0, -1, 1]])
773
+ tf1 = RigidTransform.from_components(t1, r1)
774
+
775
+ composed = tf2 * tf1
776
+ expected = tf2.apply(tf1.apply(vec))
777
+ xp_assert_close(composed.apply(vec), expected, atol=atol)
778
+ assert not composed.single
779
+
780
+ expected = t2 + r2.apply(t1 + r1.apply(vec))
781
+ xp_assert_close(composed.apply(vec), expected, atol=atol)
782
+
783
+
784
+ def test_pow(xp):
785
+ atol = 1e-12
786
+ num = 10
787
+ rng = np.random.default_rng(100)
788
+ t = xp.asarray(rng.normal(size=(num, 3)))
789
+ r = rotation_to_xp(Rotation.random(num, rng=rng), xp=xp)
790
+ p = RigidTransform.from_components(t, r)
791
+ p_inv = p.inv()
792
+
793
+ # Test the short-cuts and other integers
794
+ for n in [-5, -2, -1, 0, 1, 2, 5]:
795
+ q = p**n
796
+ r = rigid_transform_to_xp(RigidTransform.identity(num), xp=xp)
797
+ for _ in range(abs(n)):
798
+ if n > 0:
799
+ r = r * p
800
+ else:
801
+ r = r * p_inv
802
+ xp_assert_close(q.as_matrix(), r.as_matrix(), atol=atol)
803
+
804
+ # Test shape preservation
805
+ r = RigidTransform.from_rotation(Rotation.from_quat(xp.asarray([0, 0, 0, 1])))
806
+ assert (r**n).as_matrix().shape == (4, 4)
807
+ r = RigidTransform.from_rotation(Rotation.from_quat(xp.asarray([[0, 0, 0, 1]])))
808
+ assert (r**n).as_matrix().shape == (1, 4, 4)
809
+
810
+ # Test fractional powers
811
+ q = p**0.5
812
+ xp_assert_close((q * q).as_matrix(), p.as_matrix(), atol=atol)
813
+ q = p**-0.5
814
+ xp_assert_close((q * q).as_matrix(), p.inv().as_matrix(), atol=atol)
815
+ q = p** 1.5
816
+ xp_assert_close((q * q).as_matrix(), (p**3).as_matrix(), atol=atol)
817
+ q = p** -1.5
818
+ xp_assert_close((q * q).as_matrix(), (p**-3).as_matrix(), atol=atol)
819
+
820
+ # pow function
821
+ tf = pow(RigidTransform.from_matrix(xp.eye(4)), 2)
822
+ xp_assert_close(tf.as_matrix(), xp.eye(4), atol=atol)
823
+
824
+
825
+ def test_pow_equivalence_with_rotation(xp):
826
+ atol = 1e-12
827
+ num = 10
828
+ rng = np.random.default_rng(100)
829
+ r = rotation_to_xp(Rotation.random(num, rng=rng), xp=xp)
830
+ p = RigidTransform.from_rotation(r)
831
+ for n in [-5, -2, -1.5, -1, -0.5, 0.0, 0.5, 1, 1.5, 2, 5]:
832
+ xp_assert_close((p**n).rotation.as_matrix(), (r**n).as_matrix(), atol=atol)
833
+
834
+
835
+ def test_inverse(xp):
836
+ atol = 1e-12
837
+
838
+ # Test inverse transform
839
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
840
+ t = xp.asarray([1, 2, 3])
841
+ tf = RigidTransform.from_components(t, r)
842
+
843
+ # Test that tf * tf.inv() equals identity
844
+ tf_inv = tf.inv()
845
+ composed = tf * tf_inv
846
+ xp_assert_close(composed.as_matrix(), xp.eye(4), atol=atol)
847
+
848
+ n = 10
849
+ rng = np.random.default_rng(1000)
850
+ t = xp.asarray(rng.normal(size=(n, 3)))
851
+ r = rotation_to_xp(Rotation.random(n, rng=rng), xp=xp)
852
+ tf = RigidTransform.from_components(t, r)
853
+ tf_inv = tf.inv()
854
+ composed = tf * tf_inv
855
+ expected = xp.repeat(xp.eye(4)[None, ...], n, axis=0)
856
+ xp_assert_close(composed.as_matrix(), expected, atol=atol)
857
+
858
+ # Test multiple transforms
859
+ r = Rotation.from_euler('zyx', xp.asarray([[90, 0, 0], [0, 90, 0]]), degrees=True)
860
+ t = xp.asarray([[1, 2, 3], [4, 5, 6]])
861
+ tf = RigidTransform.from_components(t, r)
862
+ tf_inv = tf.inv()
863
+ composed = tf * tf_inv
864
+ expected = xp.repeat(xp.eye(4)[None, ...], 2, axis=0)
865
+ xp_assert_close(composed.as_matrix(), expected, atol=atol)
866
+
867
+
868
+ def test_properties(xp):
869
+ atol = 1e-12
870
+
871
+ # Test rotation and translation properties for single transform
872
+ r = Rotation.from_euler('z', xp.asarray(90), degrees=True)
873
+ t = xp.asarray([1.0, 2, 3])
874
+ tf = RigidTransform.from_components(t, r)
875
+
876
+ xp_assert_close(tf.rotation.as_matrix(), r.as_matrix(), atol=atol)
877
+ assert tf.rotation.approx_equal(r)
878
+ xp_assert_close(tf.translation, t, atol=atol)
879
+
880
+ # Test rotation and translation properties for multiple transforms
881
+ r = Rotation.from_euler('zyx', xp.asarray([[90, 0, 0], [0, 90, 0]]), degrees=True)
882
+ t = xp.asarray([[1.0, 2, 3], [4, 5, 6]])
883
+ tf = RigidTransform.from_components(t, r)
884
+
885
+ xp_assert_close(tf.rotation.as_matrix(), r.as_matrix(), atol=atol)
886
+ assert all(tf.rotation.approx_equal(r))
887
+ xp_assert_close(tf.translation, t, atol=atol)
888
+
889
+
890
+ def test_indexing(xp):
891
+ atol = 1e-12
892
+
893
+ # Test indexing for multiple transforms
894
+ r = Rotation.from_euler('zyx', xp.asarray([[90, 0, 0], [0, 90, 0]]), degrees=True)
895
+ t = xp.asarray([[1.0, 2, 3], [4, 5, 6]])
896
+ tf = RigidTransform.from_components(t, r)
897
+
898
+ # Test single index
899
+ xp_assert_close(tf[0].as_matrix()[:3, :3], r[0].as_matrix(), atol=atol)
900
+ xp_assert_close(tf[0].as_matrix()[:3, 3], t[0, ...], atol=atol)
901
+
902
+ # Test slice
903
+ tf_slice = tf[0:2]
904
+ xp_assert_close(tf_slice.as_matrix()[:, :3, :3], r[0:2].as_matrix(), atol=atol)
905
+ xp_assert_close(tf_slice.as_matrix()[:, :3, 3], t[0:2, ...], atol=atol)
906
+
907
+ # Test boolean indexing
908
+ tf_masked = tf[xp.asarray([True, True])]
909
+ xp_assert_close(tf_masked.as_matrix()[:, :3, :3], r.as_matrix(), atol=atol)
910
+ xp_assert_close(tf_masked.as_matrix()[:, :3, 3], t, atol=atol)
911
+
912
+ tf_masked = tf[xp.asarray([False, True])]
913
+ xp_assert_close(tf_masked.as_matrix()[:, :3, :3],
914
+ r[xp.asarray([False, True])].as_matrix(), atol=atol)
915
+ xp_assert_close(tf_masked.as_matrix()[:, :3, 3], t[xp.asarray([False, True])],
916
+ atol=atol)
917
+
918
+ tf_masked = tf[xp.asarray([False, False])]
919
+ assert len(tf_masked) == 0
920
+
921
+
922
+ def test_indexing_array_like():
923
+ atol = 1e-12
924
+
925
+ r = Rotation.from_euler('zyx', np.array([[90, 0, 0], [0, 90, 0]]), degrees=True)
926
+ t = np.array([[1.0, 2, 3], [4, 5, 6]])
927
+ tf = RigidTransform.from_components(t, r)
928
+
929
+ tf_masked = tf[[False, True]]
930
+ xp_assert_close(tf_masked.as_matrix()[:, :3, :3], r[[False, True]].as_matrix(),
931
+ atol=atol)
932
+ xp_assert_close(tf_masked.as_matrix()[:, :3, 3], t[[False, True]], atol=atol)
933
+ tf_masked = tf[[False, False]]
934
+ assert len(tf_masked) == 0
935
+
936
+
937
+ def test_concatenate(xp):
938
+ atol = 1e-12
939
+
940
+ # Test concatenation of transforms
941
+ t1 = xp.asarray([1, 0, 0])
942
+ r1 = Rotation.from_euler('z', xp.asarray(90), degrees=True)
943
+ tf1 = RigidTransform.from_components(t1, r1)
944
+
945
+ t2 = xp.asarray([0, 1, 0])
946
+ r2 = Rotation.from_euler('x', xp.asarray(90), degrees=True)
947
+ tf2 = RigidTransform.from_components(t2, r2)
948
+
949
+ # Concatenate single transforms
950
+ concatenated1 = RigidTransform.concatenate([tf1, tf2])
951
+ xp_assert_close(concatenated1[0].as_matrix(), tf1.as_matrix(), atol=atol)
952
+ xp_assert_close(concatenated1[1].as_matrix(), tf2.as_matrix(), atol=atol)
953
+
954
+ # Concatenate multiple transforms
955
+ concatenated2 = RigidTransform.concatenate([tf1, concatenated1])
956
+ xp_assert_close(concatenated2[0].as_matrix(), tf1.as_matrix(), atol=atol)
957
+ xp_assert_close(concatenated2[1].as_matrix(), tf1.as_matrix(), atol=atol)
958
+ xp_assert_close(concatenated2[2].as_matrix(), tf2.as_matrix(), atol=atol)
959
+
960
+
961
+ def test_input_validation(xp):
962
+ # Test invalid matrix shapes
963
+ inputs = [xp.eye(3), xp.zeros((4, 3)), [], xp.zeros((1, 1, 4, 4))]
964
+ for input in inputs:
965
+ with pytest.raises(ValueError, match="Expected `matrix` to have shape"):
966
+ RigidTransform.from_matrix(input)
967
+
968
+ # Test invalid last row
969
+ matrix = xp.eye(4)
970
+ matrix = xpx.at(matrix)[3, :].set(xp.asarray([1, 0, 0, 1]))
971
+ if is_lazy_array(matrix):
972
+ matrix = RigidTransform.from_matrix(matrix).as_matrix()
973
+ assert xp.all(xp.isnan(matrix))
974
+ else:
975
+ with pytest.raises(ValueError, match="last row of transformation matrix 0"):
976
+ RigidTransform.from_matrix(matrix)
977
+
978
+ # Test invalid last row for multiple transforms
979
+ matrix = xp.zeros((2, 4, 4))
980
+ matrix = xpx.at(matrix)[...].set(xp.eye(4))
981
+ matrix = xpx.at(matrix)[1, 3, :].set(xp.asarray([1, 0, 0, 1]))
982
+ if is_lazy_array(matrix):
983
+ matrix = RigidTransform.from_matrix(matrix).as_matrix()
984
+ assert not xp.any(xp.isnan(matrix[0, ...]))
985
+ assert xp.all(xp.isnan(matrix[1, ...]))
986
+ else:
987
+ with pytest.raises(ValueError, match="last row of transformation matrix 1"):
988
+ RigidTransform.from_matrix(matrix)
989
+
990
+ # Test left handed rotation matrix
991
+ matrix = xp.eye(4)
992
+ matrix = xpx.at(matrix)[0, 0].set(-1)
993
+ if is_lazy_array(matrix):
994
+ matrix = RigidTransform.from_matrix(matrix).as_matrix()
995
+ assert xp.all(xp.isnan(matrix[..., :3, :3]))
996
+ else:
997
+ with pytest.raises(ValueError, match="Non-positive determinant"):
998
+ RigidTransform(matrix, normalize=True)
999
+
1000
+ # Test non-Rotation input
1001
+ with pytest.raises(ValueError,
1002
+ match="Expected `rotation` to be a `Rotation` instance"):
1003
+ RigidTransform.from_rotation(xp.eye(3))
1004
+
1005
+
1006
+ def test_translation_validation(xp):
1007
+ # Test invalid translation shapes
1008
+ with pytest.raises(ValueError, match="Expected `translation` to have shape"):
1009
+ RigidTransform.from_translation(xp.asarray([1, 2]))
1010
+
1011
+ with pytest.raises(ValueError, match="Expected `translation` to have shape"):
1012
+ RigidTransform.from_translation(xp.zeros((2, 2)))
1013
+
1014
+ with pytest.raises(ValueError, match="Expected `translation` to have shape"):
1015
+ RigidTransform.from_translation(xp.zeros((1, 1, 3)))
1016
+
1017
+
1018
+ def test_vector_validation(xp):
1019
+ tf = rigid_transform_to_xp(RigidTransform.identity(2), xp=xp)
1020
+
1021
+ # Test invalid vector shapes
1022
+ with pytest.raises(ValueError, match="Expected vector to have shape"):
1023
+ tf.apply(xp.asarray([1, 2]))
1024
+
1025
+ with pytest.raises(ValueError, match="Expected vector to have shape"):
1026
+ tf.apply(xp.zeros((2, 2)))
1027
+
1028
+ with pytest.raises(ValueError, match="Expected vector to have shape"):
1029
+ tf.apply(xp.zeros((1, 1, 3)))
1030
+
1031
+
1032
+ def test_indexing_validation(xp):
1033
+ tf = RigidTransform.from_matrix(xp.eye(4))
1034
+
1035
+ # Test indexing on single transform
1036
+ with pytest.raises(TypeError, match="Single transform is not subscriptable"):
1037
+ tf[0]
1038
+
1039
+ with pytest.raises(TypeError, match="Single transform is not subscriptable"):
1040
+ tf[0:1]
1041
+
1042
+ # Test length on single transform
1043
+ with pytest.raises(TypeError, match="Single transform has no len"):
1044
+ len(tf)
1045
+
1046
+
1047
+ def test_composition_validation(xp):
1048
+ tf2 = RigidTransform.from_translation(xp.asarray([[1, 2, 3], [4, 5, 6]]))
1049
+ tf3 = RigidTransform.from_translation(xp.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))
1050
+
1051
+ # Test incompatible shapes
1052
+ with pytest.raises(ValueError, match="Expected equal number of transforms"):
1053
+ tf2 * tf3
1054
+
1055
+
1056
+ def test_concatenate_validation(xp):
1057
+ tf = RigidTransform.from_matrix(xp.eye(4))
1058
+
1059
+ # Test invalid inputs
1060
+ with pytest.raises(TypeError,
1061
+ match="input must contain RigidTransform objects"):
1062
+ RigidTransform.concatenate([tf, xp.eye(4)])
1063
+
1064
+
1065
+ def test_setitem_validation(xp):
1066
+ tf = RigidTransform.from_translation(xp.asarray([[1, 2, 3], [4, 5, 6]]))
1067
+ single = RigidTransform.from_matrix(xp.eye(4))
1068
+
1069
+ # Test setting item on single transform
1070
+ with pytest.raises(TypeError, match="Single transform is not subscriptable"):
1071
+ single[0] = tf
1072
+
1073
+ # Test invalid value type
1074
+ with pytest.raises(TypeError, match="value must be a RigidTransform"):
1075
+ tf[0] = xp.eye(4)
1076
+
1077
+
1078
+ @pytest.mark.skip_xp_backends("jax.numpy",
1079
+ reason="JAX does not support memory sharing")
1080
+ def test_copy_flag(xp):
1081
+ # Test that copy=True creates new memory
1082
+ matrix = xp.eye(4)
1083
+ tf = RigidTransform(matrix, normalize=False, copy=True)
1084
+ matrix[0, 0] = 2
1085
+ assert tf.as_matrix()[0, 0] == 1
1086
+
1087
+ # Test that copy=False shares memory
1088
+ matrix = xp.eye(4)
1089
+ tf = RigidTransform(matrix, normalize=False, copy=False)
1090
+ matrix[0, 0] = 2
1091
+ assert tf.as_matrix()[0, 0] == 2
1092
+
1093
+
1094
+ def test_normalize_dual_quaternion(xp):
1095
+ dual_quat = normalize_dual_quaternion(xp.zeros((1, 8)))
1096
+ xp_assert_close(xp_vector_norm(dual_quat[0, :4], axis=-1), xp.asarray(1.0)[()],
1097
+ atol=1e-12)
1098
+ xp_assert_close(xp.vecdot(dual_quat[0, :4], dual_quat[0, 4:])[()],
1099
+ xp.asarray(0.0)[()], atol=1e-12)
1100
+
1101
+ rng = np.random.default_rng(103213650)
1102
+ dual_quat = xp.asarray(rng.normal(size=(1000, 8)))
1103
+ dual_quat = normalize_dual_quaternion(dual_quat)
1104
+ expected = xp.ones(dual_quat.shape[0])
1105
+ xp_assert_close(xp_vector_norm(dual_quat[:, :4], axis=-1), expected, atol=1e-12)
1106
+ expected = xp.zeros(dual_quat.shape[0])
1107
+ xp_assert_close(xp.vecdot(dual_quat[:, :4], dual_quat[:, 4:]), expected, atol=1e-12)
1108
+
1109
+
1110
+ def test_empty_transform_construction(xp):
1111
+ tf = RigidTransform.from_matrix(xp.empty((0, 4, 4)))
1112
+ assert len(tf) == 0
1113
+ assert not tf.single
1114
+
1115
+ tf = RigidTransform.from_rotation(Rotation.from_quat(xp.zeros((0, 4))))
1116
+ assert len(tf) == 0
1117
+ assert not tf.single
1118
+
1119
+ tf = RigidTransform.from_translation(xp.empty((0, 3)))
1120
+ assert len(tf) == 0
1121
+ assert not tf.single
1122
+
1123
+ empty_rot = Rotation.from_quat(xp.zeros((0, 4)))
1124
+ tf = RigidTransform.from_components(xp.empty((0, 3)), empty_rot)
1125
+ assert len(tf) == 0
1126
+ assert not tf.single
1127
+
1128
+ tf = RigidTransform.from_exp_coords(xp.empty((0, 6)))
1129
+ assert len(tf) == 0
1130
+ assert not tf.single
1131
+
1132
+ tf = RigidTransform.from_dual_quat(xp.empty((0, 8)))
1133
+ assert len(tf) == 0
1134
+ assert not tf.single
1135
+
1136
+ tf = RigidTransform.identity(0)
1137
+ assert len(tf) == 0
1138
+ assert not tf.single
1139
+
1140
+
1141
+ def test_empty_transform_representation(xp):
1142
+ tf = RigidTransform.from_matrix(xp.empty((0, 4, 4)))
1143
+
1144
+ assert len(tf.rotation) == 0
1145
+ assert tf.translation.shape == (0, 3)
1146
+
1147
+ t, r = tf.as_components()
1148
+ assert t.shape == (0, 3)
1149
+ assert len(r) == 0
1150
+
1151
+ assert tf.as_matrix().shape == (0, 4, 4)
1152
+ assert tf.as_exp_coords().shape == (0, 6)
1153
+ assert tf.as_dual_quat().shape == (0, 8)
1154
+
1155
+
1156
+ def test_empty_transform_application(xp):
1157
+ tf = RigidTransform.from_matrix(xp.empty((0, 4, 4)))
1158
+
1159
+ assert tf.apply(xp.zeros((3,))).shape == (0, 3)
1160
+ assert tf.apply(xp.empty((0, 3))).shape == (0, 3)
1161
+
1162
+ with pytest.raises(ValueError, match="operands could not be broadcast together"):
1163
+ tf.apply(xp.zeros((2, 3)))
1164
+
1165
+
1166
+ def test_empty_transform_composition(xp):
1167
+ tf_empty = RigidTransform.from_matrix(xp.empty((0, 4, 4)))
1168
+ tf_single = RigidTransform.from_matrix(xp.eye(4))
1169
+ tf_many = rigid_transform_to_xp(RigidTransform.identity(3), xp=xp)
1170
+
1171
+ assert len(tf_empty * tf_empty) == 0
1172
+ assert len(tf_empty * tf_single) == 0
1173
+ assert len(tf_single * tf_empty) == 0
1174
+
1175
+ with pytest.raises(ValueError, match="Expected equal number of transforms"):
1176
+ tf_many * tf_empty
1177
+
1178
+ with pytest.raises(ValueError, match="Expected equal number of transforms"):
1179
+ tf_empty * tf_many
1180
+
1181
+
1182
+ def test_empty_transform_concatenation(xp):
1183
+ tf_empty = RigidTransform.from_matrix(xp.empty((0, 4, 4)))
1184
+ tf_single = RigidTransform.from_matrix(xp.eye(4))
1185
+ tf_many = rigid_transform_to_xp(RigidTransform.identity(2), xp=xp)
1186
+
1187
+ assert len(RigidTransform.concatenate([tf_empty, tf_empty])) == 0
1188
+ assert len(RigidTransform.concatenate([tf_empty, tf_single])) == 1
1189
+ assert len(RigidTransform.concatenate([tf_single, tf_empty])) == 1
1190
+ assert len(RigidTransform.concatenate([tf_empty, tf_many])) == 2
1191
+ assert len(RigidTransform.concatenate([tf_many, tf_empty])) == 2
1192
+ assert len(RigidTransform.concatenate([tf_many, tf_empty, tf_single])) == 3
1193
+
1194
+
1195
+ def test_empty_transform_inv_and_pow(xp):
1196
+ tf = RigidTransform.from_matrix(xp.empty((0, 4, 4)))
1197
+ assert len(tf.inv()) == 0
1198
+ assert len(tf ** 0) == 0
1199
+ assert len(tf ** 1) == 0
1200
+ assert len(tf ** -1) == 0
1201
+ assert len(tf ** 0.5) == 0
1202
+
1203
+
1204
+ def test_empty_transform_indexing(xp):
1205
+ tf_many = rigid_transform_to_xp(RigidTransform.identity(3), xp=xp)
1206
+ tf_zero = tf_many[xp.asarray([], dtype=xp.int64)]
1207
+ assert len(tf_zero) == 0
1208
+
1209
+ assert len(tf_zero[xp.asarray([], dtype=xp.int64)]) == 0
1210
+ # Array API does not specify out-of-bounds indexing. Only check for numpy.
1211
+ if is_numpy(xp):
1212
+ assert len(tf_zero[:5]) == 0 # Slices can go out of bounds.
1213
+
1214
+ with pytest.raises(IndexError):
1215
+ tf_zero[0]
1216
+
1217
+ with pytest.raises(IndexError):
1218
+ tf_zero[xp.asarray([0, 2])]
1219
+
1220
+ with pytest.raises(IndexError):
1221
+ tf_zero[xp.asarray([False, True])]