scipy 1.16.2__cp314-cp314-win_arm64.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 (1530) hide show
  1. scipy/__config__.py +161 -0
  2. scipy/__init__.py +150 -0
  3. scipy/_cyutility.cp314-win_arm64.lib +0 -0
  4. scipy/_cyutility.cp314-win_arm64.pyd +0 -0
  5. scipy/_distributor_init.py +18 -0
  6. scipy/_lib/__init__.py +14 -0
  7. scipy/_lib/_array_api.py +931 -0
  8. scipy/_lib/_array_api_compat_vendor.py +9 -0
  9. scipy/_lib/_array_api_no_0d.py +103 -0
  10. scipy/_lib/_bunch.py +229 -0
  11. scipy/_lib/_ccallback.py +251 -0
  12. scipy/_lib/_ccallback_c.cp314-win_arm64.lib +0 -0
  13. scipy/_lib/_ccallback_c.cp314-win_arm64.pyd +0 -0
  14. scipy/_lib/_disjoint_set.py +254 -0
  15. scipy/_lib/_docscrape.py +761 -0
  16. scipy/_lib/_elementwise_iterative_method.py +346 -0
  17. scipy/_lib/_fpumode.cp314-win_arm64.lib +0 -0
  18. scipy/_lib/_fpumode.cp314-win_arm64.pyd +0 -0
  19. scipy/_lib/_gcutils.py +105 -0
  20. scipy/_lib/_pep440.py +487 -0
  21. scipy/_lib/_sparse.py +41 -0
  22. scipy/_lib/_test_ccallback.cp314-win_arm64.lib +0 -0
  23. scipy/_lib/_test_ccallback.cp314-win_arm64.pyd +0 -0
  24. scipy/_lib/_test_deprecation_call.cp314-win_arm64.lib +0 -0
  25. scipy/_lib/_test_deprecation_call.cp314-win_arm64.pyd +0 -0
  26. scipy/_lib/_test_deprecation_def.cp314-win_arm64.lib +0 -0
  27. scipy/_lib/_test_deprecation_def.cp314-win_arm64.pyd +0 -0
  28. scipy/_lib/_testutils.py +373 -0
  29. scipy/_lib/_threadsafety.py +58 -0
  30. scipy/_lib/_tmpdirs.py +86 -0
  31. scipy/_lib/_uarray/LICENSE +29 -0
  32. scipy/_lib/_uarray/__init__.py +116 -0
  33. scipy/_lib/_uarray/_backend.py +707 -0
  34. scipy/_lib/_uarray/_uarray.cp314-win_arm64.lib +0 -0
  35. scipy/_lib/_uarray/_uarray.cp314-win_arm64.pyd +0 -0
  36. scipy/_lib/_util.py +1283 -0
  37. scipy/_lib/array_api_compat/__init__.py +22 -0
  38. scipy/_lib/array_api_compat/_internal.py +59 -0
  39. scipy/_lib/array_api_compat/common/__init__.py +1 -0
  40. scipy/_lib/array_api_compat/common/_aliases.py +727 -0
  41. scipy/_lib/array_api_compat/common/_fft.py +213 -0
  42. scipy/_lib/array_api_compat/common/_helpers.py +1058 -0
  43. scipy/_lib/array_api_compat/common/_linalg.py +232 -0
  44. scipy/_lib/array_api_compat/common/_typing.py +192 -0
  45. scipy/_lib/array_api_compat/cupy/__init__.py +13 -0
  46. scipy/_lib/array_api_compat/cupy/_aliases.py +156 -0
  47. scipy/_lib/array_api_compat/cupy/_info.py +336 -0
  48. scipy/_lib/array_api_compat/cupy/_typing.py +31 -0
  49. scipy/_lib/array_api_compat/cupy/fft.py +36 -0
  50. scipy/_lib/array_api_compat/cupy/linalg.py +49 -0
  51. scipy/_lib/array_api_compat/dask/__init__.py +0 -0
  52. scipy/_lib/array_api_compat/dask/array/__init__.py +12 -0
  53. scipy/_lib/array_api_compat/dask/array/_aliases.py +376 -0
  54. scipy/_lib/array_api_compat/dask/array/_info.py +416 -0
  55. scipy/_lib/array_api_compat/dask/array/fft.py +21 -0
  56. scipy/_lib/array_api_compat/dask/array/linalg.py +72 -0
  57. scipy/_lib/array_api_compat/numpy/__init__.py +28 -0
  58. scipy/_lib/array_api_compat/numpy/_aliases.py +190 -0
  59. scipy/_lib/array_api_compat/numpy/_info.py +366 -0
  60. scipy/_lib/array_api_compat/numpy/_typing.py +30 -0
  61. scipy/_lib/array_api_compat/numpy/fft.py +35 -0
  62. scipy/_lib/array_api_compat/numpy/linalg.py +143 -0
  63. scipy/_lib/array_api_compat/torch/__init__.py +22 -0
  64. scipy/_lib/array_api_compat/torch/_aliases.py +855 -0
  65. scipy/_lib/array_api_compat/torch/_info.py +369 -0
  66. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  67. scipy/_lib/array_api_compat/torch/fft.py +85 -0
  68. scipy/_lib/array_api_compat/torch/linalg.py +121 -0
  69. scipy/_lib/array_api_extra/__init__.py +38 -0
  70. scipy/_lib/array_api_extra/_delegation.py +171 -0
  71. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  72. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  73. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  74. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  75. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  76. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  77. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  78. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  79. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  80. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  81. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  82. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  83. scipy/_lib/array_api_extra/testing.py +359 -0
  84. scipy/_lib/cobyqa/__init__.py +20 -0
  85. scipy/_lib/cobyqa/framework.py +1240 -0
  86. scipy/_lib/cobyqa/main.py +1506 -0
  87. scipy/_lib/cobyqa/models.py +1529 -0
  88. scipy/_lib/cobyqa/problem.py +1296 -0
  89. scipy/_lib/cobyqa/settings.py +132 -0
  90. scipy/_lib/cobyqa/subsolvers/__init__.py +14 -0
  91. scipy/_lib/cobyqa/subsolvers/geometry.py +387 -0
  92. scipy/_lib/cobyqa/subsolvers/optim.py +1203 -0
  93. scipy/_lib/cobyqa/utils/__init__.py +18 -0
  94. scipy/_lib/cobyqa/utils/exceptions.py +22 -0
  95. scipy/_lib/cobyqa/utils/math.py +77 -0
  96. scipy/_lib/cobyqa/utils/versions.py +67 -0
  97. scipy/_lib/decorator.py +399 -0
  98. scipy/_lib/deprecation.py +274 -0
  99. scipy/_lib/doccer.py +366 -0
  100. scipy/_lib/messagestream.cp314-win_arm64.lib +0 -0
  101. scipy/_lib/messagestream.cp314-win_arm64.pyd +0 -0
  102. scipy/_lib/pyprima/__init__.py +212 -0
  103. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  104. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  105. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  106. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  107. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  108. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  109. scipy/_lib/pyprima/cobyla/update.py +289 -0
  110. scipy/_lib/pyprima/common/__init__.py +0 -0
  111. scipy/_lib/pyprima/common/_bounds.py +34 -0
  112. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  113. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  114. scipy/_lib/pyprima/common/_project.py +173 -0
  115. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  116. scipy/_lib/pyprima/common/consts.py +47 -0
  117. scipy/_lib/pyprima/common/evaluate.py +99 -0
  118. scipy/_lib/pyprima/common/history.py +38 -0
  119. scipy/_lib/pyprima/common/infos.py +30 -0
  120. scipy/_lib/pyprima/common/linalg.py +435 -0
  121. scipy/_lib/pyprima/common/message.py +290 -0
  122. scipy/_lib/pyprima/common/powalg.py +131 -0
  123. scipy/_lib/pyprima/common/preproc.py +277 -0
  124. scipy/_lib/pyprima/common/present.py +5 -0
  125. scipy/_lib/pyprima/common/ratio.py +54 -0
  126. scipy/_lib/pyprima/common/redrho.py +47 -0
  127. scipy/_lib/pyprima/common/selectx.py +296 -0
  128. scipy/_lib/tests/__init__.py +0 -0
  129. scipy/_lib/tests/test__gcutils.py +110 -0
  130. scipy/_lib/tests/test__pep440.py +67 -0
  131. scipy/_lib/tests/test__testutils.py +32 -0
  132. scipy/_lib/tests/test__threadsafety.py +51 -0
  133. scipy/_lib/tests/test__util.py +641 -0
  134. scipy/_lib/tests/test_array_api.py +322 -0
  135. scipy/_lib/tests/test_bunch.py +169 -0
  136. scipy/_lib/tests/test_ccallback.py +196 -0
  137. scipy/_lib/tests/test_config.py +45 -0
  138. scipy/_lib/tests/test_deprecation.py +10 -0
  139. scipy/_lib/tests/test_doccer.py +143 -0
  140. scipy/_lib/tests/test_import_cycles.py +18 -0
  141. scipy/_lib/tests/test_public_api.py +482 -0
  142. scipy/_lib/tests/test_scipy_version.py +28 -0
  143. scipy/_lib/tests/test_tmpdirs.py +48 -0
  144. scipy/_lib/tests/test_warnings.py +137 -0
  145. scipy/_lib/uarray.py +31 -0
  146. scipy/cluster/__init__.py +31 -0
  147. scipy/cluster/_hierarchy.cp314-win_arm64.lib +0 -0
  148. scipy/cluster/_hierarchy.cp314-win_arm64.pyd +0 -0
  149. scipy/cluster/_optimal_leaf_ordering.cp314-win_arm64.lib +0 -0
  150. scipy/cluster/_optimal_leaf_ordering.cp314-win_arm64.pyd +0 -0
  151. scipy/cluster/_vq.cp314-win_arm64.lib +0 -0
  152. scipy/cluster/_vq.cp314-win_arm64.pyd +0 -0
  153. scipy/cluster/hierarchy.py +4348 -0
  154. scipy/cluster/tests/__init__.py +0 -0
  155. scipy/cluster/tests/hierarchy_test_data.py +145 -0
  156. scipy/cluster/tests/test_disjoint_set.py +202 -0
  157. scipy/cluster/tests/test_hierarchy.py +1238 -0
  158. scipy/cluster/tests/test_vq.py +434 -0
  159. scipy/cluster/vq.py +832 -0
  160. scipy/conftest.py +683 -0
  161. scipy/constants/__init__.py +358 -0
  162. scipy/constants/_codata.py +2266 -0
  163. scipy/constants/_constants.py +369 -0
  164. scipy/constants/codata.py +21 -0
  165. scipy/constants/constants.py +53 -0
  166. scipy/constants/tests/__init__.py +0 -0
  167. scipy/constants/tests/test_codata.py +78 -0
  168. scipy/constants/tests/test_constants.py +83 -0
  169. scipy/datasets/__init__.py +90 -0
  170. scipy/datasets/_download_all.py +71 -0
  171. scipy/datasets/_fetchers.py +225 -0
  172. scipy/datasets/_registry.py +26 -0
  173. scipy/datasets/_utils.py +81 -0
  174. scipy/datasets/tests/__init__.py +0 -0
  175. scipy/datasets/tests/test_data.py +128 -0
  176. scipy/differentiate/__init__.py +27 -0
  177. scipy/differentiate/_differentiate.py +1129 -0
  178. scipy/differentiate/tests/__init__.py +0 -0
  179. scipy/differentiate/tests/test_differentiate.py +694 -0
  180. scipy/fft/__init__.py +114 -0
  181. scipy/fft/_backend.py +196 -0
  182. scipy/fft/_basic.py +1650 -0
  183. scipy/fft/_basic_backend.py +197 -0
  184. scipy/fft/_debug_backends.py +22 -0
  185. scipy/fft/_fftlog.py +223 -0
  186. scipy/fft/_fftlog_backend.py +200 -0
  187. scipy/fft/_helper.py +348 -0
  188. scipy/fft/_pocketfft/LICENSE.md +25 -0
  189. scipy/fft/_pocketfft/__init__.py +9 -0
  190. scipy/fft/_pocketfft/basic.py +251 -0
  191. scipy/fft/_pocketfft/helper.py +249 -0
  192. scipy/fft/_pocketfft/pypocketfft.cp314-win_arm64.lib +0 -0
  193. scipy/fft/_pocketfft/pypocketfft.cp314-win_arm64.pyd +0 -0
  194. scipy/fft/_pocketfft/realtransforms.py +109 -0
  195. scipy/fft/_pocketfft/tests/__init__.py +0 -0
  196. scipy/fft/_pocketfft/tests/test_basic.py +1011 -0
  197. scipy/fft/_pocketfft/tests/test_real_transforms.py +505 -0
  198. scipy/fft/_realtransforms.py +706 -0
  199. scipy/fft/_realtransforms_backend.py +63 -0
  200. scipy/fft/tests/__init__.py +0 -0
  201. scipy/fft/tests/mock_backend.py +96 -0
  202. scipy/fft/tests/test_backend.py +98 -0
  203. scipy/fft/tests/test_basic.py +504 -0
  204. scipy/fft/tests/test_fftlog.py +215 -0
  205. scipy/fft/tests/test_helper.py +558 -0
  206. scipy/fft/tests/test_multithreading.py +84 -0
  207. scipy/fft/tests/test_real_transforms.py +247 -0
  208. scipy/fftpack/__init__.py +103 -0
  209. scipy/fftpack/_basic.py +428 -0
  210. scipy/fftpack/_helper.py +115 -0
  211. scipy/fftpack/_pseudo_diffs.py +554 -0
  212. scipy/fftpack/_realtransforms.py +598 -0
  213. scipy/fftpack/basic.py +20 -0
  214. scipy/fftpack/convolve.cp314-win_arm64.lib +0 -0
  215. scipy/fftpack/convolve.cp314-win_arm64.pyd +0 -0
  216. scipy/fftpack/helper.py +19 -0
  217. scipy/fftpack/pseudo_diffs.py +22 -0
  218. scipy/fftpack/realtransforms.py +19 -0
  219. scipy/fftpack/tests/__init__.py +0 -0
  220. scipy/fftpack/tests/fftw_double_ref.npz +0 -0
  221. scipy/fftpack/tests/fftw_longdouble_ref.npz +0 -0
  222. scipy/fftpack/tests/fftw_single_ref.npz +0 -0
  223. scipy/fftpack/tests/test.npz +0 -0
  224. scipy/fftpack/tests/test_basic.py +877 -0
  225. scipy/fftpack/tests/test_helper.py +54 -0
  226. scipy/fftpack/tests/test_import.py +33 -0
  227. scipy/fftpack/tests/test_pseudo_diffs.py +388 -0
  228. scipy/fftpack/tests/test_real_transforms.py +836 -0
  229. scipy/integrate/__init__.py +122 -0
  230. scipy/integrate/_bvp.py +1160 -0
  231. scipy/integrate/_cubature.py +729 -0
  232. scipy/integrate/_dop.cp314-win_arm64.lib +0 -0
  233. scipy/integrate/_dop.cp314-win_arm64.pyd +0 -0
  234. scipy/integrate/_ivp/__init__.py +8 -0
  235. scipy/integrate/_ivp/base.py +290 -0
  236. scipy/integrate/_ivp/bdf.py +478 -0
  237. scipy/integrate/_ivp/common.py +451 -0
  238. scipy/integrate/_ivp/dop853_coefficients.py +193 -0
  239. scipy/integrate/_ivp/ivp.py +755 -0
  240. scipy/integrate/_ivp/lsoda.py +224 -0
  241. scipy/integrate/_ivp/radau.py +572 -0
  242. scipy/integrate/_ivp/rk.py +601 -0
  243. scipy/integrate/_ivp/tests/__init__.py +0 -0
  244. scipy/integrate/_ivp/tests/test_ivp.py +1287 -0
  245. scipy/integrate/_ivp/tests/test_rk.py +37 -0
  246. scipy/integrate/_lebedev.py +5450 -0
  247. scipy/integrate/_lsoda.cp314-win_arm64.lib +0 -0
  248. scipy/integrate/_lsoda.cp314-win_arm64.pyd +0 -0
  249. scipy/integrate/_ode.py +1395 -0
  250. scipy/integrate/_odepack.cp314-win_arm64.lib +0 -0
  251. scipy/integrate/_odepack.cp314-win_arm64.pyd +0 -0
  252. scipy/integrate/_odepack_py.py +273 -0
  253. scipy/integrate/_quad_vec.py +674 -0
  254. scipy/integrate/_quadpack.cp314-win_arm64.lib +0 -0
  255. scipy/integrate/_quadpack.cp314-win_arm64.pyd +0 -0
  256. scipy/integrate/_quadpack_py.py +1283 -0
  257. scipy/integrate/_quadrature.py +1336 -0
  258. scipy/integrate/_rules/__init__.py +12 -0
  259. scipy/integrate/_rules/_base.py +518 -0
  260. scipy/integrate/_rules/_gauss_kronrod.py +202 -0
  261. scipy/integrate/_rules/_gauss_legendre.py +62 -0
  262. scipy/integrate/_rules/_genz_malik.py +210 -0
  263. scipy/integrate/_tanhsinh.py +1385 -0
  264. scipy/integrate/_test_multivariate.cp314-win_arm64.lib +0 -0
  265. scipy/integrate/_test_multivariate.cp314-win_arm64.pyd +0 -0
  266. scipy/integrate/_test_odeint_banded.cp314-win_arm64.lib +0 -0
  267. scipy/integrate/_test_odeint_banded.cp314-win_arm64.pyd +0 -0
  268. scipy/integrate/_vode.cp314-win_arm64.lib +0 -0
  269. scipy/integrate/_vode.cp314-win_arm64.pyd +0 -0
  270. scipy/integrate/dop.py +15 -0
  271. scipy/integrate/lsoda.py +15 -0
  272. scipy/integrate/odepack.py +17 -0
  273. scipy/integrate/quadpack.py +23 -0
  274. scipy/integrate/tests/__init__.py +0 -0
  275. scipy/integrate/tests/test__quad_vec.py +211 -0
  276. scipy/integrate/tests/test_banded_ode_solvers.py +305 -0
  277. scipy/integrate/tests/test_bvp.py +714 -0
  278. scipy/integrate/tests/test_cubature.py +1375 -0
  279. scipy/integrate/tests/test_integrate.py +840 -0
  280. scipy/integrate/tests/test_odeint_jac.py +74 -0
  281. scipy/integrate/tests/test_quadpack.py +680 -0
  282. scipy/integrate/tests/test_quadrature.py +730 -0
  283. scipy/integrate/tests/test_tanhsinh.py +1171 -0
  284. scipy/integrate/vode.py +15 -0
  285. scipy/interpolate/__init__.py +228 -0
  286. scipy/interpolate/_bary_rational.py +715 -0
  287. scipy/interpolate/_bsplines.py +2469 -0
  288. scipy/interpolate/_cubic.py +973 -0
  289. scipy/interpolate/_dfitpack.cp314-win_arm64.lib +0 -0
  290. scipy/interpolate/_dfitpack.cp314-win_arm64.pyd +0 -0
  291. scipy/interpolate/_dierckx.cp314-win_arm64.lib +0 -0
  292. scipy/interpolate/_dierckx.cp314-win_arm64.pyd +0 -0
  293. scipy/interpolate/_fitpack.cp314-win_arm64.lib +0 -0
  294. scipy/interpolate/_fitpack.cp314-win_arm64.pyd +0 -0
  295. scipy/interpolate/_fitpack2.py +2397 -0
  296. scipy/interpolate/_fitpack_impl.py +811 -0
  297. scipy/interpolate/_fitpack_py.py +898 -0
  298. scipy/interpolate/_fitpack_repro.py +996 -0
  299. scipy/interpolate/_interpnd.cp314-win_arm64.lib +0 -0
  300. scipy/interpolate/_interpnd.cp314-win_arm64.pyd +0 -0
  301. scipy/interpolate/_interpolate.py +2266 -0
  302. scipy/interpolate/_ndbspline.py +415 -0
  303. scipy/interpolate/_ndgriddata.py +329 -0
  304. scipy/interpolate/_pade.py +67 -0
  305. scipy/interpolate/_polyint.py +1025 -0
  306. scipy/interpolate/_ppoly.cp314-win_arm64.lib +0 -0
  307. scipy/interpolate/_ppoly.cp314-win_arm64.pyd +0 -0
  308. scipy/interpolate/_rbf.py +290 -0
  309. scipy/interpolate/_rbfinterp.py +550 -0
  310. scipy/interpolate/_rbfinterp_pythran.cp314-win_arm64.lib +0 -0
  311. scipy/interpolate/_rbfinterp_pythran.cp314-win_arm64.pyd +0 -0
  312. scipy/interpolate/_rgi.py +764 -0
  313. scipy/interpolate/_rgi_cython.cp314-win_arm64.lib +0 -0
  314. scipy/interpolate/_rgi_cython.cp314-win_arm64.pyd +0 -0
  315. scipy/interpolate/dfitpack.py +24 -0
  316. scipy/interpolate/fitpack.py +31 -0
  317. scipy/interpolate/fitpack2.py +29 -0
  318. scipy/interpolate/interpnd.py +24 -0
  319. scipy/interpolate/interpolate.py +30 -0
  320. scipy/interpolate/ndgriddata.py +23 -0
  321. scipy/interpolate/polyint.py +24 -0
  322. scipy/interpolate/rbf.py +18 -0
  323. scipy/interpolate/tests/__init__.py +0 -0
  324. scipy/interpolate/tests/data/bug-1310.npz +0 -0
  325. scipy/interpolate/tests/data/estimate_gradients_hang.npy +0 -0
  326. scipy/interpolate/tests/data/gcvspl.npz +0 -0
  327. scipy/interpolate/tests/test_bary_rational.py +368 -0
  328. scipy/interpolate/tests/test_bsplines.py +3754 -0
  329. scipy/interpolate/tests/test_fitpack.py +519 -0
  330. scipy/interpolate/tests/test_fitpack2.py +1431 -0
  331. scipy/interpolate/tests/test_gil.py +64 -0
  332. scipy/interpolate/tests/test_interpnd.py +452 -0
  333. scipy/interpolate/tests/test_interpolate.py +2630 -0
  334. scipy/interpolate/tests/test_ndgriddata.py +308 -0
  335. scipy/interpolate/tests/test_pade.py +107 -0
  336. scipy/interpolate/tests/test_polyint.py +972 -0
  337. scipy/interpolate/tests/test_rbf.py +246 -0
  338. scipy/interpolate/tests/test_rbfinterp.py +534 -0
  339. scipy/interpolate/tests/test_rgi.py +1151 -0
  340. scipy/io/__init__.py +116 -0
  341. scipy/io/_fast_matrix_market/__init__.py +600 -0
  342. scipy/io/_fast_matrix_market/_fmm_core.cp314-win_arm64.lib +0 -0
  343. scipy/io/_fast_matrix_market/_fmm_core.cp314-win_arm64.pyd +0 -0
  344. scipy/io/_fortran.py +354 -0
  345. scipy/io/_harwell_boeing/__init__.py +7 -0
  346. scipy/io/_harwell_boeing/_fortran_format_parser.py +316 -0
  347. scipy/io/_harwell_boeing/hb.py +571 -0
  348. scipy/io/_harwell_boeing/tests/__init__.py +0 -0
  349. scipy/io/_harwell_boeing/tests/test_fortran_format.py +74 -0
  350. scipy/io/_harwell_boeing/tests/test_hb.py +70 -0
  351. scipy/io/_idl.py +917 -0
  352. scipy/io/_mmio.py +968 -0
  353. scipy/io/_netcdf.py +1104 -0
  354. scipy/io/_test_fortran.cp314-win_arm64.lib +0 -0
  355. scipy/io/_test_fortran.cp314-win_arm64.pyd +0 -0
  356. scipy/io/arff/__init__.py +28 -0
  357. scipy/io/arff/_arffread.py +873 -0
  358. scipy/io/arff/arffread.py +19 -0
  359. scipy/io/arff/tests/__init__.py +0 -0
  360. scipy/io/arff/tests/data/iris.arff +225 -0
  361. scipy/io/arff/tests/data/missing.arff +8 -0
  362. scipy/io/arff/tests/data/nodata.arff +11 -0
  363. scipy/io/arff/tests/data/quoted_nominal.arff +13 -0
  364. scipy/io/arff/tests/data/quoted_nominal_spaces.arff +13 -0
  365. scipy/io/arff/tests/data/test1.arff +10 -0
  366. scipy/io/arff/tests/data/test10.arff +8 -0
  367. scipy/io/arff/tests/data/test11.arff +11 -0
  368. scipy/io/arff/tests/data/test2.arff +15 -0
  369. scipy/io/arff/tests/data/test3.arff +6 -0
  370. scipy/io/arff/tests/data/test4.arff +11 -0
  371. scipy/io/arff/tests/data/test5.arff +26 -0
  372. scipy/io/arff/tests/data/test6.arff +12 -0
  373. scipy/io/arff/tests/data/test7.arff +15 -0
  374. scipy/io/arff/tests/data/test8.arff +12 -0
  375. scipy/io/arff/tests/data/test9.arff +14 -0
  376. scipy/io/arff/tests/test_arffread.py +421 -0
  377. scipy/io/harwell_boeing.py +17 -0
  378. scipy/io/idl.py +17 -0
  379. scipy/io/matlab/__init__.py +66 -0
  380. scipy/io/matlab/_byteordercodes.py +75 -0
  381. scipy/io/matlab/_mio.py +375 -0
  382. scipy/io/matlab/_mio4.py +632 -0
  383. scipy/io/matlab/_mio5.py +901 -0
  384. scipy/io/matlab/_mio5_params.py +281 -0
  385. scipy/io/matlab/_mio5_utils.cp314-win_arm64.lib +0 -0
  386. scipy/io/matlab/_mio5_utils.cp314-win_arm64.pyd +0 -0
  387. scipy/io/matlab/_mio_utils.cp314-win_arm64.lib +0 -0
  388. scipy/io/matlab/_mio_utils.cp314-win_arm64.pyd +0 -0
  389. scipy/io/matlab/_miobase.py +435 -0
  390. scipy/io/matlab/_streams.cp314-win_arm64.lib +0 -0
  391. scipy/io/matlab/_streams.cp314-win_arm64.pyd +0 -0
  392. scipy/io/matlab/byteordercodes.py +17 -0
  393. scipy/io/matlab/mio.py +16 -0
  394. scipy/io/matlab/mio4.py +17 -0
  395. scipy/io/matlab/mio5.py +19 -0
  396. scipy/io/matlab/mio5_params.py +18 -0
  397. scipy/io/matlab/mio5_utils.py +17 -0
  398. scipy/io/matlab/mio_utils.py +17 -0
  399. scipy/io/matlab/miobase.py +16 -0
  400. scipy/io/matlab/streams.py +16 -0
  401. scipy/io/matlab/tests/__init__.py +0 -0
  402. scipy/io/matlab/tests/data/bad_miuint32.mat +0 -0
  403. scipy/io/matlab/tests/data/bad_miutf8_array_name.mat +0 -0
  404. scipy/io/matlab/tests/data/big_endian.mat +0 -0
  405. scipy/io/matlab/tests/data/broken_utf8.mat +0 -0
  406. scipy/io/matlab/tests/data/corrupted_zlib_checksum.mat +0 -0
  407. scipy/io/matlab/tests/data/corrupted_zlib_data.mat +0 -0
  408. scipy/io/matlab/tests/data/debigged_m4.mat +0 -0
  409. scipy/io/matlab/tests/data/japanese_utf8.txt +5 -0
  410. scipy/io/matlab/tests/data/little_endian.mat +0 -0
  411. scipy/io/matlab/tests/data/logical_sparse.mat +0 -0
  412. scipy/io/matlab/tests/data/malformed1.mat +0 -0
  413. scipy/io/matlab/tests/data/miuint32_for_miint32.mat +0 -0
  414. scipy/io/matlab/tests/data/miutf8_array_name.mat +0 -0
  415. scipy/io/matlab/tests/data/nasty_duplicate_fieldnames.mat +0 -0
  416. scipy/io/matlab/tests/data/one_by_zero_char.mat +0 -0
  417. scipy/io/matlab/tests/data/parabola.mat +0 -0
  418. scipy/io/matlab/tests/data/single_empty_string.mat +0 -0
  419. scipy/io/matlab/tests/data/some_functions.mat +0 -0
  420. scipy/io/matlab/tests/data/sqr.mat +0 -0
  421. scipy/io/matlab/tests/data/test3dmatrix_6.1_SOL2.mat +0 -0
  422. scipy/io/matlab/tests/data/test3dmatrix_6.5.1_GLNX86.mat +0 -0
  423. scipy/io/matlab/tests/data/test3dmatrix_7.1_GLNX86.mat +0 -0
  424. scipy/io/matlab/tests/data/test3dmatrix_7.4_GLNX86.mat +0 -0
  425. scipy/io/matlab/tests/data/test_empty_struct.mat +0 -0
  426. scipy/io/matlab/tests/data/test_mat4_le_floats.mat +0 -0
  427. scipy/io/matlab/tests/data/test_skip_variable.mat +0 -0
  428. scipy/io/matlab/tests/data/testbool_8_WIN64.mat +0 -0
  429. scipy/io/matlab/tests/data/testcell_6.1_SOL2.mat +0 -0
  430. scipy/io/matlab/tests/data/testcell_6.5.1_GLNX86.mat +0 -0
  431. scipy/io/matlab/tests/data/testcell_7.1_GLNX86.mat +0 -0
  432. scipy/io/matlab/tests/data/testcell_7.4_GLNX86.mat +0 -0
  433. scipy/io/matlab/tests/data/testcellnest_6.1_SOL2.mat +0 -0
  434. scipy/io/matlab/tests/data/testcellnest_6.5.1_GLNX86.mat +0 -0
  435. scipy/io/matlab/tests/data/testcellnest_7.1_GLNX86.mat +0 -0
  436. scipy/io/matlab/tests/data/testcellnest_7.4_GLNX86.mat +0 -0
  437. scipy/io/matlab/tests/data/testcomplex_4.2c_SOL2.mat +0 -0
  438. scipy/io/matlab/tests/data/testcomplex_6.1_SOL2.mat +0 -0
  439. scipy/io/matlab/tests/data/testcomplex_6.5.1_GLNX86.mat +0 -0
  440. scipy/io/matlab/tests/data/testcomplex_7.1_GLNX86.mat +0 -0
  441. scipy/io/matlab/tests/data/testcomplex_7.4_GLNX86.mat +0 -0
  442. scipy/io/matlab/tests/data/testdouble_4.2c_SOL2.mat +0 -0
  443. scipy/io/matlab/tests/data/testdouble_6.1_SOL2.mat +0 -0
  444. scipy/io/matlab/tests/data/testdouble_6.5.1_GLNX86.mat +0 -0
  445. scipy/io/matlab/tests/data/testdouble_7.1_GLNX86.mat +0 -0
  446. scipy/io/matlab/tests/data/testdouble_7.4_GLNX86.mat +0 -0
  447. scipy/io/matlab/tests/data/testemptycell_5.3_SOL2.mat +0 -0
  448. scipy/io/matlab/tests/data/testemptycell_6.5.1_GLNX86.mat +0 -0
  449. scipy/io/matlab/tests/data/testemptycell_7.1_GLNX86.mat +0 -0
  450. scipy/io/matlab/tests/data/testemptycell_7.4_GLNX86.mat +0 -0
  451. scipy/io/matlab/tests/data/testfunc_7.4_GLNX86.mat +0 -0
  452. scipy/io/matlab/tests/data/testhdf5_7.4_GLNX86.mat +0 -0
  453. scipy/io/matlab/tests/data/testmatrix_4.2c_SOL2.mat +0 -0
  454. scipy/io/matlab/tests/data/testmatrix_6.1_SOL2.mat +0 -0
  455. scipy/io/matlab/tests/data/testmatrix_6.5.1_GLNX86.mat +0 -0
  456. scipy/io/matlab/tests/data/testmatrix_7.1_GLNX86.mat +0 -0
  457. scipy/io/matlab/tests/data/testmatrix_7.4_GLNX86.mat +0 -0
  458. scipy/io/matlab/tests/data/testminus_4.2c_SOL2.mat +0 -0
  459. scipy/io/matlab/tests/data/testminus_6.1_SOL2.mat +0 -0
  460. scipy/io/matlab/tests/data/testminus_6.5.1_GLNX86.mat +0 -0
  461. scipy/io/matlab/tests/data/testminus_7.1_GLNX86.mat +0 -0
  462. scipy/io/matlab/tests/data/testminus_7.4_GLNX86.mat +0 -0
  463. scipy/io/matlab/tests/data/testmulti_4.2c_SOL2.mat +0 -0
  464. scipy/io/matlab/tests/data/testmulti_7.1_GLNX86.mat +0 -0
  465. scipy/io/matlab/tests/data/testmulti_7.4_GLNX86.mat +0 -0
  466. scipy/io/matlab/tests/data/testobject_6.1_SOL2.mat +0 -0
  467. scipy/io/matlab/tests/data/testobject_6.5.1_GLNX86.mat +0 -0
  468. scipy/io/matlab/tests/data/testobject_7.1_GLNX86.mat +0 -0
  469. scipy/io/matlab/tests/data/testobject_7.4_GLNX86.mat +0 -0
  470. scipy/io/matlab/tests/data/testonechar_4.2c_SOL2.mat +0 -0
  471. scipy/io/matlab/tests/data/testonechar_6.1_SOL2.mat +0 -0
  472. scipy/io/matlab/tests/data/testonechar_6.5.1_GLNX86.mat +0 -0
  473. scipy/io/matlab/tests/data/testonechar_7.1_GLNX86.mat +0 -0
  474. scipy/io/matlab/tests/data/testonechar_7.4_GLNX86.mat +0 -0
  475. scipy/io/matlab/tests/data/testscalarcell_7.4_GLNX86.mat +0 -0
  476. scipy/io/matlab/tests/data/testsimplecell.mat +0 -0
  477. scipy/io/matlab/tests/data/testsparse_4.2c_SOL2.mat +0 -0
  478. scipy/io/matlab/tests/data/testsparse_6.1_SOL2.mat +0 -0
  479. scipy/io/matlab/tests/data/testsparse_6.5.1_GLNX86.mat +0 -0
  480. scipy/io/matlab/tests/data/testsparse_7.1_GLNX86.mat +0 -0
  481. scipy/io/matlab/tests/data/testsparse_7.4_GLNX86.mat +0 -0
  482. scipy/io/matlab/tests/data/testsparsecomplex_4.2c_SOL2.mat +0 -0
  483. scipy/io/matlab/tests/data/testsparsecomplex_6.1_SOL2.mat +0 -0
  484. scipy/io/matlab/tests/data/testsparsecomplex_6.5.1_GLNX86.mat +0 -0
  485. scipy/io/matlab/tests/data/testsparsecomplex_7.1_GLNX86.mat +0 -0
  486. scipy/io/matlab/tests/data/testsparsecomplex_7.4_GLNX86.mat +0 -0
  487. scipy/io/matlab/tests/data/testsparsefloat_7.4_GLNX86.mat +0 -0
  488. scipy/io/matlab/tests/data/teststring_4.2c_SOL2.mat +0 -0
  489. scipy/io/matlab/tests/data/teststring_6.1_SOL2.mat +0 -0
  490. scipy/io/matlab/tests/data/teststring_6.5.1_GLNX86.mat +0 -0
  491. scipy/io/matlab/tests/data/teststring_7.1_GLNX86.mat +0 -0
  492. scipy/io/matlab/tests/data/teststring_7.4_GLNX86.mat +0 -0
  493. scipy/io/matlab/tests/data/teststringarray_4.2c_SOL2.mat +0 -0
  494. scipy/io/matlab/tests/data/teststringarray_6.1_SOL2.mat +0 -0
  495. scipy/io/matlab/tests/data/teststringarray_6.5.1_GLNX86.mat +0 -0
  496. scipy/io/matlab/tests/data/teststringarray_7.1_GLNX86.mat +0 -0
  497. scipy/io/matlab/tests/data/teststringarray_7.4_GLNX86.mat +0 -0
  498. scipy/io/matlab/tests/data/teststruct_6.1_SOL2.mat +0 -0
  499. scipy/io/matlab/tests/data/teststruct_6.5.1_GLNX86.mat +0 -0
  500. scipy/io/matlab/tests/data/teststruct_7.1_GLNX86.mat +0 -0
  501. scipy/io/matlab/tests/data/teststruct_7.4_GLNX86.mat +0 -0
  502. scipy/io/matlab/tests/data/teststructarr_6.1_SOL2.mat +0 -0
  503. scipy/io/matlab/tests/data/teststructarr_6.5.1_GLNX86.mat +0 -0
  504. scipy/io/matlab/tests/data/teststructarr_7.1_GLNX86.mat +0 -0
  505. scipy/io/matlab/tests/data/teststructarr_7.4_GLNX86.mat +0 -0
  506. scipy/io/matlab/tests/data/teststructnest_6.1_SOL2.mat +0 -0
  507. scipy/io/matlab/tests/data/teststructnest_6.5.1_GLNX86.mat +0 -0
  508. scipy/io/matlab/tests/data/teststructnest_7.1_GLNX86.mat +0 -0
  509. scipy/io/matlab/tests/data/teststructnest_7.4_GLNX86.mat +0 -0
  510. scipy/io/matlab/tests/data/testunicode_7.1_GLNX86.mat +0 -0
  511. scipy/io/matlab/tests/data/testunicode_7.4_GLNX86.mat +0 -0
  512. scipy/io/matlab/tests/data/testvec_4_GLNX86.mat +0 -0
  513. scipy/io/matlab/tests/test_byteordercodes.py +29 -0
  514. scipy/io/matlab/tests/test_mio.py +1399 -0
  515. scipy/io/matlab/tests/test_mio5_utils.py +179 -0
  516. scipy/io/matlab/tests/test_mio_funcs.py +51 -0
  517. scipy/io/matlab/tests/test_mio_utils.py +45 -0
  518. scipy/io/matlab/tests/test_miobase.py +32 -0
  519. scipy/io/matlab/tests/test_pathological.py +33 -0
  520. scipy/io/matlab/tests/test_streams.py +241 -0
  521. scipy/io/mmio.py +17 -0
  522. scipy/io/netcdf.py +17 -0
  523. scipy/io/tests/__init__.py +0 -0
  524. scipy/io/tests/data/Transparent Busy.ani +0 -0
  525. scipy/io/tests/data/array_float32_1d.sav +0 -0
  526. scipy/io/tests/data/array_float32_2d.sav +0 -0
  527. scipy/io/tests/data/array_float32_3d.sav +0 -0
  528. scipy/io/tests/data/array_float32_4d.sav +0 -0
  529. scipy/io/tests/data/array_float32_5d.sav +0 -0
  530. scipy/io/tests/data/array_float32_6d.sav +0 -0
  531. scipy/io/tests/data/array_float32_7d.sav +0 -0
  532. scipy/io/tests/data/array_float32_8d.sav +0 -0
  533. scipy/io/tests/data/array_float32_pointer_1d.sav +0 -0
  534. scipy/io/tests/data/array_float32_pointer_2d.sav +0 -0
  535. scipy/io/tests/data/array_float32_pointer_3d.sav +0 -0
  536. scipy/io/tests/data/array_float32_pointer_4d.sav +0 -0
  537. scipy/io/tests/data/array_float32_pointer_5d.sav +0 -0
  538. scipy/io/tests/data/array_float32_pointer_6d.sav +0 -0
  539. scipy/io/tests/data/array_float32_pointer_7d.sav +0 -0
  540. scipy/io/tests/data/array_float32_pointer_8d.sav +0 -0
  541. scipy/io/tests/data/example_1.nc +0 -0
  542. scipy/io/tests/data/example_2.nc +0 -0
  543. scipy/io/tests/data/example_3_maskedvals.nc +0 -0
  544. scipy/io/tests/data/fortran-3x3d-2i.dat +0 -0
  545. scipy/io/tests/data/fortran-mixed.dat +0 -0
  546. scipy/io/tests/data/fortran-sf8-11x1x10.dat +0 -0
  547. scipy/io/tests/data/fortran-sf8-15x10x22.dat +0 -0
  548. scipy/io/tests/data/fortran-sf8-1x1x1.dat +0 -0
  549. scipy/io/tests/data/fortran-sf8-1x1x5.dat +0 -0
  550. scipy/io/tests/data/fortran-sf8-1x1x7.dat +0 -0
  551. scipy/io/tests/data/fortran-sf8-1x3x5.dat +0 -0
  552. scipy/io/tests/data/fortran-si4-11x1x10.dat +0 -0
  553. scipy/io/tests/data/fortran-si4-15x10x22.dat +0 -0
  554. scipy/io/tests/data/fortran-si4-1x1x1.dat +0 -0
  555. scipy/io/tests/data/fortran-si4-1x1x5.dat +0 -0
  556. scipy/io/tests/data/fortran-si4-1x1x7.dat +0 -0
  557. scipy/io/tests/data/fortran-si4-1x3x5.dat +0 -0
  558. scipy/io/tests/data/invalid_pointer.sav +0 -0
  559. scipy/io/tests/data/null_pointer.sav +0 -0
  560. scipy/io/tests/data/scalar_byte.sav +0 -0
  561. scipy/io/tests/data/scalar_byte_descr.sav +0 -0
  562. scipy/io/tests/data/scalar_complex32.sav +0 -0
  563. scipy/io/tests/data/scalar_complex64.sav +0 -0
  564. scipy/io/tests/data/scalar_float32.sav +0 -0
  565. scipy/io/tests/data/scalar_float64.sav +0 -0
  566. scipy/io/tests/data/scalar_heap_pointer.sav +0 -0
  567. scipy/io/tests/data/scalar_int16.sav +0 -0
  568. scipy/io/tests/data/scalar_int32.sav +0 -0
  569. scipy/io/tests/data/scalar_int64.sav +0 -0
  570. scipy/io/tests/data/scalar_string.sav +0 -0
  571. scipy/io/tests/data/scalar_uint16.sav +0 -0
  572. scipy/io/tests/data/scalar_uint32.sav +0 -0
  573. scipy/io/tests/data/scalar_uint64.sav +0 -0
  574. scipy/io/tests/data/struct_arrays.sav +0 -0
  575. scipy/io/tests/data/struct_arrays_byte_idl80.sav +0 -0
  576. scipy/io/tests/data/struct_arrays_replicated.sav +0 -0
  577. scipy/io/tests/data/struct_arrays_replicated_3d.sav +0 -0
  578. scipy/io/tests/data/struct_inherit.sav +0 -0
  579. scipy/io/tests/data/struct_pointer_arrays.sav +0 -0
  580. scipy/io/tests/data/struct_pointer_arrays_replicated.sav +0 -0
  581. scipy/io/tests/data/struct_pointer_arrays_replicated_3d.sav +0 -0
  582. scipy/io/tests/data/struct_pointers.sav +0 -0
  583. scipy/io/tests/data/struct_pointers_replicated.sav +0 -0
  584. scipy/io/tests/data/struct_pointers_replicated_3d.sav +0 -0
  585. scipy/io/tests/data/struct_scalars.sav +0 -0
  586. scipy/io/tests/data/struct_scalars_replicated.sav +0 -0
  587. scipy/io/tests/data/struct_scalars_replicated_3d.sav +0 -0
  588. scipy/io/tests/data/test-1234Hz-le-1ch-10S-20bit-extra.wav +0 -0
  589. scipy/io/tests/data/test-44100Hz-2ch-32bit-float-be.wav +0 -0
  590. scipy/io/tests/data/test-44100Hz-2ch-32bit-float-le.wav +0 -0
  591. scipy/io/tests/data/test-44100Hz-be-1ch-4bytes.wav +0 -0
  592. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof-no-data.wav +0 -0
  593. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof.wav +0 -0
  594. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-incomplete-chunk.wav +0 -0
  595. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-rf64.wav +0 -0
  596. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes.wav +0 -0
  597. scipy/io/tests/data/test-48000Hz-2ch-64bit-float-le-wavex.wav +0 -0
  598. scipy/io/tests/data/test-8000Hz-be-3ch-5S-24bit.wav +0 -0
  599. scipy/io/tests/data/test-8000Hz-le-1ch-1byte-ulaw.wav +0 -0
  600. scipy/io/tests/data/test-8000Hz-le-2ch-1byteu.wav +0 -0
  601. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-inconsistent.wav +0 -0
  602. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-rf64.wav +0 -0
  603. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit.wav +0 -0
  604. scipy/io/tests/data/test-8000Hz-le-3ch-5S-36bit.wav +0 -0
  605. scipy/io/tests/data/test-8000Hz-le-3ch-5S-45bit.wav +0 -0
  606. scipy/io/tests/data/test-8000Hz-le-3ch-5S-53bit.wav +0 -0
  607. scipy/io/tests/data/test-8000Hz-le-3ch-5S-64bit.wav +0 -0
  608. scipy/io/tests/data/test-8000Hz-le-4ch-9S-12bit.wav +0 -0
  609. scipy/io/tests/data/test-8000Hz-le-5ch-9S-5bit.wav +0 -0
  610. scipy/io/tests/data/various_compressed.sav +0 -0
  611. scipy/io/tests/test_fortran.py +264 -0
  612. scipy/io/tests/test_idl.py +483 -0
  613. scipy/io/tests/test_mmio.py +831 -0
  614. scipy/io/tests/test_netcdf.py +550 -0
  615. scipy/io/tests/test_paths.py +93 -0
  616. scipy/io/tests/test_wavfile.py +501 -0
  617. scipy/io/wavfile.py +938 -0
  618. scipy/linalg/__init__.pxd +1 -0
  619. scipy/linalg/__init__.py +236 -0
  620. scipy/linalg/_basic.py +2146 -0
  621. scipy/linalg/_blas_subroutines.h +164 -0
  622. scipy/linalg/_cythonized_array_utils.cp314-win_arm64.lib +0 -0
  623. scipy/linalg/_cythonized_array_utils.cp314-win_arm64.pyd +0 -0
  624. scipy/linalg/_cythonized_array_utils.pxd +40 -0
  625. scipy/linalg/_cythonized_array_utils.pyi +16 -0
  626. scipy/linalg/_decomp.py +1645 -0
  627. scipy/linalg/_decomp_cholesky.py +413 -0
  628. scipy/linalg/_decomp_cossin.py +236 -0
  629. scipy/linalg/_decomp_interpolative.cp314-win_arm64.lib +0 -0
  630. scipy/linalg/_decomp_interpolative.cp314-win_arm64.pyd +0 -0
  631. scipy/linalg/_decomp_ldl.py +356 -0
  632. scipy/linalg/_decomp_lu.py +401 -0
  633. scipy/linalg/_decomp_lu_cython.cp314-win_arm64.lib +0 -0
  634. scipy/linalg/_decomp_lu_cython.cp314-win_arm64.pyd +0 -0
  635. scipy/linalg/_decomp_lu_cython.pyi +6 -0
  636. scipy/linalg/_decomp_polar.py +113 -0
  637. scipy/linalg/_decomp_qr.py +494 -0
  638. scipy/linalg/_decomp_qz.py +452 -0
  639. scipy/linalg/_decomp_schur.py +336 -0
  640. scipy/linalg/_decomp_svd.py +545 -0
  641. scipy/linalg/_decomp_update.cp314-win_arm64.lib +0 -0
  642. scipy/linalg/_decomp_update.cp314-win_arm64.pyd +0 -0
  643. scipy/linalg/_expm_frechet.py +417 -0
  644. scipy/linalg/_fblas.cp314-win_arm64.lib +0 -0
  645. scipy/linalg/_fblas.cp314-win_arm64.pyd +0 -0
  646. scipy/linalg/_flapack.cp314-win_arm64.lib +0 -0
  647. scipy/linalg/_flapack.cp314-win_arm64.pyd +0 -0
  648. scipy/linalg/_lapack_subroutines.h +1521 -0
  649. scipy/linalg/_linalg_pythran.cp314-win_arm64.lib +0 -0
  650. scipy/linalg/_linalg_pythran.cp314-win_arm64.pyd +0 -0
  651. scipy/linalg/_matfuncs.py +1050 -0
  652. scipy/linalg/_matfuncs_expm.cp314-win_arm64.lib +0 -0
  653. scipy/linalg/_matfuncs_expm.cp314-win_arm64.pyd +0 -0
  654. scipy/linalg/_matfuncs_expm.pyi +6 -0
  655. scipy/linalg/_matfuncs_inv_ssq.py +886 -0
  656. scipy/linalg/_matfuncs_schur_sqrtm.cp314-win_arm64.lib +0 -0
  657. scipy/linalg/_matfuncs_schur_sqrtm.cp314-win_arm64.pyd +0 -0
  658. scipy/linalg/_matfuncs_sqrtm.py +107 -0
  659. scipy/linalg/_matfuncs_sqrtm_triu.cp314-win_arm64.lib +0 -0
  660. scipy/linalg/_matfuncs_sqrtm_triu.cp314-win_arm64.pyd +0 -0
  661. scipy/linalg/_misc.py +191 -0
  662. scipy/linalg/_procrustes.py +113 -0
  663. scipy/linalg/_sketches.py +189 -0
  664. scipy/linalg/_solve_toeplitz.cp314-win_arm64.lib +0 -0
  665. scipy/linalg/_solve_toeplitz.cp314-win_arm64.pyd +0 -0
  666. scipy/linalg/_solvers.py +862 -0
  667. scipy/linalg/_special_matrices.py +1322 -0
  668. scipy/linalg/_testutils.py +65 -0
  669. scipy/linalg/basic.py +23 -0
  670. scipy/linalg/blas.py +495 -0
  671. scipy/linalg/cython_blas.cp314-win_arm64.lib +0 -0
  672. scipy/linalg/cython_blas.cp314-win_arm64.pyd +0 -0
  673. scipy/linalg/cython_blas.pxd +169 -0
  674. scipy/linalg/cython_blas.pyx +1432 -0
  675. scipy/linalg/cython_lapack.cp314-win_arm64.lib +0 -0
  676. scipy/linalg/cython_lapack.cp314-win_arm64.pyd +0 -0
  677. scipy/linalg/cython_lapack.pxd +1528 -0
  678. scipy/linalg/cython_lapack.pyx +12045 -0
  679. scipy/linalg/decomp.py +23 -0
  680. scipy/linalg/decomp_cholesky.py +21 -0
  681. scipy/linalg/decomp_lu.py +21 -0
  682. scipy/linalg/decomp_qr.py +20 -0
  683. scipy/linalg/decomp_schur.py +21 -0
  684. scipy/linalg/decomp_svd.py +21 -0
  685. scipy/linalg/interpolative.py +989 -0
  686. scipy/linalg/lapack.py +1081 -0
  687. scipy/linalg/matfuncs.py +23 -0
  688. scipy/linalg/misc.py +21 -0
  689. scipy/linalg/special_matrices.py +22 -0
  690. scipy/linalg/tests/__init__.py +0 -0
  691. scipy/linalg/tests/_cython_examples/extending.pyx +23 -0
  692. scipy/linalg/tests/_cython_examples/meson.build +34 -0
  693. scipy/linalg/tests/data/carex_15_data.npz +0 -0
  694. scipy/linalg/tests/data/carex_18_data.npz +0 -0
  695. scipy/linalg/tests/data/carex_19_data.npz +0 -0
  696. scipy/linalg/tests/data/carex_20_data.npz +0 -0
  697. scipy/linalg/tests/data/carex_6_data.npz +0 -0
  698. scipy/linalg/tests/data/gendare_20170120_data.npz +0 -0
  699. scipy/linalg/tests/test_basic.py +2074 -0
  700. scipy/linalg/tests/test_batch.py +588 -0
  701. scipy/linalg/tests/test_blas.py +1127 -0
  702. scipy/linalg/tests/test_cython_blas.py +118 -0
  703. scipy/linalg/tests/test_cython_lapack.py +22 -0
  704. scipy/linalg/tests/test_cythonized_array_utils.py +130 -0
  705. scipy/linalg/tests/test_decomp.py +3189 -0
  706. scipy/linalg/tests/test_decomp_cholesky.py +268 -0
  707. scipy/linalg/tests/test_decomp_cossin.py +314 -0
  708. scipy/linalg/tests/test_decomp_ldl.py +137 -0
  709. scipy/linalg/tests/test_decomp_lu.py +308 -0
  710. scipy/linalg/tests/test_decomp_polar.py +110 -0
  711. scipy/linalg/tests/test_decomp_update.py +1701 -0
  712. scipy/linalg/tests/test_extending.py +46 -0
  713. scipy/linalg/tests/test_fblas.py +607 -0
  714. scipy/linalg/tests/test_interpolative.py +232 -0
  715. scipy/linalg/tests/test_lapack.py +3620 -0
  716. scipy/linalg/tests/test_matfuncs.py +1125 -0
  717. scipy/linalg/tests/test_matmul_toeplitz.py +136 -0
  718. scipy/linalg/tests/test_procrustes.py +214 -0
  719. scipy/linalg/tests/test_sketches.py +118 -0
  720. scipy/linalg/tests/test_solve_toeplitz.py +150 -0
  721. scipy/linalg/tests/test_solvers.py +844 -0
  722. scipy/linalg/tests/test_special_matrices.py +636 -0
  723. scipy/misc/__init__.py +6 -0
  724. scipy/misc/common.py +6 -0
  725. scipy/misc/doccer.py +6 -0
  726. scipy/ndimage/__init__.py +174 -0
  727. scipy/ndimage/_ctest.cp314-win_arm64.lib +0 -0
  728. scipy/ndimage/_ctest.cp314-win_arm64.pyd +0 -0
  729. scipy/ndimage/_cytest.cp314-win_arm64.lib +0 -0
  730. scipy/ndimage/_cytest.cp314-win_arm64.pyd +0 -0
  731. scipy/ndimage/_delegators.py +303 -0
  732. scipy/ndimage/_filters.py +2422 -0
  733. scipy/ndimage/_fourier.py +306 -0
  734. scipy/ndimage/_interpolation.py +1033 -0
  735. scipy/ndimage/_measurements.py +1689 -0
  736. scipy/ndimage/_morphology.py +2634 -0
  737. scipy/ndimage/_nd_image.cp314-win_arm64.lib +0 -0
  738. scipy/ndimage/_nd_image.cp314-win_arm64.pyd +0 -0
  739. scipy/ndimage/_ndimage_api.py +16 -0
  740. scipy/ndimage/_ni_docstrings.py +214 -0
  741. scipy/ndimage/_ni_label.cp314-win_arm64.lib +0 -0
  742. scipy/ndimage/_ni_label.cp314-win_arm64.pyd +0 -0
  743. scipy/ndimage/_ni_support.py +139 -0
  744. scipy/ndimage/_rank_filter_1d.cp314-win_arm64.lib +0 -0
  745. scipy/ndimage/_rank_filter_1d.cp314-win_arm64.pyd +0 -0
  746. scipy/ndimage/_support_alternative_backends.py +84 -0
  747. scipy/ndimage/filters.py +27 -0
  748. scipy/ndimage/fourier.py +21 -0
  749. scipy/ndimage/interpolation.py +22 -0
  750. scipy/ndimage/measurements.py +24 -0
  751. scipy/ndimage/morphology.py +27 -0
  752. scipy/ndimage/tests/__init__.py +12 -0
  753. scipy/ndimage/tests/data/label_inputs.txt +21 -0
  754. scipy/ndimage/tests/data/label_results.txt +294 -0
  755. scipy/ndimage/tests/data/label_strels.txt +42 -0
  756. scipy/ndimage/tests/dots.png +0 -0
  757. scipy/ndimage/tests/test_c_api.py +102 -0
  758. scipy/ndimage/tests/test_datatypes.py +67 -0
  759. scipy/ndimage/tests/test_filters.py +3083 -0
  760. scipy/ndimage/tests/test_fourier.py +187 -0
  761. scipy/ndimage/tests/test_interpolation.py +1491 -0
  762. scipy/ndimage/tests/test_measurements.py +1592 -0
  763. scipy/ndimage/tests/test_morphology.py +2950 -0
  764. scipy/ndimage/tests/test_ni_support.py +78 -0
  765. scipy/ndimage/tests/test_splines.py +70 -0
  766. scipy/odr/__init__.py +131 -0
  767. scipy/odr/__odrpack.cp314-win_arm64.lib +0 -0
  768. scipy/odr/__odrpack.cp314-win_arm64.pyd +0 -0
  769. scipy/odr/_add_newdocs.py +34 -0
  770. scipy/odr/_models.py +315 -0
  771. scipy/odr/_odrpack.py +1154 -0
  772. scipy/odr/models.py +20 -0
  773. scipy/odr/odrpack.py +21 -0
  774. scipy/odr/tests/__init__.py +0 -0
  775. scipy/odr/tests/test_odr.py +607 -0
  776. scipy/optimize/__init__.pxd +1 -0
  777. scipy/optimize/__init__.py +460 -0
  778. scipy/optimize/_basinhopping.py +741 -0
  779. scipy/optimize/_bglu_dense.cp314-win_arm64.lib +0 -0
  780. scipy/optimize/_bglu_dense.cp314-win_arm64.pyd +0 -0
  781. scipy/optimize/_bracket.py +706 -0
  782. scipy/optimize/_chandrupatla.py +551 -0
  783. scipy/optimize/_cobyla_py.py +297 -0
  784. scipy/optimize/_cobyqa_py.py +72 -0
  785. scipy/optimize/_constraints.py +598 -0
  786. scipy/optimize/_dcsrch.py +728 -0
  787. scipy/optimize/_differentiable_functions.py +835 -0
  788. scipy/optimize/_differentialevolution.py +1970 -0
  789. scipy/optimize/_direct.cp314-win_arm64.lib +0 -0
  790. scipy/optimize/_direct.cp314-win_arm64.pyd +0 -0
  791. scipy/optimize/_direct_py.py +280 -0
  792. scipy/optimize/_dual_annealing.py +732 -0
  793. scipy/optimize/_elementwise.py +798 -0
  794. scipy/optimize/_group_columns.cp314-win_arm64.lib +0 -0
  795. scipy/optimize/_group_columns.cp314-win_arm64.pyd +0 -0
  796. scipy/optimize/_hessian_update_strategy.py +479 -0
  797. scipy/optimize/_highspy/__init__.py +0 -0
  798. scipy/optimize/_highspy/_core.cp314-win_arm64.lib +0 -0
  799. scipy/optimize/_highspy/_core.cp314-win_arm64.pyd +0 -0
  800. scipy/optimize/_highspy/_highs_options.cp314-win_arm64.lib +0 -0
  801. scipy/optimize/_highspy/_highs_options.cp314-win_arm64.pyd +0 -0
  802. scipy/optimize/_highspy/_highs_wrapper.py +338 -0
  803. scipy/optimize/_isotonic.py +157 -0
  804. scipy/optimize/_lbfgsb.cp314-win_arm64.lib +0 -0
  805. scipy/optimize/_lbfgsb.cp314-win_arm64.pyd +0 -0
  806. scipy/optimize/_lbfgsb_py.py +634 -0
  807. scipy/optimize/_linesearch.py +896 -0
  808. scipy/optimize/_linprog.py +733 -0
  809. scipy/optimize/_linprog_doc.py +1434 -0
  810. scipy/optimize/_linprog_highs.py +422 -0
  811. scipy/optimize/_linprog_ip.py +1141 -0
  812. scipy/optimize/_linprog_rs.py +572 -0
  813. scipy/optimize/_linprog_simplex.py +663 -0
  814. scipy/optimize/_linprog_util.py +1521 -0
  815. scipy/optimize/_lsap.cp314-win_arm64.lib +0 -0
  816. scipy/optimize/_lsap.cp314-win_arm64.pyd +0 -0
  817. scipy/optimize/_lsq/__init__.py +5 -0
  818. scipy/optimize/_lsq/bvls.py +183 -0
  819. scipy/optimize/_lsq/common.py +731 -0
  820. scipy/optimize/_lsq/dogbox.py +345 -0
  821. scipy/optimize/_lsq/givens_elimination.cp314-win_arm64.lib +0 -0
  822. scipy/optimize/_lsq/givens_elimination.cp314-win_arm64.pyd +0 -0
  823. scipy/optimize/_lsq/least_squares.py +1044 -0
  824. scipy/optimize/_lsq/lsq_linear.py +361 -0
  825. scipy/optimize/_lsq/trf.py +587 -0
  826. scipy/optimize/_lsq/trf_linear.py +249 -0
  827. scipy/optimize/_milp.py +394 -0
  828. scipy/optimize/_minimize.py +1199 -0
  829. scipy/optimize/_minpack.cp314-win_arm64.lib +0 -0
  830. scipy/optimize/_minpack.cp314-win_arm64.pyd +0 -0
  831. scipy/optimize/_minpack_py.py +1178 -0
  832. scipy/optimize/_moduleTNC.cp314-win_arm64.lib +0 -0
  833. scipy/optimize/_moduleTNC.cp314-win_arm64.pyd +0 -0
  834. scipy/optimize/_nnls.py +96 -0
  835. scipy/optimize/_nonlin.py +1634 -0
  836. scipy/optimize/_numdiff.py +963 -0
  837. scipy/optimize/_optimize.py +4169 -0
  838. scipy/optimize/_pava_pybind.cp314-win_arm64.lib +0 -0
  839. scipy/optimize/_pava_pybind.cp314-win_arm64.pyd +0 -0
  840. scipy/optimize/_qap.py +760 -0
  841. scipy/optimize/_remove_redundancy.py +522 -0
  842. scipy/optimize/_root.py +732 -0
  843. scipy/optimize/_root_scalar.py +538 -0
  844. scipy/optimize/_shgo.py +1606 -0
  845. scipy/optimize/_shgo_lib/__init__.py +0 -0
  846. scipy/optimize/_shgo_lib/_complex.py +1225 -0
  847. scipy/optimize/_shgo_lib/_vertex.py +460 -0
  848. scipy/optimize/_slsqp_py.py +603 -0
  849. scipy/optimize/_slsqplib.cp314-win_arm64.lib +0 -0
  850. scipy/optimize/_slsqplib.cp314-win_arm64.pyd +0 -0
  851. scipy/optimize/_spectral.py +260 -0
  852. scipy/optimize/_tnc.py +438 -0
  853. scipy/optimize/_trlib/__init__.py +12 -0
  854. scipy/optimize/_trlib/_trlib.cp314-win_arm64.lib +0 -0
  855. scipy/optimize/_trlib/_trlib.cp314-win_arm64.pyd +0 -0
  856. scipy/optimize/_trustregion.py +318 -0
  857. scipy/optimize/_trustregion_constr/__init__.py +6 -0
  858. scipy/optimize/_trustregion_constr/canonical_constraint.py +390 -0
  859. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +231 -0
  860. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +584 -0
  861. scipy/optimize/_trustregion_constr/projections.py +411 -0
  862. scipy/optimize/_trustregion_constr/qp_subproblem.py +637 -0
  863. scipy/optimize/_trustregion_constr/report.py +49 -0
  864. scipy/optimize/_trustregion_constr/tests/__init__.py +0 -0
  865. scipy/optimize/_trustregion_constr/tests/test_canonical_constraint.py +296 -0
  866. scipy/optimize/_trustregion_constr/tests/test_nested_minimize.py +39 -0
  867. scipy/optimize/_trustregion_constr/tests/test_projections.py +214 -0
  868. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +645 -0
  869. scipy/optimize/_trustregion_constr/tests/test_report.py +34 -0
  870. scipy/optimize/_trustregion_constr/tr_interior_point.py +361 -0
  871. scipy/optimize/_trustregion_dogleg.py +122 -0
  872. scipy/optimize/_trustregion_exact.py +437 -0
  873. scipy/optimize/_trustregion_krylov.py +65 -0
  874. scipy/optimize/_trustregion_ncg.py +126 -0
  875. scipy/optimize/_tstutils.py +972 -0
  876. scipy/optimize/_zeros.cp314-win_arm64.lib +0 -0
  877. scipy/optimize/_zeros.cp314-win_arm64.pyd +0 -0
  878. scipy/optimize/_zeros_py.py +1475 -0
  879. scipy/optimize/cobyla.py +19 -0
  880. scipy/optimize/cython_optimize/__init__.py +133 -0
  881. scipy/optimize/cython_optimize/_zeros.cp314-win_arm64.lib +0 -0
  882. scipy/optimize/cython_optimize/_zeros.cp314-win_arm64.pyd +0 -0
  883. scipy/optimize/cython_optimize/_zeros.pxd +33 -0
  884. scipy/optimize/cython_optimize/c_zeros.pxd +26 -0
  885. scipy/optimize/cython_optimize.pxd +11 -0
  886. scipy/optimize/elementwise.py +38 -0
  887. scipy/optimize/lbfgsb.py +23 -0
  888. scipy/optimize/linesearch.py +18 -0
  889. scipy/optimize/minpack.py +27 -0
  890. scipy/optimize/minpack2.py +17 -0
  891. scipy/optimize/moduleTNC.py +19 -0
  892. scipy/optimize/nonlin.py +29 -0
  893. scipy/optimize/optimize.py +40 -0
  894. scipy/optimize/slsqp.py +22 -0
  895. scipy/optimize/tests/__init__.py +0 -0
  896. scipy/optimize/tests/_cython_examples/extending.pyx +43 -0
  897. scipy/optimize/tests/_cython_examples/meson.build +32 -0
  898. scipy/optimize/tests/test__basinhopping.py +535 -0
  899. scipy/optimize/tests/test__differential_evolution.py +1703 -0
  900. scipy/optimize/tests/test__dual_annealing.py +416 -0
  901. scipy/optimize/tests/test__linprog_clean_inputs.py +312 -0
  902. scipy/optimize/tests/test__numdiff.py +885 -0
  903. scipy/optimize/tests/test__remove_redundancy.py +228 -0
  904. scipy/optimize/tests/test__root.py +124 -0
  905. scipy/optimize/tests/test__shgo.py +1164 -0
  906. scipy/optimize/tests/test__spectral.py +226 -0
  907. scipy/optimize/tests/test_bracket.py +896 -0
  908. scipy/optimize/tests/test_chandrupatla.py +982 -0
  909. scipy/optimize/tests/test_cobyla.py +195 -0
  910. scipy/optimize/tests/test_cobyqa.py +252 -0
  911. scipy/optimize/tests/test_constraint_conversion.py +286 -0
  912. scipy/optimize/tests/test_constraints.py +255 -0
  913. scipy/optimize/tests/test_cython_optimize.py +92 -0
  914. scipy/optimize/tests/test_differentiable_functions.py +1025 -0
  915. scipy/optimize/tests/test_direct.py +321 -0
  916. scipy/optimize/tests/test_extending.py +28 -0
  917. scipy/optimize/tests/test_hessian_update_strategy.py +300 -0
  918. scipy/optimize/tests/test_isotonic_regression.py +167 -0
  919. scipy/optimize/tests/test_lbfgsb_hessinv.py +65 -0
  920. scipy/optimize/tests/test_lbfgsb_setulb.py +122 -0
  921. scipy/optimize/tests/test_least_squares.py +986 -0
  922. scipy/optimize/tests/test_linear_assignment.py +116 -0
  923. scipy/optimize/tests/test_linesearch.py +328 -0
  924. scipy/optimize/tests/test_linprog.py +2577 -0
  925. scipy/optimize/tests/test_lsq_common.py +297 -0
  926. scipy/optimize/tests/test_lsq_linear.py +287 -0
  927. scipy/optimize/tests/test_milp.py +459 -0
  928. scipy/optimize/tests/test_minimize_constrained.py +845 -0
  929. scipy/optimize/tests/test_minpack.py +1194 -0
  930. scipy/optimize/tests/test_nnls.py +469 -0
  931. scipy/optimize/tests/test_nonlin.py +572 -0
  932. scipy/optimize/tests/test_optimize.py +3344 -0
  933. scipy/optimize/tests/test_quadratic_assignment.py +455 -0
  934. scipy/optimize/tests/test_regression.py +40 -0
  935. scipy/optimize/tests/test_slsqp.py +645 -0
  936. scipy/optimize/tests/test_tnc.py +345 -0
  937. scipy/optimize/tests/test_trustregion.py +110 -0
  938. scipy/optimize/tests/test_trustregion_exact.py +351 -0
  939. scipy/optimize/tests/test_trustregion_krylov.py +170 -0
  940. scipy/optimize/tests/test_zeros.py +998 -0
  941. scipy/optimize/tnc.py +22 -0
  942. scipy/optimize/zeros.py +26 -0
  943. scipy/signal/__init__.py +316 -0
  944. scipy/signal/_arraytools.py +264 -0
  945. scipy/signal/_czt.py +575 -0
  946. scipy/signal/_delegators.py +568 -0
  947. scipy/signal/_filter_design.py +5893 -0
  948. scipy/signal/_fir_filter_design.py +1458 -0
  949. scipy/signal/_lti_conversion.py +534 -0
  950. scipy/signal/_ltisys.py +3546 -0
  951. scipy/signal/_max_len_seq.py +139 -0
  952. scipy/signal/_max_len_seq_inner.cp314-win_arm64.lib +0 -0
  953. scipy/signal/_max_len_seq_inner.cp314-win_arm64.pyd +0 -0
  954. scipy/signal/_peak_finding.py +1310 -0
  955. scipy/signal/_peak_finding_utils.cp314-win_arm64.lib +0 -0
  956. scipy/signal/_peak_finding_utils.cp314-win_arm64.pyd +0 -0
  957. scipy/signal/_polyutils.py +172 -0
  958. scipy/signal/_savitzky_golay.py +357 -0
  959. scipy/signal/_short_time_fft.py +2228 -0
  960. scipy/signal/_signal_api.py +30 -0
  961. scipy/signal/_signaltools.py +5309 -0
  962. scipy/signal/_sigtools.cp314-win_arm64.lib +0 -0
  963. scipy/signal/_sigtools.cp314-win_arm64.pyd +0 -0
  964. scipy/signal/_sosfilt.cp314-win_arm64.lib +0 -0
  965. scipy/signal/_sosfilt.cp314-win_arm64.pyd +0 -0
  966. scipy/signal/_spectral_py.py +2471 -0
  967. scipy/signal/_spline.cp314-win_arm64.lib +0 -0
  968. scipy/signal/_spline.cp314-win_arm64.pyd +0 -0
  969. scipy/signal/_spline.pyi +34 -0
  970. scipy/signal/_spline_filters.py +848 -0
  971. scipy/signal/_support_alternative_backends.py +73 -0
  972. scipy/signal/_upfirdn.py +219 -0
  973. scipy/signal/_upfirdn_apply.cp314-win_arm64.lib +0 -0
  974. scipy/signal/_upfirdn_apply.cp314-win_arm64.pyd +0 -0
  975. scipy/signal/_waveforms.py +687 -0
  976. scipy/signal/_wavelets.py +29 -0
  977. scipy/signal/bsplines.py +21 -0
  978. scipy/signal/filter_design.py +28 -0
  979. scipy/signal/fir_filter_design.py +21 -0
  980. scipy/signal/lti_conversion.py +20 -0
  981. scipy/signal/ltisys.py +25 -0
  982. scipy/signal/signaltools.py +27 -0
  983. scipy/signal/spectral.py +21 -0
  984. scipy/signal/spline.py +18 -0
  985. scipy/signal/tests/__init__.py +0 -0
  986. scipy/signal/tests/_scipy_spectral_test_shim.py +311 -0
  987. scipy/signal/tests/mpsig.py +122 -0
  988. scipy/signal/tests/test_array_tools.py +111 -0
  989. scipy/signal/tests/test_bsplines.py +365 -0
  990. scipy/signal/tests/test_cont2discrete.py +424 -0
  991. scipy/signal/tests/test_czt.py +221 -0
  992. scipy/signal/tests/test_dltisys.py +599 -0
  993. scipy/signal/tests/test_filter_design.py +4744 -0
  994. scipy/signal/tests/test_fir_filter_design.py +851 -0
  995. scipy/signal/tests/test_ltisys.py +1225 -0
  996. scipy/signal/tests/test_max_len_seq.py +71 -0
  997. scipy/signal/tests/test_peak_finding.py +915 -0
  998. scipy/signal/tests/test_result_type.py +51 -0
  999. scipy/signal/tests/test_savitzky_golay.py +363 -0
  1000. scipy/signal/tests/test_short_time_fft.py +1107 -0
  1001. scipy/signal/tests/test_signaltools.py +4735 -0
  1002. scipy/signal/tests/test_spectral.py +2141 -0
  1003. scipy/signal/tests/test_splines.py +427 -0
  1004. scipy/signal/tests/test_upfirdn.py +322 -0
  1005. scipy/signal/tests/test_waveforms.py +400 -0
  1006. scipy/signal/tests/test_wavelets.py +59 -0
  1007. scipy/signal/tests/test_windows.py +987 -0
  1008. scipy/signal/waveforms.py +20 -0
  1009. scipy/signal/wavelets.py +17 -0
  1010. scipy/signal/windows/__init__.py +52 -0
  1011. scipy/signal/windows/_windows.py +2513 -0
  1012. scipy/signal/windows/windows.py +23 -0
  1013. scipy/sparse/__init__.py +350 -0
  1014. scipy/sparse/_base.py +1613 -0
  1015. scipy/sparse/_bsr.py +880 -0
  1016. scipy/sparse/_compressed.py +1328 -0
  1017. scipy/sparse/_construct.py +1454 -0
  1018. scipy/sparse/_coo.py +1581 -0
  1019. scipy/sparse/_csc.py +367 -0
  1020. scipy/sparse/_csparsetools.cp314-win_arm64.lib +0 -0
  1021. scipy/sparse/_csparsetools.cp314-win_arm64.pyd +0 -0
  1022. scipy/sparse/_csr.py +558 -0
  1023. scipy/sparse/_data.py +569 -0
  1024. scipy/sparse/_dia.py +677 -0
  1025. scipy/sparse/_dok.py +669 -0
  1026. scipy/sparse/_extract.py +178 -0
  1027. scipy/sparse/_index.py +444 -0
  1028. scipy/sparse/_lil.py +632 -0
  1029. scipy/sparse/_matrix.py +169 -0
  1030. scipy/sparse/_matrix_io.py +167 -0
  1031. scipy/sparse/_sparsetools.cp314-win_arm64.lib +0 -0
  1032. scipy/sparse/_sparsetools.cp314-win_arm64.pyd +0 -0
  1033. scipy/sparse/_spfuncs.py +76 -0
  1034. scipy/sparse/_sputils.py +632 -0
  1035. scipy/sparse/base.py +24 -0
  1036. scipy/sparse/bsr.py +22 -0
  1037. scipy/sparse/compressed.py +20 -0
  1038. scipy/sparse/construct.py +38 -0
  1039. scipy/sparse/coo.py +23 -0
  1040. scipy/sparse/csc.py +22 -0
  1041. scipy/sparse/csgraph/__init__.py +210 -0
  1042. scipy/sparse/csgraph/_flow.cp314-win_arm64.lib +0 -0
  1043. scipy/sparse/csgraph/_flow.cp314-win_arm64.pyd +0 -0
  1044. scipy/sparse/csgraph/_laplacian.py +563 -0
  1045. scipy/sparse/csgraph/_matching.cp314-win_arm64.lib +0 -0
  1046. scipy/sparse/csgraph/_matching.cp314-win_arm64.pyd +0 -0
  1047. scipy/sparse/csgraph/_min_spanning_tree.cp314-win_arm64.lib +0 -0
  1048. scipy/sparse/csgraph/_min_spanning_tree.cp314-win_arm64.pyd +0 -0
  1049. scipy/sparse/csgraph/_reordering.cp314-win_arm64.lib +0 -0
  1050. scipy/sparse/csgraph/_reordering.cp314-win_arm64.pyd +0 -0
  1051. scipy/sparse/csgraph/_shortest_path.cp314-win_arm64.lib +0 -0
  1052. scipy/sparse/csgraph/_shortest_path.cp314-win_arm64.pyd +0 -0
  1053. scipy/sparse/csgraph/_tools.cp314-win_arm64.lib +0 -0
  1054. scipy/sparse/csgraph/_tools.cp314-win_arm64.pyd +0 -0
  1055. scipy/sparse/csgraph/_traversal.cp314-win_arm64.lib +0 -0
  1056. scipy/sparse/csgraph/_traversal.cp314-win_arm64.pyd +0 -0
  1057. scipy/sparse/csgraph/_validation.py +66 -0
  1058. scipy/sparse/csgraph/tests/__init__.py +0 -0
  1059. scipy/sparse/csgraph/tests/test_connected_components.py +119 -0
  1060. scipy/sparse/csgraph/tests/test_conversions.py +61 -0
  1061. scipy/sparse/csgraph/tests/test_flow.py +209 -0
  1062. scipy/sparse/csgraph/tests/test_graph_laplacian.py +368 -0
  1063. scipy/sparse/csgraph/tests/test_matching.py +307 -0
  1064. scipy/sparse/csgraph/tests/test_pydata_sparse.py +197 -0
  1065. scipy/sparse/csgraph/tests/test_reordering.py +70 -0
  1066. scipy/sparse/csgraph/tests/test_shortest_path.py +540 -0
  1067. scipy/sparse/csgraph/tests/test_spanning_tree.py +66 -0
  1068. scipy/sparse/csgraph/tests/test_traversal.py +148 -0
  1069. scipy/sparse/csr.py +22 -0
  1070. scipy/sparse/data.py +18 -0
  1071. scipy/sparse/dia.py +22 -0
  1072. scipy/sparse/dok.py +22 -0
  1073. scipy/sparse/extract.py +23 -0
  1074. scipy/sparse/lil.py +22 -0
  1075. scipy/sparse/linalg/__init__.py +148 -0
  1076. scipy/sparse/linalg/_dsolve/__init__.py +71 -0
  1077. scipy/sparse/linalg/_dsolve/_add_newdocs.py +147 -0
  1078. scipy/sparse/linalg/_dsolve/_superlu.cp314-win_arm64.lib +0 -0
  1079. scipy/sparse/linalg/_dsolve/_superlu.cp314-win_arm64.pyd +0 -0
  1080. scipy/sparse/linalg/_dsolve/linsolve.py +882 -0
  1081. scipy/sparse/linalg/_dsolve/tests/__init__.py +0 -0
  1082. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +928 -0
  1083. scipy/sparse/linalg/_eigen/__init__.py +22 -0
  1084. scipy/sparse/linalg/_eigen/_svds.py +540 -0
  1085. scipy/sparse/linalg/_eigen/_svds_doc.py +382 -0
  1086. scipy/sparse/linalg/_eigen/arpack/COPYING +45 -0
  1087. scipy/sparse/linalg/_eigen/arpack/__init__.py +20 -0
  1088. scipy/sparse/linalg/_eigen/arpack/_arpack.cp314-win_arm64.lib +0 -0
  1089. scipy/sparse/linalg/_eigen/arpack/_arpack.cp314-win_arm64.pyd +0 -0
  1090. scipy/sparse/linalg/_eigen/arpack/arpack.py +1706 -0
  1091. scipy/sparse/linalg/_eigen/arpack/tests/__init__.py +0 -0
  1092. scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py +717 -0
  1093. scipy/sparse/linalg/_eigen/lobpcg/__init__.py +16 -0
  1094. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +1110 -0
  1095. scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py +0 -0
  1096. scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py +725 -0
  1097. scipy/sparse/linalg/_eigen/tests/__init__.py +0 -0
  1098. scipy/sparse/linalg/_eigen/tests/test_svds.py +886 -0
  1099. scipy/sparse/linalg/_expm_multiply.py +816 -0
  1100. scipy/sparse/linalg/_interface.py +920 -0
  1101. scipy/sparse/linalg/_isolve/__init__.py +20 -0
  1102. scipy/sparse/linalg/_isolve/_gcrotmk.py +503 -0
  1103. scipy/sparse/linalg/_isolve/iterative.py +1051 -0
  1104. scipy/sparse/linalg/_isolve/lgmres.py +230 -0
  1105. scipy/sparse/linalg/_isolve/lsmr.py +486 -0
  1106. scipy/sparse/linalg/_isolve/lsqr.py +589 -0
  1107. scipy/sparse/linalg/_isolve/minres.py +372 -0
  1108. scipy/sparse/linalg/_isolve/tests/__init__.py +0 -0
  1109. scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py +183 -0
  1110. scipy/sparse/linalg/_isolve/tests/test_iterative.py +809 -0
  1111. scipy/sparse/linalg/_isolve/tests/test_lgmres.py +225 -0
  1112. scipy/sparse/linalg/_isolve/tests/test_lsmr.py +185 -0
  1113. scipy/sparse/linalg/_isolve/tests/test_lsqr.py +120 -0
  1114. scipy/sparse/linalg/_isolve/tests/test_minres.py +97 -0
  1115. scipy/sparse/linalg/_isolve/tests/test_utils.py +9 -0
  1116. scipy/sparse/linalg/_isolve/tfqmr.py +179 -0
  1117. scipy/sparse/linalg/_isolve/utils.py +121 -0
  1118. scipy/sparse/linalg/_matfuncs.py +940 -0
  1119. scipy/sparse/linalg/_norm.py +195 -0
  1120. scipy/sparse/linalg/_onenormest.py +467 -0
  1121. scipy/sparse/linalg/_propack/_cpropack.cp314-win_arm64.lib +0 -0
  1122. scipy/sparse/linalg/_propack/_cpropack.cp314-win_arm64.pyd +0 -0
  1123. scipy/sparse/linalg/_propack/_dpropack.cp314-win_arm64.lib +0 -0
  1124. scipy/sparse/linalg/_propack/_dpropack.cp314-win_arm64.pyd +0 -0
  1125. scipy/sparse/linalg/_propack/_spropack.cp314-win_arm64.lib +0 -0
  1126. scipy/sparse/linalg/_propack/_spropack.cp314-win_arm64.pyd +0 -0
  1127. scipy/sparse/linalg/_propack/_zpropack.cp314-win_arm64.lib +0 -0
  1128. scipy/sparse/linalg/_propack/_zpropack.cp314-win_arm64.pyd +0 -0
  1129. scipy/sparse/linalg/_special_sparse_arrays.py +949 -0
  1130. scipy/sparse/linalg/_svdp.py +309 -0
  1131. scipy/sparse/linalg/dsolve.py +22 -0
  1132. scipy/sparse/linalg/eigen.py +21 -0
  1133. scipy/sparse/linalg/interface.py +20 -0
  1134. scipy/sparse/linalg/isolve.py +22 -0
  1135. scipy/sparse/linalg/matfuncs.py +18 -0
  1136. scipy/sparse/linalg/tests/__init__.py +0 -0
  1137. scipy/sparse/linalg/tests/propack_test_data.npz +0 -0
  1138. scipy/sparse/linalg/tests/test_expm_multiply.py +367 -0
  1139. scipy/sparse/linalg/tests/test_interface.py +561 -0
  1140. scipy/sparse/linalg/tests/test_matfuncs.py +592 -0
  1141. scipy/sparse/linalg/tests/test_norm.py +154 -0
  1142. scipy/sparse/linalg/tests/test_onenormest.py +252 -0
  1143. scipy/sparse/linalg/tests/test_propack.py +165 -0
  1144. scipy/sparse/linalg/tests/test_pydata_sparse.py +272 -0
  1145. scipy/sparse/linalg/tests/test_special_sparse_arrays.py +337 -0
  1146. scipy/sparse/sparsetools.py +17 -0
  1147. scipy/sparse/spfuncs.py +17 -0
  1148. scipy/sparse/sputils.py +17 -0
  1149. scipy/sparse/tests/__init__.py +0 -0
  1150. scipy/sparse/tests/data/csc_py2.npz +0 -0
  1151. scipy/sparse/tests/data/csc_py3.npz +0 -0
  1152. scipy/sparse/tests/test_arithmetic1d.py +341 -0
  1153. scipy/sparse/tests/test_array_api.py +561 -0
  1154. scipy/sparse/tests/test_base.py +5870 -0
  1155. scipy/sparse/tests/test_common1d.py +447 -0
  1156. scipy/sparse/tests/test_construct.py +872 -0
  1157. scipy/sparse/tests/test_coo.py +1119 -0
  1158. scipy/sparse/tests/test_csc.py +98 -0
  1159. scipy/sparse/tests/test_csr.py +214 -0
  1160. scipy/sparse/tests/test_dok.py +209 -0
  1161. scipy/sparse/tests/test_extract.py +51 -0
  1162. scipy/sparse/tests/test_indexing1d.py +603 -0
  1163. scipy/sparse/tests/test_matrix_io.py +109 -0
  1164. scipy/sparse/tests/test_minmax1d.py +128 -0
  1165. scipy/sparse/tests/test_sparsetools.py +344 -0
  1166. scipy/sparse/tests/test_spfuncs.py +97 -0
  1167. scipy/sparse/tests/test_sputils.py +424 -0
  1168. scipy/spatial/__init__.py +129 -0
  1169. scipy/spatial/_ckdtree.cp314-win_arm64.lib +0 -0
  1170. scipy/spatial/_ckdtree.cp314-win_arm64.pyd +0 -0
  1171. scipy/spatial/_distance_pybind.cp314-win_arm64.lib +0 -0
  1172. scipy/spatial/_distance_pybind.cp314-win_arm64.pyd +0 -0
  1173. scipy/spatial/_distance_wrap.cp314-win_arm64.lib +0 -0
  1174. scipy/spatial/_distance_wrap.cp314-win_arm64.pyd +0 -0
  1175. scipy/spatial/_geometric_slerp.py +238 -0
  1176. scipy/spatial/_hausdorff.cp314-win_arm64.lib +0 -0
  1177. scipy/spatial/_hausdorff.cp314-win_arm64.pyd +0 -0
  1178. scipy/spatial/_kdtree.py +920 -0
  1179. scipy/spatial/_plotutils.py +274 -0
  1180. scipy/spatial/_procrustes.py +132 -0
  1181. scipy/spatial/_qhull.cp314-win_arm64.lib +0 -0
  1182. scipy/spatial/_qhull.cp314-win_arm64.pyd +0 -0
  1183. scipy/spatial/_qhull.pyi +213 -0
  1184. scipy/spatial/_spherical_voronoi.py +341 -0
  1185. scipy/spatial/_voronoi.cp314-win_arm64.lib +0 -0
  1186. scipy/spatial/_voronoi.cp314-win_arm64.pyd +0 -0
  1187. scipy/spatial/_voronoi.pyi +4 -0
  1188. scipy/spatial/ckdtree.py +18 -0
  1189. scipy/spatial/distance.py +3147 -0
  1190. scipy/spatial/distance.pyi +210 -0
  1191. scipy/spatial/kdtree.py +25 -0
  1192. scipy/spatial/qhull.py +25 -0
  1193. scipy/spatial/qhull_src/COPYING_QHULL.txt +39 -0
  1194. scipy/spatial/tests/__init__.py +0 -0
  1195. scipy/spatial/tests/data/cdist-X1.txt +10 -0
  1196. scipy/spatial/tests/data/cdist-X2.txt +20 -0
  1197. scipy/spatial/tests/data/degenerate_pointset.npz +0 -0
  1198. scipy/spatial/tests/data/iris.txt +150 -0
  1199. scipy/spatial/tests/data/pdist-boolean-inp.txt +20 -0
  1200. scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt +1 -0
  1201. scipy/spatial/tests/data/pdist-chebyshev-ml.txt +1 -0
  1202. scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt +1 -0
  1203. scipy/spatial/tests/data/pdist-cityblock-ml.txt +1 -0
  1204. scipy/spatial/tests/data/pdist-correlation-ml-iris.txt +1 -0
  1205. scipy/spatial/tests/data/pdist-correlation-ml.txt +1 -0
  1206. scipy/spatial/tests/data/pdist-cosine-ml-iris.txt +1 -0
  1207. scipy/spatial/tests/data/pdist-cosine-ml.txt +1 -0
  1208. scipy/spatial/tests/data/pdist-double-inp.txt +20 -0
  1209. scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt +1 -0
  1210. scipy/spatial/tests/data/pdist-euclidean-ml.txt +1 -0
  1211. scipy/spatial/tests/data/pdist-hamming-ml.txt +1 -0
  1212. scipy/spatial/tests/data/pdist-jaccard-ml.txt +1 -0
  1213. scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt +1 -0
  1214. scipy/spatial/tests/data/pdist-jensenshannon-ml.txt +1 -0
  1215. scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt +1 -0
  1216. scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt +1 -0
  1217. scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt +1 -0
  1218. scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt +1 -0
  1219. scipy/spatial/tests/data/pdist-seuclidean-ml.txt +1 -0
  1220. scipy/spatial/tests/data/pdist-spearman-ml.txt +1 -0
  1221. scipy/spatial/tests/data/random-bool-data.txt +100 -0
  1222. scipy/spatial/tests/data/random-double-data.txt +100 -0
  1223. scipy/spatial/tests/data/random-int-data.txt +100 -0
  1224. scipy/spatial/tests/data/random-uint-data.txt +100 -0
  1225. scipy/spatial/tests/data/selfdual-4d-polytope.txt +27 -0
  1226. scipy/spatial/tests/test__plotutils.py +91 -0
  1227. scipy/spatial/tests/test__procrustes.py +116 -0
  1228. scipy/spatial/tests/test_distance.py +2389 -0
  1229. scipy/spatial/tests/test_hausdorff.py +199 -0
  1230. scipy/spatial/tests/test_kdtree.py +1536 -0
  1231. scipy/spatial/tests/test_qhull.py +1313 -0
  1232. scipy/spatial/tests/test_slerp.py +417 -0
  1233. scipy/spatial/tests/test_spherical_voronoi.py +358 -0
  1234. scipy/spatial/transform/__init__.py +31 -0
  1235. scipy/spatial/transform/_rigid_transform.cp314-win_arm64.lib +0 -0
  1236. scipy/spatial/transform/_rigid_transform.cp314-win_arm64.pyd +0 -0
  1237. scipy/spatial/transform/_rotation.cp314-win_arm64.lib +0 -0
  1238. scipy/spatial/transform/_rotation.cp314-win_arm64.pyd +0 -0
  1239. scipy/spatial/transform/_rotation_groups.py +140 -0
  1240. scipy/spatial/transform/_rotation_spline.py +460 -0
  1241. scipy/spatial/transform/rotation.py +21 -0
  1242. scipy/spatial/transform/tests/__init__.py +0 -0
  1243. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  1244. scipy/spatial/transform/tests/test_rotation.py +2569 -0
  1245. scipy/spatial/transform/tests/test_rotation_groups.py +169 -0
  1246. scipy/spatial/transform/tests/test_rotation_spline.py +183 -0
  1247. scipy/special/__init__.pxd +1 -0
  1248. scipy/special/__init__.py +841 -0
  1249. scipy/special/_add_newdocs.py +9961 -0
  1250. scipy/special/_basic.py +3576 -0
  1251. scipy/special/_comb.cp314-win_arm64.lib +0 -0
  1252. scipy/special/_comb.cp314-win_arm64.pyd +0 -0
  1253. scipy/special/_ellip_harm.py +214 -0
  1254. scipy/special/_ellip_harm_2.cp314-win_arm64.lib +0 -0
  1255. scipy/special/_ellip_harm_2.cp314-win_arm64.pyd +0 -0
  1256. scipy/special/_gufuncs.cp314-win_arm64.lib +0 -0
  1257. scipy/special/_gufuncs.cp314-win_arm64.pyd +0 -0
  1258. scipy/special/_input_validation.py +17 -0
  1259. scipy/special/_lambertw.py +149 -0
  1260. scipy/special/_logsumexp.py +426 -0
  1261. scipy/special/_mptestutils.py +453 -0
  1262. scipy/special/_multiufuncs.py +610 -0
  1263. scipy/special/_orthogonal.py +2592 -0
  1264. scipy/special/_orthogonal.pyi +330 -0
  1265. scipy/special/_precompute/__init__.py +0 -0
  1266. scipy/special/_precompute/cosine_cdf.py +17 -0
  1267. scipy/special/_precompute/expn_asy.py +54 -0
  1268. scipy/special/_precompute/gammainc_asy.py +116 -0
  1269. scipy/special/_precompute/gammainc_data.py +124 -0
  1270. scipy/special/_precompute/hyp2f1_data.py +484 -0
  1271. scipy/special/_precompute/lambertw.py +68 -0
  1272. scipy/special/_precompute/loggamma.py +43 -0
  1273. scipy/special/_precompute/struve_convergence.py +131 -0
  1274. scipy/special/_precompute/utils.py +38 -0
  1275. scipy/special/_precompute/wright_bessel.py +342 -0
  1276. scipy/special/_precompute/wright_bessel_data.py +152 -0
  1277. scipy/special/_precompute/wrightomega.py +41 -0
  1278. scipy/special/_precompute/zetac.py +27 -0
  1279. scipy/special/_sf_error.py +15 -0
  1280. scipy/special/_specfun.cp314-win_arm64.lib +0 -0
  1281. scipy/special/_specfun.cp314-win_arm64.pyd +0 -0
  1282. scipy/special/_special_ufuncs.cp314-win_arm64.lib +0 -0
  1283. scipy/special/_special_ufuncs.cp314-win_arm64.pyd +0 -0
  1284. scipy/special/_spfun_stats.py +106 -0
  1285. scipy/special/_spherical_bessel.py +397 -0
  1286. scipy/special/_support_alternative_backends.py +295 -0
  1287. scipy/special/_test_internal.cp314-win_arm64.lib +0 -0
  1288. scipy/special/_test_internal.cp314-win_arm64.pyd +0 -0
  1289. scipy/special/_test_internal.pyi +9 -0
  1290. scipy/special/_testutils.py +321 -0
  1291. scipy/special/_ufuncs.cp314-win_arm64.lib +0 -0
  1292. scipy/special/_ufuncs.cp314-win_arm64.pyd +0 -0
  1293. scipy/special/_ufuncs.pyi +522 -0
  1294. scipy/special/_ufuncs.pyx +13173 -0
  1295. scipy/special/_ufuncs_cxx.cp314-win_arm64.lib +0 -0
  1296. scipy/special/_ufuncs_cxx.cp314-win_arm64.pyd +0 -0
  1297. scipy/special/_ufuncs_cxx.pxd +142 -0
  1298. scipy/special/_ufuncs_cxx.pyx +427 -0
  1299. scipy/special/_ufuncs_cxx_defs.h +147 -0
  1300. scipy/special/_ufuncs_defs.h +57 -0
  1301. scipy/special/add_newdocs.py +15 -0
  1302. scipy/special/basic.py +87 -0
  1303. scipy/special/cython_special.cp314-win_arm64.lib +0 -0
  1304. scipy/special/cython_special.cp314-win_arm64.pyd +0 -0
  1305. scipy/special/cython_special.pxd +259 -0
  1306. scipy/special/cython_special.pyi +3 -0
  1307. scipy/special/orthogonal.py +45 -0
  1308. scipy/special/sf_error.py +20 -0
  1309. scipy/special/specfun.py +24 -0
  1310. scipy/special/spfun_stats.py +17 -0
  1311. scipy/special/tests/__init__.py +0 -0
  1312. scipy/special/tests/_cython_examples/extending.pyx +12 -0
  1313. scipy/special/tests/_cython_examples/meson.build +34 -0
  1314. scipy/special/tests/data/__init__.py +0 -0
  1315. scipy/special/tests/data/boost.npz +0 -0
  1316. scipy/special/tests/data/gsl.npz +0 -0
  1317. scipy/special/tests/data/local.npz +0 -0
  1318. scipy/special/tests/test_basic.py +4815 -0
  1319. scipy/special/tests/test_bdtr.py +112 -0
  1320. scipy/special/tests/test_boost_ufuncs.py +64 -0
  1321. scipy/special/tests/test_boxcox.py +125 -0
  1322. scipy/special/tests/test_cdflib.py +712 -0
  1323. scipy/special/tests/test_cdft_asymptotic.py +49 -0
  1324. scipy/special/tests/test_cephes_intp_cast.py +29 -0
  1325. scipy/special/tests/test_cosine_distr.py +83 -0
  1326. scipy/special/tests/test_cython_special.py +363 -0
  1327. scipy/special/tests/test_data.py +719 -0
  1328. scipy/special/tests/test_dd.py +42 -0
  1329. scipy/special/tests/test_digamma.py +45 -0
  1330. scipy/special/tests/test_ellip_harm.py +278 -0
  1331. scipy/special/tests/test_erfinv.py +89 -0
  1332. scipy/special/tests/test_exponential_integrals.py +118 -0
  1333. scipy/special/tests/test_extending.py +28 -0
  1334. scipy/special/tests/test_faddeeva.py +85 -0
  1335. scipy/special/tests/test_gamma.py +12 -0
  1336. scipy/special/tests/test_gammainc.py +152 -0
  1337. scipy/special/tests/test_hyp2f1.py +2566 -0
  1338. scipy/special/tests/test_hypergeometric.py +234 -0
  1339. scipy/special/tests/test_iv_ratio.py +249 -0
  1340. scipy/special/tests/test_kolmogorov.py +491 -0
  1341. scipy/special/tests/test_lambertw.py +109 -0
  1342. scipy/special/tests/test_legendre.py +1518 -0
  1343. scipy/special/tests/test_log1mexp.py +85 -0
  1344. scipy/special/tests/test_loggamma.py +70 -0
  1345. scipy/special/tests/test_logit.py +162 -0
  1346. scipy/special/tests/test_logsumexp.py +469 -0
  1347. scipy/special/tests/test_mpmath.py +2293 -0
  1348. scipy/special/tests/test_nan_inputs.py +65 -0
  1349. scipy/special/tests/test_ndtr.py +77 -0
  1350. scipy/special/tests/test_ndtri_exp.py +94 -0
  1351. scipy/special/tests/test_orthogonal.py +821 -0
  1352. scipy/special/tests/test_orthogonal_eval.py +275 -0
  1353. scipy/special/tests/test_owens_t.py +53 -0
  1354. scipy/special/tests/test_pcf.py +24 -0
  1355. scipy/special/tests/test_pdtr.py +48 -0
  1356. scipy/special/tests/test_powm1.py +65 -0
  1357. scipy/special/tests/test_precompute_expn_asy.py +24 -0
  1358. scipy/special/tests/test_precompute_gammainc.py +108 -0
  1359. scipy/special/tests/test_precompute_utils.py +36 -0
  1360. scipy/special/tests/test_round.py +18 -0
  1361. scipy/special/tests/test_sf_error.py +146 -0
  1362. scipy/special/tests/test_sici.py +36 -0
  1363. scipy/special/tests/test_specfun.py +48 -0
  1364. scipy/special/tests/test_spence.py +32 -0
  1365. scipy/special/tests/test_spfun_stats.py +61 -0
  1366. scipy/special/tests/test_sph_harm.py +85 -0
  1367. scipy/special/tests/test_spherical_bessel.py +400 -0
  1368. scipy/special/tests/test_support_alternative_backends.py +248 -0
  1369. scipy/special/tests/test_trig.py +72 -0
  1370. scipy/special/tests/test_ufunc_signatures.py +46 -0
  1371. scipy/special/tests/test_wright_bessel.py +205 -0
  1372. scipy/special/tests/test_wrightomega.py +117 -0
  1373. scipy/special/tests/test_zeta.py +301 -0
  1374. scipy/stats/__init__.py +670 -0
  1375. scipy/stats/_ansari_swilk_statistics.cp314-win_arm64.lib +0 -0
  1376. scipy/stats/_ansari_swilk_statistics.cp314-win_arm64.pyd +0 -0
  1377. scipy/stats/_axis_nan_policy.py +692 -0
  1378. scipy/stats/_biasedurn.cp314-win_arm64.lib +0 -0
  1379. scipy/stats/_biasedurn.cp314-win_arm64.pyd +0 -0
  1380. scipy/stats/_biasedurn.pxd +27 -0
  1381. scipy/stats/_binned_statistic.py +795 -0
  1382. scipy/stats/_binomtest.py +375 -0
  1383. scipy/stats/_bws_test.py +177 -0
  1384. scipy/stats/_censored_data.py +459 -0
  1385. scipy/stats/_common.py +5 -0
  1386. scipy/stats/_constants.py +42 -0
  1387. scipy/stats/_continued_fraction.py +387 -0
  1388. scipy/stats/_continuous_distns.py +12486 -0
  1389. scipy/stats/_correlation.py +210 -0
  1390. scipy/stats/_covariance.py +636 -0
  1391. scipy/stats/_crosstab.py +204 -0
  1392. scipy/stats/_discrete_distns.py +2098 -0
  1393. scipy/stats/_distn_infrastructure.py +4201 -0
  1394. scipy/stats/_distr_params.py +299 -0
  1395. scipy/stats/_distribution_infrastructure.py +5750 -0
  1396. scipy/stats/_entropy.py +428 -0
  1397. scipy/stats/_finite_differences.py +145 -0
  1398. scipy/stats/_fit.py +1351 -0
  1399. scipy/stats/_hypotests.py +2060 -0
  1400. scipy/stats/_kde.py +732 -0
  1401. scipy/stats/_ksstats.py +600 -0
  1402. scipy/stats/_levy_stable/__init__.py +1231 -0
  1403. scipy/stats/_levy_stable/levyst.cp314-win_arm64.lib +0 -0
  1404. scipy/stats/_levy_stable/levyst.cp314-win_arm64.pyd +0 -0
  1405. scipy/stats/_mannwhitneyu.py +492 -0
  1406. scipy/stats/_mgc.py +550 -0
  1407. scipy/stats/_morestats.py +4626 -0
  1408. scipy/stats/_mstats_basic.py +3658 -0
  1409. scipy/stats/_mstats_extras.py +521 -0
  1410. scipy/stats/_multicomp.py +449 -0
  1411. scipy/stats/_multivariate.py +7281 -0
  1412. scipy/stats/_new_distributions.py +452 -0
  1413. scipy/stats/_odds_ratio.py +466 -0
  1414. scipy/stats/_page_trend_test.py +486 -0
  1415. scipy/stats/_probability_distribution.py +1964 -0
  1416. scipy/stats/_qmc.py +2956 -0
  1417. scipy/stats/_qmc_cy.cp314-win_arm64.lib +0 -0
  1418. scipy/stats/_qmc_cy.cp314-win_arm64.pyd +0 -0
  1419. scipy/stats/_qmc_cy.pyi +54 -0
  1420. scipy/stats/_qmvnt.py +454 -0
  1421. scipy/stats/_qmvnt_cy.cp314-win_arm64.lib +0 -0
  1422. scipy/stats/_qmvnt_cy.cp314-win_arm64.pyd +0 -0
  1423. scipy/stats/_quantile.py +335 -0
  1424. scipy/stats/_rcont/__init__.py +4 -0
  1425. scipy/stats/_rcont/rcont.cp314-win_arm64.lib +0 -0
  1426. scipy/stats/_rcont/rcont.cp314-win_arm64.pyd +0 -0
  1427. scipy/stats/_relative_risk.py +263 -0
  1428. scipy/stats/_resampling.py +2352 -0
  1429. scipy/stats/_result_classes.py +40 -0
  1430. scipy/stats/_sampling.py +1314 -0
  1431. scipy/stats/_sensitivity_analysis.py +713 -0
  1432. scipy/stats/_sobol.cp314-win_arm64.lib +0 -0
  1433. scipy/stats/_sobol.cp314-win_arm64.pyd +0 -0
  1434. scipy/stats/_sobol.pyi +54 -0
  1435. scipy/stats/_sobol_direction_numbers.npz +0 -0
  1436. scipy/stats/_stats.cp314-win_arm64.lib +0 -0
  1437. scipy/stats/_stats.cp314-win_arm64.pyd +0 -0
  1438. scipy/stats/_stats.pxd +10 -0
  1439. scipy/stats/_stats_mstats_common.py +322 -0
  1440. scipy/stats/_stats_py.py +11089 -0
  1441. scipy/stats/_stats_pythran.cp314-win_arm64.lib +0 -0
  1442. scipy/stats/_stats_pythran.cp314-win_arm64.pyd +0 -0
  1443. scipy/stats/_survival.py +683 -0
  1444. scipy/stats/_tukeylambda_stats.py +199 -0
  1445. scipy/stats/_unuran/__init__.py +0 -0
  1446. scipy/stats/_unuran/unuran_wrapper.cp314-win_arm64.lib +0 -0
  1447. scipy/stats/_unuran/unuran_wrapper.cp314-win_arm64.pyd +0 -0
  1448. scipy/stats/_unuran/unuran_wrapper.pyi +179 -0
  1449. scipy/stats/_variation.py +126 -0
  1450. scipy/stats/_warnings_errors.py +38 -0
  1451. scipy/stats/_wilcoxon.py +265 -0
  1452. scipy/stats/biasedurn.py +16 -0
  1453. scipy/stats/contingency.py +521 -0
  1454. scipy/stats/distributions.py +24 -0
  1455. scipy/stats/kde.py +18 -0
  1456. scipy/stats/morestats.py +27 -0
  1457. scipy/stats/mstats.py +140 -0
  1458. scipy/stats/mstats_basic.py +42 -0
  1459. scipy/stats/mstats_extras.py +25 -0
  1460. scipy/stats/mvn.py +17 -0
  1461. scipy/stats/qmc.py +236 -0
  1462. scipy/stats/sampling.py +73 -0
  1463. scipy/stats/stats.py +41 -0
  1464. scipy/stats/tests/__init__.py +0 -0
  1465. scipy/stats/tests/common_tests.py +356 -0
  1466. scipy/stats/tests/data/_mvt.py +171 -0
  1467. scipy/stats/tests/data/fisher_exact_results_from_r.py +607 -0
  1468. scipy/stats/tests/data/jf_skew_t_gamlss_pdf_data.npy +0 -0
  1469. scipy/stats/tests/data/levy_stable/stable-Z1-cdf-sample-data.npy +0 -0
  1470. scipy/stats/tests/data/levy_stable/stable-Z1-pdf-sample-data.npy +0 -0
  1471. scipy/stats/tests/data/levy_stable/stable-loc-scale-sample-data.npy +0 -0
  1472. scipy/stats/tests/data/nist_anova/AtmWtAg.dat +108 -0
  1473. scipy/stats/tests/data/nist_anova/SiRstv.dat +85 -0
  1474. scipy/stats/tests/data/nist_anova/SmLs01.dat +249 -0
  1475. scipy/stats/tests/data/nist_anova/SmLs02.dat +1869 -0
  1476. scipy/stats/tests/data/nist_anova/SmLs03.dat +18069 -0
  1477. scipy/stats/tests/data/nist_anova/SmLs04.dat +249 -0
  1478. scipy/stats/tests/data/nist_anova/SmLs05.dat +1869 -0
  1479. scipy/stats/tests/data/nist_anova/SmLs06.dat +18069 -0
  1480. scipy/stats/tests/data/nist_anova/SmLs07.dat +249 -0
  1481. scipy/stats/tests/data/nist_anova/SmLs08.dat +1869 -0
  1482. scipy/stats/tests/data/nist_anova/SmLs09.dat +18069 -0
  1483. scipy/stats/tests/data/nist_linregress/Norris.dat +97 -0
  1484. scipy/stats/tests/data/rel_breitwigner_pdf_sample_data_ROOT.npy +0 -0
  1485. scipy/stats/tests/data/studentized_range_mpmath_ref.json +1499 -0
  1486. scipy/stats/tests/test_axis_nan_policy.py +1388 -0
  1487. scipy/stats/tests/test_binned_statistic.py +568 -0
  1488. scipy/stats/tests/test_censored_data.py +152 -0
  1489. scipy/stats/tests/test_contingency.py +294 -0
  1490. scipy/stats/tests/test_continued_fraction.py +173 -0
  1491. scipy/stats/tests/test_continuous.py +2198 -0
  1492. scipy/stats/tests/test_continuous_basic.py +1053 -0
  1493. scipy/stats/tests/test_continuous_fit_censored.py +683 -0
  1494. scipy/stats/tests/test_correlation.py +80 -0
  1495. scipy/stats/tests/test_crosstab.py +115 -0
  1496. scipy/stats/tests/test_discrete_basic.py +580 -0
  1497. scipy/stats/tests/test_discrete_distns.py +700 -0
  1498. scipy/stats/tests/test_distributions.py +10413 -0
  1499. scipy/stats/tests/test_entropy.py +322 -0
  1500. scipy/stats/tests/test_fast_gen_inversion.py +435 -0
  1501. scipy/stats/tests/test_fit.py +1090 -0
  1502. scipy/stats/tests/test_hypotests.py +1991 -0
  1503. scipy/stats/tests/test_kdeoth.py +676 -0
  1504. scipy/stats/tests/test_marray.py +289 -0
  1505. scipy/stats/tests/test_mgc.py +217 -0
  1506. scipy/stats/tests/test_morestats.py +3259 -0
  1507. scipy/stats/tests/test_mstats_basic.py +2071 -0
  1508. scipy/stats/tests/test_mstats_extras.py +172 -0
  1509. scipy/stats/tests/test_multicomp.py +405 -0
  1510. scipy/stats/tests/test_multivariate.py +4381 -0
  1511. scipy/stats/tests/test_odds_ratio.py +148 -0
  1512. scipy/stats/tests/test_qmc.py +1492 -0
  1513. scipy/stats/tests/test_quantile.py +199 -0
  1514. scipy/stats/tests/test_rank.py +345 -0
  1515. scipy/stats/tests/test_relative_risk.py +95 -0
  1516. scipy/stats/tests/test_resampling.py +2000 -0
  1517. scipy/stats/tests/test_sampling.py +1450 -0
  1518. scipy/stats/tests/test_sensitivity_analysis.py +310 -0
  1519. scipy/stats/tests/test_stats.py +9707 -0
  1520. scipy/stats/tests/test_survival.py +466 -0
  1521. scipy/stats/tests/test_tukeylambda_stats.py +85 -0
  1522. scipy/stats/tests/test_variation.py +216 -0
  1523. scipy/version.py +12 -0
  1524. scipy-1.16.2.dist-info/DELVEWHEEL +2 -0
  1525. scipy-1.16.2.dist-info/LICENSE.txt +912 -0
  1526. scipy-1.16.2.dist-info/METADATA +1061 -0
  1527. scipy-1.16.2.dist-info/RECORD +1530 -0
  1528. scipy-1.16.2.dist-info/WHEEL +4 -0
  1529. scipy.libs/msvcp140-5f1c5dd31916990d94181e07bc3afb32.dll +0 -0
  1530. scipy.libs/scipy_openblas-f3ac85b1f412f7e86514c923dc4058d1.dll +0 -0
@@ -0,0 +1,2569 @@
1
+ import math
2
+
3
+ import pytest
4
+
5
+ import numpy as np
6
+ from numpy.testing import assert_equal
7
+ from scipy.spatial.transform import Rotation, Slerp
8
+ from scipy.stats import special_ortho_group
9
+ from itertools import permutations, product
10
+ from scipy._lib._array_api import (
11
+ xp_assert_equal,
12
+ is_numpy,
13
+ is_lazy_array,
14
+ xp_vector_norm,
15
+ xp_assert_close,
16
+ eager_warns,
17
+ xp_default_dtype
18
+ )
19
+ import scipy._lib.array_api_extra as xpx
20
+
21
+ import pickle
22
+ import copy
23
+
24
+
25
+ pytestmark = pytest.mark.skip_xp_backends(np_only=True)
26
+
27
+
28
+ def basis_vec(axis):
29
+ if axis == 'x':
30
+ return [1, 0, 0]
31
+ elif axis == 'y':
32
+ return [0, 1, 0]
33
+ elif axis == 'z':
34
+ return [0, 0, 1]
35
+
36
+
37
+ def rotation_to_xp(r: Rotation, xp):
38
+ return Rotation.from_quat(xp.asarray(r.as_quat()))
39
+
40
+
41
+ def test_init_non_array():
42
+ Rotation((0, 0, 0, 1))
43
+ Rotation([0, 0, 0, 1])
44
+
45
+
46
+ def test_generic_quat_matrix(xp):
47
+ x = xp.asarray([[3.0, 4, 0, 0], [5, 12, 0, 0]])
48
+ r = Rotation.from_quat(x)
49
+ expected_quat = x / xp.asarray([[5.0], [13.0]])
50
+ xp_assert_close(r.as_quat(), expected_quat)
51
+
52
+
53
+ def test_from_single_1d_quaternion(xp):
54
+ x = xp.asarray([3.0, 4, 0, 0])
55
+ r = Rotation.from_quat(x)
56
+ expected_quat = x / 5
57
+ xp_assert_close(r.as_quat(), expected_quat)
58
+
59
+
60
+ def test_from_single_2d_quaternion(xp):
61
+ x = xp.asarray([[3.0, 4, 0, 0]])
62
+ r = Rotation.from_quat(x)
63
+ expected_quat = x / 5
64
+ xp_assert_close(r.as_quat(), expected_quat)
65
+
66
+
67
+ def test_from_quat_scalar_first(xp):
68
+ rng = np.random.RandomState(0)
69
+
70
+ r = Rotation.from_quat(xp.asarray([1, 0, 0, 0]), scalar_first=True)
71
+ xp_assert_close(r.as_matrix(), xp.eye(3), rtol=1e-15, atol=1e-16)
72
+
73
+ q = xp.tile(xp.asarray([1, 0, 0, 0]), (10, 1))
74
+ r = Rotation.from_quat(q, scalar_first=True)
75
+ xp_assert_close(
76
+ r.as_matrix(), xp.tile(xp.eye(3), (10, 1, 1)), rtol=1e-15, atol=1e-16
77
+ )
78
+
79
+ q = xp.asarray(rng.randn(100, 4))
80
+ q /= xp_vector_norm(q, axis=1)[:, None]
81
+ for i in range(q.shape[0]): # Array API conforming loop
82
+ qi = q[i, ...]
83
+ r = Rotation.from_quat(qi, scalar_first=True)
84
+ xp_assert_close(xp.roll(r.as_quat(), 1), qi, rtol=1e-15)
85
+
86
+ r = Rotation.from_quat(q, scalar_first=True)
87
+ xp_assert_close(xp.roll(r.as_quat(), 1, axis=1), q, rtol=1e-15)
88
+
89
+
90
+ def test_from_quat_array_like():
91
+ rng = np.random.default_rng(123)
92
+ # Single rotation
93
+ r_expected = Rotation.random(rng=rng)
94
+ r = Rotation.from_quat(r_expected.as_quat().tolist())
95
+ assert r_expected.approx_equal(r, atol=1e-12)
96
+
97
+ # Multiple rotations
98
+ r_expected = Rotation.random(3, rng=rng)
99
+ r = Rotation.from_quat(r_expected.as_quat().tolist())
100
+ assert np.all(r_expected.approx_equal(r, atol=1e-12))
101
+
102
+
103
+ def test_from_quat_int_dtype(xp):
104
+ r = Rotation.from_quat(xp.asarray([1, 0, 0, 0]))
105
+ assert r.as_quat().dtype == xp_default_dtype(xp)
106
+
107
+
108
+ def test_as_quat_scalar_first(xp):
109
+ rng = np.random.RandomState(0)
110
+
111
+ r = Rotation.from_euler('xyz', xp.zeros(3))
112
+ xp_assert_close(r.as_quat(scalar_first=True), xp.asarray([1.0, 0, 0, 0]),
113
+ rtol=1e-15, atol=1e-16)
114
+
115
+ r = Rotation.from_euler('xyz', xp.zeros((10, 3)))
116
+ xp_assert_close(r.as_quat(scalar_first=True),
117
+ xp.tile(xp.asarray([1.0, 0, 0, 0]), (10, 1)),
118
+ rtol=1e-15, atol=1e-16)
119
+
120
+ q = xp.asarray(rng.randn(100, 4))
121
+ q /= xp_vector_norm(q, axis=1)[:, None]
122
+ for i in range(q.shape[0]): # Array API conforming loop
123
+ qi = q[i, ...]
124
+ r = Rotation.from_quat(qi)
125
+ xp_assert_close(r.as_quat(scalar_first=True), xp.roll(qi, 1),
126
+ rtol=1e-15)
127
+
128
+ xp_assert_close(r.as_quat(canonical=True, scalar_first=True),
129
+ xp.roll(r.as_quat(canonical=True), 1),
130
+ rtol=1e-15)
131
+
132
+ r = Rotation.from_quat(q)
133
+ xp_assert_close(r.as_quat(scalar_first=True), xp.roll(q, 1, axis=1),
134
+ rtol=1e-15)
135
+
136
+ xp_assert_close(r.as_quat(canonical=True, scalar_first=True),
137
+ xp.roll(r.as_quat(canonical=True), 1, axis=1), rtol=1e-15)
138
+
139
+
140
+ def test_from_square_quat_matrix(xp):
141
+ # Ensure proper norm array broadcasting
142
+ x = xp.asarray([
143
+ [3.0, 0, 0, 4],
144
+ [5, 0, 12, 0],
145
+ [0, 0, 0, 1],
146
+ [-1, -1, -1, 1],
147
+ [0, 0, 0, -1], # Check double cover
148
+ [-1, -1, -1, -1] # Check double cover
149
+ ])
150
+ r = Rotation.from_quat(x)
151
+ expected_quat = x / xp.asarray([[5.0], [13], [1], [2], [1], [2]])
152
+ xp_assert_close(r.as_quat(), expected_quat)
153
+
154
+
155
+ def test_quat_double_to_canonical_single_cover(xp):
156
+ x = xp.asarray([
157
+ [-1.0, 0, 0, 0],
158
+ [0, -1, 0, 0],
159
+ [0, 0, -1, 0],
160
+ [0, 0, 0, -1],
161
+ [-1, -1, -1, -1]
162
+ ])
163
+ r = Rotation.from_quat(x)
164
+ expected_quat = xp.abs(x) / xp_vector_norm(x, axis=1)[:, None]
165
+ xp_assert_close(r.as_quat(canonical=True), expected_quat)
166
+
167
+
168
+ def test_quat_double_cover(xp):
169
+ # See the Rotation.from_quat() docstring for scope of the quaternion
170
+ # double cover property.
171
+ # Check from_quat and as_quat(canonical=False)
172
+ q = xp.asarray([0.0, 0, 0, -1])
173
+ r = Rotation.from_quat(q)
174
+ xp_assert_equal(q, r.as_quat(canonical=False))
175
+ # Check composition and inverse
176
+ q = xp.asarray([1.0, 0, 0, 1])/math.sqrt(2) # 90 deg rotation about x
177
+ r = Rotation.from_quat(q)
178
+ r3 = r*r*r
179
+ xp_assert_close(r.as_quat(canonical=False)*math.sqrt(2),
180
+ xp.asarray([1.0, 0, 0, 1]))
181
+ xp_assert_close(r.inv().as_quat(canonical=False)*math.sqrt(2),
182
+ xp.asarray([-1.0, 0, 0, 1]))
183
+ xp_assert_close(r3.as_quat(canonical=False)*math.sqrt(2),
184
+ xp.asarray([1.0, 0, 0, -1]))
185
+ xp_assert_close(r3.inv().as_quat(canonical=False)*math.sqrt(2),
186
+ xp.asarray([-1.0, 0, 0, -1]))
187
+
188
+ # More sanity checks
189
+ xp_assert_close((r*r.inv()).as_quat(canonical=False),
190
+ xp.asarray([0.0, 0, 0, 1]), atol=2e-16)
191
+ xp_assert_close((r3*r3.inv()).as_quat(canonical=False),
192
+ xp.asarray([0.0, 0, 0, 1]), atol=2e-16)
193
+ xp_assert_close((r*r3).as_quat(canonical=False),
194
+ xp.asarray([0.0, 0, 0, -1]), atol=2e-16)
195
+ xp_assert_close((r.inv() * r3.inv()).as_quat(canonical=False),
196
+ xp.asarray([0.0, 0, 0, -1]), atol=2e-16)
197
+
198
+
199
+ def test_from_quat_wrong_shape(xp):
200
+ # Wrong shape 1d array
201
+ with pytest.raises(ValueError, match='Expected `quat` to have shape'):
202
+ Rotation.from_quat(xp.asarray([1, 2, 3]))
203
+
204
+ # Wrong shape 2d array
205
+ with pytest.raises(ValueError, match='Expected `quat` to have shape'):
206
+ Rotation.from_quat(xp.asarray([
207
+ [1, 2, 3, 4, 5],
208
+ [4, 5, 6, 7, 8]
209
+ ]))
210
+
211
+ # 3d array
212
+ with pytest.raises(ValueError, match='Expected `quat` to have shape'):
213
+ Rotation.from_quat(xp.asarray([
214
+ [[1, 2, 3, 4]],
215
+ [[4, 5, 6, 7]]
216
+ ]))
217
+
218
+
219
+ def test_zero_norms_from_quat(xp):
220
+ x = xp.asarray([
221
+ [3, 4, 0, 0],
222
+ [0, 0, 0, 0],
223
+ [5, 0, 12, 0]
224
+ ])
225
+ if is_lazy_array(x):
226
+ assert xp.all(xp.isnan(Rotation.from_quat(x).as_quat()[1, ...]))
227
+ else:
228
+ with pytest.raises(ValueError):
229
+ Rotation.from_quat(x)
230
+
231
+
232
+ def test_as_matrix_single_1d_quaternion(xp):
233
+ quat = xp.asarray([0, 0, 0, 1])
234
+ mat = Rotation.from_quat(quat).as_matrix()
235
+ # mat.shape == (3,3) due to 1d input
236
+ xp_assert_close(mat, xp.eye(3))
237
+
238
+
239
+ def test_as_matrix_single_2d_quaternion(xp):
240
+ quat = xp.asarray([[0, 0, 1, 1]])
241
+ mat = Rotation.from_quat(quat).as_matrix()
242
+ assert_equal(mat.shape, (1, 3, 3))
243
+ expected_mat = xp.asarray([
244
+ [0.0, -1, 0],
245
+ [1, 0, 0],
246
+ [0, 0, 1]
247
+ ])
248
+ xp_assert_close(mat[0, ...], expected_mat)
249
+
250
+
251
+ def test_as_matrix_from_square_input(xp):
252
+ quats = xp.asarray([
253
+ [0, 0, 1, 1],
254
+ [0, 1, 0, 1],
255
+ [0, 0, 0, 1],
256
+ [0, 0, 0, -1]
257
+ ])
258
+ mat = Rotation.from_quat(quats).as_matrix()
259
+ assert_equal(mat.shape, (4, 3, 3))
260
+
261
+ expected0 = xp.asarray([
262
+ [0.0, -1, 0],
263
+ [1, 0, 0],
264
+ [0, 0, 1]
265
+ ])
266
+ xp_assert_close(mat[0, ...], expected0)
267
+
268
+ expected1 = xp.asarray([
269
+ [0.0, 0, 1],
270
+ [0, 1, 0],
271
+ [-1, 0, 0]
272
+ ])
273
+ xp_assert_close(mat[1, ...], expected1)
274
+
275
+ xp_assert_close(mat[2, ...], xp.eye(3))
276
+ xp_assert_close(mat[3, ...], xp.eye(3))
277
+
278
+
279
+ def test_as_matrix_from_generic_input(xp):
280
+ quats = xp.asarray([
281
+ [0, 0, 1, 1],
282
+ [0, 1, 0, 1],
283
+ [1, 2, 3, 4]
284
+ ])
285
+ mat = Rotation.from_quat(quats).as_matrix()
286
+ assert_equal(mat.shape, (3, 3, 3))
287
+
288
+ expected0 = xp.asarray([
289
+ [0.0, -1, 0],
290
+ [1, 0, 0],
291
+ [0, 0, 1]
292
+ ])
293
+ xp_assert_close(mat[0, ...], expected0)
294
+
295
+ expected1 = xp.asarray([
296
+ [0.0, 0, 1],
297
+ [0, 1, 0],
298
+ [-1, 0, 0]
299
+ ])
300
+ xp_assert_close(mat[1, ...], expected1)
301
+
302
+ expected2 = xp.asarray([
303
+ [0.4, -2, 2.2],
304
+ [2.8, 1, 0.4],
305
+ [-1, 2, 2]
306
+ ]) / 3
307
+ xp_assert_close(mat[2, ...], expected2)
308
+
309
+
310
+ def test_from_single_2d_matrix(xp):
311
+ mat = xp.asarray([
312
+ [0, 0, 1],
313
+ [1, 0, 0],
314
+ [0, 1, 0]
315
+ ])
316
+ expected_quat = xp.asarray([0.5, 0.5, 0.5, 0.5])
317
+ xp_assert_close(Rotation.from_matrix(mat).as_quat(), expected_quat)
318
+
319
+
320
+ def test_from_single_3d_matrix(xp):
321
+ mat = xp.asarray([[
322
+ [0, 0, 1],
323
+ [1, 0, 0],
324
+ [0, 1, 0],
325
+ ]])
326
+ expected_quat = xp.asarray([[0.5, 0.5, 0.5, 0.5]])
327
+ xp_assert_close(Rotation.from_matrix(mat).as_quat(), expected_quat)
328
+
329
+
330
+ def test_from_matrix_calculation(xp):
331
+ atol = 1e-8
332
+ expected_quat = xp.asarray([1.0, 1, 6, 1]) / math.sqrt(39)
333
+ mat = xp.asarray([
334
+ [-0.8974359, -0.2564103, 0.3589744],
335
+ [0.3589744, -0.8974359, 0.2564103],
336
+ [0.2564103, 0.3589744, 0.8974359]
337
+ ])
338
+ xp_assert_close(Rotation.from_matrix(mat).as_quat(), expected_quat, atol=atol)
339
+ xp_assert_close(Rotation.from_matrix(xp.reshape(mat, (1, 3, 3))).as_quat(),
340
+ xp.reshape(expected_quat, (1, 4)),
341
+ atol=atol)
342
+
343
+
344
+ def test_matrix_calculation_pipeline(xp):
345
+ mat = xp.asarray(special_ortho_group.rvs(3, size=10, random_state=0))
346
+ xp_assert_close(Rotation.from_matrix(mat).as_matrix(), mat)
347
+
348
+
349
+ def test_from_matrix_ortho_output(xp):
350
+ atol = 1e-12
351
+ rnd = np.random.RandomState(0)
352
+ mat = xp.asarray(rnd.random_sample((100, 3, 3)))
353
+ dets = xp.linalg.det(mat)
354
+ for i in range(dets.shape[0]):
355
+ # Make sure we have a right-handed rotation matrix
356
+ if dets[i] < 0:
357
+ mat = xpx.at(mat)[i, ...].set(-mat[i, ...])
358
+ ortho_mat = Rotation.from_matrix(mat).as_matrix()
359
+
360
+ mult_result = xp.matmul(ortho_mat, xp.matrix_transpose(ortho_mat))
361
+
362
+ eye3d = xp.zeros((100, 3, 3)) + xp.eye(3)
363
+ xp_assert_close(mult_result, eye3d, atol=atol)
364
+
365
+
366
+ def test_from_matrix_normalize(xp):
367
+ mat = xp.asarray([
368
+ [1, 1, 0],
369
+ [0, 1, 0],
370
+ [0, 0, 1]])
371
+ expected = xp.asarray([[ 0.894427, 0.447214, 0.0],
372
+ [-0.447214, 0.894427, 0.0],
373
+ [ 0.0, 0.0, 1.0]])
374
+ xp_assert_close(Rotation.from_matrix(mat).as_matrix(), expected, atol=1e-6)
375
+
376
+ mat = xp.asarray([
377
+ [0, -0.5, 0 ],
378
+ [0.5, 0 , 0 ],
379
+ [0, 0 , 0.5]])
380
+ expected = xp.asarray([[0.0, -1, 0],
381
+ [ 1, 0, 0],
382
+ [ 0, 0, 1]])
383
+ xp_assert_close(Rotation.from_matrix(mat).as_matrix(), expected, atol=1e-6)
384
+
385
+
386
+ def test_from_matrix_non_positive_determinant(xp):
387
+ mat = xp.eye(3)
388
+ mat = xpx.at(mat)[0, 0].set(0)
389
+ if is_lazy_array(mat):
390
+ assert xp.all(xp.isnan(Rotation.from_matrix(mat).as_matrix()))
391
+ else:
392
+ with pytest.raises(ValueError, match="Non-positive determinant"):
393
+ Rotation.from_matrix(mat)
394
+
395
+ mat = xpx.at(mat)[0, 0].set(-1)
396
+ if is_lazy_array(mat):
397
+ assert xp.all(xp.isnan(Rotation.from_matrix(mat).as_matrix()))
398
+ else:
399
+ with pytest.raises(ValueError, match="Non-positive determinant"):
400
+ Rotation.from_matrix(mat)
401
+
402
+
403
+ def test_from_matrix_array_like():
404
+ rng = np.random.default_rng(123)
405
+ # Single rotation
406
+ r_expected = Rotation.random(rng=rng)
407
+ r = Rotation.from_matrix(r_expected.as_matrix().tolist())
408
+ assert r_expected.approx_equal(r, atol=1e-12)
409
+
410
+ # Multiple rotations
411
+ r_expected = Rotation.random(3, rng=rng)
412
+ r = Rotation.from_matrix(r_expected.as_matrix().tolist())
413
+ assert np.all(r_expected.approx_equal(r, atol=1e-12))
414
+
415
+
416
+ def test_from_matrix_int_dtype(xp):
417
+ mat = xp.asarray([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
418
+ r = Rotation.from_matrix(mat)
419
+ assert r.as_quat().dtype == xp_default_dtype(xp)
420
+
421
+
422
+ def test_from_1d_single_rotvec(xp):
423
+ atol = 1e-7
424
+ rotvec = xp.asarray([1, 0, 0])
425
+ expected_quat = xp.asarray([0.4794255, 0, 0, 0.8775826])
426
+ result = Rotation.from_rotvec(rotvec)
427
+ xp_assert_close(result.as_quat(), expected_quat, atol=atol)
428
+
429
+
430
+ def test_from_2d_single_rotvec(xp):
431
+ atol = 1e-7
432
+ rotvec = xp.asarray([[1, 0, 0]])
433
+ expected_quat = xp.asarray([[0.4794255, 0, 0, 0.8775826]])
434
+ result = Rotation.from_rotvec(rotvec)
435
+ xp_assert_close(result.as_quat(), expected_quat, atol=atol)
436
+
437
+
438
+ def test_from_generic_rotvec(xp):
439
+ atol = 1e-7
440
+ rotvec = xp.asarray([
441
+ [1, 2, 2],
442
+ [1, -1, 0.5],
443
+ [0, 0, 0]])
444
+ expected_quat = xp.asarray([
445
+ [0.3324983, 0.6649967, 0.6649967, 0.0707372],
446
+ [0.4544258, -0.4544258, 0.2272129, 0.7316889],
447
+ [0, 0, 0, 1]
448
+ ])
449
+ xp_assert_close(Rotation.from_rotvec(rotvec).as_quat(), expected_quat, atol=atol)
450
+
451
+
452
+ def test_from_rotvec_small_angle(xp):
453
+ rotvec = xp.asarray([
454
+ [5e-4 / math.sqrt(3), -5e-4 / math.sqrt(3), 5e-4 / math.sqrt(3)],
455
+ [0.2, 0.3, 0.4],
456
+ [0, 0, 0]
457
+ ])
458
+
459
+ quat = Rotation.from_rotvec(rotvec).as_quat()
460
+ # cos(theta/2) ~~ 1 for small theta
461
+ xp_assert_close(quat[0, 3], xp.asarray(1.0)[()])
462
+ # sin(theta/2) / theta ~~ 0.5 for small theta
463
+ xp_assert_close(quat[0, :3], rotvec[0, ...] * 0.5)
464
+
465
+ xp_assert_close(quat[1, 3], xp.asarray(0.9639685)[()])
466
+ xp_assert_close(quat[1, :3],
467
+ xp.asarray([
468
+ 0.09879603932153465,
469
+ 0.14819405898230198,
470
+ 0.19759207864306931]))
471
+
472
+ xp_assert_equal(quat[2, ...], xp.asarray([0.0, 0, 0, 1]))
473
+
474
+
475
+ def test_from_rotvec_array_like():
476
+ rng = np.random.default_rng(123)
477
+ # Single rotation
478
+ r_expected = Rotation.random(rng=rng)
479
+ r = Rotation.from_rotvec(r_expected.as_rotvec().tolist())
480
+ assert r_expected.approx_equal(r, atol=1e-12)
481
+
482
+ # Multiple rotations
483
+ r_expected = Rotation.random(3, rng=rng)
484
+ r = Rotation.from_rotvec(r_expected.as_rotvec().tolist())
485
+ assert np.all(r_expected.approx_equal(r, atol=1e-12))
486
+
487
+
488
+ def test_from_rotvec_int_dtype(xp):
489
+ rotvec = xp.asarray([1, 0, 0])
490
+ r = Rotation.from_rotvec(rotvec)
491
+ assert r.as_quat().dtype == xp_default_dtype(xp)
492
+
493
+
494
+ def test_degrees_from_rotvec(xp):
495
+ rotvec1 = xp.asarray([1 / 3 ** (1/3)] * 3)
496
+ rot1 = Rotation.from_rotvec(rotvec1, degrees=True)
497
+ quat1 = rot1.as_quat()
498
+
499
+ # deg2rad is not implemented in Array API -> / 180 * xp.pi
500
+ rotvec2 = xp.asarray(rotvec1 / 180 * xp.pi)
501
+ rot2 = Rotation.from_rotvec(rotvec2)
502
+ quat2 = rot2.as_quat()
503
+
504
+ xp_assert_close(quat1, quat2)
505
+
506
+
507
+ def test_malformed_1d_from_rotvec(xp):
508
+ with pytest.raises(ValueError, match='Expected `rot_vec` to have shape'):
509
+ Rotation.from_rotvec(xp.asarray([1, 2]))
510
+
511
+
512
+ def test_malformed_2d_from_rotvec(xp):
513
+ with pytest.raises(ValueError, match='Expected `rot_vec` to have shape'):
514
+ Rotation.from_rotvec(xp.asarray([
515
+ [1, 2, 3, 4],
516
+ [5, 6, 7, 8]
517
+ ]))
518
+
519
+
520
+ def test_as_generic_rotvec(xp):
521
+ quat = xp.asarray([
522
+ [1, 2, -1, 0.5],
523
+ [1, -1, 1, 0.0003],
524
+ [0, 0, 0, 1]
525
+ ])
526
+ quat /= xp_vector_norm(quat, axis=-1, keepdims=True)
527
+
528
+ rotvec = Rotation.from_quat(quat).as_rotvec()
529
+ angle = xp_vector_norm(rotvec, axis=-1)
530
+
531
+ xp_assert_close(quat[:, 3], xp.cos(angle / 2))
532
+ xp_assert_close(xp.linalg.cross(rotvec, quat[:, :3]), xp.zeros((3, 3)), atol=1e-15)
533
+
534
+
535
+ def test_as_rotvec_single_1d_input(xp):
536
+ quat = xp.asarray([1, 2, -3, 2])
537
+ expected_rotvec = xp.asarray([0.5772381, 1.1544763, -1.7317144])
538
+
539
+ actual_rotvec = Rotation.from_quat(quat).as_rotvec()
540
+
541
+ assert_equal(actual_rotvec.shape, (3,))
542
+ xp_assert_close(actual_rotvec, expected_rotvec)
543
+
544
+
545
+ def test_as_rotvec_single_2d_input(xp):
546
+ quat = xp.asarray([[1, 2, -3, 2]])
547
+ expected_rotvec = xp.asarray([[0.5772381, 1.1544763, -1.7317144]])
548
+
549
+ actual_rotvec = Rotation.from_quat(quat).as_rotvec()
550
+
551
+ assert_equal(actual_rotvec.shape, (1, 3))
552
+ xp_assert_close(actual_rotvec, expected_rotvec)
553
+
554
+
555
+ def test_as_rotvec_degrees(xp):
556
+ # x->y, y->z, z->x
557
+ mat = xp.asarray([[0, 0, 1], [1, 0, 0], [0, 1, 0]])
558
+ rot = Rotation.from_matrix(mat)
559
+ rotvec = rot.as_rotvec(degrees=True)
560
+ angle = xp_vector_norm(rotvec, axis=-1)
561
+ xp_assert_close(angle, xp.asarray(120.0)[()])
562
+ xp_assert_close(rotvec[0], rotvec[1])
563
+ xp_assert_close(rotvec[1], rotvec[2])
564
+
565
+
566
+ def test_rotvec_calc_pipeline(xp):
567
+ # Include small angles
568
+ rotvec = xp.asarray([
569
+ [0, 0, 0],
570
+ [1, -1, 2],
571
+ [-3e-4, 3.5e-4, 7.5e-5]
572
+ ])
573
+ xp_assert_close(Rotation.from_rotvec(rotvec).as_rotvec(), rotvec)
574
+ xp_assert_close(Rotation.from_rotvec(rotvec, degrees=True).as_rotvec(degrees=True),
575
+ rotvec)
576
+
577
+
578
+ def test_from_1d_single_mrp(xp):
579
+ mrp = xp.asarray([0, 0, 1.0])
580
+ expected_quat = xp.asarray([0.0, 0, 1, 0])
581
+ result = Rotation.from_mrp(mrp)
582
+ xp_assert_close(result.as_quat(), expected_quat, atol=1e-12)
583
+
584
+
585
+ def test_from_2d_single_mrp(xp):
586
+ mrp = xp.asarray([[0, 0, 1.0]])
587
+ expected_quat = xp.asarray([[0.0, 0, 1, 0]])
588
+ result = Rotation.from_mrp(mrp)
589
+ xp_assert_close(result.as_quat(), expected_quat)
590
+
591
+
592
+ def test_from_mrp_array_like():
593
+ rng = np.random.default_rng(123)
594
+ # Single rotation
595
+ r_expected = Rotation.random(rng=rng)
596
+ r = Rotation.from_mrp(r_expected.as_mrp().tolist())
597
+ assert r_expected.approx_equal(r, atol=1e-12)
598
+
599
+ # Multiple rotations
600
+ r_expected = Rotation.random(3, rng=rng)
601
+ r = Rotation.from_mrp(r_expected.as_mrp().tolist())
602
+ assert np.all(r_expected.approx_equal(r, atol=1e-12))
603
+
604
+
605
+ def test_from_mrp_int_dtype(xp):
606
+ mrp = xp.asarray([0, 0, 1])
607
+ r = Rotation.from_mrp(mrp)
608
+ assert r.as_quat().dtype == xp_default_dtype(xp)
609
+
610
+
611
+ def test_from_generic_mrp(xp):
612
+ mrp = xp.asarray([
613
+ [1, 2, 2],
614
+ [1, -1, 0.5],
615
+ [0, 0, 0]])
616
+ expected_quat = xp.asarray([
617
+ [0.2, 0.4, 0.4, -0.8],
618
+ [0.61538462, -0.61538462, 0.30769231, -0.38461538],
619
+ [0, 0, 0, 1]])
620
+ xp_assert_close(Rotation.from_mrp(mrp).as_quat(), expected_quat)
621
+
622
+
623
+ def test_malformed_1d_from_mrp(xp):
624
+ with pytest.raises(ValueError, match='Expected `mrp` to have shape'):
625
+ Rotation.from_mrp(xp.asarray([1, 2]))
626
+
627
+
628
+ def test_malformed_2d_from_mrp(xp):
629
+ with pytest.raises(ValueError, match='Expected `mrp` to have shape'):
630
+ Rotation.from_mrp(xp.asarray([
631
+ [1, 2, 3, 4],
632
+ [5, 6, 7, 8]
633
+ ]))
634
+
635
+
636
+ def test_as_generic_mrp(xp):
637
+ quat = xp.asarray([
638
+ [1, 2, -1, 0.5],
639
+ [1, -1, 1, 0.0003],
640
+ [0, 0, 0, 1]])
641
+ quat /= xp_vector_norm(quat, axis=1)[:, None]
642
+
643
+ expected_mrp = xp.asarray([
644
+ [0.33333333, 0.66666667, -0.33333333],
645
+ [0.57725028, -0.57725028, 0.57725028],
646
+ [0, 0, 0]])
647
+ xp_assert_close(Rotation.from_quat(quat).as_mrp(), expected_mrp)
648
+
649
+
650
+ def test_past_180_degree_rotation(xp):
651
+ # ensure that a > 180 degree rotation is returned as a <180 rotation in MRPs
652
+ # in this case 270 should be returned as -90
653
+ expected_mrp = xp.asarray([-math.tan(xp.pi / 2 / 4), 0.0, 0])
654
+ xp_assert_close(
655
+ Rotation.from_euler('xyz', xp.asarray([270, 0, 0]), degrees=True).as_mrp(),
656
+ expected_mrp,
657
+ )
658
+
659
+
660
+ def test_as_mrp_single_1d_input(xp):
661
+ quat = xp.asarray([1, 2, -3, 2])
662
+ expected_mrp = xp.asarray([0.16018862, 0.32037724, -0.48056586])
663
+
664
+ actual_mrp = Rotation.from_quat(quat).as_mrp()
665
+
666
+ assert_equal(actual_mrp.shape, (3,))
667
+ xp_assert_close(actual_mrp, expected_mrp)
668
+
669
+
670
+ def test_as_mrp_single_2d_input(xp):
671
+ quat = xp.asarray([[1, 2, -3, 2]])
672
+ expected_mrp = xp.asarray([[0.16018862, 0.32037724, -0.48056586]])
673
+
674
+ actual_mrp = Rotation.from_quat(quat).as_mrp()
675
+
676
+ assert_equal(actual_mrp.shape, (1, 3))
677
+ xp_assert_close(actual_mrp, expected_mrp)
678
+
679
+
680
+ def test_mrp_calc_pipeline(xp):
681
+ actual_mrp = xp.asarray([
682
+ [0, 0, 0],
683
+ [1, -1, 2],
684
+ [0.41421356, 0, 0],
685
+ [0.1, 0.2, 0.1]])
686
+ expected_mrp = xp.asarray([
687
+ [0, 0, 0],
688
+ [-0.16666667, 0.16666667, -0.33333333],
689
+ [0.41421356, 0, 0],
690
+ [0.1, 0.2, 0.1]])
691
+ xp_assert_close(Rotation.from_mrp(actual_mrp).as_mrp(), expected_mrp)
692
+
693
+
694
+ def test_from_euler_single_rotation(xp):
695
+ quat = Rotation.from_euler("z", xp.asarray(90), degrees=True).as_quat()
696
+ expected_quat = xp.asarray([0.0, 0, 1, 1]) / math.sqrt(2)
697
+ xp_assert_close(quat, expected_quat)
698
+
699
+
700
+ def test_single_intrinsic_extrinsic_rotation(xp):
701
+ extrinsic = Rotation.from_euler('z', xp.asarray(90), degrees=True).as_matrix()
702
+ intrinsic = Rotation.from_euler('Z', xp.asarray(90), degrees=True).as_matrix()
703
+ xp_assert_close(extrinsic, intrinsic)
704
+
705
+
706
+ def test_from_euler_rotation_order(xp):
707
+ # Intrinsic rotation is same as extrinsic with order reversed
708
+ rnd = np.random.RandomState(0)
709
+ a = xp.asarray(rnd.randint(low=0, high=180, size=(6, 3)))
710
+ b = xp.flip(a, axis=-1)
711
+ x = Rotation.from_euler('xyz', a, degrees=True).as_quat()
712
+ y = Rotation.from_euler('ZYX', b, degrees=True).as_quat()
713
+ xp_assert_close(x, y)
714
+
715
+
716
+ def test_from_euler_elementary_extrinsic_rotation(xp):
717
+ atol = 1e-12
718
+ # Simple test to check if extrinsic rotations are implemented correctly
719
+ mat = Rotation.from_euler('zx', xp.asarray([90, 90]), degrees=True).as_matrix()
720
+ expected_mat = xp.asarray([
721
+ [0.0, -1, 0],
722
+ [0, 0, -1],
723
+ [1, 0, 0]
724
+ ])
725
+ xp_assert_close(mat, expected_mat, atol=atol)
726
+
727
+
728
+ def test_from_euler_intrinsic_rotation_312(xp):
729
+ atol = 1e-7
730
+ angles = xp.asarray([
731
+ [30, 60, 45],
732
+ [30, 60, 30],
733
+ [45, 30, 60]
734
+ ])
735
+ mat = Rotation.from_euler('ZXY', angles, degrees=True).as_matrix()
736
+
737
+ xp_assert_close(mat[0, ...], xp.asarray([
738
+ [0.3061862, -0.2500000, 0.9185587],
739
+ [0.8838835, 0.4330127, -0.1767767],
740
+ [-0.3535534, 0.8660254, 0.3535534]
741
+ ]), atol=atol)
742
+
743
+ xp_assert_close(mat[1, ...], xp.asarray([
744
+ [0.5334936, -0.2500000, 0.8080127],
745
+ [0.8080127, 0.4330127, -0.3995191],
746
+ [-0.2500000, 0.8660254, 0.4330127]
747
+ ]), atol=atol)
748
+
749
+ xp_assert_close(mat[2, ...], xp.asarray([
750
+ [0.0473672, -0.6123725, 0.7891491],
751
+ [0.6597396, 0.6123725, 0.4355958],
752
+ [-0.7500000, 0.5000000, 0.4330127]
753
+ ]), atol=atol)
754
+
755
+
756
+ def test_from_euler_intrinsic_rotation_313(xp):
757
+ angles = xp.asarray([
758
+ [30, 60, 45],
759
+ [30, 60, 30],
760
+ [45, 30, 60]
761
+ ])
762
+ mat = Rotation.from_euler('ZXZ', angles, degrees=True).as_matrix()
763
+
764
+ xp_assert_close(mat[0, ...], xp.asarray([
765
+ [0.43559574, -0.78914913, 0.4330127],
766
+ [0.65973961, -0.04736717, -0.750000],
767
+ [0.61237244, 0.61237244, 0.500000]
768
+ ]))
769
+
770
+ xp_assert_close(mat[1, ...], xp.asarray([
771
+ [0.6250000, -0.64951905, 0.4330127],
772
+ [0.64951905, 0.1250000, -0.750000],
773
+ [0.4330127, 0.750000, 0.500000]
774
+ ]))
775
+
776
+ xp_assert_close(mat[2, ...], xp.asarray([
777
+ [-0.1767767, -0.91855865, 0.35355339],
778
+ [0.88388348, -0.30618622, -0.35355339],
779
+ [0.4330127, 0.25000000, 0.8660254]
780
+ ]))
781
+
782
+
783
+ def test_from_euler_extrinsic_rotation_312(xp):
784
+ angles = xp.asarray([
785
+ [30, 60, 45],
786
+ [30, 60, 30],
787
+ [45, 30, 60]
788
+ ])
789
+ mat = Rotation.from_euler('zxy', angles, degrees=True).as_matrix()
790
+
791
+ xp_assert_close(mat[0, ...], xp.asarray([
792
+ [0.91855865, 0.1767767, 0.35355339],
793
+ [0.25000000, 0.4330127, -0.8660254],
794
+ [-0.30618622, 0.88388348, 0.35355339]
795
+ ]))
796
+
797
+ xp_assert_close(mat[1, ...], xp.asarray([
798
+ [0.96650635, -0.0580127, 0.2500000],
799
+ [0.25000000, 0.4330127, -0.8660254],
800
+ [-0.0580127, 0.89951905, 0.4330127]
801
+ ]))
802
+
803
+ xp_assert_close(mat[2, ...], xp.asarray([
804
+ [0.65973961, -0.04736717, 0.7500000],
805
+ [0.61237244, 0.61237244, -0.5000000],
806
+ [-0.43559574, 0.78914913, 0.4330127]
807
+ ]))
808
+
809
+
810
+ def test_from_euler_extrinsic_rotation_313(xp):
811
+ angles = xp.asarray([
812
+ [30, 60, 45],
813
+ [30, 60, 30],
814
+ [45, 30, 60]
815
+ ])
816
+ mat = Rotation.from_euler('zxz', angles, degrees=True).as_matrix()
817
+
818
+ xp_assert_close(mat[0, ...], xp.asarray([
819
+ [0.43559574, -0.65973961, 0.61237244],
820
+ [0.78914913, -0.04736717, -0.61237244],
821
+ [0.4330127, 0.75000000, 0.500000]
822
+ ]))
823
+
824
+ xp_assert_close(mat[1, ...], xp.asarray([
825
+ [0.62500000, -0.64951905, 0.4330127],
826
+ [0.64951905, 0.12500000, -0.750000],
827
+ [0.4330127, 0.75000000, 0.500000]
828
+ ]))
829
+
830
+ xp_assert_close(mat[2, ...], xp.asarray([
831
+ [-0.1767767, -0.88388348, 0.4330127],
832
+ [0.91855865, -0.30618622, -0.250000],
833
+ [0.35355339, 0.35355339, 0.8660254]
834
+ ]))
835
+
836
+
837
+ def test_from_euler_array_like():
838
+ rng = np.random.default_rng(123)
839
+ order = "xyz"
840
+ # Single rotation
841
+ r_expected = Rotation.random(rng=rng)
842
+ r = Rotation.from_euler(order, r_expected.as_euler(order).tolist())
843
+ assert r_expected.approx_equal(r, atol=1e-12)
844
+
845
+ # Multiple rotations
846
+ r_expected = Rotation.random(3, rng=rng)
847
+ r = Rotation.from_euler(order, r_expected.as_euler(order).tolist())
848
+ assert np.all(r_expected.approx_equal(r, atol=1e-12))
849
+
850
+
851
+ def test_from_euler_scalar():
852
+ rng = np.random.default_rng(123)
853
+ deg = rng.uniform(low=-180, high=180)
854
+ r_expected = Rotation.from_euler("x", deg, degrees=True)
855
+ r = Rotation.from_euler("x", float(deg), degrees=True)
856
+ assert r_expected.approx_equal(r, atol=1e-12)
857
+
858
+
859
+ @pytest.mark.parametrize("seq_tuple", permutations("xyz"))
860
+ @pytest.mark.parametrize("intrinsic", (False, True))
861
+ def test_as_euler_asymmetric_axes(xp, seq_tuple, intrinsic):
862
+ # helper function for mean error tests
863
+ def test_stats(error, mean_max, rms_max):
864
+ mean = xp.mean(error, axis=0)
865
+ std = xp.std(error, axis=0)
866
+ rms = xp.hypot(mean, std)
867
+ assert xp.all(xp.abs(mean) < mean_max)
868
+ assert xp.all(rms < rms_max)
869
+
870
+ rnd = np.random.RandomState(0)
871
+ n = 1000
872
+ angles = np.empty((n, 3))
873
+ angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
874
+ angles[:, 1] = rnd.uniform(low=-np.pi / 2, high=np.pi / 2, size=(n,))
875
+ angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
876
+ angles = xp.asarray(angles)
877
+
878
+ seq = "".join(seq_tuple)
879
+ if intrinsic:
880
+ # Extrinsic rotation (wrt to global world) at lower case
881
+ # intrinsic (WRT the object itself) lower case.
882
+ seq = seq.upper()
883
+ rotation = Rotation.from_euler(seq, angles)
884
+ angles_quat = rotation.as_euler(seq)
885
+ # TODO: Why are we using _as_euler_from_matrix here? As a sanity check? It is not
886
+ # part of the public API and should not be used anywhere else
887
+ angles_mat = rotation._as_euler_from_matrix(seq)
888
+ xp_assert_close(angles, angles_quat, atol=0, rtol=1e-12)
889
+ xp_assert_close(angles, angles_mat, atol=0, rtol=1e-12)
890
+ test_stats(angles_quat - angles, 1e-15, 1e-14)
891
+ test_stats(angles_mat - angles, 1e-15, 1e-14)
892
+
893
+
894
+
895
+ @pytest.mark.parametrize("seq_tuple", permutations("xyz"))
896
+ @pytest.mark.parametrize("intrinsic", (False, True))
897
+ def test_as_euler_symmetric_axes(xp, seq_tuple, intrinsic):
898
+ # helper function for mean error tests
899
+ def test_stats(error, mean_max, rms_max):
900
+ mean = xp.mean(error, axis=0)
901
+ std = xp.std(error, axis=0)
902
+ rms = xp.hypot(mean, std)
903
+ assert xp.all(xp.abs(mean) < mean_max)
904
+ assert xp.all(rms < rms_max)
905
+
906
+ rnd = np.random.RandomState(0)
907
+ n = 1000
908
+ angles = np.empty((n, 3))
909
+ angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
910
+ angles[:, 1] = rnd.uniform(low=0, high=np.pi, size=(n,))
911
+ angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
912
+ angles = xp.asarray(angles)
913
+
914
+ # Rotation of the form A/B/A are rotation around symmetric axes
915
+ seq = "".join([seq_tuple[0], seq_tuple[1], seq_tuple[0]])
916
+ if intrinsic:
917
+ seq = seq.upper()
918
+ rotation = Rotation.from_euler(seq, angles)
919
+ angles_quat = rotation.as_euler(seq)
920
+ # TODO: Same as before: Remove _as_euler_from_matrix?
921
+ angles_mat = rotation._as_euler_from_matrix(seq)
922
+ xp_assert_close(angles, angles_quat, atol=0, rtol=1e-13)
923
+ xp_assert_close(angles, angles_mat, atol=0, rtol=1e-9)
924
+ test_stats(angles_quat - angles, 1e-16, 1e-14)
925
+ test_stats(angles_mat - angles, 1e-15, 1e-13)
926
+
927
+
928
+ @pytest.mark.thread_unsafe
929
+ @pytest.mark.parametrize("seq_tuple", permutations("xyz"))
930
+ @pytest.mark.parametrize("intrinsic", (False, True))
931
+ def test_as_euler_degenerate_asymmetric_axes(xp, seq_tuple, intrinsic):
932
+ atol = 1e-12
933
+ # Since we cannot check for angle equality, we check for rotation matrix
934
+ # equality
935
+ angles = xp.asarray([
936
+ [45, 90, 35],
937
+ [35, -90, 20],
938
+ [35, 90, 25],
939
+ [25, -90, 15]])
940
+
941
+ seq = "".join(seq_tuple)
942
+ if intrinsic:
943
+ # Extrinsic rotation (wrt to global world) at lower case
944
+ # Intrinsic (WRT the object itself) upper case.
945
+ seq = seq.upper()
946
+ rotation = Rotation.from_euler(seq, angles, degrees=True)
947
+ mat_expected = rotation.as_matrix()
948
+
949
+ # We can only warn on non-lazy backends because we'd need to condition on traced
950
+ # booleans
951
+ with eager_warns(mat_expected, UserWarning, match="Gimbal lock"):
952
+ angle_estimates = rotation.as_euler(seq, degrees=True)
953
+ mat_estimated = Rotation.from_euler(seq, angle_estimates, degrees=True).as_matrix()
954
+
955
+ xp_assert_close(mat_expected, mat_estimated, atol=atol)
956
+
957
+
958
+ @pytest.mark.thread_unsafe
959
+ @pytest.mark.parametrize("seq_tuple", permutations("xyz"))
960
+ @pytest.mark.parametrize("intrinsic", (False, True))
961
+ def test_as_euler_degenerate_symmetric_axes(xp, seq_tuple, intrinsic):
962
+ atol = 1e-12
963
+ # Since we cannot check for angle equality, we check for rotation matrix
964
+ # equality
965
+ angles = xp.asarray([
966
+ [15, 0, 60],
967
+ [35, 0, 75],
968
+ [60, 180, 35],
969
+ [15, -180, 25]])
970
+
971
+ # Rotation of the form A/B/A are rotation around symmetric axes
972
+ seq = "".join([seq_tuple[0], seq_tuple[1], seq_tuple[0]])
973
+ if intrinsic:
974
+ # Extrinsic rotation (wrt to global world) at lower case
975
+ # Intrinsic (WRT the object itself) upper case.
976
+ seq = seq.upper()
977
+ rotation = Rotation.from_euler(seq, angles, degrees=True)
978
+ mat_expected = rotation.as_matrix()
979
+
980
+ # We can only warn on non-lazy backends
981
+ with eager_warns(mat_expected, UserWarning, match="Gimbal lock"):
982
+ angle_estimates = rotation.as_euler(seq, degrees=True)
983
+ mat_estimated = Rotation.from_euler(seq, angle_estimates, degrees=True).as_matrix()
984
+
985
+ xp_assert_close(mat_expected, mat_estimated, atol=atol)
986
+
987
+
988
+ @pytest.mark.thread_unsafe
989
+ @pytest.mark.parametrize("seq_tuple", permutations("xyz"))
990
+ @pytest.mark.parametrize("intrinsic", (False, True))
991
+ def test_as_euler_degenerate_compare_algorithms(xp, seq_tuple, intrinsic):
992
+ # this test makes sure that both algorithms are doing the same choices
993
+ # in degenerate cases
994
+
995
+ # asymmetric axes
996
+ angles = xp.asarray([
997
+ [45, 90, 35],
998
+ [35, -90, 20],
999
+ [35, 90, 25],
1000
+ [25, -90, 15]])
1001
+
1002
+ seq = "".join(seq_tuple)
1003
+ if intrinsic:
1004
+ # Extrinsic rotation (wrt to global world at lower case
1005
+ # Intrinsic (WRT the object itself) upper case.
1006
+ seq = seq.upper()
1007
+
1008
+ rot = Rotation.from_euler(seq, angles, degrees=True)
1009
+ with eager_warns(rot, UserWarning, match="Gimbal lock"):
1010
+ estimates_matrix = rot._as_euler_from_matrix(seq, degrees=True)
1011
+ estimates_quat = rot.as_euler(seq, degrees=True)
1012
+ xp_assert_close(
1013
+ estimates_matrix[:, [0, 2]], estimates_quat[:, [0, 2]], atol=0, rtol=1e-12
1014
+ )
1015
+ xp_assert_close(estimates_matrix[:, 1], estimates_quat[:, 1], atol=0, rtol=1e-7)
1016
+
1017
+ # symmetric axes
1018
+ # Absolute error tolerance must be looser to directly compare the results
1019
+ # from both algorithms, because of numerical loss of precision for the
1020
+ # method _as_euler_from_matrix near a zero angle value
1021
+
1022
+ angles = xp.asarray([
1023
+ [15, 0, 60],
1024
+ [35, 0, 75],
1025
+ [60, 180, 35],
1026
+ [15, -180, 25]])
1027
+
1028
+ idx = angles[:, 1] == 0 # find problematic angles indices
1029
+
1030
+ # Rotation of the form A/B/A are rotation around symmetric axes
1031
+ seq = "".join([seq_tuple[0], seq_tuple[1], seq_tuple[0]])
1032
+ if intrinsic:
1033
+ # Extrinsic rotation (wrt to global world) at lower case
1034
+ # Intrinsic (WRT the object itself) upper case.
1035
+ seq = seq.upper()
1036
+
1037
+ rot = Rotation.from_euler(seq, angles, degrees=True)
1038
+ with eager_warns(rot, UserWarning, match="Gimbal lock"):
1039
+ estimates_matrix = rot._as_euler_from_matrix(seq, degrees=True)
1040
+ with eager_warns(rot, UserWarning, match="Gimbal lock"):
1041
+ estimates_quat = rot.as_euler(seq, degrees=True)
1042
+ xp_assert_close(
1043
+ estimates_matrix[:, [0, 2]], estimates_quat[:, [0, 2]], atol=0, rtol=1e-12
1044
+ )
1045
+
1046
+ xp_assert_close(
1047
+ estimates_matrix[~idx, 1], estimates_quat[~idx, 1], atol=0, rtol=1e-7
1048
+ )
1049
+
1050
+ xp_assert_close(
1051
+ estimates_matrix[idx, 1], estimates_quat[idx, 1], atol=1e-6
1052
+ ) # problematic, angles[1] = 0
1053
+
1054
+
1055
+ def test_inv(xp):
1056
+ atol = 1e-12
1057
+ rnd = np.random.RandomState(0)
1058
+ n = 10
1059
+ # preserve use of old random_state during SPEC 7 transition
1060
+ p = Rotation.random(num=n, random_state=rnd)
1061
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
1062
+ q = p.inv()
1063
+
1064
+ p_mat = p.as_matrix()
1065
+ q_mat = q.as_matrix()
1066
+ result1 = xp.asarray(np.einsum("...ij,...jk->...ik", p_mat, q_mat))
1067
+ result2 = xp.asarray(np.einsum("...ij,...jk->...ik", q_mat, p_mat))
1068
+
1069
+ eye3d = xp.empty((n, 3, 3))
1070
+ eye3d = xpx.at(eye3d)[..., :3, :3].set(xp.eye(3))
1071
+
1072
+ xp_assert_close(result1, eye3d, atol=atol)
1073
+ xp_assert_close(result2, eye3d, atol=atol)
1074
+
1075
+
1076
+ def test_inv_single_rotation(xp):
1077
+ atol = 1e-12
1078
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1079
+ p = Rotation.random(rng=rng)
1080
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
1081
+ q = p.inv()
1082
+
1083
+ p_mat = p.as_matrix()
1084
+ q_mat = q.as_matrix()
1085
+ res1 = np.dot(p_mat, q_mat)
1086
+ res2 = np.dot(q_mat, p_mat)
1087
+
1088
+ eye = xp.eye(3)
1089
+
1090
+ xp_assert_close(res1, eye, atol=atol)
1091
+ xp_assert_close(res2, eye, atol=atol)
1092
+
1093
+ x = Rotation.random(num=1, rng=rng)
1094
+ x = Rotation.from_quat(xp.asarray(x.as_quat()))
1095
+ y = x.inv()
1096
+
1097
+ x_matrix = x.as_matrix()
1098
+ y_matrix = y.as_matrix()
1099
+ result1 = xp.linalg.matmul(x_matrix, y_matrix)
1100
+ result2 = xp.linalg.matmul(y_matrix, x_matrix)
1101
+
1102
+ eye3d = xp.empty((1, 3, 3))
1103
+ eye3d = xpx.at(eye3d)[..., :3, :3].set(xp.eye(3))
1104
+
1105
+ xp_assert_close(result1, eye3d, atol=atol)
1106
+ xp_assert_close(result2, eye3d, atol=atol)
1107
+
1108
+
1109
+ def test_identity_magnitude(xp):
1110
+ n = 10
1111
+ r = Rotation.identity(n)
1112
+ r = Rotation.from_quat(xp.asarray(r.as_quat()))
1113
+ expected = xp.zeros(n)
1114
+ xp_assert_close(r.magnitude(), expected)
1115
+ xp_assert_close(r.inv().magnitude(), expected)
1116
+
1117
+
1118
+ def test_single_identity_magnitude(xp):
1119
+ r = Rotation.from_quat(xp.asarray(Rotation.identity().as_quat()))
1120
+ assert r.magnitude() == 0
1121
+ assert r.inv().magnitude() == 0
1122
+
1123
+
1124
+ def test_identity_invariance(xp):
1125
+ atol = 1e-12
1126
+ n = 10
1127
+ p = Rotation.random(n, rng=0)
1128
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
1129
+ q = Rotation.from_quat(xp.asarray(Rotation.identity(n).as_quat()))
1130
+ result = p * q
1131
+ xp_assert_close(p.as_quat(), result.as_quat())
1132
+
1133
+ result = result * p.inv()
1134
+ xp_assert_close(result.magnitude(), xp.zeros(n), atol=atol)
1135
+
1136
+
1137
+ def test_single_identity_invariance(xp):
1138
+ atol = 1e-12
1139
+ n = 10
1140
+ p = Rotation.random(n, rng=0)
1141
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
1142
+
1143
+ q = Rotation.from_quat(xp.asarray(Rotation.identity().as_quat()))
1144
+ result = p * q
1145
+ xp_assert_close(p.as_quat(), result.as_quat())
1146
+
1147
+ result = result * p.inv()
1148
+ xp_assert_close(result.magnitude(), xp.zeros(n), atol=atol)
1149
+
1150
+
1151
+ def test_magnitude(xp):
1152
+ r = Rotation.from_quat(xp.eye(4))
1153
+ result = r.magnitude()
1154
+ xp_assert_close(result, xp.asarray([xp.pi, xp.pi, xp.pi, 0]))
1155
+
1156
+ r = Rotation.from_quat(-xp.eye(4))
1157
+ result = r.magnitude()
1158
+ xp_assert_close(result, xp.asarray([xp.pi, xp.pi, xp.pi, 0]))
1159
+
1160
+
1161
+ def test_magnitude_single_rotation(xp):
1162
+ r = Rotation.from_quat(xp.eye(4))
1163
+ result1 = r[0].magnitude()
1164
+ xp_assert_close(result1, xp.pi)
1165
+
1166
+ result2 = r[3].magnitude()
1167
+ xp_assert_close(result2, 0.0)
1168
+
1169
+
1170
+ def test_approx_equal(xp):
1171
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1172
+ p = Rotation.random(10, rng=rng)
1173
+ q = Rotation.random(10, rng=rng)
1174
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
1175
+ q = Rotation.from_quat(xp.asarray(q.as_quat()))
1176
+ r = p * q.inv()
1177
+ r_mag = r.magnitude()
1178
+ atol = xp.asarray(np.median(r_mag)) # ensure we get mix of Trues and Falses
1179
+ xp_assert_equal(p.approx_equal(q, atol), (r_mag < atol))
1180
+
1181
+
1182
+ @pytest.mark.thread_unsafe
1183
+ def test_approx_equal_single_rotation(xp):
1184
+ # also tests passing single argument to approx_equal
1185
+ p = Rotation.from_rotvec(xp.asarray([0, 0, 1e-9])) # less than default atol of 1e-8
1186
+ q = Rotation.from_quat(xp.eye(4))
1187
+ assert p.approx_equal(q[3])
1188
+ assert not p.approx_equal(q[0])
1189
+
1190
+ # test passing atol and using degrees
1191
+ assert not p.approx_equal(q[3], atol=1e-10)
1192
+ assert not p.approx_equal(q[3], atol=1e-8, degrees=True)
1193
+ with pytest.warns(UserWarning, match="atol must be set"):
1194
+ assert p.approx_equal(q[3], degrees=True)
1195
+
1196
+
1197
+ def test_mean(xp):
1198
+ axes = xp.concat((-xp.eye(3), xp.eye(3)))
1199
+ axes = np.concatenate((-np.eye(3), np.eye(3)))
1200
+ thetas = xp.linspace(0, xp.pi / 2, 100)
1201
+ for t in thetas:
1202
+ r = Rotation.from_rotvec(t * axes)
1203
+ xp_assert_close(r.mean().magnitude(), 0.0, atol=1e-10)
1204
+
1205
+
1206
+ def test_weighted_mean(xp):
1207
+ # test that doubling a weight is equivalent to including a rotation twice.
1208
+ axes = xp.asarray([[0.0, 0, 0], [1, 0, 0], [1, 0, 0]])
1209
+ thetas = xp.linspace(0, xp.pi / 2, 100)
1210
+ for t in thetas:
1211
+ rw = Rotation.from_rotvec(t * axes[:2, ...])
1212
+ mw = rw.mean(weights=[1, 2])
1213
+
1214
+ r = Rotation.from_rotvec(t * axes)
1215
+ m = r.mean()
1216
+ xp_assert_close((m * mw.inv()).magnitude(), 0.0, atol=1e-10)
1217
+
1218
+
1219
+ def test_mean_invalid_weights(xp):
1220
+ r = Rotation.from_quat(xp.eye(4))
1221
+ if is_lazy_array(r.as_quat()):
1222
+ m = r.mean(weights=-xp.ones(4))
1223
+ assert all(xp.isnan(m._quat))
1224
+ else:
1225
+ with pytest.raises(ValueError, match="non-negative"):
1226
+ r.mean(weights=-xp.ones(4))
1227
+
1228
+
1229
+ def test_reduction_no_indices(xp):
1230
+ r = Rotation.from_quat(xp.asarray([0.0, 0.0, 0.0, 1.0]))
1231
+ result = r.reduce(return_indices=False)
1232
+ assert isinstance(result, Rotation)
1233
+
1234
+
1235
+ def test_reduction_none_indices(xp):
1236
+ r = Rotation.from_quat(xp.asarray([0.0, 0.0, 0.0, 1.0]))
1237
+ result = r.reduce(return_indices=True)
1238
+ assert type(result) is tuple
1239
+ assert len(result) == 3
1240
+
1241
+ reduced, left_best, right_best = result
1242
+ assert left_best is None
1243
+ assert right_best is None
1244
+
1245
+
1246
+ def test_reduction_scalar_calculation(xp):
1247
+ atol = 1e-12
1248
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1249
+ l = Rotation.from_quat(xp.asarray(Rotation.random(5, rng=rng).as_quat()))
1250
+ r = Rotation.from_quat(xp.asarray(Rotation.random(10, rng=rng).as_quat()))
1251
+ p = Rotation.from_quat(xp.asarray(Rotation.random(7, rng=rng).as_quat()))
1252
+ reduced, left_best, right_best = p.reduce(l, r, return_indices=True)
1253
+
1254
+ # Loop implementation of the vectorized calculation in Rotation.reduce
1255
+ scalars = np.zeros((len(l), len(p), len(r)))
1256
+ for i, li in enumerate(l):
1257
+ for j, pj in enumerate(p):
1258
+ for k, rk in enumerate(r):
1259
+ scalars[i, j, k] = np.abs((li * pj * rk).as_quat()[3])
1260
+ scalars = np.reshape(np.moveaxis(scalars, 1, 0), (scalars.shape[1], -1))
1261
+
1262
+ max_ind = np.argmax(np.reshape(scalars, (len(p), -1)), axis=1)
1263
+ left_best_check = xp.asarray(max_ind // len(r))
1264
+ right_best_check = xp.asarray(max_ind % len(r))
1265
+ assert xp.all(left_best == left_best_check)
1266
+ assert xp.all(right_best == right_best_check)
1267
+
1268
+ reduced_check = l[left_best_check] * p * r[right_best_check]
1269
+ mag = (reduced.inv() * reduced_check).magnitude()
1270
+ xp_assert_close(mag, xp.zeros(len(p)), atol=atol)
1271
+
1272
+
1273
+ def test_apply_single_rotation_single_point(xp):
1274
+ mat = xp.asarray([
1275
+ [0, -1, 0],
1276
+ [1, 0, 0],
1277
+ [0, 0, 1]
1278
+ ])
1279
+ r_1d = Rotation.from_matrix(mat)
1280
+ r_2d = Rotation.from_matrix(xp.expand_dims(mat, axis=0))
1281
+
1282
+ v_1d = xp.asarray([1.0, 2, 3])
1283
+ v_2d = xp.expand_dims(v_1d, axis=0)
1284
+ v1d_rotated = xp.asarray([-2.0, 1, 3])
1285
+ v2d_rotated = xp.expand_dims(v1d_rotated, axis=0)
1286
+
1287
+ xp_assert_close(r_1d.apply(v_1d), v1d_rotated)
1288
+ xp_assert_close(r_1d.apply(v_2d), v2d_rotated)
1289
+ xp_assert_close(r_2d.apply(v_1d), v2d_rotated)
1290
+ xp_assert_close(r_2d.apply(v_2d), v2d_rotated)
1291
+
1292
+ v1d_inverse = xp.asarray([2.0, -1, 3])
1293
+ v2d_inverse = xp.expand_dims(v1d_inverse, axis=0)
1294
+
1295
+ xp_assert_close(r_1d.apply(v_1d, inverse=True), v1d_inverse)
1296
+ xp_assert_close(r_1d.apply(v_2d, inverse=True), v2d_inverse)
1297
+ xp_assert_close(r_2d.apply(v_1d, inverse=True), v2d_inverse)
1298
+ xp_assert_close(r_2d.apply(v_2d, inverse=True), v2d_inverse)
1299
+
1300
+
1301
+ def test_apply_single_rotation_multiple_points(xp):
1302
+ mat = xp.asarray([
1303
+ [0, -1, 0],
1304
+ [1, 0, 0],
1305
+ [0, 0, 1]
1306
+ ])
1307
+ r1 = Rotation.from_matrix(mat)
1308
+ r2 = Rotation.from_matrix(xp.expand_dims(mat, axis=0))
1309
+
1310
+ v = xp.asarray([[1, 2, 3], [4, 5, 6]])
1311
+ v_rotated = xp.asarray([[-2.0, 1, 3], [-5, 4, 6]])
1312
+
1313
+ xp_assert_close(r1.apply(v), v_rotated)
1314
+ xp_assert_close(r2.apply(v), v_rotated)
1315
+
1316
+ v_inverse = xp.asarray([[2.0, -1, 3], [5, -4, 6]])
1317
+
1318
+ xp_assert_close(r1.apply(v, inverse=True), v_inverse)
1319
+ xp_assert_close(r2.apply(v, inverse=True), v_inverse)
1320
+
1321
+
1322
+ def test_apply_multiple_rotations_single_point(xp):
1323
+ mat = np.empty((2, 3, 3))
1324
+ mat[0] = np.array([
1325
+ [0, -1, 0],
1326
+ [1, 0, 0],
1327
+ [0, 0, 1]
1328
+ ])
1329
+ mat[1] = np.array([
1330
+ [1, 0, 0],
1331
+ [0, 0, -1],
1332
+ [0, 1, 0]
1333
+ ])
1334
+ mat = xp.asarray(mat)
1335
+ r = Rotation.from_matrix(mat)
1336
+
1337
+ v1 = xp.asarray([1, 2, 3])
1338
+ v2 = xp.expand_dims(v1, axis=0)
1339
+
1340
+ v_rotated = xp.asarray([[-2.0, 1, 3], [1, -3, 2]])
1341
+
1342
+ xp_assert_close(r.apply(v1), v_rotated)
1343
+ xp_assert_close(r.apply(v2), v_rotated)
1344
+
1345
+ v_inverse = xp.asarray([[2.0, -1, 3], [1, 3, -2]])
1346
+
1347
+ xp_assert_close(r.apply(v1, inverse=True), v_inverse)
1348
+ xp_assert_close(r.apply(v2, inverse=True), v_inverse)
1349
+
1350
+
1351
+ def test_apply_multiple_rotations_multiple_points(xp):
1352
+ mat = np.empty((2, 3, 3))
1353
+ mat[0] = np.array([
1354
+ [0, -1, 0],
1355
+ [1, 0, 0],
1356
+ [0, 0, 1]
1357
+ ])
1358
+ mat[1] = np.array([
1359
+ [1, 0, 0],
1360
+ [0, 0, -1],
1361
+ [0, 1, 0]
1362
+ ])
1363
+ mat = xp.asarray(mat)
1364
+ r = Rotation.from_matrix(mat)
1365
+
1366
+ v = xp.asarray([[1, 2, 3], [4, 5, 6]])
1367
+ v_rotated = xp.asarray([[-2.0, 1, 3], [4, -6, 5]])
1368
+ xp_assert_close(r.apply(v), v_rotated)
1369
+
1370
+ v_inverse = xp.asarray([[2.0, -1, 3], [4, 6, -5]])
1371
+ xp_assert_close(r.apply(v, inverse=True), v_inverse)
1372
+
1373
+
1374
+ def test_apply_shapes(xp):
1375
+ vector0 = xp.asarray([1.0, 2.0, 3.0])
1376
+ vector1 = xp.asarray([vector0])
1377
+ vector2 = xp.asarray([vector0, vector0])
1378
+ matrix0 = xp.eye(3)
1379
+ matrix1 = xp.asarray([matrix0])
1380
+ matrix2 = xp.asarray([matrix0, matrix0])
1381
+
1382
+ for m, v in product([matrix0, matrix1, matrix2], [vector0, vector1, vector2]):
1383
+ r = Rotation.from_matrix(m)
1384
+ shape = v.shape
1385
+ if not r.single and (v.shape == (3,) or v.shape == (1, 3)):
1386
+ shape = (len(r), 3)
1387
+ x = r.apply(v)
1388
+ assert x.shape == shape
1389
+ x = r.apply(v, inverse=True)
1390
+ assert x.shape == shape
1391
+
1392
+
1393
+ def test_apply_array_like():
1394
+ rng = np.random.default_rng(123)
1395
+ # Single vector
1396
+ r = Rotation.random(rng=rng)
1397
+ t = rng.uniform(-100, 100, size=(3,))
1398
+ v = r.apply(t.tolist())
1399
+ v_expected = r.apply(t)
1400
+ xp_assert_close(v, v_expected, atol=1e-12)
1401
+ # Multiple vectors
1402
+ t = rng.uniform(-100, 100, size=(2, 3))
1403
+ v = r.apply(t.tolist())
1404
+ v_expected = r.apply(t)
1405
+ xp_assert_close(v, v_expected, atol=1e-12)
1406
+
1407
+
1408
+ def test_getitem(xp):
1409
+ mat = np.empty((2, 3, 3))
1410
+ mat[0] = np.array([
1411
+ [0, -1, 0],
1412
+ [1, 0, 0],
1413
+ [0, 0, 1]
1414
+ ])
1415
+ mat[1] = np.array([
1416
+ [1, 0, 0],
1417
+ [0, 0, -1],
1418
+ [0, 1, 0]
1419
+ ])
1420
+ mat = xp.asarray(mat)
1421
+ r = Rotation.from_matrix(mat)
1422
+
1423
+ xp_assert_close(r[0].as_matrix(), mat[0], atol=1e-15)
1424
+ xp_assert_close(r[1].as_matrix(), mat[1, ...], atol=1e-15)
1425
+ xp_assert_close(r[:-1].as_matrix(), xp.expand_dims(mat[0, ...], axis=0), atol=1e-15)
1426
+
1427
+
1428
+ def test_getitem_single(xp):
1429
+ with pytest.raises(TypeError, match='not subscriptable'):
1430
+ Rotation.from_quat(xp.asarray([0, 0, 0, 1]))[0]
1431
+
1432
+
1433
+ def test_getitem_array_like():
1434
+ mat = np.array([[[0.0, -1, 0],
1435
+ [1, 0, 0],
1436
+ [0, 0, 1]],
1437
+ [[1, 0, 0],
1438
+ [0, 0, -1],
1439
+ [0, 1, 0]]])
1440
+ r = Rotation.from_matrix(mat)
1441
+ xp_assert_close(r[[0]].as_matrix(), mat[[0]], atol=1e-15)
1442
+ xp_assert_close(r[[0, 1]].as_matrix(), mat[[0, 1]], atol=1e-15)
1443
+
1444
+
1445
+ def test_setitem_single(xp):
1446
+ r = Rotation.from_quat(xp.asarray([0, 0, 0, 1]))
1447
+ with pytest.raises(TypeError, match='not subscriptable'):
1448
+ r[0] = Rotation.from_quat(xp.asarray([0, 0, 0, 1]))
1449
+
1450
+
1451
+ def test_setitem_slice(xp):
1452
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1453
+ r1 = Rotation.from_quat(xp.asarray(Rotation.random(10, rng=rng).as_quat()))
1454
+ r2 = Rotation.from_quat(xp.asarray(Rotation.random(5, rng=rng).as_quat()))
1455
+ r1[1:6] = r2
1456
+ xp_assert_equal(r1[1:6].as_quat(), r2.as_quat())
1457
+
1458
+
1459
+ def test_setitem_integer(xp):
1460
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1461
+ r1 = Rotation.from_quat(xp.asarray(Rotation.random(10, rng=rng).as_quat()))
1462
+ r2 = Rotation.from_quat(xp.asarray(Rotation.random(rng=rng).as_quat()))
1463
+ r1[1] = r2
1464
+ xp_assert_equal(r1[1].as_quat(), r2.as_quat())
1465
+
1466
+
1467
+ def test_setitem_wrong_type(xp):
1468
+ r = Rotation.from_quat(xp.asarray(Rotation.random(10, rng=0).as_quat()))
1469
+ with pytest.raises(TypeError, match='Rotation object'):
1470
+ r[0] = 1
1471
+
1472
+
1473
+ def test_n_rotations(xp):
1474
+ mat = np.empty((2, 3, 3))
1475
+ mat[0] = np.array([
1476
+ [0, -1, 0],
1477
+ [1, 0, 0],
1478
+ [0, 0, 1]
1479
+ ])
1480
+ mat[1] = np.array([
1481
+ [1, 0, 0],
1482
+ [0, 0, -1],
1483
+ [0, 1, 0]
1484
+ ])
1485
+ mat = xp.asarray(mat)
1486
+ r = Rotation.from_matrix(mat)
1487
+
1488
+ assert_equal(len(r), 2)
1489
+ assert_equal(len(r[:-1]), 1)
1490
+
1491
+
1492
+ def test_random_rotation_shape():
1493
+ # No xp testing since random rotations are always using NumPy
1494
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1495
+ assert_equal(Rotation.random(rng=rng).as_quat().shape, (4,))
1496
+ assert_equal(Rotation.random(None, rng=rng).as_quat().shape, (4,))
1497
+
1498
+ assert_equal(Rotation.random(1, rng=rng).as_quat().shape, (1, 4))
1499
+ assert_equal(Rotation.random(5, rng=rng).as_quat().shape, (5, 4))
1500
+
1501
+
1502
+ def test_align_vectors_no_rotation(xp):
1503
+ x = xp.asarray([[1, 2, 3], [4, 5, 6]])
1504
+ y = xp.asarray(x, copy=True)
1505
+
1506
+ r, rssd = Rotation.align_vectors(x, y)
1507
+ xp_assert_close(r.as_matrix(), xp.eye(3), atol=1e-12)
1508
+ xp_assert_close(rssd, xp.asarray(0.0)[()], check_shape=False, atol=1e-6)
1509
+
1510
+
1511
+ def test_align_vectors_no_noise(xp):
1512
+ rng = np.random.default_rng(14697284569885399755764481408308808739)
1513
+ c = Rotation.from_quat(xp.asarray(Rotation.random(rng=rng).as_quat()))
1514
+ b = xp.asarray(rng.normal(size=(5, 3)))
1515
+ a = c.apply(b)
1516
+
1517
+ est, rssd = Rotation.align_vectors(a, b)
1518
+ xp_assert_close(c.as_quat(), est.as_quat())
1519
+ xp_assert_close(rssd, xp.asarray(0.0)[()], check_shape=False, atol=1e-7)
1520
+
1521
+
1522
+ def test_align_vectors_improper_rotation(xp):
1523
+ # Tests correct logic for issue #10444
1524
+ x = xp.asarray([[0.89299824, -0.44372674, 0.0752378],
1525
+ [0.60221789, -0.47564102, -0.6411702]])
1526
+ y = xp.asarray([[0.02386536, -0.82176463, 0.5693271],
1527
+ [-0.27654929, -0.95191427, -0.1318321]])
1528
+
1529
+ est, rssd = Rotation.align_vectors(x, y)
1530
+ xp_assert_close(x, est.apply(y), atol=1e-6)
1531
+ xp_assert_close(rssd, xp.asarray(0.0)[()], check_shape=False, atol=1e-7)
1532
+
1533
+
1534
+ def test_align_vectors_rssd_sensitivity(xp):
1535
+ rssd_expected = xp.asarray(0.141421356237308)[()]
1536
+ sens_expected = xp.asarray([[0.2, 0. , 0.],
1537
+ [0. , 1.5, 1.],
1538
+ [0. , 1. , 1.]])
1539
+ atol = 1e-6
1540
+ a = xp.asarray([[0, 1, 0], [0, 1, 1], [0, 1, 1]])
1541
+ b = xp.asarray([[1, 0, 0], [1, 1.1, 0], [1, 0.9, 0]])
1542
+ rot, rssd, sens = Rotation.align_vectors(a, b, return_sensitivity=True)
1543
+ xp_assert_close(rssd, rssd_expected, atol=atol)
1544
+ xp_assert_close(sens, sens_expected, atol=atol)
1545
+
1546
+
1547
+ def test_align_vectors_scaled_weights(xp):
1548
+ n = 10
1549
+ a = xp.asarray(Rotation.random(n, rng=0).apply([1, 0, 0]))
1550
+ b = xp.asarray(Rotation.random(n, rng=1).apply([1, 0, 0]))
1551
+ scale = 2
1552
+
1553
+ est1, rssd1, cov1 = Rotation.align_vectors(a, b, xp.ones(n), True)
1554
+ est2, rssd2, cov2 = Rotation.align_vectors(a, b, scale * xp.ones(n), True)
1555
+
1556
+ xp_assert_close(est1.as_matrix(), est2.as_matrix())
1557
+ xp_assert_close(math.sqrt(scale) * rssd1, rssd2, atol=1e-6)
1558
+ xp_assert_close(cov1, cov2)
1559
+
1560
+
1561
+ def test_align_vectors_noise(xp):
1562
+ rng = np.random.default_rng(146972845698875399755764481408308808739)
1563
+ n_vectors = 100
1564
+ rot = rotation_to_xp(Rotation.random(rng=rng), xp)
1565
+ vectors = xp.asarray(rng.normal(size=(n_vectors, 3)))
1566
+ result = rot.apply(vectors)
1567
+
1568
+ # The paper adds noise as independently distributed angular errors
1569
+ sigma = np.deg2rad(1)
1570
+ tolerance = 1.5 * sigma
1571
+ noise = Rotation.from_rotvec(
1572
+ xp.asarray(rng.normal(
1573
+ size=(n_vectors, 3),
1574
+ scale=sigma
1575
+ ))
1576
+ )
1577
+
1578
+ # Attitude errors must preserve norm. Hence apply individual random
1579
+ # rotations to each vector.
1580
+ noisy_result = noise.apply(result)
1581
+
1582
+ est, rssd, cov = Rotation.align_vectors(noisy_result, vectors,
1583
+ return_sensitivity=True)
1584
+
1585
+ # Use rotation compositions to find out closeness
1586
+ error_vector = (rot * est.inv()).as_rotvec()
1587
+ xp_assert_close(error_vector[0], xp.asarray(0.0)[()], atol=tolerance)
1588
+ xp_assert_close(error_vector[1], xp.asarray(0.0)[()], atol=tolerance)
1589
+ xp_assert_close(error_vector[2], xp.asarray(0.0)[()], atol=tolerance)
1590
+
1591
+ # Check error bounds using covariance matrix
1592
+ cov *= sigma
1593
+ xp_assert_close(cov[0, 0], xp.asarray(0.0)[()], atol=tolerance)
1594
+ xp_assert_close(cov[1, 1], xp.asarray(0.0)[()], atol=tolerance)
1595
+ xp_assert_close(cov[2, 2], xp.asarray(0.0)[()], atol=tolerance)
1596
+
1597
+ rssd_check = xp.sum((noisy_result - est.apply(vectors)) ** 2) ** 0.5
1598
+ xp_assert_close(rssd, rssd_check, check_shape=False)
1599
+
1600
+
1601
+ def test_align_vectors_invalid_input(xp):
1602
+ with pytest.raises(ValueError, match="Expected input `a` to have shape"):
1603
+ a, b = xp.asarray([1, 2, 3, 4]), xp.asarray([1, 2, 3])
1604
+ Rotation.align_vectors(a, b)
1605
+
1606
+ with pytest.raises(ValueError, match="Expected input `b` to have shape"):
1607
+ a, b = xp.asarray([1, 2, 3]), xp.asarray([1, 2, 3, 4])
1608
+ Rotation.align_vectors(a, b)
1609
+
1610
+ with pytest.raises(ValueError, match="Expected inputs `a` and `b` "
1611
+ "to have same shapes"):
1612
+ a, b = xp.asarray([[1, 2, 3], [4, 5, 6]]), xp.asarray([[1, 2, 3]])
1613
+ Rotation.align_vectors(a, b)
1614
+
1615
+ with pytest.raises(ValueError,
1616
+ match="Expected `weights` to be 1 dimensional"):
1617
+ a, b = xp.asarray([[1, 2, 3]]), xp.asarray([[1, 2, 3]])
1618
+ weights = xp.asarray([[1]])
1619
+ Rotation.align_vectors(a, b, weights)
1620
+
1621
+ with pytest.raises(ValueError,
1622
+ match="Expected `weights` to have number of values"):
1623
+ a, b = xp.asarray([[1, 2, 3], [4, 5, 6]]), xp.asarray([[1, 2, 3], [4, 5, 6]])
1624
+ weights = xp.asarray([1, 2, 3])
1625
+ Rotation.align_vectors(a, b, weights)
1626
+
1627
+ a, b = xp.asarray([[1, 2, 3]]), xp.asarray([[1, 2, 3]])
1628
+ weights = xp.asarray([-1])
1629
+ if is_lazy_array(weights):
1630
+ r, rssd = Rotation.align_vectors(a, b, weights)
1631
+ assert xp.all(xp.isnan(r.as_quat())), "Quaternion should be nan"
1632
+ assert xp.isnan(rssd), "RSSD should be nan"
1633
+ else:
1634
+ with pytest.raises(ValueError,
1635
+ match="`weights` may not contain negative values"):
1636
+ Rotation.align_vectors(a, b, weights)
1637
+
1638
+ a, b = xp.asarray([[1, 2, 3], [4, 5, 6]]), xp.asarray([[1, 2, 3], [4, 5, 6]])
1639
+ weights = xp.asarray([xp.inf, xp.inf])
1640
+ if is_lazy_array(weights):
1641
+ r, rssd = Rotation.align_vectors(a, b, weights)
1642
+ assert xp.all(xp.isnan(r.as_quat())), "Quaternion should be nan"
1643
+ assert xp.isnan(rssd), "RSSD should be nan"
1644
+ else:
1645
+ with pytest.raises(ValueError,
1646
+ match="Only one infinite weight is allowed"):
1647
+ Rotation.align_vectors(a, b, weights)
1648
+
1649
+ a, b = xp.asarray([[0, 0, 0]]), xp.asarray([[1, 2, 3]])
1650
+ if is_lazy_array(a):
1651
+ r, rssd = Rotation.align_vectors(a, b)
1652
+ assert xp.all(xp.isnan(r.as_quat())), "Quaternion should be nan"
1653
+ assert xp.isnan(rssd), "RSSD should be nan"
1654
+ else:
1655
+ with pytest.raises(ValueError,
1656
+ match="Cannot align zero length primary vectors"):
1657
+ Rotation.align_vectors(a, b)
1658
+
1659
+ a, b = xp.asarray([[1, 2, 3], [4, 5, 6]]), xp.asarray([[1, 2, 3], [4, 5, 6]])
1660
+ weights = xp.asarray([xp.inf, 1])
1661
+ if is_lazy_array(a):
1662
+ r, rssd, sens = Rotation.align_vectors(a, b, weights, return_sensitivity=True)
1663
+ assert xp.all(xp.isnan(sens)), "Sensitivity matrix should be nan"
1664
+ else:
1665
+ with pytest.raises(ValueError,
1666
+ match="Cannot return sensitivity matrix"):
1667
+ Rotation.align_vectors(a, b, weights, return_sensitivity=True)
1668
+
1669
+ a, b = xp.asarray([[1, 2, 3]]), xp.asarray([[1, 2, 3]])
1670
+ if is_lazy_array(a):
1671
+ r, rssd, sens = Rotation.align_vectors(a, b, return_sensitivity=True)
1672
+ assert xp.all(xp.isnan(sens)), "Sensitivity matrix should be nan"
1673
+ else:
1674
+ with pytest.raises(ValueError,
1675
+ match="Cannot return sensitivity matrix"):
1676
+ Rotation.align_vectors(a, b, return_sensitivity=True)
1677
+
1678
+
1679
+ def test_align_vectors_align_constrain(xp):
1680
+ # Align the primary +X B axis with the primary +Y A axis, and rotate about
1681
+ # it such that the +Y B axis (residual of the [1, 1, 0] secondary b vector)
1682
+ # is aligned with the +Z A axis (residual of the [0, 1, 1] secondary a
1683
+ # vector)
1684
+ atol = 1e-12
1685
+ b = xp.asarray([[1, 0, 0], [1, 1, 0]])
1686
+ a = xp.asarray([[0.0, 1, 0], [0, 1, 1]])
1687
+ m_expected = xp.asarray([[0.0, 0, 1],
1688
+ [1, 0, 0],
1689
+ [0, 1, 0]])
1690
+ R, rssd = Rotation.align_vectors(a, b, weights=xp.asarray([xp.inf, 1]))
1691
+ xp_assert_close(R.as_matrix(), m_expected, atol=atol)
1692
+ xp_assert_close(R.apply(b), a, atol=atol) # Pri and sec align exactly
1693
+ assert xpx.isclose(rssd, 0.0, atol=atol, xp=xp)
1694
+
1695
+ # Do the same but with an inexact secondary rotation
1696
+ b = xp.asarray([[1, 0, 0], [1, 2, 0]])
1697
+ rssd_expected = 1.0
1698
+ R, rssd = Rotation.align_vectors(a, b, weights=xp.asarray([xp.inf, 1]))
1699
+ xp_assert_close(R.as_matrix(), m_expected, atol=atol)
1700
+ xp_assert_close(R.apply(b)[0, ...], a[0, ...], atol=atol) # Only pri aligns exactly
1701
+ assert xpx.isclose(rssd, rssd_expected, atol=atol, xp=xp)
1702
+ a_expected = xp.asarray([[0.0, 1, 0], [0, 1, 2]])
1703
+ xp_assert_close(R.apply(b), a_expected, atol=atol)
1704
+
1705
+ # Check random vectors
1706
+ b = xp.asarray([[1, 2, 3], [-2, 3, -1]])
1707
+ a = xp.asarray([[-1.0, 3, 2], [1, -1, 2]])
1708
+ rssd_expected = 1.3101595297515016
1709
+ R, rssd = Rotation.align_vectors(a, b, weights=xp.asarray([xp.inf, 1]))
1710
+ xp_assert_close(R.apply(b)[0, ...], a[0, ...], atol=atol) # Only pri aligns exactly
1711
+ assert xpx.isclose(rssd, rssd_expected, atol=atol, xp=xp)
1712
+
1713
+
1714
+ def test_align_vectors_near_inf(xp):
1715
+ # align_vectors should return near the same result for high weights as for
1716
+ # infinite weights. rssd will be different with floating point error on the
1717
+ # exactly aligned vector being multiplied by a large non-infinite weight
1718
+ n = 100
1719
+ mats = []
1720
+ for i in range(6):
1721
+ mats.append(Rotation.random(n, rng=10 + i).as_matrix())
1722
+
1723
+ for i in range(n):
1724
+ # Get random pairs of 3-element vectors
1725
+ a = xp.asarray([1 * mats[0][i][0], 2 * mats[1][i][0]])
1726
+ b = xp.asarray([3 * mats[2][i][0], 4 * mats[3][i][0]])
1727
+
1728
+ R, _ = Rotation.align_vectors(a, b, weights=[1e10, 1])
1729
+ R2, _ = Rotation.align_vectors(a, b, weights=[xp.inf, 1])
1730
+ xp_assert_close(R.as_matrix(), R2.as_matrix(), atol=1e-4)
1731
+
1732
+ for i in range(n):
1733
+ # Get random triplets of 3-element vectors
1734
+ a = xp.asarray([1*mats[0][i][0], 2*mats[1][i][0], 3*mats[2][i][0]])
1735
+ b = xp.asarray([4*mats[3][i][0], 5*mats[4][i][0], 6*mats[5][i][0]])
1736
+
1737
+ R, _ = Rotation.align_vectors(a, b, weights=[1e10, 2, 1])
1738
+ R2, _ = Rotation.align_vectors(a, b, weights=[xp.inf, 2, 1])
1739
+ xp_assert_close(R.as_matrix(), R2.as_matrix(), atol=1e-4)
1740
+
1741
+
1742
+ def test_align_vectors_parallel(xp):
1743
+ atol = 1e-12
1744
+ a = xp.asarray([[1.0, 0, 0], [0, 1, 0]])
1745
+ b = xp.asarray([[0.0, 1, 0], [0, 1, 0]])
1746
+ m_expected = xp.asarray([[0.0, 1, 0],
1747
+ [-1, 0, 0],
1748
+ [0, 0, 1]])
1749
+ R, _ = Rotation.align_vectors(a, b, weights=[xp.inf, 1])
1750
+ xp_assert_close(R.as_matrix(), m_expected, atol=atol)
1751
+ R, _ = Rotation.align_vectors(a[0, ...], b[0, ...])
1752
+ xp_assert_close(R.as_matrix(), m_expected, atol=atol)
1753
+ xp_assert_close(R.apply(b[0, ...]), a[0, ...], atol=atol)
1754
+
1755
+ b = xp.asarray([[1, 0, 0], [1, 0, 0]])
1756
+ m_expected = xp.asarray([[1.0, 0, 0],
1757
+ [0, 1, 0],
1758
+ [0, 0, 1]])
1759
+ R, _ = Rotation.align_vectors(a, b, weights=[xp.inf, 1])
1760
+ xp_assert_close(R.as_matrix(), m_expected, atol=atol)
1761
+ R, _ = Rotation.align_vectors(a[0, ...], b[0, ...])
1762
+ xp_assert_close(R.as_matrix(), m_expected, atol=atol)
1763
+ xp_assert_close(R.apply(b[0, ...]), a[0, ...], atol=atol)
1764
+
1765
+
1766
+ def test_align_vectors_antiparallel(xp):
1767
+ # Test exact 180 deg rotation
1768
+ atol = 1e-12
1769
+ as_to_test = np.array([[[1.0, 0, 0], [0, 1, 0]],
1770
+ [[0, 1, 0], [1, 0, 0]],
1771
+ [[0, 0, 1], [0, 1, 0]]])
1772
+
1773
+ bs_to_test = [[-a[0], a[1]] for a in as_to_test]
1774
+ for a, b in zip(as_to_test, bs_to_test):
1775
+ a, b = xp.asarray(a), xp.asarray(b)
1776
+ R, _ = Rotation.align_vectors(a, b, weights=[xp.inf, 1])
1777
+ xp_assert_close(R.magnitude(), xp.pi, atol=atol)
1778
+ xp_assert_close(R.apply(b[0, ...]), a[0, ...], atol=atol)
1779
+
1780
+ # Test exact rotations near 180 deg
1781
+ Rs = Rotation.random(100, rng=0)
1782
+ dRs = Rotation.from_rotvec(Rs.as_rotvec()*1e-4) # scale down to small angle
1783
+ a = [[ 1, 0, 0], [0, 1, 0]]
1784
+ b = [[-1, 0, 0], [0, 1, 0]]
1785
+ as_to_test = []
1786
+ for dR in dRs:
1787
+ as_to_test.append(np.array([dR.apply(a[0]), a[1]]))
1788
+ for a in as_to_test:
1789
+ a, b = xp.asarray(a), xp.asarray(b)
1790
+ R, _ = Rotation.align_vectors(a, b, weights=[xp.inf, 1])
1791
+ R2, _ = Rotation.align_vectors(a, b, weights=[1e10, 1])
1792
+ xp_assert_close(R.as_matrix(), R2.as_matrix(), atol=atol)
1793
+
1794
+
1795
+ def test_align_vectors_primary_only(xp):
1796
+ atol = 1e-12
1797
+ mats_a = Rotation.random(100, rng=0).as_matrix()
1798
+ mats_b = Rotation.random(100, rng=1).as_matrix()
1799
+
1800
+ for mat_a, mat_b in zip(mats_a, mats_b):
1801
+ # Get random 3-element unit vectors
1802
+ a = xp.asarray(mat_a[0])
1803
+ b = xp.asarray(mat_b[0])
1804
+
1805
+ # Compare to align_vectors with primary only
1806
+ R, rssd = Rotation.align_vectors(a, b)
1807
+ xp_assert_close(R.apply(b), a, atol=atol)
1808
+ assert np.isclose(rssd, 0, atol=atol)
1809
+
1810
+
1811
+ def test_align_vectors_array_like():
1812
+ rng = np.random.default_rng(123)
1813
+ c = Rotation.random(rng=rng)
1814
+ b = rng.normal(size=(5, 3))
1815
+ a = c.apply(b)
1816
+
1817
+ est_expected, rssd_expected = Rotation.align_vectors(a, b)
1818
+ est, rssd = Rotation.align_vectors(a.tolist(), b.tolist())
1819
+ xp_assert_close(est_expected.as_quat(), est.as_quat())
1820
+ xp_assert_close(rssd, rssd_expected)
1821
+
1822
+
1823
+ def test_repr_single_rotation(xp):
1824
+ q = xp.asarray([0, 0, 0, 1])
1825
+ actual = repr(Rotation.from_quat(q))
1826
+ if is_numpy(xp):
1827
+ expected = """\
1828
+ Rotation.from_matrix(array([[1., 0., 0.],
1829
+ [0., 1., 0.],
1830
+ [0., 0., 1.]]))"""
1831
+ assert actual == expected
1832
+ else:
1833
+ assert actual.startswith("Rotation.from_matrix(")
1834
+
1835
+
1836
+ def test_repr_rotation_sequence(xp):
1837
+ q = xp.asarray([[0.0, 1, 0, 1], [0, 0, 1, 1]]) / math.sqrt(2)
1838
+ actual = f"{Rotation.from_quat(q)!r}"
1839
+ if is_numpy(xp):
1840
+ expected = """\
1841
+ Rotation.from_matrix(array([[[ 0., 0., 1.],
1842
+ [ 0., 1., 0.],
1843
+ [-1., 0., 0.]],
1844
+
1845
+ [[ 0., -1., 0.],
1846
+ [ 1., 0., 0.],
1847
+ [ 0., 0., 1.]]]))"""
1848
+ assert actual == expected
1849
+ else:
1850
+ assert actual.startswith("Rotation.from_matrix(")
1851
+
1852
+
1853
+ def test_slerp(xp):
1854
+ rnd = np.random.RandomState(0)
1855
+
1856
+ key_rots = Rotation.from_quat(xp.asarray(rnd.uniform(size=(5, 4))))
1857
+ key_quats = key_rots.as_quat()
1858
+
1859
+ key_times = [0, 1, 2, 3, 4]
1860
+ interpolator = Slerp(key_times, key_rots)
1861
+ assert isinstance(interpolator.times, type(xp.asarray(0)))
1862
+
1863
+ times = [0, 0.5, 0.25, 1, 1.5, 2, 2.75, 3, 3.25, 3.60, 4]
1864
+ interp_rots = interpolator(times)
1865
+ interp_quats = interp_rots.as_quat()
1866
+
1867
+ # Dot products are affected by sign of quaternions
1868
+ mask = (interp_quats[:, -1] < 0)[:, None]
1869
+ interp_quats = xp.where(mask, -interp_quats, interp_quats)
1870
+ # Checking for quaternion equality, perform same operation
1871
+ mask = (key_quats[:, -1] < 0)[:, None]
1872
+ key_quats = xp.where(mask, -key_quats, key_quats)
1873
+
1874
+ # Equality at keyframes, including both endpoints
1875
+ xp_assert_close(interp_quats[0, ...], key_quats[0, ...])
1876
+ xp_assert_close(interp_quats[3, ...], key_quats[1, ...])
1877
+ xp_assert_close(interp_quats[5, ...], key_quats[2, ...])
1878
+ xp_assert_close(interp_quats[7, ...], key_quats[3, ...])
1879
+ xp_assert_close(interp_quats[10, ...], key_quats[4, ...])
1880
+
1881
+ # Constant angular velocity between keyframes. Check by equating
1882
+ # cos(theta) between quaternion pairs with equal time difference.
1883
+ cos_theta1 = xp.sum(interp_quats[0, ...] * interp_quats[2, ...])
1884
+ cos_theta2 = xp.sum(interp_quats[2, ...] * interp_quats[1, ...])
1885
+ xp_assert_close(cos_theta1, cos_theta2)
1886
+
1887
+ cos_theta4 = xp.sum(interp_quats[3, ...] * interp_quats[4, ...])
1888
+ cos_theta5 = xp.sum(interp_quats[4, ...] * interp_quats[5, ...])
1889
+ xp_assert_close(cos_theta4, cos_theta5)
1890
+
1891
+ # theta1: 0 -> 0.25, theta3 : 0.5 -> 1
1892
+ # Use double angle formula for double the time difference
1893
+ cos_theta3 = xp.sum(interp_quats[1, ...] * interp_quats[3, ...])
1894
+ xp_assert_close(cos_theta3, 2 * (cos_theta1**2) - 1)
1895
+
1896
+ # Miscellaneous checks
1897
+ assert_equal(len(interp_rots), len(times))
1898
+
1899
+
1900
+ def test_slerp_rot_is_rotation(xp):
1901
+ with pytest.raises(TypeError, match="must be a `Rotation` instance"):
1902
+ r = xp.asarray([[1,2,3,4],
1903
+ [0,0,0,1]])
1904
+ t = xp.asarray([0, 1])
1905
+ Slerp(t, r)
1906
+
1907
+
1908
+ SLERP_EXCEPTION_MESSAGE = "must be a sequence of at least 2 rotations"
1909
+
1910
+
1911
+ def test_slerp_single_rot(xp):
1912
+ r = Rotation.from_quat(xp.asarray([[1.0, 2, 3, 4]]))
1913
+ with pytest.raises(ValueError, match=SLERP_EXCEPTION_MESSAGE):
1914
+ Slerp([1], r)
1915
+
1916
+
1917
+ def test_slerp_rot_len0(xp):
1918
+ r = Rotation.random()
1919
+ r = Rotation.from_quat(xp.asarray(r.as_quat()))
1920
+ with pytest.raises(ValueError, match=SLERP_EXCEPTION_MESSAGE):
1921
+ Slerp([], r)
1922
+
1923
+
1924
+ def test_slerp_rot_len1(xp):
1925
+ r = Rotation.random(1)
1926
+ r = Rotation.from_quat(xp.asarray(r.as_quat()))
1927
+ with pytest.raises(ValueError, match=SLERP_EXCEPTION_MESSAGE):
1928
+ Slerp([1], r)
1929
+
1930
+
1931
+ def test_slerp_time_dim_mismatch(xp):
1932
+ with pytest.raises(ValueError,
1933
+ match="times to be specified in a 1 dimensional array"):
1934
+ rnd = np.random.RandomState(0)
1935
+ r = Rotation.from_quat(xp.asarray(rnd.uniform(size=(2, 4))))
1936
+ t = xp.asarray([[1],
1937
+ [2]])
1938
+ Slerp(t, r)
1939
+
1940
+
1941
+ def test_slerp_num_rotations_mismatch(xp):
1942
+ with pytest.raises(ValueError, match="number of rotations to be equal to "
1943
+ "number of timestamps"):
1944
+ rnd = np.random.RandomState(0)
1945
+ r = Rotation.from_quat(xp.asarray(rnd.uniform(size=(5, 4))))
1946
+ t = xp.arange(7)
1947
+ Slerp(t, r)
1948
+
1949
+
1950
+ def test_slerp_equal_times(xp):
1951
+ rnd = np.random.RandomState(0)
1952
+ q = xp.asarray(rnd.uniform(size=(5, 4)))
1953
+ r = Rotation.from_quat(q)
1954
+ t = [0, 1, 2, 2, 4]
1955
+ if is_lazy_array(q):
1956
+ s = Slerp(t, r)
1957
+ assert xp.all(xp.isnan(s.times))
1958
+ else:
1959
+ with pytest.raises(ValueError, match="strictly increasing order"):
1960
+ Slerp(t, r)
1961
+
1962
+
1963
+ def test_slerp_decreasing_times(xp):
1964
+ rnd = np.random.RandomState(0)
1965
+ q = xp.asarray(rnd.uniform(size=(5, 4)))
1966
+ r = Rotation.from_quat(q)
1967
+ t = [0, 1, 3, 2, 4]
1968
+ if is_lazy_array(q):
1969
+ s = Slerp(t, r)
1970
+ assert xp.all(xp.isnan(s.times))
1971
+ else:
1972
+ with pytest.raises(ValueError, match="strictly increasing order"):
1973
+ Slerp(t, r)
1974
+
1975
+
1976
+ def test_slerp_call_time_dim_mismatch(xp):
1977
+ rnd = np.random.RandomState(0)
1978
+ r = Rotation.from_quat(xp.asarray(rnd.uniform(size=(5, 4))))
1979
+ t = xp.arange(5)
1980
+ s = Slerp(t, r)
1981
+
1982
+ with pytest.raises(ValueError,
1983
+ match="`times` must be at most 1-dimensional."):
1984
+ interp_times = xp.asarray([[3.5],
1985
+ [4.2]])
1986
+ s(interp_times)
1987
+
1988
+
1989
+ def test_slerp_call_time_out_of_range(xp):
1990
+ rnd = np.random.RandomState(0)
1991
+ r = Rotation.from_quat(xp.asarray(rnd.uniform(size=(5, 4))))
1992
+ t = xp.arange(5) + 1
1993
+ s = Slerp(t, r)
1994
+
1995
+ times_low = xp.asarray([0, 1, 2])
1996
+ times_high = xp.asarray([1, 2, 6])
1997
+ if is_lazy_array(times_low):
1998
+ q = s(times_low).as_quat()
1999
+ in_range = xp.logical_and(times_low >= xp.min(t), times_low <= xp.max(t))
2000
+ assert xp.all(xp.isnan(q[~in_range, ...]))
2001
+ assert xp.all(~xp.isnan(q[in_range, ...]))
2002
+ q = s(times_high).as_quat()
2003
+ in_range = xp.logical_and(times_high >= xp.min(t), times_high <= xp.max(t))
2004
+ assert xp.all(xp.isnan(q[~in_range, ...]))
2005
+ assert xp.all(~xp.isnan(q[in_range, ...]))
2006
+ else:
2007
+ with pytest.raises(ValueError, match="times must be within the range"):
2008
+ s(times_low)
2009
+ with pytest.raises(ValueError, match="times must be within the range"):
2010
+ s(times_high)
2011
+
2012
+
2013
+ def test_slerp_call_scalar_time(xp):
2014
+ r = Rotation.from_euler('X', xp.asarray([0, 80]), degrees=True)
2015
+ s = Slerp([0, 1], r)
2016
+
2017
+ r_interpolated = s(0.25)
2018
+ r_interpolated_expected = Rotation.from_euler('X', xp.asarray(20), degrees=True)
2019
+
2020
+ delta = r_interpolated * r_interpolated_expected.inv()
2021
+
2022
+ assert xp.allclose(delta.magnitude(), 0, atol=1e-16)
2023
+
2024
+
2025
+ def test_multiplication_stability(xp):
2026
+ qs = Rotation.random(50, rng=0)
2027
+ qs = Rotation.from_quat(xp.asarray(qs.as_quat()))
2028
+ rs = Rotation.random(1000, rng=1)
2029
+ rs = Rotation.from_quat(xp.asarray(rs.as_quat()))
2030
+ expected = xp.ones(len(rs))
2031
+ for q in qs:
2032
+ rs *= q * rs
2033
+ xp_assert_close(xp_vector_norm(rs.as_quat(), axis=1), expected)
2034
+
2035
+
2036
+ def test_pow(xp):
2037
+ atol = 1e-14
2038
+ p = Rotation.random(10, rng=0)
2039
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
2040
+ p_inv = p.inv()
2041
+ # Test the short-cuts and other integers
2042
+ for n in [-5, -2, -1, 0, 1, 2, 5]:
2043
+ # Test accuracy
2044
+ q = p ** n
2045
+ r = Rotation.identity(10)
2046
+ r = Rotation.from_quat(xp.asarray(r.as_quat()))
2047
+ for _ in range(abs(n)):
2048
+ if n > 0:
2049
+ r = r * p
2050
+ else:
2051
+ r = r * p_inv
2052
+ ang = (q * r.inv()).magnitude()
2053
+ assert xp.all(ang < atol)
2054
+
2055
+ # Test shape preservation
2056
+ r = Rotation.from_quat(xp.asarray([0, 0, 0, 1]))
2057
+ assert (r**n).as_quat().shape == (4,)
2058
+ r = Rotation.from_quat(xp.asarray([[0, 0, 0, 1]]))
2059
+ assert (r**n).as_quat().shape == (1, 4)
2060
+
2061
+ # Large angle fractional
2062
+ for n in [-1.5, -0.5, -0.0, 0.0, 0.5, 1.5]:
2063
+ q = p ** n
2064
+ r = Rotation.from_rotvec(n * p.as_rotvec())
2065
+ xp_assert_close(q.as_quat(), r.as_quat(), atol=atol)
2066
+
2067
+ # Small angle
2068
+ p = Rotation.from_rotvec(xp.asarray([1e-12, 0, 0]))
2069
+ n = 3
2070
+ q = p ** n
2071
+ r = Rotation.from_rotvec(n * p.as_rotvec())
2072
+ xp_assert_close(q.as_quat(), r.as_quat(), atol=atol)
2073
+
2074
+
2075
+ def test_pow_errors(xp):
2076
+ p = Rotation.random(rng=0)
2077
+ p = Rotation.from_quat(xp.asarray(p.as_quat()))
2078
+ with pytest.raises(NotImplementedError, match='modulus not supported'):
2079
+ pow(p, 1, 1)
2080
+
2081
+
2082
+ def test_rotation_within_numpy_array():
2083
+ # TODO: Do we want to support this for all Array API frameworks?
2084
+ single = Rotation.random(rng=0)
2085
+ multiple = Rotation.random(2, rng=1)
2086
+
2087
+ array = np.array(single)
2088
+ assert_equal(array.shape, ())
2089
+
2090
+ array = np.array(multiple)
2091
+ assert_equal(array.shape, (2,))
2092
+ xp_assert_close(array[0].as_matrix(), multiple[0].as_matrix())
2093
+ xp_assert_close(array[1].as_matrix(), multiple[1].as_matrix())
2094
+
2095
+ array = np.array([single])
2096
+ assert_equal(array.shape, (1,))
2097
+ assert_equal(array[0], single)
2098
+
2099
+ array = np.array([multiple])
2100
+ assert_equal(array.shape, (1, 2))
2101
+ xp_assert_close(array[0, 0].as_matrix(), multiple[0].as_matrix())
2102
+ xp_assert_close(array[0, 1].as_matrix(), multiple[1].as_matrix())
2103
+
2104
+ array = np.array([single, multiple], dtype=object)
2105
+ assert_equal(array.shape, (2,))
2106
+ assert_equal(array[0], single)
2107
+ assert_equal(array[1], multiple)
2108
+
2109
+ array = np.array([multiple, multiple, multiple])
2110
+ assert_equal(array.shape, (3, 2))
2111
+
2112
+
2113
+ def test_pickling(xp):
2114
+ # Note: Array API makes no provision for arrays to be pickleable, so
2115
+ # it's OK to skip this test for the backends that don't support it
2116
+ r = Rotation.from_quat(xp.asarray([0, 0, math.sin(np.pi/4), math.cos(np.pi/4)]))
2117
+ pkl = pickle.dumps(r)
2118
+ unpickled = pickle.loads(pkl)
2119
+ xp_assert_close(r.as_matrix(), unpickled.as_matrix(), atol=1e-15)
2120
+
2121
+
2122
+ def test_deepcopy(xp):
2123
+ # Note: Array API makes no provision for arrays to support the `__copy__`
2124
+ # protocol, so it's OK to skip this test for the backends that don't
2125
+ r = Rotation.from_quat(xp.asarray([0, 0, math.sin(np.pi/4), math.cos(np.pi/4)]))
2126
+ r1 = copy.deepcopy(r)
2127
+ xp_assert_close(r.as_matrix(), r1.as_matrix(), atol=1e-15)
2128
+
2129
+
2130
+ def test_as_euler_contiguous():
2131
+ # The Array API does not specify contiguous arrays, so we can only check for NumPy
2132
+ r = Rotation.from_quat([0, 0, 0, 1])
2133
+ e1 = r.as_euler('xyz') # extrinsic euler rotation
2134
+ e2 = r.as_euler('XYZ') # intrinsic
2135
+ assert e1.flags['C_CONTIGUOUS'] is True
2136
+ assert e2.flags['C_CONTIGUOUS'] is True
2137
+ assert all(i >= 0 for i in e1.strides)
2138
+ assert all(i >= 0 for i in e2.strides)
2139
+
2140
+
2141
+ def test_concatenate(xp):
2142
+ rotation = Rotation.random(10, rng=0)
2143
+ rotation = Rotation.from_quat(xp.asarray(rotation.as_quat()))
2144
+ sizes = [1, 2, 3, 1, 3]
2145
+ starts = [0] + list(np.cumsum(sizes))
2146
+ split = [rotation[i:i + n] for i, n in zip(starts, sizes)]
2147
+ result = Rotation.concatenate(split)
2148
+ xp_assert_equal(rotation.as_quat(), result.as_quat())
2149
+
2150
+ # Test Rotation input for multiple rotations
2151
+ result = Rotation.concatenate(rotation)
2152
+ xp_assert_equal(rotation.as_quat(), result.as_quat())
2153
+
2154
+ # Test that a copy is returned
2155
+ assert rotation is not result
2156
+
2157
+ # Test Rotation input for single rotations
2158
+ rot = Rotation.from_quat(xp.asarray(Rotation.identity().as_quat()))
2159
+ result = Rotation.concatenate(rot)
2160
+ xp_assert_equal(rot.as_quat(), result.as_quat())
2161
+
2162
+
2163
+ def test_concatenate_wrong_type(xp):
2164
+ with pytest.raises(TypeError, match='Rotation objects only'):
2165
+ rot = Rotation(xp.asarray(Rotation.identity().as_quat()))
2166
+ Rotation.concatenate([rot, 1, None])
2167
+
2168
+
2169
+ # Regression test for gh-16663
2170
+ def test_len_and_bool(xp):
2171
+ rotation_multi_one = Rotation(xp.asarray([[0, 0, 0, 1]]))
2172
+ rotation_multi = Rotation(xp.asarray([[0, 0, 0, 1], [0, 0, 0, 1]]))
2173
+ rotation_single = Rotation(xp.asarray([0, 0, 0, 1]))
2174
+
2175
+ assert len(rotation_multi_one) == 1
2176
+ assert len(rotation_multi) == 2
2177
+ with pytest.raises(TypeError, match="Single rotation has no len()."):
2178
+ len(rotation_single)
2179
+
2180
+ # Rotation should always be truthy. See gh-16663
2181
+ assert rotation_multi_one
2182
+ assert rotation_multi
2183
+ assert rotation_single
2184
+
2185
+
2186
+ def test_from_davenport_single_rotation(xp):
2187
+ axis = xp.asarray([0, 0, 1])
2188
+ quat = Rotation.from_davenport(axis, 'extrinsic', 90,
2189
+ degrees=True).as_quat()
2190
+ expected_quat = xp.asarray([0.0, 0, 1, 1]) / math.sqrt(2)
2191
+ xp_assert_close(quat, expected_quat)
2192
+
2193
+
2194
+ def test_from_davenport_one_or_two_axes(xp):
2195
+ ez = xp.asarray([0.0, 0, 1])
2196
+ ey = xp.asarray([0.0, 1, 0])
2197
+
2198
+ # Single rotation, single axis, axes.shape == (3, )
2199
+ rot = Rotation.from_rotvec(ez * xp.pi/4)
2200
+ rot_dav = Rotation.from_davenport(ez, 'e', xp.pi/4)
2201
+ xp_assert_close(rot.as_quat(canonical=True),
2202
+ rot_dav.as_quat(canonical=True))
2203
+
2204
+ # Single rotation, single axis, axes.shape == (1, 3)
2205
+ axes = xp.reshape(ez, (1, 3)) # Torch can't create tensors from xp.asarray([ez])
2206
+ rot = Rotation.from_rotvec(axes * xp.pi/4)
2207
+ rot_dav = Rotation.from_davenport(axes, 'e', [xp.pi/4])
2208
+ xp_assert_close(rot.as_quat(canonical=True),
2209
+ rot_dav.as_quat(canonical=True))
2210
+
2211
+ # Single rotation, two axes, axes.shape == (2, 3)
2212
+ axes = xp.stack([ez, ey], axis=0)
2213
+ rot = Rotation.from_rotvec(axes * xp.asarray([[xp.pi/4], [xp.pi/6]]))
2214
+ rot = rot[0] * rot[1]
2215
+ axes_dav = xp.stack([ey, ez], axis=0)
2216
+ rot_dav = Rotation.from_davenport(axes_dav, 'e', [xp.pi/6, xp.pi/4])
2217
+ xp_assert_close(rot.as_quat(canonical=True),
2218
+ rot_dav.as_quat(canonical=True))
2219
+
2220
+ # Two rotations, single axis, axes.shape == (3, )
2221
+ axes = xp.stack([ez, ez], axis=0)
2222
+ rot = Rotation.from_rotvec(axes * xp.asarray([[xp.pi/6], [xp.pi/4]]))
2223
+ axes_dav = xp.reshape(ez, (1, 3))
2224
+ rot_dav = Rotation.from_davenport(axes_dav, 'e', [xp.pi/6, xp.pi/4])
2225
+ xp_assert_close(rot.as_quat(canonical=True),
2226
+ rot_dav.as_quat(canonical=True))
2227
+
2228
+
2229
+ def test_from_davenport_invalid_input(xp):
2230
+ ez = [0, 0, 1]
2231
+ ey = [0, 1, 0]
2232
+ ezy = [0, 1, 1]
2233
+ # We can only raise in non-lazy frameworks.
2234
+ axes = xp.asarray([ez, ezy])
2235
+ if is_lazy_array(axes):
2236
+ q = Rotation.from_davenport(axes, 'e', [0, 0]).as_quat()
2237
+ assert xp.all(xp.isnan(q))
2238
+ else:
2239
+ with pytest.raises(ValueError, match="must be orthogonal"):
2240
+ Rotation.from_davenport(axes, 'e', [0, 0])
2241
+ axes = xp.asarray([ez, ey, ezy])
2242
+ if is_lazy_array(axes):
2243
+ q = Rotation.from_davenport(axes, 'e', [0, 0, 0]).as_quat()
2244
+ assert xp.all(xp.isnan(q))
2245
+ else:
2246
+ with pytest.raises(ValueError, match="must be orthogonal"):
2247
+ Rotation.from_davenport(axes, 'e', [0, 0, 0])
2248
+ with pytest.raises(ValueError, match="order should be"):
2249
+ Rotation.from_davenport(xp.asarray([ez]), 'xyz', [0])
2250
+ with pytest.raises(ValueError, match="Expected `angles`"):
2251
+ Rotation.from_davenport(xp.asarray([ez, ey, ez]), 'e', [0, 1, 2, 3])
2252
+
2253
+
2254
+ def test_from_davenport_array_like():
2255
+ rng = np.random.default_rng(123)
2256
+ # Single rotation
2257
+ e1 = np.array([1, 0, 0])
2258
+ e2 = np.array([0, 1, 0])
2259
+ e3 = np.array([0, 0, 1])
2260
+ r_expected = Rotation.random(rng=rng)
2261
+ angles = r_expected.as_davenport([e1, e2, e3], 'e')
2262
+ r = Rotation.from_davenport([e1, e2, e3], 'e', angles.tolist())
2263
+ assert r_expected.approx_equal(r, atol=1e-12)
2264
+
2265
+ # Multiple rotations
2266
+ r_expected = Rotation.random(2, rng=rng)
2267
+ angles = r_expected.as_davenport([e1, e2, e3], 'e')
2268
+ r = Rotation.from_davenport([e1, e2, e3], 'e', angles.tolist())
2269
+ assert np.all(r_expected.approx_equal(r, atol=1e-12))
2270
+
2271
+
2272
+ def test_as_davenport(xp):
2273
+ rnd = np.random.RandomState(0)
2274
+ n = 100
2275
+ angles = np.empty((n, 3))
2276
+ angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2277
+ angles_middle = rnd.uniform(low=0, high=np.pi, size=(n,))
2278
+ angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2279
+ lambdas = rnd.uniform(low=0, high=np.pi, size=(20,))
2280
+
2281
+ e1 = xp.asarray([1.0, 0, 0])
2282
+ e2 = xp.asarray([0.0, 1, 0])
2283
+
2284
+ for lamb in lambdas:
2285
+ e3 = xp.asarray(Rotation.from_rotvec(lamb*e2).apply(e1))
2286
+ ax_lamb = xp.stack([e1, e2, e3], axis=0)
2287
+ angles[:, 1] = angles_middle - lamb
2288
+ for order in ['extrinsic', 'intrinsic']:
2289
+ ax = ax_lamb if order == "intrinsic" else xp.flip(ax_lamb, axis=0)
2290
+ rot = Rotation.from_davenport(xp.asarray(ax), order, angles)
2291
+ angles_dav = rot.as_davenport(xp.asarray(ax), order)
2292
+ xp_assert_close(angles_dav, xp.asarray(angles))
2293
+
2294
+
2295
+ @pytest.mark.thread_unsafe
2296
+ def test_as_davenport_degenerate(xp):
2297
+ # Since we cannot check for angle equality, we check for rotation matrix
2298
+ # equality
2299
+ rnd = np.random.RandomState(0)
2300
+ n = 5
2301
+ angles = np.empty((n, 3))
2302
+
2303
+ # symmetric sequences
2304
+ angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2305
+ angles_middle = [rnd.choice([0, np.pi]) for i in range(n)]
2306
+ angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2307
+ lambdas = rnd.uniform(low=0, high=np.pi, size=(5,))
2308
+
2309
+ e1 = xp.asarray([1.0, 0, 0])
2310
+ e2 = xp.asarray([0.0, 1, 0])
2311
+
2312
+ for lamb in lambdas:
2313
+ e3 = xp.asarray(Rotation.from_rotvec(lamb*e2).apply(e1))
2314
+ ax_lamb = xp.stack([e1, e2, e3], axis=0)
2315
+ angles[:, 1] = angles_middle - lamb
2316
+ for order in ['extrinsic', 'intrinsic']:
2317
+ ax = ax_lamb if order == 'intrinsic' else ax_lamb[::-1]
2318
+ rot = Rotation.from_davenport(xp.asarray(ax), order, angles)
2319
+ with eager_warns(rot, UserWarning, match="Gimbal lock"):
2320
+ angles_dav = rot.as_davenport(xp.asarray(ax), order)
2321
+ mat_expected = rot.as_matrix()
2322
+ rot_estimated = Rotation.from_davenport(xp.asarray(ax), order, angles_dav)
2323
+ mat_estimated = rot_estimated.as_matrix()
2324
+ xp_assert_close(mat_expected, mat_estimated, atol=1e-12)
2325
+
2326
+
2327
+ def test_compare_from_davenport_from_euler(xp):
2328
+ rnd = np.random.RandomState(0)
2329
+ n = 100
2330
+ angles = np.empty((n, 3))
2331
+
2332
+ # symmetric sequences
2333
+ angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2334
+ angles[:, 1] = rnd.uniform(low=0, high=np.pi, size=(n,))
2335
+ angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2336
+ for order in ['extrinsic', 'intrinsic']:
2337
+ for seq_tuple in permutations('xyz'):
2338
+ seq = ''.join([seq_tuple[0], seq_tuple[1], seq_tuple[0]])
2339
+ ax = [basis_vec(i) for i in seq]
2340
+ if order == 'intrinsic':
2341
+ seq = seq.upper()
2342
+ eul = Rotation.from_euler(seq, xp.asarray(angles))
2343
+ dav = Rotation.from_davenport(xp.asarray(ax), order, xp.asarray(angles))
2344
+ xp_assert_close(eul.as_quat(canonical=True), dav.as_quat(canonical=True),
2345
+ rtol=1e-12)
2346
+
2347
+ # asymmetric sequences
2348
+ angles[:, 1] -= np.pi / 2
2349
+ for order in ['extrinsic', 'intrinsic']:
2350
+ for seq_tuple in permutations('xyz'):
2351
+ seq = ''.join(seq_tuple)
2352
+ ax = [basis_vec(i) for i in seq]
2353
+ if order == 'intrinsic':
2354
+ seq = seq.upper()
2355
+ eul = Rotation.from_euler(seq, xp.asarray(angles))
2356
+ dav = Rotation.from_davenport(xp.asarray(ax), order, xp.asarray(angles))
2357
+ xp_assert_close(eul.as_quat(), dav.as_quat(), rtol=1e-12)
2358
+
2359
+
2360
+ def test_compare_as_davenport_as_euler(xp):
2361
+ rnd = np.random.RandomState(0)
2362
+ n = 100
2363
+ angles = np.empty((n, 3))
2364
+
2365
+ # symmetric sequences
2366
+ angles[:, 0] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2367
+ angles[:, 1] = rnd.uniform(low=0, high=np.pi, size=(n,))
2368
+ angles[:, 2] = rnd.uniform(low=-np.pi, high=np.pi, size=(n,))
2369
+ for order in ['extrinsic', 'intrinsic']:
2370
+ for seq_tuple in permutations('xyz'):
2371
+ seq = ''.join([seq_tuple[0], seq_tuple[1], seq_tuple[0]])
2372
+ ax = [basis_vec(i) for i in seq]
2373
+ if order == 'intrinsic':
2374
+ seq = seq.upper()
2375
+ rot = Rotation.from_euler(seq, xp.asarray(angles))
2376
+ eul = rot.as_euler(seq)
2377
+ dav = rot.as_davenport(xp.asarray(ax), order)
2378
+ xp_assert_close(eul, dav, rtol=1e-12)
2379
+
2380
+ # asymmetric sequences
2381
+ angles[:, 1] -= np.pi / 2
2382
+ for order in ['extrinsic', 'intrinsic']:
2383
+ for seq_tuple in permutations('xyz'):
2384
+ seq = ''.join(seq_tuple)
2385
+ ax = [basis_vec(i) for i in seq]
2386
+ if order == 'intrinsic':
2387
+ seq = seq.upper()
2388
+ rot = Rotation.from_euler(seq, xp.asarray(angles))
2389
+ eul = rot.as_euler(seq)
2390
+ dav = rot.as_davenport(xp.asarray(ax), order)
2391
+ xp_assert_close(eul, dav, rtol=1e-12)
2392
+
2393
+
2394
+ def test_zero_rotation_construction(xp):
2395
+ r = Rotation.random(num=0)
2396
+ assert len(r) == 0
2397
+
2398
+ r_ide = Rotation.identity(num=0)
2399
+ assert len(r_ide) == 0
2400
+
2401
+ r_get = Rotation.random(num=3)[[]]
2402
+ assert len(r_get) == 0
2403
+
2404
+ r_quat = Rotation.from_quat(xp.zeros((0, 4)))
2405
+ assert len(r_quat) == 0
2406
+
2407
+ r_matrix = Rotation.from_matrix(xp.zeros((0, 3, 3)))
2408
+ assert len(r_matrix) == 0
2409
+
2410
+ r_euler = Rotation.from_euler("xyz", xp.zeros((0, 3)))
2411
+ assert len(r_euler) == 0
2412
+
2413
+ r_vec = Rotation.from_rotvec(xp.zeros((0, 3)))
2414
+ assert len(r_vec) == 0
2415
+
2416
+ r_dav = Rotation.from_davenport(xp.eye(3), "extrinsic", xp.zeros((0, 3)))
2417
+ assert len(r_dav) == 0
2418
+
2419
+ r_mrp = Rotation.from_mrp(xp.zeros((0, 3)))
2420
+ assert len(r_mrp) == 0
2421
+
2422
+
2423
+ def test_zero_rotation_representation(xp):
2424
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2425
+ assert r.as_quat().shape == (0, 4)
2426
+ assert r.as_matrix().shape == (0, 3, 3)
2427
+ assert r.as_euler("xyz").shape == (0, 3)
2428
+ assert r.as_rotvec().shape == (0, 3)
2429
+ assert r.as_mrp().shape == (0, 3)
2430
+ assert r.as_davenport(xp.eye(3), "extrinsic").shape == (0, 3)
2431
+
2432
+
2433
+ def test_zero_rotation_array_rotation(xp):
2434
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2435
+
2436
+ v = xp.asarray([1, 2, 3])
2437
+ v_rotated = r.apply(v)
2438
+ assert v_rotated.shape == (0, 3)
2439
+
2440
+ v0 = xp.zeros((0, 3))
2441
+ v0_rot = r.apply(v0)
2442
+ assert v0_rot.shape == (0, 3)
2443
+
2444
+ v2 = xp.ones((2, 3))
2445
+ with pytest.raises(
2446
+ ValueError, match="Expected equal numbers of rotations and vectors"):
2447
+ r.apply(v2)
2448
+
2449
+
2450
+ def test_zero_rotation_multiplication(xp):
2451
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2452
+
2453
+ r_single = Rotation.from_quat(xp.asarray([0.0, 0, 0, 1]))
2454
+ r_mult_left = r * r_single
2455
+ assert len(r_mult_left) == 0
2456
+
2457
+ r_mult_right = r_single * r
2458
+ assert len(r_mult_right) == 0
2459
+
2460
+ r0 = Rotation.from_quat(xp.zeros((0, 4)))
2461
+ r_mult = r * r0
2462
+ assert len(r_mult) == 0
2463
+
2464
+ msg_rotation_error = "Expected equal number of rotations"
2465
+ r2 = Rotation.random(2)
2466
+ r2 = Rotation.from_quat(xp.asarray(r2.as_quat()))
2467
+ with pytest.raises(ValueError, match=msg_rotation_error):
2468
+ r0 * r2
2469
+
2470
+ with pytest.raises(ValueError, match=msg_rotation_error):
2471
+ r2 * r0
2472
+
2473
+
2474
+ def test_zero_rotation_concatentation(xp):
2475
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2476
+
2477
+ r0 = Rotation.concatenate([r, r])
2478
+ assert len(r0) == 0
2479
+
2480
+ r1 = Rotation.from_quat(xp.asarray([0.0, 0, 0, 1]))
2481
+ r1 = r.concatenate([r1, r])
2482
+ assert len(r1) == 1
2483
+
2484
+ r3 = Rotation.from_quat(xp.asarray(Rotation.random(3).as_quat()))
2485
+ r3 = r.concatenate([r3, r])
2486
+ assert len(r3) == 3
2487
+
2488
+ r4 = Rotation.from_quat(xp.asarray(Rotation.random(4).as_quat()))
2489
+ r4 = r.concatenate([r, r4])
2490
+ r4 = r.concatenate([r, r4])
2491
+ assert len(r4) == 4
2492
+
2493
+
2494
+ def test_zero_rotation_power(xp):
2495
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2496
+ for pp in [-1.5, -1, 0, 1, 1.5]:
2497
+ pow0 = r**pp
2498
+ assert len(pow0) == 0
2499
+
2500
+
2501
+ def test_zero_rotation_inverse(xp):
2502
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2503
+ r_inv = r.inv()
2504
+ assert len(r_inv) == 0
2505
+
2506
+
2507
+ def test_zero_rotation_magnitude(xp):
2508
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2509
+ magnitude = r.magnitude()
2510
+ assert magnitude.shape == (0,)
2511
+
2512
+
2513
+ def test_zero_rotation_mean(xp):
2514
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2515
+ with pytest.raises(ValueError, match="Mean of an empty rotation set is undefined."):
2516
+ r.mean()
2517
+
2518
+
2519
+ def test_zero_rotation_approx_equal(xp):
2520
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2521
+ r0 = Rotation.from_quat(xp.zeros((0, 4)))
2522
+ assert r.approx_equal(r0).shape == (0,)
2523
+ r1 = Rotation.from_quat(xp.asarray([0.0, 0, 0, 1]))
2524
+ assert r.approx_equal(r1).shape == (0,)
2525
+ r2 = Rotation.from_quat(xp.asarray(Rotation.random().as_quat()))
2526
+ assert r2.approx_equal(r).shape == (0,)
2527
+
2528
+ approx_msg = "Expected equal number of rotations"
2529
+ r3 = Rotation.from_quat(xp.asarray(Rotation.random(2).as_quat()))
2530
+ with pytest.raises(ValueError, match=approx_msg):
2531
+ r.approx_equal(r3)
2532
+
2533
+ with pytest.raises(ValueError, match=approx_msg):
2534
+ r3.approx_equal(r)
2535
+
2536
+
2537
+ def test_zero_rotation_get_set(xp):
2538
+ r = Rotation.from_quat(xp.zeros((0, 4)))
2539
+
2540
+ r_get = r[xp.asarray([], dtype=xp.bool)]
2541
+ assert len(r_get) == 0
2542
+
2543
+ r_slice = r[:0]
2544
+ assert len(r_slice) == 0
2545
+
2546
+ with pytest.raises(IndexError):
2547
+ r[xp.asarray([0])]
2548
+
2549
+ with pytest.raises(IndexError):
2550
+ r[xp.asarray([True])]
2551
+
2552
+ with pytest.raises(IndexError):
2553
+ r[0] = Rotation.from_quat(xp.asarray([0, 0, 0, 1]))
2554
+
2555
+
2556
+ def test_boolean_indexes(xp):
2557
+ r = Rotation.from_quat(xp.asarray(Rotation.random(3).as_quat()))
2558
+
2559
+ r0 = r[xp.asarray([False, False, False])]
2560
+ assert len(r0) == 0
2561
+
2562
+ r1 = r[xp.asarray([False, True, False])]
2563
+ assert len(r1) == 1
2564
+
2565
+ r3 = r[xp.asarray([True, True, True])]
2566
+ assert len(r3) == 3
2567
+
2568
+ with pytest.raises(IndexError):
2569
+ r[xp.asarray([True, True])]