scipy 1.16.2__cp313-cp313t-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.cp313t-win_arm64.lib +0 -0
  4. scipy/_cyutility.cp313t-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.cp313t-win_arm64.lib +0 -0
  13. scipy/_lib/_ccallback_c.cp313t-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.cp313t-win_arm64.lib +0 -0
  18. scipy/_lib/_fpumode.cp313t-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.cp313t-win_arm64.lib +0 -0
  23. scipy/_lib/_test_ccallback.cp313t-win_arm64.pyd +0 -0
  24. scipy/_lib/_test_deprecation_call.cp313t-win_arm64.lib +0 -0
  25. scipy/_lib/_test_deprecation_call.cp313t-win_arm64.pyd +0 -0
  26. scipy/_lib/_test_deprecation_def.cp313t-win_arm64.lib +0 -0
  27. scipy/_lib/_test_deprecation_def.cp313t-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.cp313t-win_arm64.lib +0 -0
  35. scipy/_lib/_uarray/_uarray.cp313t-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.cp313t-win_arm64.lib +0 -0
  101. scipy/_lib/messagestream.cp313t-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.cp313t-win_arm64.lib +0 -0
  148. scipy/cluster/_hierarchy.cp313t-win_arm64.pyd +0 -0
  149. scipy/cluster/_optimal_leaf_ordering.cp313t-win_arm64.lib +0 -0
  150. scipy/cluster/_optimal_leaf_ordering.cp313t-win_arm64.pyd +0 -0
  151. scipy/cluster/_vq.cp313t-win_arm64.lib +0 -0
  152. scipy/cluster/_vq.cp313t-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.cp313t-win_arm64.lib +0 -0
  193. scipy/fft/_pocketfft/pypocketfft.cp313t-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.cp313t-win_arm64.lib +0 -0
  215. scipy/fftpack/convolve.cp313t-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.cp313t-win_arm64.lib +0 -0
  233. scipy/integrate/_dop.cp313t-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.cp313t-win_arm64.lib +0 -0
  248. scipy/integrate/_lsoda.cp313t-win_arm64.pyd +0 -0
  249. scipy/integrate/_ode.py +1395 -0
  250. scipy/integrate/_odepack.cp313t-win_arm64.lib +0 -0
  251. scipy/integrate/_odepack.cp313t-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.cp313t-win_arm64.lib +0 -0
  255. scipy/integrate/_quadpack.cp313t-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.cp313t-win_arm64.lib +0 -0
  265. scipy/integrate/_test_multivariate.cp313t-win_arm64.pyd +0 -0
  266. scipy/integrate/_test_odeint_banded.cp313t-win_arm64.lib +0 -0
  267. scipy/integrate/_test_odeint_banded.cp313t-win_arm64.pyd +0 -0
  268. scipy/integrate/_vode.cp313t-win_arm64.lib +0 -0
  269. scipy/integrate/_vode.cp313t-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.cp313t-win_arm64.lib +0 -0
  290. scipy/interpolate/_dfitpack.cp313t-win_arm64.pyd +0 -0
  291. scipy/interpolate/_dierckx.cp313t-win_arm64.lib +0 -0
  292. scipy/interpolate/_dierckx.cp313t-win_arm64.pyd +0 -0
  293. scipy/interpolate/_fitpack.cp313t-win_arm64.lib +0 -0
  294. scipy/interpolate/_fitpack.cp313t-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.cp313t-win_arm64.lib +0 -0
  300. scipy/interpolate/_interpnd.cp313t-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.cp313t-win_arm64.lib +0 -0
  307. scipy/interpolate/_ppoly.cp313t-win_arm64.pyd +0 -0
  308. scipy/interpolate/_rbf.py +290 -0
  309. scipy/interpolate/_rbfinterp.py +550 -0
  310. scipy/interpolate/_rbfinterp_pythran.cp313t-win_arm64.lib +0 -0
  311. scipy/interpolate/_rbfinterp_pythran.cp313t-win_arm64.pyd +0 -0
  312. scipy/interpolate/_rgi.py +764 -0
  313. scipy/interpolate/_rgi_cython.cp313t-win_arm64.lib +0 -0
  314. scipy/interpolate/_rgi_cython.cp313t-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.cp313t-win_arm64.lib +0 -0
  343. scipy/io/_fast_matrix_market/_fmm_core.cp313t-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.cp313t-win_arm64.lib +0 -0
  355. scipy/io/_test_fortran.cp313t-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.cp313t-win_arm64.lib +0 -0
  386. scipy/io/matlab/_mio5_utils.cp313t-win_arm64.pyd +0 -0
  387. scipy/io/matlab/_mio_utils.cp313t-win_arm64.lib +0 -0
  388. scipy/io/matlab/_mio_utils.cp313t-win_arm64.pyd +0 -0
  389. scipy/io/matlab/_miobase.py +435 -0
  390. scipy/io/matlab/_streams.cp313t-win_arm64.lib +0 -0
  391. scipy/io/matlab/_streams.cp313t-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.cp313t-win_arm64.lib +0 -0
  623. scipy/linalg/_cythonized_array_utils.cp313t-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.cp313t-win_arm64.lib +0 -0
  630. scipy/linalg/_decomp_interpolative.cp313t-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.cp313t-win_arm64.lib +0 -0
  634. scipy/linalg/_decomp_lu_cython.cp313t-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.cp313t-win_arm64.lib +0 -0
  642. scipy/linalg/_decomp_update.cp313t-win_arm64.pyd +0 -0
  643. scipy/linalg/_expm_frechet.py +417 -0
  644. scipy/linalg/_fblas.cp313t-win_arm64.lib +0 -0
  645. scipy/linalg/_fblas.cp313t-win_arm64.pyd +0 -0
  646. scipy/linalg/_flapack.cp313t-win_arm64.lib +0 -0
  647. scipy/linalg/_flapack.cp313t-win_arm64.pyd +0 -0
  648. scipy/linalg/_lapack_subroutines.h +1521 -0
  649. scipy/linalg/_linalg_pythran.cp313t-win_arm64.lib +0 -0
  650. scipy/linalg/_linalg_pythran.cp313t-win_arm64.pyd +0 -0
  651. scipy/linalg/_matfuncs.py +1050 -0
  652. scipy/linalg/_matfuncs_expm.cp313t-win_arm64.lib +0 -0
  653. scipy/linalg/_matfuncs_expm.cp313t-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.cp313t-win_arm64.lib +0 -0
  657. scipy/linalg/_matfuncs_schur_sqrtm.cp313t-win_arm64.pyd +0 -0
  658. scipy/linalg/_matfuncs_sqrtm.py +107 -0
  659. scipy/linalg/_matfuncs_sqrtm_triu.cp313t-win_arm64.lib +0 -0
  660. scipy/linalg/_matfuncs_sqrtm_triu.cp313t-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.cp313t-win_arm64.lib +0 -0
  665. scipy/linalg/_solve_toeplitz.cp313t-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.cp313t-win_arm64.lib +0 -0
  672. scipy/linalg/cython_blas.cp313t-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.cp313t-win_arm64.lib +0 -0
  676. scipy/linalg/cython_lapack.cp313t-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.cp313t-win_arm64.lib +0 -0
  728. scipy/ndimage/_ctest.cp313t-win_arm64.pyd +0 -0
  729. scipy/ndimage/_cytest.cp313t-win_arm64.lib +0 -0
  730. scipy/ndimage/_cytest.cp313t-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.cp313t-win_arm64.lib +0 -0
  738. scipy/ndimage/_nd_image.cp313t-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.cp313t-win_arm64.lib +0 -0
  742. scipy/ndimage/_ni_label.cp313t-win_arm64.pyd +0 -0
  743. scipy/ndimage/_ni_support.py +139 -0
  744. scipy/ndimage/_rank_filter_1d.cp313t-win_arm64.lib +0 -0
  745. scipy/ndimage/_rank_filter_1d.cp313t-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.cp313t-win_arm64.lib +0 -0
  768. scipy/odr/__odrpack.cp313t-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.cp313t-win_arm64.lib +0 -0
  780. scipy/optimize/_bglu_dense.cp313t-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.cp313t-win_arm64.lib +0 -0
  790. scipy/optimize/_direct.cp313t-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.cp313t-win_arm64.lib +0 -0
  795. scipy/optimize/_group_columns.cp313t-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.cp313t-win_arm64.lib +0 -0
  799. scipy/optimize/_highspy/_core.cp313t-win_arm64.pyd +0 -0
  800. scipy/optimize/_highspy/_highs_options.cp313t-win_arm64.lib +0 -0
  801. scipy/optimize/_highspy/_highs_options.cp313t-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.cp313t-win_arm64.lib +0 -0
  805. scipy/optimize/_lbfgsb.cp313t-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.cp313t-win_arm64.lib +0 -0
  816. scipy/optimize/_lsap.cp313t-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.cp313t-win_arm64.lib +0 -0
  822. scipy/optimize/_lsq/givens_elimination.cp313t-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.cp313t-win_arm64.lib +0 -0
  830. scipy/optimize/_minpack.cp313t-win_arm64.pyd +0 -0
  831. scipy/optimize/_minpack_py.py +1178 -0
  832. scipy/optimize/_moduleTNC.cp313t-win_arm64.lib +0 -0
  833. scipy/optimize/_moduleTNC.cp313t-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.cp313t-win_arm64.lib +0 -0
  839. scipy/optimize/_pava_pybind.cp313t-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.cp313t-win_arm64.lib +0 -0
  850. scipy/optimize/_slsqplib.cp313t-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.cp313t-win_arm64.lib +0 -0
  855. scipy/optimize/_trlib/_trlib.cp313t-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.cp313t-win_arm64.lib +0 -0
  877. scipy/optimize/_zeros.cp313t-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.cp313t-win_arm64.lib +0 -0
  882. scipy/optimize/cython_optimize/_zeros.cp313t-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.cp313t-win_arm64.lib +0 -0
  953. scipy/signal/_max_len_seq_inner.cp313t-win_arm64.pyd +0 -0
  954. scipy/signal/_peak_finding.py +1310 -0
  955. scipy/signal/_peak_finding_utils.cp313t-win_arm64.lib +0 -0
  956. scipy/signal/_peak_finding_utils.cp313t-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.cp313t-win_arm64.lib +0 -0
  963. scipy/signal/_sigtools.cp313t-win_arm64.pyd +0 -0
  964. scipy/signal/_sosfilt.cp313t-win_arm64.lib +0 -0
  965. scipy/signal/_sosfilt.cp313t-win_arm64.pyd +0 -0
  966. scipy/signal/_spectral_py.py +2471 -0
  967. scipy/signal/_spline.cp313t-win_arm64.lib +0 -0
  968. scipy/signal/_spline.cp313t-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.cp313t-win_arm64.lib +0 -0
  974. scipy/signal/_upfirdn_apply.cp313t-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.cp313t-win_arm64.lib +0 -0
  1021. scipy/sparse/_csparsetools.cp313t-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.cp313t-win_arm64.lib +0 -0
  1032. scipy/sparse/_sparsetools.cp313t-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.cp313t-win_arm64.lib +0 -0
  1043. scipy/sparse/csgraph/_flow.cp313t-win_arm64.pyd +0 -0
  1044. scipy/sparse/csgraph/_laplacian.py +563 -0
  1045. scipy/sparse/csgraph/_matching.cp313t-win_arm64.lib +0 -0
  1046. scipy/sparse/csgraph/_matching.cp313t-win_arm64.pyd +0 -0
  1047. scipy/sparse/csgraph/_min_spanning_tree.cp313t-win_arm64.lib +0 -0
  1048. scipy/sparse/csgraph/_min_spanning_tree.cp313t-win_arm64.pyd +0 -0
  1049. scipy/sparse/csgraph/_reordering.cp313t-win_arm64.lib +0 -0
  1050. scipy/sparse/csgraph/_reordering.cp313t-win_arm64.pyd +0 -0
  1051. scipy/sparse/csgraph/_shortest_path.cp313t-win_arm64.lib +0 -0
  1052. scipy/sparse/csgraph/_shortest_path.cp313t-win_arm64.pyd +0 -0
  1053. scipy/sparse/csgraph/_tools.cp313t-win_arm64.lib +0 -0
  1054. scipy/sparse/csgraph/_tools.cp313t-win_arm64.pyd +0 -0
  1055. scipy/sparse/csgraph/_traversal.cp313t-win_arm64.lib +0 -0
  1056. scipy/sparse/csgraph/_traversal.cp313t-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.cp313t-win_arm64.lib +0 -0
  1079. scipy/sparse/linalg/_dsolve/_superlu.cp313t-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.cp313t-win_arm64.lib +0 -0
  1089. scipy/sparse/linalg/_eigen/arpack/_arpack.cp313t-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.cp313t-win_arm64.lib +0 -0
  1122. scipy/sparse/linalg/_propack/_cpropack.cp313t-win_arm64.pyd +0 -0
  1123. scipy/sparse/linalg/_propack/_dpropack.cp313t-win_arm64.lib +0 -0
  1124. scipy/sparse/linalg/_propack/_dpropack.cp313t-win_arm64.pyd +0 -0
  1125. scipy/sparse/linalg/_propack/_spropack.cp313t-win_arm64.lib +0 -0
  1126. scipy/sparse/linalg/_propack/_spropack.cp313t-win_arm64.pyd +0 -0
  1127. scipy/sparse/linalg/_propack/_zpropack.cp313t-win_arm64.lib +0 -0
  1128. scipy/sparse/linalg/_propack/_zpropack.cp313t-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.cp313t-win_arm64.lib +0 -0
  1170. scipy/spatial/_ckdtree.cp313t-win_arm64.pyd +0 -0
  1171. scipy/spatial/_distance_pybind.cp313t-win_arm64.lib +0 -0
  1172. scipy/spatial/_distance_pybind.cp313t-win_arm64.pyd +0 -0
  1173. scipy/spatial/_distance_wrap.cp313t-win_arm64.lib +0 -0
  1174. scipy/spatial/_distance_wrap.cp313t-win_arm64.pyd +0 -0
  1175. scipy/spatial/_geometric_slerp.py +238 -0
  1176. scipy/spatial/_hausdorff.cp313t-win_arm64.lib +0 -0
  1177. scipy/spatial/_hausdorff.cp313t-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.cp313t-win_arm64.lib +0 -0
  1182. scipy/spatial/_qhull.cp313t-win_arm64.pyd +0 -0
  1183. scipy/spatial/_qhull.pyi +213 -0
  1184. scipy/spatial/_spherical_voronoi.py +341 -0
  1185. scipy/spatial/_voronoi.cp313t-win_arm64.lib +0 -0
  1186. scipy/spatial/_voronoi.cp313t-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.cp313t-win_arm64.lib +0 -0
  1236. scipy/spatial/transform/_rigid_transform.cp313t-win_arm64.pyd +0 -0
  1237. scipy/spatial/transform/_rotation.cp313t-win_arm64.lib +0 -0
  1238. scipy/spatial/transform/_rotation.cp313t-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.cp313t-win_arm64.lib +0 -0
  1252. scipy/special/_comb.cp313t-win_arm64.pyd +0 -0
  1253. scipy/special/_ellip_harm.py +214 -0
  1254. scipy/special/_ellip_harm_2.cp313t-win_arm64.lib +0 -0
  1255. scipy/special/_ellip_harm_2.cp313t-win_arm64.pyd +0 -0
  1256. scipy/special/_gufuncs.cp313t-win_arm64.lib +0 -0
  1257. scipy/special/_gufuncs.cp313t-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.cp313t-win_arm64.lib +0 -0
  1281. scipy/special/_specfun.cp313t-win_arm64.pyd +0 -0
  1282. scipy/special/_special_ufuncs.cp313t-win_arm64.lib +0 -0
  1283. scipy/special/_special_ufuncs.cp313t-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.cp313t-win_arm64.lib +0 -0
  1288. scipy/special/_test_internal.cp313t-win_arm64.pyd +0 -0
  1289. scipy/special/_test_internal.pyi +9 -0
  1290. scipy/special/_testutils.py +321 -0
  1291. scipy/special/_ufuncs.cp313t-win_arm64.lib +0 -0
  1292. scipy/special/_ufuncs.cp313t-win_arm64.pyd +0 -0
  1293. scipy/special/_ufuncs.pyi +522 -0
  1294. scipy/special/_ufuncs.pyx +13173 -0
  1295. scipy/special/_ufuncs_cxx.cp313t-win_arm64.lib +0 -0
  1296. scipy/special/_ufuncs_cxx.cp313t-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.cp313t-win_arm64.lib +0 -0
  1304. scipy/special/cython_special.cp313t-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.cp313t-win_arm64.lib +0 -0
  1376. scipy/stats/_ansari_swilk_statistics.cp313t-win_arm64.pyd +0 -0
  1377. scipy/stats/_axis_nan_policy.py +692 -0
  1378. scipy/stats/_biasedurn.cp313t-win_arm64.lib +0 -0
  1379. scipy/stats/_biasedurn.cp313t-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.cp313t-win_arm64.lib +0 -0
  1404. scipy/stats/_levy_stable/levyst.cp313t-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.cp313t-win_arm64.lib +0 -0
  1418. scipy/stats/_qmc_cy.cp313t-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.cp313t-win_arm64.lib +0 -0
  1422. scipy/stats/_qmvnt_cy.cp313t-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.cp313t-win_arm64.lib +0 -0
  1426. scipy/stats/_rcont/rcont.cp313t-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.cp313t-win_arm64.lib +0 -0
  1433. scipy/stats/_sobol.cp313t-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.cp313t-win_arm64.lib +0 -0
  1437. scipy/stats/_stats.cp313t-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.cp313t-win_arm64.lib +0 -0
  1442. scipy/stats/_stats_pythran.cp313t-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.cp313t-win_arm64.lib +0 -0
  1447. scipy/stats/_unuran/unuran_wrapper.cp313t-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,4735 @@
1
+ import sys
2
+ import math
3
+
4
+ from concurrent.futures import ThreadPoolExecutor, as_completed
5
+ from decimal import Decimal
6
+ from itertools import product
7
+ from math import gcd
8
+
9
+ import pytest
10
+ from pytest import raises as assert_raises
11
+ from numpy.testing import (
12
+ assert_allclose, # until object arrays are gone
13
+ suppress_warnings)
14
+ import numpy as np
15
+ from numpy.exceptions import ComplexWarning
16
+
17
+ from scipy import fft as sp_fft
18
+ from scipy.ndimage import correlate1d
19
+ from scipy.optimize import fmin, linear_sum_assignment
20
+ from scipy import signal
21
+ from scipy.signal import (
22
+ correlate, correlate2d, correlation_lags, convolve, convolve2d,
23
+ fftconvolve, oaconvolve, choose_conv_method, envelope,
24
+ hilbert, hilbert2, lfilter, lfilter_zi, filtfilt, butter, zpk2tf, zpk2sos,
25
+ invres, invresz, vectorstrength, lfiltic, tf2sos, sosfilt, sosfiltfilt,
26
+ sosfilt_zi, tf2zpk, BadCoefficients, detrend, unique_roots, residue,
27
+ residuez)
28
+ from scipy.signal.windows import hann
29
+ from scipy.signal._signaltools import (_filtfilt_gust, _compute_factors,
30
+ _group_poles)
31
+ from scipy.signal._upfirdn import _upfirdn_modes
32
+ from scipy._lib import _testutils
33
+
34
+ from scipy._lib._array_api import (
35
+ xp_assert_close, xp_assert_equal, is_numpy, is_torch, is_jax, is_cupy,
36
+ assert_array_almost_equal, assert_almost_equal,
37
+ xp_copy, xp_size, xp_default_dtype, array_namespace
38
+ )
39
+ skip_xp_backends = pytest.mark.skip_xp_backends
40
+ xfail_xp_backends = pytest.mark.xfail_xp_backends
41
+
42
+
43
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
44
+ class TestConvolve:
45
+
46
+ @skip_xp_backends("jax.numpy",
47
+ reason="jax returns floats; scipy returns ints; cf gh-6076")
48
+ def test_basic(self, xp):
49
+ a = xp.asarray([3, 4, 5, 6, 5, 4])
50
+ b = xp.asarray([1, 2, 3])
51
+ c = convolve(a, b)
52
+ xp_assert_equal(c, xp.asarray([3, 10, 22, 28, 32, 32, 23, 12]))
53
+
54
+ @skip_xp_backends("jax.numpy",
55
+ reason="jax returns floats; scipy returns ints; cf gh-6076")
56
+ def test_same(self, xp):
57
+ a = xp.asarray([3, 4, 5])
58
+ b = xp.asarray([1, 2, 3, 4])
59
+ c = convolve(a, b, mode="same")
60
+ xp_assert_equal(c, xp.asarray([10, 22, 34]))
61
+
62
+ @skip_xp_backends("jax.numpy",
63
+ reason="jax returns floats; scipy returns ints; cf gh-6076")
64
+ def test_same_eq(self, xp):
65
+ a = xp.asarray([3, 4, 5])
66
+ b = xp.asarray([1, 2, 3])
67
+ c = convolve(a, b, mode="same")
68
+ xp_assert_equal(c, xp.asarray([10, 22, 22]))
69
+
70
+ def test_complex(self, xp):
71
+ x = xp.asarray([1 + 1j, 2 + 1j, 3 + 1j])
72
+ y = xp.asarray([1 + 1j, 2 + 1j])
73
+ z = convolve(x, y)
74
+ xp_assert_equal(z, xp.asarray([2j, 2 + 6j, 5 + 8j, 5 + 5j]))
75
+
76
+ @xfail_xp_backends("jax.numpy", reason="wrong output dtype")
77
+ def test_zero_rank(self, xp):
78
+ a = xp.asarray(1289)
79
+ b = xp.asarray(4567)
80
+ c = convolve(a, b)
81
+ xp_assert_equal(c, a * b)
82
+
83
+ @skip_xp_backends(np_only=True, reason="pure python")
84
+ def test_zero_rank_python_scalars(self, xp):
85
+ a = 1289
86
+ b = 4567
87
+ c = convolve(a, b)
88
+ assert c == a * b
89
+
90
+ def test_broadcastable(self, xp):
91
+ a = xp.reshape(xp.arange(27), (3, 3, 3))
92
+ b = xp.arange(3)
93
+ for i in range(3):
94
+ b_shape = [1]*3
95
+ b_shape[i] = 3
96
+
97
+ x = convolve(a, xp.reshape(b, b_shape), method='direct')
98
+ y = convolve(a, xp.reshape(b, b_shape), method='fft')
99
+ xp_assert_close(x, y, atol=1e-14)
100
+
101
+ @xfail_xp_backends("jax.numpy", reason="wrong output dtype")
102
+ def test_single_element(self, xp):
103
+ a = xp.asarray([4967])
104
+ b = xp.asarray([3920])
105
+ c = convolve(a, b)
106
+ xp_assert_equal(c, a * b)
107
+
108
+ @skip_xp_backends("jax.numpy",)
109
+ @skip_xp_backends("cupy")
110
+ def test_2d_arrays(self, xp):
111
+ a = xp.asarray([[1, 2, 3], [3, 4, 5]])
112
+ b = xp.asarray([[2, 3, 4], [4, 5, 6]])
113
+ c = convolve(a, b)
114
+ d = xp.asarray([[2, 7, 16, 17, 12],
115
+ [10, 30, 62, 58, 38],
116
+ [12, 31, 58, 49, 30]])
117
+ xp_assert_equal(c, d)
118
+
119
+ @skip_xp_backends("torch")
120
+ @skip_xp_backends("cupy")
121
+ def test_input_swapping(self, xp):
122
+ small = xp.reshape(xp.arange(8), (2, 2, 2))
123
+ big = 1j * xp.reshape(xp.arange(27, dtype=xp.complex128), (3, 3, 3))
124
+ big += xp.reshape(xp.arange(27, dtype=xp.complex128)[::-1], (3, 3, 3))
125
+
126
+ out_array = xp.asarray(
127
+ [[[0 + 0j, 26 + 0j, 25 + 1j, 24 + 2j],
128
+ [52 + 0j, 151 + 5j, 145 + 11j, 93 + 11j],
129
+ [46 + 6j, 133 + 23j, 127 + 29j, 81 + 23j],
130
+ [40 + 12j, 98 + 32j, 93 + 37j, 54 + 24j]],
131
+
132
+ [[104 + 0j, 247 + 13j, 237 + 23j, 135 + 21j],
133
+ [282 + 30j, 632 + 96j, 604 + 124j, 330 + 86j],
134
+ [246 + 66j, 548 + 180j, 520 + 208j, 282 + 134j],
135
+ [142 + 66j, 307 + 161j, 289 + 179j, 153 + 107j]],
136
+
137
+ [[68 + 36j, 157 + 103j, 147 + 113j, 81 + 75j],
138
+ [174 + 138j, 380 + 348j, 352 + 376j, 186 + 230j],
139
+ [138 + 174j, 296 + 432j, 268 + 460j, 138 + 278j],
140
+ [70 + 138j, 145 + 323j, 127 + 341j, 63 + 197j]],
141
+
142
+ [[32 + 72j, 68 + 166j, 59 + 175j, 30 + 100j],
143
+ [68 + 192j, 139 + 433j, 117 + 455j, 57 + 255j],
144
+ [38 + 222j, 73 + 499j, 51 + 521j, 21 + 291j],
145
+ [12 + 144j, 20 + 318j, 7 + 331j, 0 + 182j]]])
146
+
147
+ xp_assert_equal(convolve(small, big, 'full'), out_array)
148
+ xp_assert_equal(convolve(big, small, 'full'), out_array)
149
+ xp_assert_equal(convolve(small, big, 'same'),
150
+ out_array[1:3, 1:3, 1:3])
151
+ xp_assert_equal(convolve(big, small, 'same'),
152
+ out_array[0:3, 0:3, 0:3])
153
+ xp_assert_equal(convolve(small, big, 'valid'),
154
+ out_array[1:3, 1:3, 1:3])
155
+ xp_assert_equal(convolve(big, small, 'valid'),
156
+ out_array[1:3, 1:3, 1:3])
157
+
158
+ def test_invalid_params(self, xp):
159
+ a = xp.asarray([3, 4, 5])
160
+ b = xp.asarray([1, 2, 3])
161
+ assert_raises(ValueError, convolve, a, b, mode='spam')
162
+ assert_raises(ValueError, convolve, a, b, mode='eggs', method='fft')
163
+ assert_raises(ValueError, convolve, a, b, mode='ham', method='direct')
164
+ assert_raises(ValueError, convolve, a, b, mode='full', method='bacon')
165
+ assert_raises(ValueError, convolve, a, b, mode='same', method='bacon')
166
+
167
+ @skip_xp_backends("jax.numpy", reason="dtypes do not match")
168
+ def test_valid_mode2(self, xp):
169
+ # See gh-5897
170
+ a = xp.asarray([1, 2, 3, 6, 5, 3])
171
+ b = xp.asarray([2, 3, 4, 5, 3, 4, 2, 2, 1])
172
+ expected = xp.asarray([70, 78, 73, 65])
173
+
174
+ out = convolve(a, b, 'valid')
175
+ xp_assert_equal(out, expected)
176
+
177
+ out = convolve(b, a, 'valid')
178
+ xp_assert_equal(out, expected)
179
+
180
+ a = xp.asarray([1 + 5j, 2 - 1j, 3 + 0j])
181
+ b = xp.asarray([2 - 3j, 1 + 0j])
182
+ expected = xp.asarray([2 - 3j, 8 - 10j])
183
+
184
+ out = convolve(a, b, 'valid')
185
+ xp_assert_equal(out, expected)
186
+
187
+ out = convolve(b, a, 'valid')
188
+ xp_assert_equal(out, expected)
189
+
190
+ @skip_xp_backends("jax.numpy", reason="dtypes do not match")
191
+ def test_same_mode(self, xp):
192
+ a = xp.asarray([1, 2, 3, 3, 1, 2])
193
+ b = xp.asarray([1, 4, 3, 4, 5, 6, 7, 4, 3, 2, 1, 1, 3])
194
+ c = convolve(a, b, 'same')
195
+ d = xp.asarray([57, 61, 63, 57, 45, 36])
196
+ xp_assert_equal(c, d)
197
+
198
+ @skip_xp_backends("cupy", reason="different exception")
199
+ def test_invalid_shapes(self, xp):
200
+ # By "invalid," we mean that no one
201
+ # array has dimensions that are all at
202
+ # least as large as the corresponding
203
+ # dimensions of the other array. This
204
+ # setup should throw a ValueError.
205
+ a = xp.reshape(xp.arange(1, 7), (2, 3))
206
+ b = xp.reshape(xp.arange(-6, 0), (3, 2))
207
+
208
+ assert_raises(ValueError, convolve, *(a, b), **{'mode': 'valid'})
209
+ assert_raises(ValueError, convolve, *(b, a), **{'mode': 'valid'})
210
+
211
+ @skip_xp_backends(np_only=True, reason="TODO: convert this test")
212
+ def test_convolve_method(self, xp, n=100):
213
+ # this types data structure was manually encoded instead of
214
+ # using custom filters on the soon-to-be-removed np.sctypes
215
+ types = {'uint16', 'uint64', 'int64', 'int32',
216
+ 'complex128', 'float64', 'float16',
217
+ 'complex64', 'float32', 'int16',
218
+ 'uint8', 'uint32', 'int8', 'bool'}
219
+ args = [(t1, t2, mode) for t1 in types for t2 in types
220
+ for mode in ['valid', 'full', 'same']]
221
+
222
+ # These are random arrays, which means test is much stronger than
223
+ # convolving testing by convolving two np.ones arrays
224
+ rng = np.random.RandomState(42)
225
+ array_types = {'i': rng.choice([0, 1], size=n),
226
+ 'f': rng.randn(n)}
227
+ array_types['b'] = array_types['u'] = array_types['i']
228
+ array_types['c'] = array_types['f'] + 0.5j*array_types['f']
229
+
230
+ for t1, t2, mode in args:
231
+ x1 = array_types[np.dtype(t1).kind].astype(t1)
232
+ x2 = array_types[np.dtype(t2).kind].astype(t2)
233
+
234
+ results = {key: convolve(x1, x2, method=key, mode=mode)
235
+ for key in ['fft', 'direct']}
236
+
237
+ assert results['fft'].dtype == results['direct'].dtype
238
+
239
+ if 'bool' in t1 and 'bool' in t2:
240
+ assert choose_conv_method(x1, x2) == 'direct'
241
+ continue
242
+
243
+ # Found by experiment. Found approx smallest value for (rtol, atol)
244
+ # threshold to have tests pass.
245
+ if any([t in {'complex64', 'float32'} for t in [t1, t2]]):
246
+ kwargs = {'rtol': 1.0e-4, 'atol': 1e-6}
247
+ elif 'float16' in [t1, t2]:
248
+ # atol is default for np.allclose
249
+ kwargs = {'rtol': 1e-3, 'atol': 1e-3}
250
+ else:
251
+ # defaults for np.allclose (different from assert_allclose)
252
+ kwargs = {'rtol': 1e-5, 'atol': 1e-8}
253
+
254
+ xp_assert_close(results['fft'], results['direct'], **kwargs)
255
+
256
+ @skip_xp_backends("jax.numpy", reason="dtypes do not match")
257
+ def test_convolve_method_large_input(self, xp):
258
+ # This is really a test that convolving two large integers goes to the
259
+ # direct method even if they're in the fft method.
260
+ for n in [10, 20, 50, 51, 52, 53, 54, 60, 62]:
261
+ z = xp.asarray([2**n], dtype=xp.int64)
262
+ fft = convolve(z, z, method='fft')
263
+ direct = convolve(z, z, method='direct')
264
+
265
+ # this is the case when integer precision gets to us
266
+ # issue #6076 has more detail, hopefully more tests after resolved
267
+ # # XXX: revisit check_dtype under np 2.0: 32bit linux & windows
268
+ if n < 50:
269
+ val = xp.asarray([2**(2*n)])
270
+ xp_assert_equal(fft, direct)
271
+ xp_assert_equal(fft, val, check_dtype=False)
272
+ xp_assert_equal(direct, val, check_dtype=False)
273
+
274
+ @skip_xp_backends(np_only=True)
275
+ def test_mismatched_dims(self, xp):
276
+ # Input arrays should have the same number of dimensions
277
+ assert_raises(ValueError, convolve, [1], 2, method='direct')
278
+ assert_raises(ValueError, convolve, 1, [2], method='direct')
279
+ assert_raises(ValueError, convolve, [1], 2, method='fft')
280
+ assert_raises(ValueError, convolve, 1, [2], method='fft')
281
+ assert_raises(ValueError, convolve, [1], [[2]])
282
+ assert_raises(ValueError, convolve, [3], 2)
283
+
284
+ @pytest.mark.thread_unsafe
285
+ @skip_xp_backends(np_only=True)
286
+ def test_dtype_deprecation(self, xp):
287
+ # gh-21211
288
+ a = np.asarray([1, 2, 3, 6, 5, 3], dtype=object)
289
+ b = np.asarray([2, 3, 4, 5, 3, 4, 2, 2, 1], dtype=object)
290
+ with pytest.deprecated_call(match="dtype=object is not supported"):
291
+ convolve(a, b)
292
+
293
+
294
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
295
+ class TestConvolve2d:
296
+
297
+ @skip_xp_backends("jax.numpy", reason="dtypes do not match")
298
+ def test_2d_arrays(self, xp):
299
+ a = xp.asarray([[1, 2, 3], [3, 4, 5]])
300
+ b = xp.asarray([[2, 3, 4], [4, 5, 6]])
301
+ d = xp.asarray([[2, 7, 16, 17, 12],
302
+ [10, 30, 62, 58, 38],
303
+ [12, 31, 58, 49, 30]])
304
+ e = convolve2d(a, b)
305
+ xp_assert_equal(e, d)
306
+
307
+ @skip_xp_backends("jax.numpy", reason="dtypes do not match")
308
+ def test_valid_mode(self, xp):
309
+ e = xp.asarray([[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]])
310
+ f = xp.asarray([[1, 2, 3], [3, 4, 5]])
311
+ h = xp.asarray([[62, 80, 98, 116, 134]])
312
+
313
+ g = convolve2d(e, f, 'valid')
314
+ xp_assert_equal(g, h)
315
+
316
+ # See gh-5897
317
+ g = convolve2d(f, e, 'valid')
318
+ xp_assert_equal(g, h)
319
+
320
+ @skip_xp_backends("torch", reason="dtypes do not match")
321
+ def test_valid_mode_complx(self, xp):
322
+ e = xp.asarray([[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]])
323
+ f = xp.asarray([[1, 2, 3], [3, 4, 5]], dtype=xp.complex128) + 1j
324
+ h = xp.asarray([[62.+24.j, 80.+30.j, 98.+36.j, 116.+42.j, 134.+48.j]])
325
+
326
+ g = convolve2d(e, f, 'valid')
327
+ xp_assert_close(g, h)
328
+
329
+ # See gh-5897
330
+ g = convolve2d(f, e, 'valid')
331
+ xp_assert_equal(g, h)
332
+
333
+ @skip_xp_backends("jax.numpy", reason="jax only allows fillvalue=0")
334
+ def test_fillvalue(self, xp):
335
+ a = xp.asarray([[1, 2, 3], [3, 4, 5]])
336
+ b = xp.asarray([[2, 3, 4], [4, 5, 6]])
337
+ fillval = 1
338
+ c = convolve2d(a, b, 'full', 'fill', fillval)
339
+ d = xp.asarray([[24, 26, 31, 34, 32],
340
+ [28, 40, 62, 64, 52],
341
+ [32, 46, 67, 62, 48]])
342
+ xp_assert_equal(c, d)
343
+
344
+ def test_fillvalue_errors(self, xp):
345
+ msg = "could not cast `fillvalue` directly to the output "
346
+ with np.testing.suppress_warnings() as sup:
347
+ sup.filter(ComplexWarning, "Casting complex values")
348
+ with assert_raises(ValueError, match=msg):
349
+ convolve2d([[1]], [[1, 2]], fillvalue=1j)
350
+
351
+ msg = "`fillvalue` must be scalar or an array with "
352
+ with assert_raises(ValueError, match=msg):
353
+ convolve2d([[1]], [[1, 2]], fillvalue=[1, 2])
354
+
355
+ def test_fillvalue_empty(self, xp):
356
+ # Check that fillvalue being empty raises an error:
357
+ assert_raises(ValueError, convolve2d, [[1]], [[1, 2]],
358
+ fillvalue=[])
359
+
360
+ @skip_xp_backends("jax.numpy", reason="jax only supports boundary='fill'")
361
+ def test_wrap_boundary(self, xp):
362
+ a = xp.asarray([[1, 2, 3], [3, 4, 5]])
363
+ b = xp.asarray([[2, 3, 4], [4, 5, 6]])
364
+ c = convolve2d(a, b, 'full', 'wrap')
365
+ d = xp.asarray([[80, 80, 74, 80, 80],
366
+ [68, 68, 62, 68, 68],
367
+ [80, 80, 74, 80, 80]])
368
+ xp_assert_equal(c, d)
369
+
370
+ @skip_xp_backends("jax.numpy", reason="jax only supports boundary='fill'")
371
+ def test_sym_boundary(self, xp):
372
+ a = xp.asarray([[1, 2, 3], [3, 4, 5]])
373
+ b = xp.asarray([[2, 3, 4], [4, 5, 6]])
374
+ c = convolve2d(a, b, 'full', 'symm')
375
+ d = xp.asarray([[34, 30, 44, 62, 66],
376
+ [52, 48, 62, 80, 84],
377
+ [82, 78, 92, 110, 114]])
378
+ xp_assert_equal(c, d)
379
+
380
+ @skip_xp_backends("jax.numpy", reason="jax only supports boundary='fill'")
381
+ @pytest.mark.parametrize('func', [convolve2d, correlate2d])
382
+ @pytest.mark.parametrize('boundary, expected',
383
+ [('symm', [[37.0, 42.0, 44.0, 45.0]]),
384
+ ('wrap', [[43.0, 44.0, 42.0, 39.0]])])
385
+ def test_same_with_boundary(self, func, boundary, expected, xp):
386
+ # Test boundary='symm' and boundary='wrap' with a "long" kernel.
387
+ # The size of the kernel requires that the values in the "image"
388
+ # be extended more than once to handle the requested boundary method.
389
+ # This is a regression test for gh-8684 and gh-8814.
390
+ image = xp.asarray([[2.0, -1.0, 3.0, 4.0]])
391
+ kernel = xp.ones((1, 21))
392
+ result = func(image, kernel, mode='same', boundary=boundary)
393
+ # The expected results were calculated "by hand". Because the
394
+ # kernel is all ones, the same result is expected for convolve2d
395
+ # and correlate2d.
396
+ xp_assert_equal(result, xp.asarray(expected))
397
+
398
+ @skip_xp_backends("jax.numpy", reason="jax only supports boundary='fill'")
399
+ def test_boundary_extension_same(self, xp):
400
+ # Regression test for gh-12686.
401
+ # Use ndimage.convolve with appropriate arguments to create the
402
+ # expected result.
403
+ import scipy.ndimage as ndi
404
+ a = xp.reshape(xp.arange(1, 10*3+1, dtype=xp.float64), (10, 3))
405
+ b = xp.reshape(xp.arange(1, 10*10+1, dtype=xp.float64), (10, 10))
406
+ c = convolve2d(a, b, mode='same', boundary='wrap')
407
+ xp_assert_equal(c, ndi.convolve(a, b, mode='wrap', origin=(-1, -1)))
408
+
409
+ @skip_xp_backends("jax.numpy", reason="jax only supports boundary='fill'")
410
+ def test_boundary_extension_full(self, xp):
411
+ # Regression test for gh-12686.
412
+ # Use ndimage.convolve with appropriate arguments to create the
413
+ # expected result.
414
+ import scipy.ndimage as ndi
415
+ a = xp.reshape(xp.arange(1, 3*3+1, dtype=xp.float64), (3, 3))
416
+ b = xp.reshape(xp.arange(1, 6*6+1, dtype=xp.float64), (6, 6))
417
+ c = convolve2d(a, b, mode='full', boundary='wrap')
418
+
419
+ a_np = np.arange(1, 3*3 +1, dtype=float).reshape(3, 3)
420
+ apad_np = np.pad(a_np, ((3, 3), (3, 3)), 'wrap')
421
+ apad = xp.asarray(apad_np)
422
+ xp_assert_equal(c, xp.asarray(ndi.convolve(apad, b, mode='wrap')[:-1, :-1]))
423
+
424
+ def test_invalid_shapes(self, xp):
425
+ # By "invalid," we mean that no one
426
+ # array has dimensions that are all at
427
+ # least as large as the corresponding
428
+ # dimensions of the other array. This
429
+ # setup should throw a ValueError.
430
+ a = xp.reshape(xp.arange(1, 7), (2, 3))
431
+ b = xp.reshape(xp.arange(-6, 0), (3, 2))
432
+
433
+ assert_raises(ValueError, convolve2d, *(a, b), **{'mode': 'valid'})
434
+ assert_raises(ValueError, convolve2d, *(b, a), **{'mode': 'valid'})
435
+
436
+ @skip_xp_backends("jax.numpy",
437
+ reason="jax returns floats; scipy returns ints; cf gh-6076")
438
+ def test_same_mode(self, xp):
439
+ e = xp.asarray([[1, 2, 3], [3, 4, 5]])
440
+ f = xp.asarray([[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]])
441
+ g = convolve2d(e, f, 'same')
442
+ h = xp.asarray([[22, 28, 34],
443
+ [80, 98, 116]])
444
+ xp_assert_equal(g, h)
445
+
446
+ @skip_xp_backends("jax.numpy",
447
+ reason="jax returns floats; scipy returns ints; cf gh-6076")
448
+ def test_valid_mode2(self, xp):
449
+ # See gh-5897
450
+ e = xp.asarray([[1, 2, 3], [3, 4, 5]])
451
+ f = xp.asarray([[2, 3, 4, 5, 6, 7, 8], [4, 5, 6, 7, 8, 9, 10]])
452
+ expected = xp.asarray([[62, 80, 98, 116, 134]])
453
+
454
+ out = convolve2d(e, f, 'valid')
455
+ xp_assert_equal(out, expected)
456
+
457
+ out = convolve2d(f, e, 'valid')
458
+ xp_assert_equal(out, expected)
459
+
460
+ e = xp.asarray([[1 + 1j, 2 - 3j], [3 + 1j, 4 + 0j]])
461
+ f = xp.asarray([[2 - 1j, 3 + 2j, 4 + 0j], [4 - 0j, 5 + 1j, 6 - 3j]])
462
+ expected = xp.asarray([[27 - 1j, 46. + 2j]])
463
+
464
+ out = convolve2d(e, f, 'valid')
465
+ xp_assert_equal(out, expected)
466
+
467
+ # See gh-5897
468
+ out = convolve2d(f, e, 'valid')
469
+ xp_assert_equal(out, expected)
470
+
471
+ @skip_xp_backends("torch",
472
+ reason="only integer tensors of a single element can be converted"
473
+ )
474
+ def test_consistency_convolve_funcs(self, xp):
475
+ # Compare np.convolve, signal.convolve, signal.convolve2d
476
+ a_np = np.arange(5)
477
+ b_np = np.asarray([3.2, 1.4, 3])
478
+ a = xp.asarray(a_np)
479
+ b = xp.asarray(b_np)
480
+
481
+ for mode in ['full', 'valid', 'same']:
482
+ xp_assert_close(
483
+ xp.asarray(np.convolve(a_np, b_np, mode=mode)),
484
+ signal.convolve(a, b, mode=mode)
485
+ )
486
+ xp_assert_close(
487
+ xp.squeeze(
488
+ signal.convolve2d(a[None, :], b[None, :], mode=mode),
489
+ axis=0
490
+ ),
491
+ signal.convolve(a, b, mode=mode)
492
+ )
493
+
494
+ def test_invalid_dims(self, xp):
495
+ assert_raises(ValueError, convolve2d, 3, 4)
496
+ assert_raises(ValueError, convolve2d, [3], [4])
497
+ assert_raises(ValueError, convolve2d, [[[3]]], [[[4]]])
498
+
499
+ @pytest.mark.slow
500
+ @pytest.mark.xfail_on_32bit("Can't create large array for test")
501
+ @skip_xp_backends(np_only=True, reason="stride_tricks")
502
+ def test_large_array(self, xp):
503
+ # Test indexing doesn't overflow an int (gh-10761)
504
+ n = 2**31 // (1000 * xp.int64().itemsize)
505
+ _testutils.check_free_memory(2 * n * 1001 * np.int64().itemsize / 1e6)
506
+
507
+ # Create a chequered pattern of 1s and 0s
508
+ a = xp.zeros(1001 * n, dtype=xp.int64)
509
+ a[::2] = 1
510
+ a = np.lib.stride_tricks.as_strided(a, shape=(n, 1000), strides=(8008, 8))
511
+
512
+ count = signal.convolve2d(a, [[1, 1]])
513
+ fails = np.where(count > 1)
514
+ assert fails[0].size == 0
515
+
516
+
517
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
518
+ class TestFFTConvolve:
519
+
520
+ @skip_xp_backends("torch", reason="dtypes do not match")
521
+ @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]])
522
+ def test_real(self, axes, xp):
523
+ a = xp.asarray([1, 2, 3])
524
+ expected = xp.asarray([1, 4, 10, 12, 9.])
525
+
526
+ if axes == '':
527
+ out = fftconvolve(a, a)
528
+ else:
529
+ if isinstance(axes, list):
530
+ axes = tuple(axes)
531
+ out = fftconvolve(a, a, axes=axes)
532
+
533
+ xp_assert_close(out, expected, atol=1.5e-6)
534
+
535
+ @skip_xp_backends("torch", reason="dtypes do not match")
536
+ @pytest.mark.parametrize('axes', [1, [1], -1, [-1]])
537
+ def test_real_axes(self, axes, xp):
538
+ a = xp.asarray([1, 2, 3])
539
+ expected = xp.asarray([1, 4, 10, 12, 9.])
540
+
541
+ a = xp.asarray(np.tile(a, [2, 1]))
542
+ expected = xp.asarray(np.tile(expected, [2, 1]))
543
+
544
+ if isinstance(axes, list):
545
+ axes = tuple(axes)
546
+
547
+ out = fftconvolve(a, a, axes=axes)
548
+ xp_assert_close(out, expected, atol=1.5e-6)
549
+
550
+ @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]])
551
+ def test_complex(self, axes, xp):
552
+ a = xp.asarray([1 + 1j, 2 + 2j, 3 + 3j])
553
+ expected = xp.asarray([0 + 2j, 0 + 8j, 0 + 20j, 0 + 24j, 0 + 18j])
554
+
555
+ if axes == '':
556
+ out = fftconvolve(a, a)
557
+ else:
558
+ if isinstance(axes, list):
559
+ axes = tuple(axes)
560
+ out = fftconvolve(a, a, axes=axes)
561
+ xp_assert_close(out, expected, atol=1.5e-6)
562
+
563
+ @pytest.mark.parametrize('axes', [1, [1], -1, [-1]])
564
+ def test_complex_axes(self, axes, xp):
565
+ a = xp.asarray([1 + 1j, 2 + 2j, 3 + 3j])
566
+ expected = xp.asarray([0 + 2j, 0 + 8j, 0 + 20j, 0 + 24j, 0 + 18j])
567
+
568
+ a = xp.asarray(np.tile(a, [2, 1]))
569
+ expected = xp.asarray(np.tile(expected, [2, 1]))
570
+
571
+ if isinstance(axes, list):
572
+ axes = tuple(axes)
573
+
574
+ out = fftconvolve(a, a, axes=axes)
575
+ xp_assert_close(out, expected, atol=1.5e-6)
576
+
577
+ @pytest.mark.parametrize('axes', ['',
578
+ None,
579
+ [0, 1],
580
+ [1, 0],
581
+ [0, -1],
582
+ [-1, 0],
583
+ [-2, 1],
584
+ [1, -2],
585
+ [-2, -1],
586
+ [-1, -2]])
587
+ def test_2d_real_same(self, axes, xp):
588
+ a = xp.asarray([[1.0, 2, 3],
589
+ [4, 5, 6]])
590
+ expected = xp.asarray([[1.0, 4, 10, 12, 9],
591
+ [8, 26, 56, 54, 36],
592
+ [16, 40, 73, 60, 36]])
593
+
594
+ if axes == '':
595
+ out = fftconvolve(a, a)
596
+ else:
597
+ if isinstance(axes, list):
598
+ axes = tuple(axes)
599
+ out = fftconvolve(a, a, axes=axes)
600
+ xp_assert_close(out, expected)
601
+
602
+ @pytest.mark.parametrize('axes', [[1, 2],
603
+ [2, 1],
604
+ [1, -1],
605
+ [-1, 1],
606
+ [-2, 2],
607
+ [2, -2],
608
+ [-2, -1],
609
+ [-1, -2]])
610
+ def test_2d_real_same_axes(self, axes, xp):
611
+ a = xp.asarray([[1, 2, 3],
612
+ [4, 5, 6]])
613
+ expected = xp.asarray([[1, 4, 10, 12, 9],
614
+ [8, 26, 56, 54, 36],
615
+ [16, 40, 73, 60, 36]])
616
+
617
+ a = xp.asarray(np.tile(a, [2, 1, 1]))
618
+ expected = xp.asarray(np.tile(expected, [2, 1, 1]))
619
+
620
+ if isinstance(axes, list):
621
+ axes = tuple(axes)
622
+
623
+ out = fftconvolve(a, a, axes=axes)
624
+ xp_assert_close(out, expected, atol=1.5e-6, check_dtype=False)
625
+
626
+ @pytest.mark.parametrize('axes', ['',
627
+ None,
628
+ [0, 1],
629
+ [1, 0],
630
+ [0, -1],
631
+ [-1, 0],
632
+ [-2, 1],
633
+ [1, -2],
634
+ [-2, -1],
635
+ [-1, -2]])
636
+ def test_2d_complex_same(self, axes, xp):
637
+ a = xp.asarray([[1 + 2j, 3 + 4j, 5 + 6j],
638
+ [2 + 1j, 4 + 3j, 6 + 5j]])
639
+ expected = xp.asarray([
640
+ [-3 + 4j, -10 + 20j, -21 + 56j, -18 + 76j, -11 + 60j],
641
+ [10j, 44j, 118j, 156j, 122j],
642
+ [3 + 4j, 10 + 20j, 21 + 56j, 18 + 76j, 11 + 60j]
643
+ ])
644
+
645
+ if axes == '':
646
+ out = fftconvolve(a, a)
647
+ else:
648
+ if isinstance(axes, list):
649
+ axes = tuple(axes)
650
+ out = fftconvolve(a, a, axes=axes)
651
+
652
+ xp_assert_close(out, expected, atol=1.5e-6)
653
+
654
+ @pytest.mark.parametrize('axes', [[1, 2],
655
+ [2, 1],
656
+ [1, -1],
657
+ [-1, 1],
658
+ [-2, 2],
659
+ [2, -2],
660
+ [-2, -1],
661
+ [-1, -2]])
662
+ def test_2d_complex_same_axes(self, axes, xp):
663
+ a = xp.asarray([[1 + 2j, 3 + 4j, 5 + 6j],
664
+ [2 + 1j, 4 + 3j, 6 + 5j]])
665
+ expected = xp.asarray([
666
+ [-3 + 4j, -10 + 20j, -21 + 56j, -18 + 76j, -11 + 60j],
667
+ [10j, 44j, 118j, 156j, 122j],
668
+ [3 + 4j, 10 + 20j, 21 + 56j, 18 + 76j, 11 + 60j]
669
+ ])
670
+
671
+ a = xp.asarray(np.tile(a, [2, 1, 1]))
672
+ expected = xp.asarray(np.tile(expected, [2, 1, 1]))
673
+
674
+ if isinstance(axes, list):
675
+ axes = tuple(axes)
676
+
677
+ out = fftconvolve(a, a, axes=axes)
678
+ xp_assert_close(out, expected, atol=1.5e-6)
679
+
680
+ @skip_xp_backends("torch", reason="dtypes do not match")
681
+ @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]])
682
+ def test_real_same_mode(self, axes, xp):
683
+ a = xp.asarray([1, 2, 3])
684
+ b = xp.asarray([3, 3, 5, 6, 8, 7, 9, 0, 1])
685
+ expected_1 = xp.asarray([35., 41., 47.])
686
+ expected_2 = xp.asarray([9., 20., 25., 35., 41., 47., 39., 28., 2.])
687
+
688
+ if axes == '':
689
+ out = fftconvolve(a, b, 'same')
690
+ else:
691
+ if isinstance(axes, list):
692
+ axes = tuple(axes)
693
+ out = fftconvolve(a, b, 'same', axes=axes)
694
+ xp_assert_close(out, expected_1)
695
+
696
+ if axes == '':
697
+ out = fftconvolve(b, a, 'same')
698
+ else:
699
+ if isinstance(axes, list):
700
+ axes = tuple(axes)
701
+ out = fftconvolve(b, a, 'same', axes=axes)
702
+ xp_assert_close(out, expected_2, atol=1.5e-6)
703
+
704
+ @skip_xp_backends("torch", reason="dtypes do not match")
705
+ @pytest.mark.parametrize('axes', [1, -1, [1], [-1]])
706
+ def test_real_same_mode_axes(self, axes, xp):
707
+ a = xp.asarray([1, 2, 3])
708
+ b = xp.asarray([3, 3, 5, 6, 8, 7, 9, 0, 1])
709
+ expected_1 = xp.asarray([35., 41., 47.])
710
+ expected_2 = xp.asarray([9., 20., 25., 35., 41., 47., 39., 28., 2.])
711
+
712
+ a = xp.asarray(np.tile(a, [2, 1]))
713
+ b = xp.asarray(np.tile(b, [2, 1]))
714
+ expected_1 = xp.asarray(np.tile(expected_1, [2, 1]))
715
+ expected_2 = xp.asarray(np.tile(expected_2, [2, 1]))
716
+
717
+ if isinstance(axes, list):
718
+ axes = tuple(axes)
719
+
720
+ out = fftconvolve(a, b, 'same', axes=axes)
721
+ xp_assert_close(out, expected_1, atol=1.5e-6)
722
+
723
+ out = fftconvolve(b, a, 'same', axes=axes)
724
+ xp_assert_close(out, expected_2, atol=1.5e-6)
725
+
726
+ @skip_xp_backends("torch", reason="dtypes do not match")
727
+ @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]])
728
+ def test_valid_mode_real(self, axes, xp):
729
+ # See gh-5897
730
+ a = xp.asarray([3, 2, 1])
731
+ b = xp.asarray([3, 3, 5, 6, 8, 7, 9, 0, 1])
732
+ expected = xp.asarray([24., 31., 41., 43., 49., 25., 12.])
733
+
734
+ if axes == '':
735
+ out = fftconvolve(a, b, 'valid')
736
+ else:
737
+ if isinstance(axes, list):
738
+ axes = tuple(axes)
739
+ out = fftconvolve(a, b, 'valid', axes=axes)
740
+ xp_assert_close(out, expected, atol=1.5e-6)
741
+
742
+ if axes == '':
743
+ out = fftconvolve(b, a, 'valid')
744
+ else:
745
+ if isinstance(axes, list):
746
+ axes = tuple(axes)
747
+ out = fftconvolve(b, a, 'valid', axes=axes)
748
+ xp_assert_close(out, expected, atol=1.5e-6)
749
+
750
+ @skip_xp_backends("torch", reason="dtypes do not match")
751
+ @pytest.mark.parametrize('axes', [1, [1]])
752
+ def test_valid_mode_real_axes(self, axes, xp):
753
+ # See gh-5897
754
+ a = xp.asarray([3, 2, 1])
755
+ b = xp.asarray([3, 3, 5, 6, 8, 7, 9, 0, 1])
756
+ expected = xp.asarray([24., 31., 41., 43., 49., 25., 12.])
757
+
758
+ a = xp.asarray(np.tile(a, [2, 1]))
759
+ b = xp.asarray(np.tile(b, [2, 1]))
760
+ expected = xp.asarray(np.tile(expected, [2, 1]))
761
+
762
+ if isinstance(axes, list):
763
+ axes = tuple(axes)
764
+
765
+ out = fftconvolve(a, b, 'valid', axes=axes)
766
+ xp_assert_close(out, expected, atol=1.5e-6)
767
+
768
+ @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]])
769
+ def test_valid_mode_complex(self, axes, xp):
770
+ a = xp.asarray([3 - 1j, 2 + 7j, 1 + 0j])
771
+ b = xp.asarray([3 + 2j, 3 - 3j, 5 + 0j, 6 - 1j, 8 + 0j])
772
+ expected = xp.asarray([45. + 12.j, 30. + 23.j, 48 + 32.j])
773
+
774
+ if axes == '':
775
+ out = fftconvolve(a, b, 'valid')
776
+ else:
777
+ if isinstance(axes, list):
778
+ axes = tuple(axes)
779
+ out = fftconvolve(a, b, 'valid', axes=axes)
780
+ xp_assert_close(out, expected, atol=1.5e-6)
781
+
782
+ if axes == '':
783
+ out = fftconvolve(b, a, 'valid')
784
+ else:
785
+ if isinstance(axes, list):
786
+ axes = tuple(axes)
787
+ out = fftconvolve(b, a, 'valid', axes=axes)
788
+ xp_assert_close(out, expected, atol=1.5e-6)
789
+
790
+ @pytest.mark.parametrize('axes', [1, [1], -1, [-1]])
791
+ def test_valid_mode_complex_axes(self, axes, xp):
792
+ a = xp.asarray([3 - 1j, 2 + 7j, 1 + 0j])
793
+ b = xp.asarray([3 + 2j, 3 - 3j, 5 + 0j, 6 - 1j, 8 + 0j])
794
+ expected = xp.asarray([45. + 12.j, 30. + 23.j, 48 + 32.j])
795
+
796
+ a = xp.asarray(np.tile(a, [2, 1]))
797
+ b = xp.asarray(np.tile(b, [2, 1]))
798
+ expected = xp.asarray(np.tile(expected, [2, 1]))
799
+
800
+ if isinstance(axes, list):
801
+ axes = tuple(axes)
802
+
803
+ out = fftconvolve(a, b, 'valid', axes=axes)
804
+ xp_assert_close(out, expected, atol=1.5e-6)
805
+
806
+ out = fftconvolve(b, a, 'valid', axes=axes)
807
+ xp_assert_close(out, expected, atol=1.5e-6)
808
+
809
+ @skip_xp_backends("jax.numpy", reason="mapped axes must have same shape")
810
+ @skip_xp_backends("torch", reason="dtypes do not match")
811
+ def test_valid_mode_ignore_nonaxes(self, xp):
812
+ # See gh-5897
813
+ a = xp.asarray([3, 2, 1])
814
+ b = xp.asarray([3, 3, 5, 6, 8, 7, 9, 0, 1])
815
+ expected = xp.asarray([24., 31., 41., 43., 49., 25., 12.])
816
+
817
+ a = xp.asarray(np.tile(a, [2, 1]))
818
+ b = xp.asarray(np.tile(b, [1, 1]))
819
+ expected = xp.asarray(np.tile(expected, [2, 1]))
820
+
821
+ out = fftconvolve(a, b, 'valid', axes=1)
822
+ xp_assert_close(out, expected, atol=1.5e-6)
823
+
824
+ @xfail_xp_backends("cupy", reason="dtypes do not match")
825
+ @xfail_xp_backends("jax.numpy", reason="assorted error messages")
826
+ @pytest.mark.parametrize("a,b", [([], []), ([5, 6], []), ([], [7])])
827
+ def test_empty(self, a, b, xp):
828
+ # Regression test for #1745: crashes with 0-length input.
829
+ xp_assert_equal(
830
+ fftconvolve(xp.asarray(a), xp.asarray(b)),
831
+ xp.asarray([]),
832
+ )
833
+
834
+ @skip_xp_backends("jax.numpy", reason="jnp.pad: pad_width with nd=0")
835
+ def test_zero_rank(self, xp):
836
+ a = xp.asarray(4967)
837
+ b = xp.asarray(3920)
838
+ out = fftconvolve(a, b)
839
+ xp_assert_equal(out, a * b)
840
+
841
+ def test_single_element(self, xp):
842
+ a = xp.asarray([4967])
843
+ b = xp.asarray([3920])
844
+ out = fftconvolve(a, b)
845
+ xp_assert_equal(out,
846
+ xp.asarray(a * b, dtype=out.dtype))
847
+
848
+ @pytest.mark.parametrize('axes', ['', None, 0, [0], -1, [-1]])
849
+ def test_random_data(self, axes, xp):
850
+ np.random.seed(1234)
851
+ a_np = np.random.rand(1233) + 1j * np.random.rand(1233)
852
+ b_np = np.random.rand(1321) + 1j * np.random.rand(1321)
853
+ expected = xp.asarray(np.convolve(a_np, b_np, 'full'))
854
+ a = xp.asarray(a_np)
855
+ b = xp.asarray(b_np)
856
+
857
+ if axes == '':
858
+ out = fftconvolve(a, b, 'full')
859
+ else:
860
+ if isinstance(axes, list):
861
+ axes = tuple(axes)
862
+ out = fftconvolve(a, b, 'full', axes=axes)
863
+ xp_assert_close(out, expected, rtol=1e-10)
864
+
865
+ @pytest.mark.parametrize('axes', [1, [1], -1, [-1]])
866
+ def test_random_data_axes(self, axes, xp):
867
+ np.random.seed(1234)
868
+ a_np = np.random.rand(1233) + 1j * np.random.rand(1233)
869
+ b_np = np.random.rand(1321) + 1j * np.random.rand(1321)
870
+ expected = np.convolve(a_np, b_np, 'full')
871
+ a_np = np.tile(a_np, [2, 1])
872
+ b_np = np.tile(b_np, [2, 1])
873
+ expected = xp.asarray(np.tile(expected, [2, 1]))
874
+
875
+ a = xp.asarray(a_np)
876
+ b = xp.asarray(b_np)
877
+
878
+ if isinstance(axes, list):
879
+ axes = tuple(axes)
880
+
881
+ out = fftconvolve(a, b, 'full', axes=axes)
882
+ xp_assert_close(out, expected, rtol=1e-10)
883
+
884
+ @xfail_xp_backends(np_only=True, reason="TODO: swapaxes")
885
+ @pytest.mark.parametrize('axes', [[1, 4],
886
+ [4, 1],
887
+ [1, -1],
888
+ [-1, 1],
889
+ [-4, 4],
890
+ [4, -4],
891
+ [-4, -1],
892
+ [-1, -4]])
893
+ def test_random_data_multidim_axes(self, axes, xp):
894
+ a_shape, b_shape = (123, 22), (132, 11)
895
+ np.random.seed(1234)
896
+ a = xp.asarray(np.random.rand(*a_shape) + 1j * np.random.rand(*a_shape))
897
+ b = xp.asarray(np.random.rand(*b_shape) + 1j * np.random.rand(*b_shape))
898
+ expected = convolve2d(a, b, 'full')
899
+
900
+ a = a[:, :, None, None, None]
901
+ b = b[:, :, None, None, None]
902
+ expected = expected[:, :, None, None, None]
903
+
904
+ a = xp.moveaxis(a.swapaxes(0, 2), 1, 4)
905
+ b = xp.moveaxis(b.swapaxes(0, 2), 1, 4)
906
+ expected = xp.moveaxis(expected.swapaxes(0, 2), 1, 4)
907
+
908
+ # use 1 for dimension 2 in a and 3 in b to test broadcasting
909
+ a = xp.asarray(np.tile(a, [2, 1, 3, 1, 1]))
910
+ b = xp.asarray(np.tile(b, [2, 1, 1, 4, 1]))
911
+ expected = xp.asarray(np.tile(expected, [2, 1, 3, 4, 1]))
912
+
913
+ out = fftconvolve(a, b, 'full', axes=axes)
914
+ xp_assert_close(out, expected, rtol=1e-10, atol=1e-10)
915
+
916
+ @pytest.mark.slow
917
+ @pytest.mark.parametrize(
918
+ 'n',
919
+ list(range(1, 100)) +
920
+ list(range(1000, 1500)) +
921
+ np.random.RandomState(1234).randint(1001, 10000, 5).tolist())
922
+ def test_many_sizes(self, n, xp):
923
+ a_np = np.random.rand(n) + 1j * np.random.rand(n)
924
+ b_np = np.random.rand(n) + 1j * np.random.rand(n)
925
+ expected = xp.asarray(np.convolve(a_np, b_np, 'full'))
926
+ a = xp.asarray(a_np)
927
+ b = xp.asarray(b_np)
928
+
929
+ out = fftconvolve(a, b, 'full')
930
+ xp_assert_close(out, expected, atol=1e-10)
931
+
932
+ out = fftconvolve(a, b, 'full', axes=[0])
933
+ xp_assert_close(out, expected, atol=1e-10)
934
+
935
+ @pytest.mark.thread_unsafe
936
+ @skip_xp_backends(np_only=True)
937
+ def test_fft_nan(self, xp):
938
+ n = 1000
939
+ rng = np.random.default_rng(43876432987)
940
+ sig_nan = xp.asarray(rng.standard_normal(n))
941
+
942
+ for val in [np.nan, np.inf]:
943
+ sig_nan[100] = val
944
+ coeffs = xp.asarray(signal.firwin(200, 0.2))
945
+
946
+ msg = "Use of fft convolution.*|invalid value encountered.*"
947
+ with pytest.warns(RuntimeWarning, match=msg):
948
+ signal.convolve(sig_nan, coeffs, mode='same', method='fft')
949
+
950
+
951
+ def fftconvolve_err(*args, **kwargs):
952
+ raise RuntimeError('Fell back to fftconvolve')
953
+
954
+
955
+ def gen_oa_shapes(sizes):
956
+ return [(a, b) for a, b in product(sizes, repeat=2)
957
+ if abs(a - b) > 3]
958
+
959
+
960
+ def gen_oa_shapes_2d(sizes):
961
+ shapes0 = gen_oa_shapes(sizes)
962
+ shapes1 = gen_oa_shapes(sizes)
963
+ shapes = [ishapes0+ishapes1 for ishapes0, ishapes1 in
964
+ zip(shapes0, shapes1)]
965
+
966
+ modes = ['full', 'valid', 'same']
967
+ return [ishapes+(imode,) for ishapes, imode in product(shapes, modes)
968
+ if imode != 'valid' or
969
+ (ishapes[0] > ishapes[1] and ishapes[2] > ishapes[3]) or
970
+ (ishapes[0] < ishapes[1] and ishapes[2] < ishapes[3])]
971
+
972
+
973
+ def gen_oa_shapes_eq(sizes):
974
+ return [(a, b) for a, b in product(sizes, repeat=2)
975
+ if a >= b]
976
+
977
+
978
+ @skip_xp_backends("jax.numpy", reason="fails all around")
979
+ @xfail_xp_backends("dask.array", reason="wrong answer")
980
+ class TestOAConvolve:
981
+ @pytest.mark.slow()
982
+ @pytest.mark.parametrize('shape_a_0, shape_b_0',
983
+ gen_oa_shapes_eq(list(range(1, 100, 1)) +
984
+ list(range(100, 1000, 23)))
985
+ )
986
+ def test_real_manylens(self, shape_a_0, shape_b_0, xp):
987
+ a = np.random.rand(shape_a_0)
988
+ b = np.random.rand(shape_b_0)
989
+ a = xp.asarray(a)
990
+ b = xp.asarray(b)
991
+
992
+ expected = fftconvolve(a, b)
993
+ out = oaconvolve(a, b)
994
+
995
+ assert_array_almost_equal(out, expected)
996
+
997
+ @pytest.mark.parametrize('shape_a_0, shape_b_0',
998
+ gen_oa_shapes([50, 47, 6, 4, 1]))
999
+ @pytest.mark.parametrize('is_complex', [True, False])
1000
+ @pytest.mark.parametrize('mode', ['full', 'valid', 'same'])
1001
+ def test_1d_noaxes(self, shape_a_0, shape_b_0,
1002
+ is_complex, mode, monkeypatch, xp):
1003
+ a = np.random.rand(shape_a_0)
1004
+ b = np.random.rand(shape_b_0)
1005
+ if is_complex:
1006
+ a = a + 1j*np.random.rand(shape_a_0)
1007
+ b = b + 1j*np.random.rand(shape_b_0)
1008
+
1009
+ a = xp.asarray(a)
1010
+ b = xp.asarray(b)
1011
+
1012
+ expected = fftconvolve(a, b, mode=mode)
1013
+
1014
+ monkeypatch.setattr(signal._signaltools, 'fftconvolve',
1015
+ fftconvolve_err)
1016
+ out = oaconvolve(a, b, mode=mode)
1017
+
1018
+ assert_array_almost_equal(out, expected)
1019
+
1020
+ @pytest.mark.parametrize('axes', [0, 1])
1021
+ @pytest.mark.parametrize('shape_a_0, shape_b_0',
1022
+ gen_oa_shapes([50, 47, 6, 4]))
1023
+ @pytest.mark.parametrize('shape_a_extra', [1, 3])
1024
+ @pytest.mark.parametrize('shape_b_extra', [1, 3])
1025
+ @pytest.mark.parametrize('is_complex', [True, False])
1026
+ @pytest.mark.parametrize('mode', ['full', 'valid', 'same'])
1027
+ def test_1d_axes(self, axes, shape_a_0, shape_b_0,
1028
+ shape_a_extra, shape_b_extra,
1029
+ is_complex, mode, monkeypatch, xp):
1030
+ ax_a = [shape_a_extra]*2
1031
+ ax_b = [shape_b_extra]*2
1032
+ ax_a[axes] = shape_a_0
1033
+ ax_b[axes] = shape_b_0
1034
+
1035
+ a = np.random.rand(*ax_a)
1036
+ b = np.random.rand(*ax_b)
1037
+ if is_complex:
1038
+ a = a + 1j*np.random.rand(*ax_a)
1039
+ b = b + 1j*np.random.rand(*ax_b)
1040
+
1041
+ a = xp.asarray(a)
1042
+ b = xp.asarray(b)
1043
+
1044
+ expected = fftconvolve(a, b, mode=mode, axes=axes)
1045
+
1046
+ monkeypatch.setattr(signal._signaltools, 'fftconvolve',
1047
+ fftconvolve_err)
1048
+ out = oaconvolve(a, b, mode=mode, axes=axes)
1049
+
1050
+ assert_array_almost_equal(out, expected)
1051
+
1052
+ @pytest.mark.parametrize('shape_a_0, shape_b_0, '
1053
+ 'shape_a_1, shape_b_1, mode',
1054
+ gen_oa_shapes_2d([50, 47, 6, 4]))
1055
+ @pytest.mark.parametrize('is_complex', [True, False])
1056
+ def test_2d_noaxes(self, shape_a_0, shape_b_0,
1057
+ shape_a_1, shape_b_1, mode,
1058
+ is_complex, monkeypatch, xp):
1059
+ a = np.random.rand(shape_a_0, shape_a_1)
1060
+ b = np.random.rand(shape_b_0, shape_b_1)
1061
+ if is_complex:
1062
+ a = a + 1j*np.random.rand(shape_a_0, shape_a_1)
1063
+ b = b + 1j*np.random.rand(shape_b_0, shape_b_1)
1064
+
1065
+ a = xp.asarray(a)
1066
+ b = xp.asarray(b)
1067
+
1068
+ expected = fftconvolve(a, b, mode=mode)
1069
+
1070
+ monkeypatch.setattr(signal._signaltools, 'fftconvolve',
1071
+ fftconvolve_err)
1072
+ out = oaconvolve(a, b, mode=mode)
1073
+
1074
+ assert_array_almost_equal(out, expected)
1075
+
1076
+ @pytest.mark.parametrize('axes', [[0, 1], [0, 2], [1, 2]])
1077
+ @pytest.mark.parametrize('shape_a_0, shape_b_0, '
1078
+ 'shape_a_1, shape_b_1, mode',
1079
+ gen_oa_shapes_2d([50, 47, 6, 4]))
1080
+ @pytest.mark.parametrize('shape_a_extra', [1, 3])
1081
+ @pytest.mark.parametrize('shape_b_extra', [1, 3])
1082
+ @pytest.mark.parametrize('is_complex', [True, False])
1083
+ def test_2d_axes(self, axes, shape_a_0, shape_b_0,
1084
+ shape_a_1, shape_b_1, mode,
1085
+ shape_a_extra, shape_b_extra,
1086
+ is_complex, monkeypatch, xp):
1087
+ ax_a = [shape_a_extra]*3
1088
+ ax_b = [shape_b_extra]*3
1089
+ ax_a[axes[0]] = shape_a_0
1090
+ ax_b[axes[0]] = shape_b_0
1091
+ ax_a[axes[1]] = shape_a_1
1092
+ ax_b[axes[1]] = shape_b_1
1093
+
1094
+ a = np.random.rand(*ax_a)
1095
+ b = np.random.rand(*ax_b)
1096
+ if is_complex:
1097
+ a = a + 1j*np.random.rand(*ax_a)
1098
+ b = b + 1j*np.random.rand(*ax_b)
1099
+
1100
+ a = xp.asarray(a)
1101
+ b = xp.asarray(b)
1102
+
1103
+ axes = tuple(axes) # XXX for CuPy
1104
+ expected = fftconvolve(a, b, mode=mode, axes=axes)
1105
+
1106
+ monkeypatch.setattr(signal._signaltools, 'fftconvolve',
1107
+ fftconvolve_err)
1108
+ out = oaconvolve(a, b, mode=mode, axes=axes)
1109
+
1110
+ assert_array_almost_equal(out, expected)
1111
+
1112
+ @xfail_xp_backends("torch", reason="ValueError: Target length must be positive")
1113
+ @pytest.mark.parametrize("a,b", [([], []), ([5, 6], []), ([], [7])])
1114
+ def test_empty(self, a, b, xp):
1115
+ # Regression test for #1745: crashes with 0-length input.
1116
+ xp_assert_equal(
1117
+ oaconvolve(xp.asarray(a), xp.asarray(b)),
1118
+ xp.asarray([]), check_dtype=False
1119
+ )
1120
+
1121
+ def test_zero_rank(self, xp):
1122
+ a = xp.asarray(4967)
1123
+ b = xp.asarray(3920)
1124
+ out = oaconvolve(a, b)
1125
+ xp_assert_equal(out, a * b)
1126
+
1127
+ def test_single_element(self, xp):
1128
+ a = xp.asarray([4967])
1129
+ b = xp.asarray([3920])
1130
+ out = oaconvolve(a, b)
1131
+ xp_assert_equal(out, a * b)
1132
+
1133
+
1134
+ @skip_xp_backends(np_only=True, reason="assertions may differ on backends")
1135
+ class TestAllFreqConvolves:
1136
+
1137
+ @pytest.mark.parametrize('convapproach',
1138
+ [fftconvolve, oaconvolve])
1139
+ def test_invalid_shapes(self, convapproach, xp):
1140
+ a = np.arange(1, 7).reshape((2, 3))
1141
+ b = np.arange(-6, 0).reshape((3, 2))
1142
+ with assert_raises(ValueError,
1143
+ match="For 'valid' mode, one must be at least "
1144
+ "as large as the other in every dimension"):
1145
+ convapproach(a, b, mode='valid')
1146
+
1147
+ @pytest.mark.parametrize('convapproach',
1148
+ [fftconvolve, oaconvolve])
1149
+ def test_invalid_shapes_axes(self, convapproach, xp):
1150
+ a = np.zeros([5, 6, 2, 1])
1151
+ b = np.zeros([5, 6, 3, 1])
1152
+ with assert_raises(ValueError,
1153
+ match=r"incompatible shapes for in1 and in2:"
1154
+ r" \(5L?, 6L?, 2L?, 1L?\) and"
1155
+ r" \(5L?, 6L?, 3L?, 1L?\)"):
1156
+ convapproach(a, b, axes=[0, 1])
1157
+
1158
+ @pytest.mark.parametrize('a,b',
1159
+ [([1], 2),
1160
+ (1, [2]),
1161
+ ([3], [[2]])])
1162
+ @pytest.mark.parametrize('convapproach',
1163
+ [fftconvolve, oaconvolve])
1164
+ def test_mismatched_dims(self, a, b, convapproach, xp):
1165
+ with assert_raises(ValueError,
1166
+ match="in1 and in2 should have the same"
1167
+ " dimensionality"):
1168
+ convapproach(a, b)
1169
+
1170
+ @pytest.mark.parametrize('convapproach',
1171
+ [fftconvolve, oaconvolve])
1172
+ def test_invalid_flags(self, convapproach, xp):
1173
+ with assert_raises(ValueError,
1174
+ match="acceptable mode flags are 'valid',"
1175
+ " 'same', or 'full'"):
1176
+ convapproach([1], [2], mode='chips')
1177
+
1178
+ with assert_raises(ValueError,
1179
+ match="when provided, axes cannot be empty"):
1180
+ convapproach([1], [2], axes=[])
1181
+
1182
+ with assert_raises(ValueError, match="axes must be a scalar or "
1183
+ "iterable of integers"):
1184
+ convapproach([1], [2], axes=[[1, 2], [3, 4]])
1185
+
1186
+ with assert_raises(ValueError, match="axes must be a scalar or "
1187
+ "iterable of integers"):
1188
+ convapproach([1], [2], axes=[1., 2., 3., 4.])
1189
+
1190
+ with assert_raises(ValueError,
1191
+ match="axes exceeds dimensionality of input"):
1192
+ convapproach([1], [2], axes=[1])
1193
+
1194
+ with assert_raises(ValueError,
1195
+ match="axes exceeds dimensionality of input"):
1196
+ convapproach([1], [2], axes=[-2])
1197
+
1198
+ with assert_raises(ValueError,
1199
+ match="all axes must be unique"):
1200
+ convapproach([1], [2], axes=[0, 0])
1201
+
1202
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
1203
+ @pytest.mark.parametrize('dtype', [np.longdouble, np.clongdouble])
1204
+ def test_longdtype_input(self, dtype, xp):
1205
+ x = np.random.random((27, 27)).astype(dtype)
1206
+ y = np.random.random((4, 4)).astype(dtype)
1207
+ if np.iscomplexobj(dtype()):
1208
+ x += .1j
1209
+ y -= .1j
1210
+
1211
+ res = fftconvolve(x, y)
1212
+ xp_assert_close(res, convolve(x, y, method='direct'))
1213
+ assert res.dtype == dtype
1214
+
1215
+
1216
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
1217
+ class TestMedFilt:
1218
+
1219
+ IN = [[50, 50, 50, 50, 50, 92, 18, 27, 65, 46],
1220
+ [50, 50, 50, 50, 50, 0, 72, 77, 68, 66],
1221
+ [50, 50, 50, 50, 50, 46, 47, 19, 64, 77],
1222
+ [50, 50, 50, 50, 50, 42, 15, 29, 95, 35],
1223
+ [50, 50, 50, 50, 50, 46, 34, 9, 21, 66],
1224
+ [70, 97, 28, 68, 78, 77, 61, 58, 71, 42],
1225
+ [64, 53, 44, 29, 68, 32, 19, 68, 24, 84],
1226
+ [3, 33, 53, 67, 1, 78, 74, 55, 12, 83],
1227
+ [7, 11, 46, 70, 60, 47, 24, 43, 61, 26],
1228
+ [32, 61, 88, 7, 39, 4, 92, 64, 45, 61]]
1229
+
1230
+ OUT = [[0, 50, 50, 50, 42, 15, 15, 18, 27, 0],
1231
+ [0, 50, 50, 50, 50, 42, 19, 21, 29, 0],
1232
+ [50, 50, 50, 50, 50, 47, 34, 34, 46, 35],
1233
+ [50, 50, 50, 50, 50, 50, 42, 47, 64, 42],
1234
+ [50, 50, 50, 50, 50, 50, 46, 55, 64, 35],
1235
+ [33, 50, 50, 50, 50, 47, 46, 43, 55, 26],
1236
+ [32, 50, 50, 50, 50, 47, 46, 45, 55, 26],
1237
+ [7, 46, 50, 50, 47, 46, 46, 43, 45, 21],
1238
+ [0, 32, 33, 39, 32, 32, 43, 43, 43, 0],
1239
+ [0, 7, 11, 7, 4, 4, 19, 19, 24, 0]]
1240
+
1241
+ KERNEL_SIZE = [7,3]
1242
+
1243
+ def test_basic(self, xp):
1244
+
1245
+ in_ = xp.asarray(self.IN)
1246
+ out_ = xp.asarray(self.OUT)
1247
+ kernel_size = xp.asarray(self.KERNEL_SIZE)
1248
+
1249
+ d = signal.medfilt(in_, kernel_size)
1250
+ e = signal.medfilt2d(xp.asarray(in_, dtype=xp.float64), kernel_size)
1251
+ xp_assert_equal(d, out_)
1252
+ xp_assert_equal(d, e, check_dtype=False)
1253
+
1254
+ @pytest.mark.parametrize('dtype', ["uint8", "int8", "uint16", "int16",
1255
+ "uint32", "int32", "uint64", "int64",
1256
+ "float32", "float64"])
1257
+ def test_types(self, dtype, xp):
1258
+ # volume input and output types match
1259
+ if is_torch(xp) and dtype in ["uint16", "uint32", "uint64"]:
1260
+ pytest.skip("torch does not support unisigned ints")
1261
+
1262
+ dtype = getattr(xp, dtype)
1263
+ in_typed = xp.asarray(self.IN, dtype=dtype)
1264
+ assert signal.medfilt(in_typed).dtype == dtype
1265
+ assert signal.medfilt2d(in_typed).dtype == dtype
1266
+
1267
+ @skip_xp_backends(np_only=True, reason="assertions may differ")
1268
+ @pytest.mark.parametrize('dtype', [np.bool_, np.complex64, np.complex128,
1269
+ np.clongdouble, np.float16,
1270
+ "float96", "float128"])
1271
+ def test_invalid_dtypes(self, dtype, xp):
1272
+ # We can only test this on platforms that support a native type of float96 or
1273
+ # float128; comparing to np.longdouble allows us to filter out non-native types
1274
+ if (dtype in ["float96", "float128"]
1275
+ and np.finfo(np.longdouble).dtype != dtype):
1276
+ pytest.skip(f"Platform does not support {dtype}")
1277
+
1278
+ in_typed = np.array(self.IN, dtype=dtype)
1279
+ with pytest.raises(ValueError, match="not supported"):
1280
+ signal.medfilt(in_typed)
1281
+
1282
+ with pytest.raises(ValueError, match="not supported"):
1283
+ signal.medfilt2d(in_typed)
1284
+
1285
+ @skip_xp_backends(np_only=True, reason="object arrays")
1286
+ def test_none(self, xp):
1287
+ # gh-1651, trac #1124. Ensure this does not segfault.
1288
+ with assert_raises((ValueError, TypeError)):
1289
+ signal.medfilt(None)
1290
+
1291
+ @skip_xp_backends(np_only=True, reason="strides are only writeable in NumPy")
1292
+ def test_odd_strides(self, xp):
1293
+ # Avoid a regression with possible contiguous
1294
+ # numpy arrays that have odd strides. The stride value below gets
1295
+ # us into wrong memory if used (but it does not need to be used)
1296
+ dummy = xp.arange(10, dtype=xp.float64)
1297
+ a = dummy[5:6]
1298
+ a = np.lib.stride_tricks.as_strided(a, strides=(16,))
1299
+ xp_assert_close(signal.medfilt(a, 1), xp.asarray([5.]))
1300
+
1301
+ @skip_xp_backends(
1302
+ "jax.numpy",
1303
+ reason="chunk assignment does not work on jax immutable arrays"
1304
+ )
1305
+ @pytest.mark.parametrize("dtype", ["uint8", "float32", "float64"])
1306
+ def test_medfilt2d_parallel(self, dtype, xp):
1307
+ dtype = getattr(xp, dtype)
1308
+ in_typed = xp.asarray(self.IN, dtype=dtype)
1309
+ expected = xp.asarray(self.OUT, dtype=dtype)
1310
+
1311
+ # This is used to simplify the indexing calculations.
1312
+ assert in_typed.shape == expected.shape
1313
+
1314
+ # We'll do the calculation in four chunks. M1 and N1 are the dimensions
1315
+ # of the first output chunk. We have to extend the input by half the
1316
+ # kernel size to be able to calculate the full output chunk.
1317
+ M1 = expected.shape[0] // 2
1318
+ N1 = expected.shape[1] // 2
1319
+ offM = self.KERNEL_SIZE[0] // 2 + 1
1320
+ offN = self.KERNEL_SIZE[1] // 2 + 1
1321
+
1322
+ def apply(chunk):
1323
+ # in = slice of in_typed to use.
1324
+ # sel = slice of output to crop it to the correct region.
1325
+ # out = slice of output array to store in.
1326
+ M, N = chunk
1327
+ if M == 0:
1328
+ Min = slice(0, M1 + offM)
1329
+ Msel = slice(0, -offM)
1330
+ Mout = slice(0, M1)
1331
+ else:
1332
+ Min = slice(M1 - offM, None)
1333
+ Msel = slice(offM, None)
1334
+ Mout = slice(M1, None)
1335
+ if N == 0:
1336
+ Nin = slice(0, N1 + offN)
1337
+ Nsel = slice(0, -offN)
1338
+ Nout = slice(0, N1)
1339
+ else:
1340
+ Nin = slice(N1 - offN, None)
1341
+ Nsel = slice(offN, None)
1342
+ Nout = slice(N1, None)
1343
+
1344
+ # Do the calculation, but do not write to the output in the threads.
1345
+ chunk_data = in_typed[Min, Nin]
1346
+ med = signal.medfilt2d(chunk_data, self.KERNEL_SIZE)
1347
+ return med[Msel, Nsel], Mout, Nout
1348
+
1349
+ # Give each chunk to a different thread.
1350
+ output = xp.zeros_like(expected)
1351
+ with ThreadPoolExecutor(max_workers=4) as pool:
1352
+ chunks = {(0, 0), (0, 1), (1, 0), (1, 1)}
1353
+ futures = {pool.submit(apply, chunk) for chunk in chunks}
1354
+
1355
+ # Store each result in the output as it arrives.
1356
+ for future in as_completed(futures):
1357
+ data, Mslice, Nslice = future.result()
1358
+ output[Mslice, Nslice] = data
1359
+
1360
+ xp_assert_equal(output, expected)
1361
+
1362
+
1363
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
1364
+ class TestWiener:
1365
+
1366
+ @skip_xp_backends("cupy", reason="XXX: can_cast in cupy <= 13.2")
1367
+ def test_basic(self, xp):
1368
+ g = xp.asarray([[5, 6, 4, 3],
1369
+ [3, 5, 6, 2],
1370
+ [2, 3, 5, 6],
1371
+ [1, 6, 9, 7]], dtype=xp.float64)
1372
+ h = xp.asarray([[2.16374269, 3.2222222222, 2.8888888889, 1.6666666667],
1373
+ [2.666666667, 4.33333333333, 4.44444444444, 2.8888888888],
1374
+ [2.222222222, 4.4444444444, 5.4444444444, 4.801066874837],
1375
+ [1.33333333333, 3.92735042735, 6.0712560386, 5.0404040404]])
1376
+ assert_array_almost_equal(signal.wiener(g), h, decimal=6)
1377
+ assert_array_almost_equal(signal.wiener(g, mysize=3), h, decimal=6)
1378
+
1379
+
1380
+ padtype_options = ["mean", "median", "minimum", "maximum", "line"]
1381
+ padtype_options += _upfirdn_modes
1382
+
1383
+
1384
+ @skip_xp_backends("dask.array", reason="XXX something in dask")
1385
+ class TestResample:
1386
+
1387
+ @skip_xp_backends("jax.numpy", reason="immutable arrays")
1388
+ @skip_xp_backends(
1389
+ cpu_only=True, reason="resample_poly/upfirdn is CPU only"
1390
+ )
1391
+ def test_basic(self, xp):
1392
+ # Some basic tests
1393
+
1394
+ # Regression test for issue #3603.
1395
+ # window.shape must equal to sig.shape[0]
1396
+ sig = xp.arange(128, dtype=xp.float64)
1397
+ num = 256
1398
+ win = signal.get_window(('kaiser', 8.0), 160)
1399
+ assert_raises(ValueError, signal.resample, sig, num, window=win)
1400
+ assert_raises(ValueError, signal.resample, sig, num, domain='INVALID')
1401
+
1402
+ # Other degenerate conditions
1403
+ assert_raises(ValueError, signal.resample_poly, sig, 'yo', 1)
1404
+ assert_raises(ValueError, signal.resample_poly, sig, 1, 0)
1405
+ assert_raises(ValueError, signal.resample_poly, sig, 1.3, 2)
1406
+ assert_raises(ValueError, signal.resample_poly, sig, 2, 1.3)
1407
+ assert_raises(ValueError, signal.resample_poly, sig, 2, 1, padtype='')
1408
+ assert_raises(ValueError, signal.resample_poly, sig, 2, 1,
1409
+ padtype='mean', cval=10)
1410
+ assert_raises(ValueError, signal.resample_poly, sig, 2, 1, window=np.eye(2))
1411
+
1412
+ # test for issue #6505 - should not modify window.shape when axis ≠ 0
1413
+ sig2 = xp.tile(xp.arange(160, dtype=xp.float64), (2, 1))
1414
+ signal.resample(sig2, num, axis=-1, window=win)
1415
+ assert win.shape == (160,)
1416
+
1417
+ # Ensure coverage for parameter cval=None and cval != None:
1418
+ x_ref = signal.resample_poly(sig, 2, 1)
1419
+ x0 = signal.resample_poly(sig, 2, 1, padtype='constant')
1420
+ x1 = signal.resample_poly(sig, 2, 1, padtype='constant', cval=0)
1421
+ xp_assert_equal(x1, x_ref)
1422
+ xp_assert_equal(x0, x_ref)
1423
+
1424
+ @pytest.mark.parametrize('window', (None, 'hamming'))
1425
+ @pytest.mark.parametrize('N', (20, 19))
1426
+ @pytest.mark.parametrize('num', (100, 101, 10, 11))
1427
+ @skip_xp_backends('jax.numpy', reason='immutable arrays')
1428
+ def test_rfft(self, N, num, window, xp):
1429
+ # Make sure the speed up using rfft gives the same result as the normal
1430
+ # way using fft
1431
+ dt_r = xp_default_dtype(xp)
1432
+ dt_c = xp.complex64 if dt_r == xp.float32 else xp.complex128
1433
+
1434
+ x = xp.linspace(0, 10, N, endpoint=False)
1435
+ y = xp.cos(-x**2/6.0)
1436
+ desired = signal.resample(xp.astype(y, dt_c), num, window=window)
1437
+ xp_assert_close(signal.resample(y, num, window=window),
1438
+ xp.real(desired))
1439
+
1440
+ y = xp.stack([xp.cos(-x**2/6.0), xp.sin(-x**2/6.0)])
1441
+ y_complex = xp.astype(y, dt_c)
1442
+ resampled = signal.resample(y_complex, num, axis=1, window=window)
1443
+
1444
+ atol = 1e-9 if dt_r == xp.float64 else 3e-7
1445
+
1446
+ xp_assert_close(
1447
+ signal.resample(y, num, axis=1, window=window),
1448
+ xp.real(resampled),
1449
+ atol=atol)
1450
+
1451
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
1452
+ def test_input_domain(self, xp):
1453
+ # Test if both input domain modes produce the same results.
1454
+ tsig = xp.astype(xp.arange(256), xp.complex128)
1455
+ fsig = sp_fft.fft(tsig)
1456
+ num = 256
1457
+ xp_assert_close(
1458
+ signal.resample(fsig, num, domain='freq'),
1459
+ signal.resample(tsig, num, domain='time'),
1460
+ atol=1e-9)
1461
+
1462
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
1463
+ @pytest.mark.parametrize('nx', (1, 2, 3, 5, 8))
1464
+ @pytest.mark.parametrize('ny', (1, 2, 3, 5, 8))
1465
+ @pytest.mark.parametrize('dtype', ('float64', 'complex128'))
1466
+ def test_dc(self, nx, ny, dtype, xp):
1467
+ dtype = getattr(xp, dtype)
1468
+ x = xp.asarray([1] * nx, dtype=dtype)
1469
+ y = signal.resample(x, ny)
1470
+ xp_assert_close(y, xp.asarray([1] * ny, dtype=y.dtype))
1471
+
1472
+ @skip_xp_backends(cpu_only=True, reason="resample_poly/upfirdn is CPU only")
1473
+ @pytest.mark.parametrize('padtype', padtype_options)
1474
+ def test_mutable_window(self, padtype, xp):
1475
+ # Test that a mutable window is not modified
1476
+ impulse = xp.zeros(3)
1477
+ window = xp.asarray(np.random.RandomState(0).randn(2))
1478
+ window_orig = xp.asarray(window, copy=True)
1479
+ signal.resample_poly(impulse, 5, 1, window=window, padtype=padtype)
1480
+ xp_assert_equal(window, window_orig)
1481
+
1482
+ @skip_xp_backends(
1483
+ cpu_only=True, reason="resample_poly/upfirdn is CPU only"
1484
+ )
1485
+ @pytest.mark.parametrize('padtype', padtype_options)
1486
+ def test_output_float32(self, padtype, xp):
1487
+ # Test that float32 inputs yield a float32 output
1488
+ x = xp.arange(10, dtype=xp.float32)
1489
+ h = xp.asarray([1, 1, 1], dtype=xp.float32)
1490
+ y = signal.resample_poly(x, 1, 2, window=h, padtype=padtype)
1491
+ assert y.dtype == xp.float32
1492
+
1493
+
1494
+ @skip_xp_backends(
1495
+ cpu_only=True, reason="resample_poly/upfirdn is CPU only"
1496
+ )
1497
+ @pytest.mark.parametrize('padtype', padtype_options)
1498
+ @pytest.mark.parametrize('dtype', ['float32', 'float64'])
1499
+ def test_output_match_dtype(self, padtype, dtype, xp):
1500
+ # Test that the dtype of x is preserved per issue #14733
1501
+ dtype = getattr(xp, dtype)
1502
+ x = xp.arange(10, dtype=dtype)
1503
+ y = signal.resample_poly(x, 1, 2, padtype=padtype)
1504
+ assert y.dtype == x.dtype
1505
+
1506
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
1507
+ @skip_xp_backends(
1508
+ cpu_only=True, reason="resample_poly/upfirdn is CPU only"
1509
+ )
1510
+ @pytest.mark.parametrize(
1511
+ "method, ext, padtype",
1512
+ [("fft", False, None)]
1513
+ + list(
1514
+ product(
1515
+ ["polyphase"], [False, True], padtype_options,
1516
+ )
1517
+ ),
1518
+ )
1519
+ def test_resample_methods(self, method, ext, padtype, xp):
1520
+ # Test resampling of sinusoids and random noise (1-sec)
1521
+ rate = 100
1522
+ rates_to = [49, 50, 51, 99, 100, 101, 199, 200, 201]
1523
+
1524
+ # Sinusoids, windowed to avoid edge artifacts
1525
+ t = xp.arange(rate, dtype=xp.float64) / float(rate)
1526
+ freqs = xp.asarray((1., 10., 40.))[:, xp.newaxis]
1527
+ x = xp.sin(2 * xp.pi * freqs * t) * hann(rate, xp=xp)
1528
+
1529
+ for rate_to in rates_to:
1530
+ t_to = xp.arange(rate_to, dtype=xp.float64) / float(rate_to)
1531
+ y_tos = xp.sin(2 * xp.pi * freqs * t_to) * hann(rate_to, xp=xp)
1532
+ if method == 'fft':
1533
+ y_resamps = signal.resample(x, rate_to, axis=-1)
1534
+ else:
1535
+ if ext and rate_to != rate:
1536
+ # Match default window design
1537
+ g = gcd(rate_to, rate)
1538
+ up = rate_to // g
1539
+ down = rate // g
1540
+ max_rate = max(up, down)
1541
+ f_c = 1. / max_rate
1542
+ half_len = 10 * max_rate
1543
+ window = signal.firwin(2 * half_len + 1, f_c,
1544
+ window=('kaiser', 5.0))
1545
+ polyargs = {'window': window, 'padtype': padtype}
1546
+ else:
1547
+ polyargs = {'padtype': padtype}
1548
+
1549
+ y_resamps = signal.resample_poly(x, rate_to, rate, axis=-1,
1550
+ **polyargs)
1551
+
1552
+ for i in range(y_tos.shape[0]):
1553
+ y_to = y_tos[i, :]
1554
+ y_resamp = y_resamps[i, :]
1555
+ freq = float(freqs[i, 0])
1556
+ if freq >= 0.5 * rate_to:
1557
+ #y_to.fill(0.) # mostly low-passed away
1558
+ y_to = xp.zeros_like(y_to) # mostly low-passed away
1559
+ if padtype in ['minimum', 'maximum']:
1560
+ xp_assert_close(y_resamp, y_to, atol=3e-1)
1561
+ else:
1562
+ xp_assert_close(y_resamp, y_to, atol=1e-3)
1563
+ else:
1564
+ assert y_to.shape == y_resamp.shape
1565
+ corr = np.corrcoef(y_to, y_resamp)[0, 1]
1566
+ assert corr > 0.99, (corr, rate, rate_to)
1567
+
1568
+ # Random data
1569
+ rng = np.random.RandomState(0)
1570
+ x = hann(rate) * np.cumsum(rng.randn(rate)) # low-pass, wind
1571
+ x = xp.asarray(x)
1572
+ for rate_to in rates_to:
1573
+ # random data
1574
+ t_to = xp.arange(rate_to, dtype=xp.float64) / float(rate_to)
1575
+ y_to = np.interp(t_to, t, x)
1576
+ if method == 'fft':
1577
+ y_resamp = signal.resample(x, rate_to)
1578
+ else:
1579
+ y_resamp = signal.resample_poly(x, rate_to, rate,
1580
+ padtype=padtype)
1581
+ assert y_to.shape == y_resamp.shape
1582
+ corr = xp.asarray(np.corrcoef(y_to, np.asarray(y_resamp))[0, 1])
1583
+ assert corr > 0.99, corr
1584
+
1585
+ # More tests of fft method (Master 0.18.1 fails these)
1586
+ if method == 'fft':
1587
+ x1 = xp.asarray([1.+0.j, 0.+0.j])
1588
+ y1_test = signal.resample(x1, 4)
1589
+ # upsampling a complex array
1590
+ y1_true = xp.asarray([1.+0.j, 0.5+0.j, 0.+0.j, 0.5+0.j])
1591
+ xp_assert_close(y1_test, y1_true, atol=1e-12)
1592
+ x2 = xp.asarray([1., 0.5, 0., 0.5])
1593
+ y2_test = signal.resample(x2, 2) # downsampling a real array
1594
+ y2_true = xp.asarray([1., 0.])
1595
+ xp_assert_close(y2_test, y2_true, atol=1e-12)
1596
+
1597
+ @pytest.mark.parametrize("n_in", (8, 9))
1598
+ @pytest.mark.parametrize("n_out", (3, 4))
1599
+ def test_resample_win_func(self, n_in, n_out):
1600
+ """Test callable window function. """
1601
+ x_in = np.ones(n_in)
1602
+
1603
+ def win(freqs):
1604
+ """Scale input by 1/2"""
1605
+ return 0.5 * np.ones_like(freqs)
1606
+
1607
+ y0 = signal.resample(x_in, n_out)
1608
+ y1 = signal.resample(x_in, n_out, window=win)
1609
+
1610
+ xp_assert_close(2*y1, y0, atol=1e-12)
1611
+
1612
+ @pytest.mark.parametrize("n_in", (6, 12))
1613
+ @pytest.mark.parametrize("n_out", (3, 4))
1614
+ def test__resample_param_t(self, n_in, n_out):
1615
+ """Verify behavior for parameter `t`.
1616
+
1617
+ Note that only `t[0]` and `t[1]` are utilized.
1618
+ """
1619
+ t0, dt = 10, 2
1620
+ x_in = np.ones(n_in)
1621
+
1622
+ y0 = signal.resample(x_in, n_out)
1623
+ y1, t1 = signal.resample(x_in, n_out, t=[t0, t0+dt])
1624
+ t_ref = 10 + np.arange(len(y0)) * dt * n_in / n_out
1625
+
1626
+ xp_assert_equal(y1, y0) # no influence of `t`
1627
+ xp_assert_close(t1, t_ref, atol=1e-12)
1628
+
1629
+ @pytest.mark.parametrize("n1", (2, 3, 7, 8))
1630
+ @pytest.mark.parametrize("n0", (2, 3, 7, 8))
1631
+ def test_resample_nyquist(self, n0, n1):
1632
+ """Test behavior at Nyquist frequency to ensure issue #14569 is fixed. """
1633
+ f_ny = min(n0, n1) // 2
1634
+ tt = (np.arange(n_) / n_ for n_ in (n0, n1))
1635
+ x0, x1 = (np.cos(2 * np.pi * f_ny * t_) for t_ in tt)
1636
+
1637
+ y1_r = signal.resample(x0, n1)
1638
+ y1_c = signal.resample(x0 + 0j, n1)
1639
+
1640
+ xp_assert_close(y1_r, x1, atol=1e-12)
1641
+ xp_assert_close(y1_c.real, x1, atol=1e-12)
1642
+
1643
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
1644
+ @skip_xp_backends(
1645
+ cpu_only=True, exceptions=["cupy"], reason="filtfilt is CPU-only"
1646
+ )
1647
+ @pytest.mark.parametrize('down_factor', [2, 11, 79])
1648
+ def test_poly_vs_filtfilt(self, down_factor, xp):
1649
+ # Check that up=1.0 gives same answer as filtfilt + slicing
1650
+ random_state = np.random.RandomState(17)
1651
+ try_types = (int, np.float32, np.complex64, float, complex)
1652
+ size = 10000
1653
+
1654
+ for dtype in try_types:
1655
+ x = random_state.randn(size).astype(dtype)
1656
+ if dtype in (np.complex64, np.complex128):
1657
+ x += 1j * random_state.randn(size)
1658
+ x = xp.asarray(x)
1659
+
1660
+ # resample_poly assumes zeros outside of signl, whereas filtfilt
1661
+ # can only constant-pad. Make them equivalent:
1662
+ x[0] = 0
1663
+ x[-1] = 0
1664
+
1665
+ h = signal.firwin(31, 1. / down_factor, window='hamming')
1666
+ h = xp.asarray(h) # XXX: convert firwin
1667
+ yf = filtfilt(h, 1.0, x, padtype='constant')[::down_factor]
1668
+
1669
+ # Need to pass convolved version of filter to resample_poly,
1670
+ # since filtfilt does forward and backward, but resample_poly
1671
+ # only goes forward
1672
+ hc = convolve(h, xp.flip(h))
1673
+ y = signal.resample_poly(x, 1, down_factor, window=hc)
1674
+ xp_assert_close(yf, y, atol=1e-7, rtol=1e-7)
1675
+
1676
+ @skip_xp_backends(
1677
+ cpu_only=True, exceptions=["cupy"], reason="correlate1d is CPU-only"
1678
+ )
1679
+ def test_correlate1d(self, xp):
1680
+ for down in [2, 4]:
1681
+ for nx in range(1, 40, down):
1682
+ for nweights in (32, 33):
1683
+ x = np.random.random((nx,))
1684
+ weights = np.random.random((nweights,))
1685
+ x, weights = map(xp.asarray, (x, weights))
1686
+ flip = array_namespace(x).flip
1687
+ y_g = correlate1d(x, flip(weights), mode='constant')
1688
+ y_s = signal.resample_poly(
1689
+ x, up=1, down=down, window=weights)
1690
+ xp_assert_close(y_g[::down], y_s)
1691
+
1692
+ @skip_xp_backends(
1693
+ cpu_only=True, reason="resample_poly/upfirdn is CPU only"
1694
+ )
1695
+ @pytest.mark.parametrize('dtype', ['int32', 'float32'])
1696
+ def test_gh_15620(self, dtype, xp):
1697
+ dtype = getattr(xp, dtype)
1698
+ data = xp.asarray([0, 1, 2, 3, 2, 1, 0], dtype=dtype)
1699
+ actual = signal.resample_poly(data,
1700
+ up=2,
1701
+ down=1,
1702
+ padtype='smooth')
1703
+ assert np.count_nonzero(actual) > 0
1704
+
1705
+
1706
+ @skip_xp_backends(np_only=True)
1707
+ class TestCSpline1DEval:
1708
+
1709
+ def test_basic(self, xp):
1710
+ y = np.array([1, 2, 3, 4, 3, 2, 1, 2, 3.0])
1711
+ x = np.arange(len(y))
1712
+ dx = x[1] - x[0]
1713
+ cj = signal.cspline1d(y)
1714
+
1715
+ x2 = np.arange(len(y) * 10.0) / 10.0
1716
+ y2 = signal.cspline1d_eval(cj, x2, dx=dx, x0=x[0])
1717
+
1718
+ # make sure interpolated values are on knot points
1719
+ assert_array_almost_equal(y2[::10], y, decimal=5)
1720
+
1721
+ def test_complex(self, xp):
1722
+ # create some smoothly varying complex signal to interpolate
1723
+ x = np.arange(2)
1724
+ y = np.zeros(x.shape, dtype=np.complex64)
1725
+ T = 10.0
1726
+ f = 1.0 / T
1727
+ y = np.exp(2.0J * np.pi * f * x)
1728
+
1729
+ # get the cspline transform
1730
+ cy = signal.cspline1d(y)
1731
+
1732
+ # determine new test x value and interpolate
1733
+ xnew = np.array([0.5])
1734
+ ynew = signal.cspline1d_eval(cy, xnew)
1735
+
1736
+ assert ynew.dtype == y.dtype
1737
+
1738
+
1739
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
1740
+ class TestOrderFilt:
1741
+
1742
+ def test_basic(self, xp):
1743
+ actual = signal.order_filter(xp.asarray([1, 2, 3]), xp.asarray([1, 0, 1]), 1)
1744
+ expect = xp.asarray([2, 3, 2])
1745
+ xp_assert_equal(actual, expect)
1746
+
1747
+ def test_doc_example(self, xp):
1748
+ x = xp.reshape(xp.arange(25, dtype=xp_default_dtype(xp)), (5, 5))
1749
+ domain = xp.eye(3, dtype=xp_default_dtype(xp))
1750
+
1751
+ # minimum of elements 1,3,9 (zero-padded) on phone pad
1752
+ # 7,5,3 on numpad
1753
+ expected = xp.asarray(
1754
+ [[0., 0., 0., 0., 0.],
1755
+ [0., 0., 1., 2., 0.],
1756
+ [0., 5., 6., 7., 0.],
1757
+ [0., 10., 11., 12., 0.],
1758
+ [0., 0., 0., 0., 0.]],
1759
+ dtype=xp_default_dtype(xp)
1760
+ )
1761
+ xp_assert_close(signal.order_filter(x, domain, 0), expected)
1762
+
1763
+ # maximum of elements 1,3,9 (zero-padded) on phone pad
1764
+ # 7,5,3 on numpad
1765
+ expected = xp.asarray(
1766
+ [[6., 7., 8., 9., 4.],
1767
+ [11., 12., 13., 14., 9.],
1768
+ [16., 17., 18., 19., 14.],
1769
+ [21., 22., 23., 24., 19.],
1770
+ [20., 21., 22., 23., 24.]],
1771
+ )
1772
+ xp_assert_close(signal.order_filter(x, domain, 2), expected)
1773
+
1774
+ # and, just to complete the set, median of zero-padded elements
1775
+ expected = xp.asarray(
1776
+ [[0, 1, 2, 3, 0],
1777
+ [5, 6, 7, 8, 3],
1778
+ [10, 11, 12, 13, 8],
1779
+ [15, 16, 17, 18, 13],
1780
+ [0, 15, 16, 17, 18]],
1781
+ dtype=xp_default_dtype(xp)
1782
+ )
1783
+ xp_assert_close(signal.order_filter(x, domain, 1), expected)
1784
+
1785
+ @xfail_xp_backends('dask.array', reason='repeat requires an axis')
1786
+ @xfail_xp_backends('torch', reason='array-api-compat#292')
1787
+ def test_medfilt_order_filter(self, xp):
1788
+ x = xp.reshape(xp.arange(25), (5, 5))
1789
+
1790
+ # median of zero-padded elements 1,5,9 on phone pad
1791
+ # 7,5,3 on numpad
1792
+ expected = xp.asarray(
1793
+ [[0, 1, 2, 3, 0],
1794
+ [1, 6, 7, 8, 4],
1795
+ [6, 11, 12, 13, 9],
1796
+ [11, 16, 17, 18, 14],
1797
+ [0, 16, 17, 18, 0]],
1798
+ )
1799
+ xp_assert_close(signal.medfilt(x, 3), expected)
1800
+
1801
+ xp_assert_close(
1802
+ signal.order_filter(x, xp.ones((3, 3)), 4),
1803
+ expected
1804
+ )
1805
+
1806
+ def test_order_filter_asymmetric(self, xp):
1807
+ x = xp.reshape(xp.arange(25), (5, 5))
1808
+ domain = xp.asarray(
1809
+ [[1, 1, 0],
1810
+ [0, 1, 0],
1811
+ [0, 0, 0]],
1812
+ )
1813
+
1814
+ expected = xp.asarray(
1815
+ [[0, 0, 0, 0, 0],
1816
+ [0, 0, 1, 2, 3],
1817
+ [0, 5, 6, 7, 8],
1818
+ [0, 10, 11, 12, 13],
1819
+ [0, 15, 16, 17, 18]]
1820
+ )
1821
+ xp_assert_close(signal.order_filter(x, domain, 0), expected)
1822
+
1823
+ expected = xp.asarray(
1824
+ [[0, 0, 0, 0, 0],
1825
+ [0, 1, 2, 3, 4],
1826
+ [5, 6, 7, 8, 9],
1827
+ [10, 11, 12, 13, 14],
1828
+ [15, 16, 17, 18, 19]]
1829
+ )
1830
+ xp_assert_close(signal.order_filter(x, domain, 1), expected)
1831
+
1832
+
1833
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
1834
+ class _TestLinearFilter:
1835
+
1836
+ def setup_method(self):
1837
+ if self.dtype == object:
1838
+ self.assert_close = np.testing.assert_allclose
1839
+ self.assert_equal = np.testing.assert_equal
1840
+ self.assert_array_almost_equal = np.testing.assert_array_almost_equal
1841
+ else:
1842
+ self.assert_close = xp_assert_close
1843
+ self.assert_equal = xp_assert_equal
1844
+ self.assert_array_almost_equal = assert_array_almost_equal
1845
+
1846
+ def generate(self, shape, xp):
1847
+ prodshape = shape if isinstance(shape, int) else math.prod(shape)
1848
+ x = xp.linspace(0, prodshape - 1, prodshape)
1849
+ if not isinstance(shape, int):
1850
+ x = xp.reshape(x, shape)
1851
+ return self.convert_dtype(x, xp)
1852
+
1853
+ def convert_dtype(self, arr, xp):
1854
+ if self.dtype == np.dtype('O'):
1855
+ arr = np.asarray(arr)
1856
+ out = np.empty(arr.shape, self.dtype)
1857
+ iter = np.nditer([arr, out], ['refs_ok','zerosize_ok'],
1858
+ [['readonly'],['writeonly']])
1859
+ for x, y in iter:
1860
+ y[...] = self.type(x[()])
1861
+ return out
1862
+ else:
1863
+ dtype = (getattr(xp, self.dtype)
1864
+ if isinstance(self.dtype, str)
1865
+ else self.dtype)
1866
+ return xp.asarray(arr, dtype=dtype)
1867
+
1868
+ def test_rank_1_IIR(self, xp):
1869
+ x = self.generate((6,), xp)
1870
+ b = self.convert_dtype([1, -1], xp)
1871
+ a = self.convert_dtype([0.5, -0.5], xp)
1872
+ y_r = self.convert_dtype([0, 2, 4, 6, 8, 10.], xp)
1873
+ self.assert_array_almost_equal(lfilter(b, a, x), y_r)
1874
+
1875
+ def test_rank_1_FIR(self, xp):
1876
+ x = self.generate((6,), xp)
1877
+ b = self.convert_dtype([1, 1], xp)
1878
+ a = self.convert_dtype([1], xp)
1879
+ y_r = self.convert_dtype([0, 1, 3, 5, 7, 9.], xp)
1880
+ self.assert_array_almost_equal(lfilter(b, a, x), y_r)
1881
+
1882
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
1883
+ def test_rank_1_IIR_init_cond(self, xp):
1884
+ x = self.generate((6,), xp)
1885
+ b = self.convert_dtype([1, 0, -1], xp)
1886
+ a = self.convert_dtype([0.5, -0.5], xp)
1887
+ zi = self.convert_dtype([1, 2], xp)
1888
+ y_r = self.convert_dtype([1, 5, 9, 13, 17, 21], xp)
1889
+ zf_r = self.convert_dtype([13, -10], xp)
1890
+ y, zf = lfilter(b, a, x, zi=zi)
1891
+ self.assert_array_almost_equal(y, y_r)
1892
+ self.assert_array_almost_equal(zf, zf_r)
1893
+
1894
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
1895
+ def test_rank_1_FIR_init_cond(self, xp):
1896
+ x = self.generate((6,), xp)
1897
+ b = self.convert_dtype([1, 1, 1], xp)
1898
+ a = self.convert_dtype([1], xp)
1899
+ zi = self.convert_dtype([1, 1], xp)
1900
+ y_r = self.convert_dtype([1, 2, 3, 6, 9, 12.], xp)
1901
+ zf_r = self.convert_dtype([9, 5], xp)
1902
+ y, zf = lfilter(b, a, x, zi=zi)
1903
+ self.assert_array_almost_equal(y, y_r)
1904
+ self.assert_array_almost_equal(zf, zf_r)
1905
+
1906
+ def test_rank_2_IIR_axis_0(self, xp):
1907
+ x = self.generate((4, 3), xp)
1908
+ b = self.convert_dtype([1, -1], xp)
1909
+ a = self.convert_dtype([0.5, 0.5], xp)
1910
+ y_r2_a0 = self.convert_dtype([[0, 2, 4], [6, 4, 2], [0, 2, 4],
1911
+ [6, 4, 2]], xp)
1912
+ y = lfilter(b, a, x, axis=0)
1913
+ self.assert_array_almost_equal(y_r2_a0, y)
1914
+
1915
+ def test_rank_2_IIR_axis_1(self, xp):
1916
+ x = self.generate((4, 3), xp)
1917
+ b = self.convert_dtype([1, -1], xp)
1918
+ a = self.convert_dtype([0.5, 0.5], xp)
1919
+ y_r2_a1 = self.convert_dtype([[0, 2, 0], [6, -4, 6], [12, -10, 12],
1920
+ [18, -16, 18]], xp)
1921
+ y = lfilter(b, a, x, axis=1)
1922
+ self.assert_array_almost_equal(y_r2_a1, y)
1923
+
1924
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
1925
+ def test_rank_2_IIR_axis_0_init_cond(self, xp):
1926
+ x = self.generate((4, 3), xp)
1927
+ b = self.convert_dtype([1, -1], xp)
1928
+ a = self.convert_dtype([0.5, 0.5], xp)
1929
+ zi = self.convert_dtype(np.ones((4,1)), xp)
1930
+
1931
+ y_r2_a0_1 = self.convert_dtype([[1, 1, 1], [7, -5, 7], [13, -11, 13],
1932
+ [19, -17, 19]], xp)
1933
+ zf_r = self.convert_dtype([-5, -17, -29, -41], xp)[:, np.newaxis]
1934
+ y, zf = lfilter(b, a, x, axis=1, zi=zi)
1935
+ self.assert_array_almost_equal(y_r2_a0_1, y)
1936
+ self.assert_array_almost_equal(zf, zf_r)
1937
+
1938
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
1939
+ def test_rank_2_IIR_axis_1_init_cond(self, xp):
1940
+ x = self.generate((4, 3), xp)
1941
+ b = self.convert_dtype([1, -1], xp)
1942
+ a = self.convert_dtype([0.5, 0.5], xp)
1943
+ zi = self.convert_dtype(np.ones((1, 3)), xp)
1944
+
1945
+ y_r2_a0_0 = self.convert_dtype([[1, 3, 5], [5, 3, 1],
1946
+ [1, 3, 5], [5, 3, 1]], xp)
1947
+ zf_r = self.convert_dtype([[-23, -23, -23]], xp)
1948
+ y, zf = lfilter(b, a, x, axis=0, zi=zi)
1949
+ self.assert_array_almost_equal(y_r2_a0_0, y)
1950
+ self.assert_array_almost_equal(zf, zf_r)
1951
+
1952
+ @skip_xp_backends(np_only=True, reason='np.apply_along_axis is np only')
1953
+ def test_rank_3_IIR(self, xp):
1954
+ x = self.generate((4, 3, 2), xp)
1955
+ b = self.convert_dtype([1, -1], xp)
1956
+ a = self.convert_dtype([0.5, 0.5], xp)
1957
+
1958
+ a_np, b_np, x_np = np.asarray(a), np.asarray(b), np.asarray(x)
1959
+ for axis in range(x.ndim):
1960
+ y = lfilter(b, a, x, axis)
1961
+ y_r = np.apply_along_axis(lambda w: lfilter(b_np, a_np, w), axis, x_np)
1962
+ self.assert_array_almost_equal(y, xp.asarray(y_r))
1963
+
1964
+ @skip_xp_backends(np_only=True, reason='np.apply_along_axis is np only')
1965
+ def test_rank_3_IIR_init_cond(self, xp):
1966
+ x = self.generate((4, 3, 2), xp)
1967
+ b = self.convert_dtype([1, -1], xp)
1968
+ a = self.convert_dtype([0.5, 0.5], xp)
1969
+
1970
+ for axis in range(x.ndim):
1971
+ zi_shape = list(x.shape)
1972
+ zi_shape[axis] = 1
1973
+ zi = self.convert_dtype(xp.ones(zi_shape), xp)
1974
+ zi1 = self.convert_dtype([1], xp)
1975
+ y, zf = lfilter(b, a, x, axis, zi)
1976
+ def lf0(w):
1977
+ return np.asarray(lfilter(b, a, w, zi=zi1)[0])
1978
+ def lf1(w):
1979
+ return np.asarray(lfilter(b, a, w, zi=zi1)[1])
1980
+ y_r = np.apply_along_axis(lf0, axis, np.asarray(x))
1981
+ zf_r = np.apply_along_axis(lf1, axis, np.asarray(x))
1982
+ self.assert_array_almost_equal(y, xp.asarray(y_r))
1983
+ self.assert_array_almost_equal(zf, xp.asarray(zf_r))
1984
+
1985
+ @skip_xp_backends(np_only=True, reason='np.apply_along_axis is np only')
1986
+ def test_rank_3_FIR(self, xp):
1987
+ x = self.generate((4, 3, 2), xp)
1988
+ b = self.convert_dtype([1, 0, -1], xp)
1989
+ a = self.convert_dtype([1], xp)
1990
+
1991
+ a_np, b_np, x_np = np.asarray(a), np.asarray(b), np.asarray(x)
1992
+ for axis in range(x.ndim):
1993
+ y = lfilter(b, a, x, axis)
1994
+ y_r = np.apply_along_axis(lambda w: lfilter(b_np, a_np, w), axis, x_np)
1995
+ self.assert_array_almost_equal(y, xp.asarray(y_r))
1996
+
1997
+ @skip_xp_backends(np_only=True, reason='np.apply_along_axis is np only')
1998
+ def test_rank_3_FIR_init_cond(self, xp):
1999
+ x = self.generate((4, 3, 2), xp)
2000
+ b = self.convert_dtype([1, 0, -1], xp)
2001
+ a = self.convert_dtype([1], xp)
2002
+
2003
+ x_np = np.asarray(x)
2004
+ for axis in range(x.ndim):
2005
+ zi_shape = list(x.shape)
2006
+ zi_shape[axis] = 2
2007
+ zi = self.convert_dtype(xp.ones(zi_shape), xp)
2008
+ zi1 = self.convert_dtype([1, 1], xp)
2009
+ y, zf = lfilter(b, a, x, axis, zi)
2010
+ def lf0(w):
2011
+ return np.asarray(lfilter(b, a, w, zi=zi1)[0])
2012
+ def lf1(w):
2013
+ return np.asarray(lfilter(b, a, w, zi=zi1)[1])
2014
+ y_r = np.apply_along_axis(lf0, axis, x_np)
2015
+ zf_r = np.apply_along_axis(lf1, axis, x_np)
2016
+ self.assert_array_almost_equal(y, xp.asarray(y_r))
2017
+ self.assert_array_almost_equal(zf, xp.asarray(zf_r))
2018
+
2019
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
2020
+ def test_zi_pseudobroadcast(self, xp):
2021
+ x = self.generate((4, 5, 20), xp)
2022
+ b, a = signal.butter(8, 0.2, output='ba')
2023
+ b = self.convert_dtype(b, xp)
2024
+ a = self.convert_dtype(a, xp)
2025
+ zi_size = b.shape[0] - 1
2026
+
2027
+ # lfilter requires x.ndim == zi.ndim exactly. However, zi can have
2028
+ # length 1 dimensions.
2029
+ zi_full = self.convert_dtype(xp.ones((4, 5, zi_size)), xp)
2030
+ zi_sing = self.convert_dtype(xp.ones((1, 1, zi_size)), xp)
2031
+
2032
+ y_full, zf_full = lfilter(b, a, x, zi=zi_full)
2033
+ y_sing, zf_sing = lfilter(b, a, x, zi=zi_sing)
2034
+
2035
+ self.assert_array_almost_equal(y_sing, y_full)
2036
+ self.assert_array_almost_equal(zf_full, zf_sing)
2037
+
2038
+ # lfilter does not prepend ones
2039
+ assert_raises(ValueError, lfilter, b, a, x, -1, xp.ones(zi_size))
2040
+
2041
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
2042
+ def test_scalar_a(self, xp):
2043
+ # a can be a scalar.
2044
+ x = self.generate(6, xp)
2045
+ b = self.convert_dtype([1, 0, -1], xp)
2046
+ a = self.convert_dtype([1], xp)
2047
+ y_r = self.convert_dtype([0, 1, 2, 2, 2, 2], xp)
2048
+
2049
+ y = lfilter(b, a[0], x)
2050
+ self.assert_array_almost_equal(y, y_r)
2051
+
2052
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
2053
+ def test_zi_some_singleton_dims(self, xp):
2054
+ # lfilter doesn't really broadcast (no prepending of 1's). But does
2055
+ # do singleton expansion if x and zi have the same ndim. This was
2056
+ # broken only if a subset of the axes were singletons (gh-4681).
2057
+ x = self.convert_dtype(xp.zeros((3, 2, 5), dtype=xp.int64), xp)
2058
+ b = self.convert_dtype(xp.ones(5, dtype=xp.int64), xp)
2059
+ a = self.convert_dtype(xp.asarray([1, 0, 0]), xp)
2060
+ zi = np.ones((3, 1, 4), dtype=np.int64)
2061
+ zi[1, :, :] *= 2
2062
+ zi[2, :, :] *= 3
2063
+ zi = xp.asarray(zi)
2064
+ zi = self.convert_dtype(zi, xp)
2065
+
2066
+ zf_expected = self.convert_dtype(xp.zeros((3, 2, 4), dtype=xp.int64), xp)
2067
+ y_expected = np.zeros((3, 2, 5), dtype=np.int64)
2068
+ y_expected[:, :, :4] = [[[1]], [[2]], [[3]]]
2069
+ y_expected = xp.asarray(y_expected)
2070
+ y_expected = self.convert_dtype(y_expected, xp)
2071
+
2072
+ # IIR
2073
+ y_iir, zf_iir = lfilter(b, a, x, -1, zi)
2074
+ self.assert_array_almost_equal(y_iir, y_expected)
2075
+ self.assert_array_almost_equal(zf_iir, zf_expected)
2076
+
2077
+ # FIR
2078
+ y_fir, zf_fir = lfilter(b, a[0], x, -1, zi)
2079
+ self.assert_array_almost_equal(y_fir, y_expected)
2080
+ self.assert_array_almost_equal(zf_fir, zf_expected)
2081
+
2082
+ def base_bad_size_zi(self, b, a, x, axis, zi, xp):
2083
+ b = self.convert_dtype(b, xp)
2084
+ a = self.convert_dtype(a, xp)
2085
+ x = self.convert_dtype(x, xp)
2086
+ zi = self.convert_dtype(zi, xp)
2087
+ # NOTE: MemoryError is currently allowed below because of:
2088
+ # https://github.com/numpy/numpy/issues/29721
2089
+ assert_raises((ValueError, MemoryError), lfilter, b, a, x, axis, zi)
2090
+
2091
+ @skip_xp_backends('cupy', reason='cupy does not raise')
2092
+ def test_bad_size_zi(self, xp):
2093
+ # rank 1
2094
+ x1 = xp.arange(6)
2095
+ self.base_bad_size_zi([1], [1], x1, -1, [1], xp)
2096
+ self.base_bad_size_zi([1, 1], [1], x1, -1, [0, 1], xp)
2097
+ self.base_bad_size_zi([1, 1], [1], x1, -1, [[0]], xp)
2098
+ self.base_bad_size_zi([1, 1], [1], x1, -1, [0, 1, 2], xp)
2099
+ self.base_bad_size_zi([1, 1, 1], [1], x1, -1, [[0]], xp)
2100
+ self.base_bad_size_zi([1, 1, 1], [1], x1, -1, [0, 1, 2], xp)
2101
+ self.base_bad_size_zi([1], [1, 1], x1, -1, [0, 1], xp)
2102
+ self.base_bad_size_zi([1], [1, 1], x1, -1, [[0]], xp)
2103
+ self.base_bad_size_zi([1], [1, 1], x1, -1, [0, 1, 2], xp)
2104
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [0], xp)
2105
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [[0], [1]], xp)
2106
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [0, 1, 2], xp)
2107
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x1, -1, [0, 1, 2, 3], xp)
2108
+ self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [0], xp)
2109
+ self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [[0], [1]], xp)
2110
+ self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [0, 1, 2], xp)
2111
+ self.base_bad_size_zi([1, 1], [1, 1, 1], x1, -1, [0, 1, 2, 3], xp)
2112
+
2113
+ # rank 2
2114
+ x2 = np.arange(12).reshape((4,3))
2115
+ x2 = xp.asarray(x2)
2116
+ # for axis=0 zi.shape should == (max(len(a),len(b))-1, 3)
2117
+ self.base_bad_size_zi([1], [1], x2, 0, [0], xp)
2118
+
2119
+ # for each of these there are 5 cases tested (in this order):
2120
+ # 1. not deep enough, right # elements
2121
+ # 2. too deep, right # elements
2122
+ # 3. right depth, right # elements, transposed
2123
+ # 4. right depth, too few elements
2124
+ # 5. right depth, too many elements
2125
+
2126
+ self.base_bad_size_zi([1, 1], [1], x2, 0, [0, 1, 2], xp)
2127
+ self.base_bad_size_zi([1, 1], [1], x2, 0, [[[0, 1, 2]]], xp)
2128
+ self.base_bad_size_zi([1, 1], [1], x2, 0, [[0], [1], [2]], xp)
2129
+ self.base_bad_size_zi([1, 1], [1], x2, 0, [[0, 1]], xp)
2130
+ self.base_bad_size_zi([1, 1], [1], x2, 0, [[0, 1, 2, 3]], xp)
2131
+
2132
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [0, 1, 2, 3, 4, 5], xp)
2133
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[[0, 1, 2], [3, 4, 5]]], xp)
2134
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0, 1], [2, 3], [4, 5]], xp)
2135
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0, 1], [2, 3]], xp)
2136
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 0, [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2137
+
2138
+ self.base_bad_size_zi([1], [1, 1], x2, 0, [0, 1, 2], xp)
2139
+ # this case is disabled on the release branch
2140
+ # because of:
2141
+ # https://github.com/scipy/scipy/pull/23543#issuecomment-3276286172
2142
+ # https://github.com/numpy/numpy/issues/29721
2143
+ #self.base_bad_size_zi([1], [1, 1], x2, 0, [[[0, 1, 2]]], xp)
2144
+ self.base_bad_size_zi([1], [1, 1], x2, 0, [[0], [1], [2]], xp)
2145
+ self.base_bad_size_zi([1], [1, 1], x2, 0, [[0, 1]], xp)
2146
+ self.base_bad_size_zi([1], [1, 1], x2, 0, [[0, 1, 2, 3]], xp)
2147
+
2148
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [0, 1, 2, 3, 4, 5], xp)
2149
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[[0, 1, 2], [3, 4, 5]]], xp)
2150
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[0, 1], [2, 3], [4, 5]], xp)
2151
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[0, 1], [2, 3]], xp)
2152
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 0, [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2153
+
2154
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [0, 1, 2, 3, 4, 5], xp)
2155
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[[0, 1, 2], [3, 4, 5]]], xp)
2156
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[0, 1], [2, 3], [4, 5]], xp)
2157
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0, [[0, 1], [2, 3]], xp)
2158
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 0,
2159
+ [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2160
+
2161
+ # for axis=1 zi.shape should == (4, max(len(a),len(b))-1)
2162
+ self.base_bad_size_zi([1], [1], x2, 1, [0], xp)
2163
+
2164
+ self.base_bad_size_zi([1, 1], [1], x2, 1, [0, 1, 2, 3], xp)
2165
+ self.base_bad_size_zi([1, 1], [1], x2, 1, [[[0], [1], [2], [3]]], xp)
2166
+ self.base_bad_size_zi([1, 1], [1], x2, 1, [[0, 1, 2, 3]], xp)
2167
+ self.base_bad_size_zi([1, 1], [1], x2, 1, [[0], [1], [2]], xp)
2168
+ self.base_bad_size_zi([1, 1], [1], x2, 1, [[0], [1], [2], [3], [4]], xp)
2169
+
2170
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [0, 1, 2, 3, 4, 5, 6, 7], xp)
2171
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 1,
2172
+ [[[0, 1], [2, 3], [4, 5], [6, 7]]], xp)
2173
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2174
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 1, [[0, 1], [2, 3], [4, 5]], xp)
2175
+ self.base_bad_size_zi([1, 1, 1], [1], x2, 1,
2176
+ [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]], xp)
2177
+
2178
+ self.base_bad_size_zi([1], [1, 1], x2, 1, [0, 1, 2, 3], xp)
2179
+ self.base_bad_size_zi([1], [1, 1], x2, 1, [[[0], [1], [2], [3]]], xp)
2180
+ self.base_bad_size_zi([1], [1, 1], x2, 1, [[0, 1, 2, 3]], xp)
2181
+ self.base_bad_size_zi([1], [1, 1], x2, 1, [[0], [1], [2]], xp)
2182
+ self.base_bad_size_zi([1], [1, 1], x2, 1, [[0], [1], [2], [3], [4]], xp)
2183
+
2184
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [0, 1, 2, 3, 4, 5, 6, 7], xp)
2185
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 1,
2186
+ [[[0, 1], [2, 3], [4, 5], [6, 7]]], xp)
2187
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2188
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[0, 1], [2, 3], [4, 5]], xp)
2189
+ self.base_bad_size_zi([1], [1, 1, 1], x2, 1, [[0, 1],
2190
+ [2, 3], [4, 5], [6, 7], [8, 9]], xp)
2191
+
2192
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [0, 1, 2, 3, 4, 5, 6, 7], xp)
2193
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1,
2194
+ [[[0, 1], [2, 3], [4, 5], [6, 7]]], xp)
2195
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1,
2196
+ [[0, 1, 2, 3], [4, 5, 6, 7]], xp)
2197
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1, [[0, 1], [2, 3], [4, 5]], xp)
2198
+ self.base_bad_size_zi([1, 1, 1], [1, 1], x2, 1,
2199
+ [[0, 1], [2, 3], [4, 5], [6, 7], [8, 9]], xp)
2200
+
2201
+ def test_empty_zi(self, xp):
2202
+ # Regression test for #880: empty array for zi crashes.
2203
+ x = self.generate((5,), xp)
2204
+ a = self.convert_dtype([1], xp)
2205
+ b = self.convert_dtype([1], xp)
2206
+ zi = self.convert_dtype([], xp)
2207
+ y, zf = lfilter(b, a, x, zi=zi)
2208
+ self.assert_array_almost_equal(y, x)
2209
+ assert zf.dtype == (getattr(xp, self.dtype)
2210
+ if isinstance(self.dtype, str)
2211
+ else self.dtype)
2212
+ assert xp_size(zf) == 0
2213
+
2214
+ @skip_xp_backends('jax.numpy', reason='jax does not support inplace ops')
2215
+ @pytest.mark.parametrize('a', (1, [1], [1, .5, 1.5], 2, [2], [2, 1, 3]),
2216
+ ids=str)
2217
+ def test_lfiltic(self, a, xp):
2218
+ # Test for #22470: lfiltic does not handle `a[0] != 1`
2219
+ # and, more in general, test that lfiltic behaves consistently with lfilter
2220
+ if is_cupy(xp) and isinstance(a, int | float):
2221
+ pytest.skip('cupy does not supoprt scalar filter coefficients')
2222
+ x = self.generate(6, xp) # arbitrary input
2223
+ b = self.convert_dtype([.5, 1., .2], xp) # arbitrary b
2224
+ a = self.convert_dtype(a, xp)
2225
+ N = xp_size(a) - 1
2226
+ M = xp_size(b) - 1
2227
+ K = M + N if is_cupy(xp) else max(N, M)
2228
+ # compute reference initial conditions as final conditions of lfilter
2229
+ y1, zi_1 = lfilter(b, a, x, zi=self.generate(K, xp))
2230
+ # copute initial conditions from lfiltic
2231
+ zi_2 = lfiltic(b, a, xp.flip(y1), xp.flip(x))
2232
+ # compare lfiltic's output with reference
2233
+ self.assert_array_almost_equal(zi_1, zi_2)
2234
+
2235
+ def test_lfiltic_bad_coeffs(xp):
2236
+ # Test for invalid filter coefficients (wrong shape or zero `a[0]`)
2237
+ assert_raises(ValueError, lfiltic, [1, 2], [], [0, 0], [0, 1])
2238
+ assert_raises(ValueError, lfiltic, [1, 2], [0, 2], [0, 0], [0, 1])
2239
+ assert_raises(ValueError, lfiltic, [1, 2], [[1], [2]], [0, 0], [0, 1])
2240
+ assert_raises(ValueError, lfiltic, [[1], [2]], [1], [0, 0], [0, 1])
2241
+
2242
+ @skip_xp_backends(
2243
+ 'array_api_strict', reason='int64 and float64 cannot be promoted together'
2244
+ )
2245
+ @skip_xp_backends('jax.numpy', reason='jax dtype defaults differ')
2246
+ def test_lfiltic_bad_zi(self, xp):
2247
+ # Regression test for #3699: bad initial conditions
2248
+ a = self.convert_dtype([1], xp)
2249
+ b = self.convert_dtype([1], xp)
2250
+ # "y" sets the datatype of zi, so it truncates if int
2251
+ zi = lfiltic(b, a, xp.asarray([1., 0]))
2252
+ zi_1 = lfiltic(b, a, xp.asarray([1.0, 0]))
2253
+ zi_2 = lfiltic(b, a, xp.asarray([True, False]))
2254
+ self.assert_equal(zi, zi_1)
2255
+
2256
+ check_dtype_arg = {} if self.dtype == object else {'check_dtype': False}
2257
+ self.assert_equal(zi, zi_2, **check_dtype_arg)
2258
+
2259
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
2260
+ def test_short_x_FIR(self, xp):
2261
+ # regression test for #5116
2262
+ # x shorter than b, with non None zi fails
2263
+ a = self.convert_dtype([1], xp)
2264
+ b = self.convert_dtype([1, 0, -1], xp)
2265
+ zi = self.convert_dtype([2, 7], xp)
2266
+ x = self.convert_dtype([72], xp)
2267
+ ye = self.convert_dtype([74], xp)
2268
+ zfe = self.convert_dtype([7, -72], xp)
2269
+ y, zf = lfilter(b, a, x, zi=zi)
2270
+ self.assert_array_almost_equal(y, ye)
2271
+ self.assert_array_almost_equal(zf, zfe)
2272
+
2273
+ @skip_xp_backends('cupy', reason='XXX https://github.com/cupy/cupy/pull/8677')
2274
+ def test_short_x_IIR(self, xp):
2275
+ # regression test for #5116
2276
+ # x shorter than b, with non None zi fails
2277
+ a = self.convert_dtype([1, 1], xp)
2278
+ b = self.convert_dtype([1, 0, -1], xp)
2279
+ zi = self.convert_dtype([2, 7], xp)
2280
+ x = self.convert_dtype([72], xp)
2281
+ ye = self.convert_dtype([74], xp)
2282
+ zfe = self.convert_dtype([-67, -72], xp)
2283
+ y, zf = lfilter(b, a, x, zi=zi)
2284
+ self.assert_array_almost_equal(y, ye)
2285
+ self.assert_array_almost_equal(zf, zfe)
2286
+
2287
+ def test_do_not_modify_a_b_IIR(self, xp):
2288
+ x = self.generate((6,), xp)
2289
+ b = self.convert_dtype([1, -1], xp)
2290
+ b0 = xp_copy(b, xp=xp)
2291
+ a = self.convert_dtype([0.5, -0.5], xp)
2292
+ a0 = xp_copy(a, xp=xp)
2293
+ y_r = self.convert_dtype([0, 2, 4, 6, 8, 10.], xp)
2294
+ y_f = lfilter(b, a, x)
2295
+ self.assert_array_almost_equal(y_f, y_r)
2296
+ self.assert_equal(b, b0)
2297
+ self.assert_equal(a, a0)
2298
+
2299
+ def test_do_not_modify_a_b_FIR(self, xp):
2300
+ x = self.generate((6,), xp)
2301
+ b = self.convert_dtype([1, 0, 1], xp)
2302
+ b0 = xp_copy(b, xp=xp)
2303
+ a = self.convert_dtype([2], xp)
2304
+ a0 = xp_copy(a, xp=xp)
2305
+ y_r = self.convert_dtype([0, 0.5, 1, 2, 3, 4.], xp)
2306
+ y_f = lfilter(b, a, x)
2307
+ self.assert_array_almost_equal(y_f, y_r)
2308
+ self.assert_equal(b, b0)
2309
+ self.assert_equal(a, a0)
2310
+
2311
+ @skip_xp_backends(np_only=True)
2312
+ @pytest.mark.parametrize("a", [1.0, [1.0], np.array(1.0)])
2313
+ @pytest.mark.parametrize("b", [1.0, [1.0], np.array(1.0)])
2314
+ def test_scalar_input(self, a, b, xp):
2315
+ data = np.random.randn(10)
2316
+ data = xp.asarray(data)
2317
+ self.assert_close(
2318
+ lfilter(xp.asarray([1.0]), xp.asarray([1.0]), data),
2319
+ lfilter(b, a, data)
2320
+ )
2321
+
2322
+ @skip_xp_backends(np_only=True)
2323
+ @pytest.mark.thread_unsafe
2324
+ def test_dtype_deprecation(self, xp):
2325
+ # gh-21211
2326
+ a = np.asarray([1, 2, 3, 6, 5, 3], dtype=object)
2327
+ b = np.asarray([2, 3, 4, 5, 3, 4, 2, 2, 1], dtype=object)
2328
+ with pytest.deprecated_call(match="dtype=object is not supported"):
2329
+ lfilter(a, b, [1, 2, 3, 4])
2330
+
2331
+
2332
+ class TestLinearFilterFloat32(_TestLinearFilter):
2333
+ dtype = 'float32'
2334
+
2335
+
2336
+ class TestLinearFilterFloat64(_TestLinearFilter):
2337
+ dtype = 'float64'
2338
+
2339
+
2340
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
2341
+ @skip_xp_backends(np_only=True)
2342
+ class TestLinearFilterFloatExtended(_TestLinearFilter):
2343
+ dtype = np.dtype('g')
2344
+
2345
+
2346
+ class TestLinearFilterComplex64(_TestLinearFilter):
2347
+ dtype = 'complex64'
2348
+
2349
+
2350
+ class TestLinearFilterComplex128(_TestLinearFilter):
2351
+ dtype = 'complex128'
2352
+
2353
+
2354
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
2355
+ @skip_xp_backends(np_only=True)
2356
+ class TestLinearFilterComplexExtended(_TestLinearFilter):
2357
+ dtype = np.dtype('G')
2358
+
2359
+
2360
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
2361
+ @skip_xp_backends(np_only=True, reason="object arrays")
2362
+ class TestLinearFilterDecimal(_TestLinearFilter):
2363
+ dtype = np.dtype('O')
2364
+
2365
+ def type(self, x):
2366
+ return Decimal(str(x))
2367
+
2368
+
2369
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
2370
+ @skip_xp_backends(np_only=True, reason="object arrays")
2371
+ class TestLinearFilterObject(_TestLinearFilter):
2372
+ dtype = np.dtype('O')
2373
+ type = float
2374
+
2375
+
2376
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
2377
+ @skip_xp_backends(np_only=True)
2378
+ def test_lfilter_bad_object(xp):
2379
+ # lfilter: object arrays with non-numeric objects raise TypeError.
2380
+ # Regression test for ticket #1452.
2381
+ if hasattr(sys, 'abiflags') and 'd' in sys.abiflags:
2382
+ pytest.skip('test is flaky when run with python3-dbg')
2383
+ assert_raises(TypeError, lfilter, [1.0], [1.0], [1.0, None, 2.0])
2384
+ assert_raises(TypeError, lfilter, [1.0], [None], [1.0, 2.0, 3.0])
2385
+ assert_raises(TypeError, lfilter, [None], [1.0], [1.0, 2.0, 3.0])
2386
+
2387
+
2388
+ @skip_xp_backends(np_only=True)
2389
+ def test_lfilter_notimplemented_input(xp):
2390
+ # Should not crash, gh-7991
2391
+ assert_raises(NotImplementedError, lfilter, [2,3], [4,5], [1,2,3,4,5])
2392
+
2393
+
2394
+ class _TestCorrelateReal:
2395
+
2396
+ def _get_assertion(self, dt):
2397
+ """Use np.testing while object arrays are a thing."""
2398
+ if dt == Decimal:
2399
+ return np.testing.assert_array_almost_equal
2400
+ else:
2401
+ return assert_array_almost_equal
2402
+
2403
+ def _setup_rank1(self, dt, xp):
2404
+ a = xp.linspace(0, 3, 4, dtype=dt)
2405
+ b = xp.linspace(1, 2, 2, dtype=dt)
2406
+
2407
+ y_r = xp.asarray([0, 2, 5, 8, 3], dtype=dt)
2408
+ return a, b, y_r
2409
+
2410
+ def equal_tolerance(self, res_dt):
2411
+ # default value of keyword
2412
+ decimal = 6
2413
+ try:
2414
+ dt_info = np.finfo(res_dt)
2415
+ if hasattr(dt_info, 'resolution'):
2416
+ decimal = int(-0.5*np.log10(dt_info.resolution))
2417
+ except Exception:
2418
+ pass
2419
+ return decimal
2420
+
2421
+ def equal_tolerance_fft(self, res_dt):
2422
+ # FFT implementations convert longdouble arguments down to
2423
+ # double so don't expect better precision, see gh-9520
2424
+ if res_dt == np.longdouble:
2425
+ return self.equal_tolerance(np.float64)
2426
+ else:
2427
+ return self.equal_tolerance(res_dt)
2428
+
2429
+ @skip_xp_backends(np_only=True, reason="order='F'")
2430
+ def test_method(self, dt, xp):
2431
+ if dt == Decimal:
2432
+ method = choose_conv_method([Decimal(4)], [Decimal(3)])
2433
+ assert method == 'direct'
2434
+ else:
2435
+ dt = getattr(xp, dt)
2436
+
2437
+ a, b, y_r = self._setup_rank3(dt, xp)
2438
+ y_fft = correlate(a, b, method='fft')
2439
+ y_direct = correlate(a, b, method='direct')
2440
+
2441
+ assert_array_almost_equal(y_r,
2442
+ y_fft,
2443
+ decimal=self.equal_tolerance_fft(y_fft.dtype),)
2444
+ assert_array_almost_equal(y_r,
2445
+ y_direct,
2446
+ decimal=self.equal_tolerance(y_direct.dtype),)
2447
+ assert y_fft.dtype == dt
2448
+ assert y_direct.dtype == dt
2449
+
2450
+ def test_rank1_valid(self, dt, xp):
2451
+ if is_torch(xp) and dt in ["uint16", "uint32", "uint64"]:
2452
+ pytest.skip("torch does not support unsigned ints")
2453
+
2454
+ dt = getattr(xp, dt) if isinstance(dt, str) else dt
2455
+ _assert_almost_equal = self._get_assertion(dt)
2456
+ a, b, y_r = self._setup_rank1(dt, xp)
2457
+ y = correlate(a, b, 'valid')
2458
+ _assert_almost_equal(y, y_r[1:4])
2459
+ assert y.dtype == dt
2460
+
2461
+ # See gh-5897
2462
+ y = correlate(b, a, 'valid')
2463
+ if y_r.dtype != object:
2464
+ _assert_almost_equal(y, xp.flip(y_r[1:4]))
2465
+ else:
2466
+ _assert_almost_equal(y, y_r[1:4][::-1])
2467
+ assert y.dtype == dt
2468
+
2469
+ def test_rank1_same(self, dt, xp):
2470
+ if is_torch(xp) and dt in ["uint16", "uint32", "uint64"]:
2471
+ pytest.skip("torch does not support unsigned ints")
2472
+
2473
+ dt = getattr(xp, dt) if isinstance(dt, str) else dt
2474
+
2475
+ a, b, y_r = self._setup_rank1(dt, xp)
2476
+ y = correlate(a, b, 'same')
2477
+ _assert_almost_equal = self._get_assertion(dt)
2478
+ _assert_almost_equal(y, y_r[:-1])
2479
+ assert y.dtype == dt
2480
+
2481
+ def test_rank1_full(self, dt, xp):
2482
+ if is_torch(xp) and dt in ["uint16", "uint32", "uint64"]:
2483
+ pytest.skip("torch does not support unsigned ints")
2484
+
2485
+ dt = getattr(xp, dt) if isinstance(dt, str) else dt
2486
+
2487
+ a, b, y_r = self._setup_rank1(dt, xp)
2488
+ y = correlate(a, b, 'full')
2489
+ _assert_almost_equal = self._get_assertion(dt)
2490
+ _assert_almost_equal(y, y_r)
2491
+ assert y.dtype == dt
2492
+
2493
+ def _setup_rank3(self, dt, xp):
2494
+ a = np.linspace(0, 39, 40).reshape((2, 4, 5), order='F').astype(
2495
+ dt)
2496
+ b = np.linspace(0, 23, 24).reshape((2, 3, 4), order='F').astype(
2497
+ dt)
2498
+
2499
+ y_r = np.array([[[0., 184., 504., 912., 1360., 888., 472., 160.],
2500
+ [46., 432., 1062., 1840., 2672., 1698., 864., 266.],
2501
+ [134., 736., 1662., 2768., 3920., 2418., 1168., 314.],
2502
+ [260., 952., 1932., 3056., 4208., 2580., 1240., 332.],
2503
+ [202., 664., 1290., 1984., 2688., 1590., 712., 150.],
2504
+ [114., 344., 642., 960., 1280., 726., 296., 38.]],
2505
+
2506
+ [[23., 400., 1035., 1832., 2696., 1737., 904., 293.],
2507
+ [134., 920., 2166., 3680., 5280., 3306., 1640., 474.],
2508
+ [325., 1544., 3369., 5512., 7720., 4683., 2192., 535.],
2509
+ [571., 1964., 3891., 6064., 8272., 4989., 2324., 565.],
2510
+ [434., 1360., 2586., 3920., 5264., 3054., 1312., 230.],
2511
+ [241., 700., 1281., 1888., 2496., 1383., 532., 39.]],
2512
+
2513
+ [[22., 214., 528., 916., 1332., 846., 430., 132.],
2514
+ [86., 484., 1098., 1832., 2600., 1602., 772., 206.],
2515
+ [188., 802., 1698., 2732., 3788., 2256., 1018., 218.],
2516
+ [308., 1006., 1950., 2996., 4052., 2400., 1078., 230.],
2517
+ [230., 692., 1290., 1928., 2568., 1458., 596., 78.],
2518
+ [126., 354., 636., 924., 1212., 654., 234., 0.]]],
2519
+ dtype=np.float64).astype(dt)
2520
+
2521
+ return a, b, y_r
2522
+
2523
+ @skip_xp_backends(np_only=True, reason="order='F'")
2524
+ def test_rank3_valid(self, dt, xp):
2525
+ dt = getattr(xp, dt) if isinstance(dt, str) else dt
2526
+ a, b, y_r = self._setup_rank3(dt, xp)
2527
+ y = correlate(a, b, "valid")
2528
+ _assert_almost_equal = self._get_assertion(dt)
2529
+ _assert_almost_equal(y, y_r[1:2, 2:4, 3:5])
2530
+ assert y.dtype == dt
2531
+
2532
+ # See gh-5897
2533
+ y = correlate(b, a, "valid")
2534
+ _assert_almost_equal = self._get_assertion(dt)
2535
+ _assert_almost_equal(y, y_r[1:2, 2:4, 3:5][::-1, ::-1, ::-1])
2536
+ assert y.dtype == dt
2537
+
2538
+ @skip_xp_backends(np_only=True, reason="order='F'")
2539
+ def test_rank3_same(self, dt, xp):
2540
+ dt = getattr(xp, dt) if isinstance(dt, str) else dt
2541
+ a, b, y_r = self._setup_rank3(dt, xp)
2542
+ y = correlate(a, b, "same")
2543
+ _assert_almost_equal = self._get_assertion(dt)
2544
+ _assert_almost_equal(y, y_r[0:-1, 1:-1, 1:-2])
2545
+ assert y.dtype == dt
2546
+
2547
+ @skip_xp_backends(np_only=True, reason="order='F'")
2548
+ def test_rank3_all(self, dt, xp):
2549
+ dt = getattr(xp, dt) if isinstance(dt, str) else dt
2550
+ a, b, y_r = self._setup_rank3(dt, xp)
2551
+ y = correlate(a, b)
2552
+ _assert_almost_equal = self._get_assertion(dt)
2553
+ _assert_almost_equal(y, y_r)
2554
+ assert y.dtype == dt
2555
+
2556
+
2557
+ @pytest.mark.parametrize('dt', ["uint8", "int8", "uint16", "int16",
2558
+ "uint32", "int32",
2559
+ "uint64", "int64",
2560
+ "float32", "float64",
2561
+ ])
2562
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
2563
+ @skip_xp_backends("jax.numpy", reason="fails all around")
2564
+ class TestCorrelateReal(_TestCorrelateReal):
2565
+ pass
2566
+
2567
+
2568
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
2569
+ @skip_xp_backends(np_only=True, reason="object arrays")
2570
+ @pytest.mark.parametrize('dt', [Decimal])
2571
+ class TestCorrelateRealDecimal(_TestCorrelateReal):
2572
+ pass
2573
+
2574
+
2575
+ class TestCorrelate:
2576
+ # Tests that don't depend on dtype
2577
+
2578
+ @skip_xp_backends(np_only=True)
2579
+ def test_invalid_shapes(self, xp):
2580
+ # By "invalid," we mean that no one
2581
+ # array has dimensions that are all at
2582
+ # least as large as the corresponding
2583
+ # dimensions of the other array. This
2584
+ # setup should throw a ValueError.
2585
+ a = np.arange(1, 7).reshape((2, 3))
2586
+ b = np.arange(-6, 0).reshape((3, 2))
2587
+
2588
+ assert_raises(ValueError, correlate, *(a, b), **{'mode': 'valid'})
2589
+ assert_raises(ValueError, correlate, *(b, a), **{'mode': 'valid'})
2590
+
2591
+ @skip_xp_backends(np_only=True)
2592
+ def test_invalid_params(self, xp):
2593
+ a = [3, 4, 5]
2594
+ b = [1, 2, 3]
2595
+ assert_raises(ValueError, correlate, a, b, mode='spam')
2596
+ assert_raises(ValueError, correlate, a, b, mode='eggs', method='fft')
2597
+ assert_raises(ValueError, correlate, a, b, mode='ham', method='direct')
2598
+ assert_raises(ValueError, correlate, a, b, mode='full', method='bacon')
2599
+ assert_raises(ValueError, correlate, a, b, mode='same', method='bacon')
2600
+
2601
+ @skip_xp_backends(np_only=True)
2602
+ def test_mismatched_dims(self, xp):
2603
+ # Input arrays should have the same number of dimensions
2604
+ assert_raises(ValueError, correlate, [1], 2, method='direct')
2605
+ assert_raises(ValueError, correlate, 1, [2], method='direct')
2606
+ assert_raises(ValueError, correlate, [1], 2, method='fft')
2607
+ assert_raises(ValueError, correlate, 1, [2], method='fft')
2608
+ assert_raises(ValueError, correlate, [1], [[2]])
2609
+ assert_raises(ValueError, correlate, [3], 2)
2610
+
2611
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
2612
+ @skip_xp_backends("jax.numpy", reason="dtype differs")
2613
+ def test_numpy_fastpath(self, xp):
2614
+ a = xp.asarray([1, 2, 3])
2615
+ b = xp.asarray([4, 5])
2616
+ xp_assert_close(correlate(a, b, mode='same'), xp.asarray([5, 14, 23]))
2617
+
2618
+ a = xp.asarray([1, 2, 3])
2619
+ b = xp.asarray([4, 5, 6])
2620
+ xp_assert_close(correlate(a, b, mode='same'), xp.asarray([17, 32, 23]))
2621
+ xp_assert_close(correlate(a, b, mode='full'), xp.asarray([6, 17, 32, 23, 12]))
2622
+ xp_assert_close(correlate(a, b, mode='valid'), xp.asarray([32]))
2623
+
2624
+ @pytest.mark.thread_unsafe
2625
+ @skip_xp_backends(np_only=True)
2626
+ def test_dtype_deprecation(self, xp):
2627
+ # gh-21211
2628
+ a = np.asarray([1, 2, 3, 6, 5, 3], dtype=object)
2629
+ b = np.asarray([2, 3, 4, 5, 3, 4, 2, 2, 1], dtype=object)
2630
+ with pytest.deprecated_call(match="dtype=object is not supported"):
2631
+ correlate(a, b)
2632
+
2633
+
2634
+ @skip_xp_backends(np_only=True, reason="accepts ints, return numpy array")
2635
+ @pytest.mark.parametrize("mode", ["valid", "same", "full"])
2636
+ @pytest.mark.parametrize("behind", [True, False])
2637
+ @pytest.mark.parametrize("input_size", [100, 101, 1000, 1001,
2638
+ pytest.param(10000, marks=[pytest.mark.slow]),
2639
+ pytest.param(10001, marks=[pytest.mark.slow])]
2640
+ )
2641
+ def test_correlation_lags(mode, behind, input_size, xp):
2642
+ # generate random data
2643
+ rng = np.random.RandomState(0)
2644
+ in1 = rng.standard_normal(input_size)
2645
+ offset = int(input_size/10)
2646
+ # generate offset version of array to correlate with
2647
+ if behind:
2648
+ # y is behind x
2649
+ in2 = np.concatenate([rng.standard_normal(offset), in1])
2650
+ expected = -offset
2651
+ else:
2652
+ # y is ahead of x
2653
+ in2 = in1[offset:]
2654
+ expected = offset
2655
+ # cross correlate, returning lag information
2656
+ correlation = correlate(in1, in2, mode=mode)
2657
+ lags = correlation_lags(in1.size, in2.size, mode=mode)
2658
+ # identify the peak
2659
+ lag_index = np.argmax(correlation)
2660
+ # Check as expected
2661
+ xp_assert_equal(lags[lag_index], expected)
2662
+ # Correlation and lags shape should match
2663
+ assert lags.shape == correlation.shape
2664
+
2665
+
2666
+ @skip_xp_backends(np_only=True)
2667
+ def test_correlation_lags_invalid_mode(xp):
2668
+ with pytest.raises(ValueError, match="Mode asdfgh is invalid"):
2669
+ correlation_lags(100, 100, mode="asdfgh")
2670
+
2671
+
2672
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
2673
+ @pytest.mark.parametrize('dt_name', ['complex64', 'complex128'])
2674
+ class TestCorrelateComplex:
2675
+ # The decimal precision to be used for comparing results.
2676
+ # This value will be passed as the 'decimal' keyword argument of
2677
+ # assert_array_almost_equal().
2678
+ # Since correlate may chose to use FFT method which converts
2679
+ # longdoubles to doubles internally don't expect better precision
2680
+ # for longdouble than for double (see gh-9520).
2681
+
2682
+ def decimal(self, dt, xp):
2683
+ if is_numpy(xp) and dt == np.clongdouble:
2684
+ dt = np.cdouble
2685
+
2686
+ # emulate np.finfo(dt).precision for complex64 and complex128
2687
+ prec = {64: 15, 32: 6}[xp.finfo(dt).bits]
2688
+ return int(2 * prec / 3)
2689
+
2690
+ def _setup_rank1(self, dt, mode, xp):
2691
+ np.random.seed(9)
2692
+ a = np.random.randn(10).astype(dt)
2693
+ a += 1j * np.random.randn(10).astype(dt)
2694
+ b = np.random.randn(8).astype(dt)
2695
+ b += 1j * np.random.randn(8).astype(dt)
2696
+
2697
+ y_r = (correlate(a.real, b.real, mode=mode) +
2698
+ correlate(a.imag, b.imag, mode=mode)).astype(dt)
2699
+ y_r += 1j * (-correlate(a.real, b.imag, mode=mode) +
2700
+ correlate(a.imag, b.real, mode=mode))
2701
+
2702
+ a, b, y_r = xp.asarray(a), xp.asarray(b), xp.asarray(y_r)
2703
+ return a, b, y_r
2704
+
2705
+ def test_rank1_valid(self, dt_name, xp):
2706
+ a, b, y_r = self._setup_rank1(dt_name, 'valid', xp)
2707
+ dt = getattr(xp, dt_name)
2708
+ y = correlate(a, b, 'valid')
2709
+ assert_array_almost_equal(y, y_r, decimal=self.decimal(dt, xp))
2710
+ assert y.dtype == dt
2711
+
2712
+ # See gh-5897
2713
+ y = correlate(b, a, 'valid')
2714
+ assert_array_almost_equal(y, xp.conj(xp.flip(y_r)),
2715
+ decimal=self.decimal(dt, xp))
2716
+ assert y.dtype == dt
2717
+
2718
+ def test_rank1_same(self, dt_name, xp):
2719
+ a, b, y_r = self._setup_rank1(dt_name, 'same', xp)
2720
+ dt = getattr(xp, dt_name)
2721
+
2722
+ y = correlate(a, b, 'same')
2723
+ assert_array_almost_equal(y, y_r, decimal=self.decimal(dt, xp))
2724
+ assert y.dtype == dt
2725
+
2726
+ def test_rank1_full(self, dt_name, xp):
2727
+ a, b, y_r = self._setup_rank1(dt_name, 'full', xp)
2728
+ dt = getattr(xp, dt_name)
2729
+ y = correlate(a, b, 'full')
2730
+ assert_array_almost_equal(y, y_r, decimal=self.decimal(dt, xp))
2731
+ assert y.dtype == dt
2732
+
2733
+ def test_swap_full(self, dt_name, xp):
2734
+ dt = getattr(xp, dt_name)
2735
+ d = xp.asarray([0.+0.j, 1.+1.j, 2.+2.j], dtype=dt)
2736
+ k = xp.asarray([1.+3.j, 2.+4.j, 3.+5.j, 4.+6.j], dtype=dt)
2737
+ y = correlate(d, k)
2738
+ xp_assert_close(
2739
+ y, xp.asarray([0.+0.j, 10.-2.j, 28.-6.j, 22.-6.j, 16.-6.j, 8.-4.j]),
2740
+ atol=1e-6, check_dtype=False
2741
+ )
2742
+
2743
+ def test_swap_same(self, dt_name, xp):
2744
+ d = xp.asarray([0.+0.j, 1.+1.j, 2.+2.j])
2745
+ k = xp.asarray([1.+3.j, 2.+4.j, 3.+5.j, 4.+6.j])
2746
+ y = correlate(d, k, mode="same")
2747
+ xp_assert_close(y, xp.asarray([10.-2.j, 28.-6.j, 22.-6.j]))
2748
+
2749
+ @skip_xp_backends("cupy", reason="notimplementederror")
2750
+ def test_rank3(self, dt_name, xp):
2751
+ a = np.random.randn(10, 8, 6).astype(dt_name)
2752
+ a += 1j * np.random.randn(10, 8, 6).astype(dt_name)
2753
+ b = np.random.randn(8, 6, 4).astype(dt_name)
2754
+ b += 1j * np.random.randn(8, 6, 4).astype(dt_name)
2755
+
2756
+ y_r = (correlate(a.real, b.real)
2757
+ + correlate(a.imag, b.imag)).astype(dt_name)
2758
+ y_r += 1j * (-correlate(a.real, b.imag) + correlate(a.imag, b.real))
2759
+
2760
+ a, b, y_r = xp.asarray(a), xp.asarray(b), xp.asarray(y_r)
2761
+ dt = getattr(xp, dt_name)
2762
+
2763
+ y = correlate(a, b, 'full')
2764
+ assert_array_almost_equal(y, y_r, decimal=self.decimal(dt, xp) - 1)
2765
+ assert y.dtype == dt
2766
+
2767
+ @skip_xp_backends(np_only=True) # XXX: check 0D/scalars on backends.
2768
+ def test_rank0(self, dt_name, xp):
2769
+ a = np.array(np.random.randn()).astype(dt_name)
2770
+ a += 1j * np.array(np.random.randn()).astype(dt_name)
2771
+ b = np.array(np.random.randn()).astype(dt_name)
2772
+ b += 1j * np.array(np.random.randn()).astype(dt_name)
2773
+ dt = getattr(xp, dt_name)
2774
+
2775
+ y_r = (correlate(a.real, b.real)
2776
+ + correlate(a.imag, b.imag)).astype(dt)
2777
+ y_r += 1j * np.array(-correlate(a.real, b.imag) +
2778
+ correlate(a.imag, b.real))
2779
+
2780
+ a, b = xp.asarray(a), xp.asarray(b)
2781
+
2782
+ y = correlate(a, b, 'full')
2783
+ assert_array_almost_equal(y, y_r, decimal=self.decimal(dt, xp) - 1)
2784
+ assert y.dtype == dt
2785
+
2786
+ xp_assert_equal(correlate([1], [2j]), np.asarray(correlate(1, 2j)),
2787
+ check_shape=False)
2788
+ xp_assert_equal(correlate([2j], [3j]), np.asarray(correlate(2j, 3j)),
2789
+ check_shape=False)
2790
+ xp_assert_equal(correlate([3j], [4]), np.asarray(correlate(3j, 4)),
2791
+ check_shape=False)
2792
+
2793
+
2794
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
2795
+ class TestCorrelate2d:
2796
+
2797
+ def test_consistency_correlate_funcs(self, xp):
2798
+ # Compare np.correlate, signal.correlate, signal.correlate2d
2799
+ a = np.arange(5)
2800
+ b = np.array([3.2, 1.4, 3])
2801
+ for mode in ['full', 'valid', 'same']:
2802
+ a_xp, b_xp = xp.asarray(a), xp.asarray(b)
2803
+ np_corr_result = np.correlate(a, b, mode=mode)
2804
+ assert_almost_equal(signal.correlate(a_xp, b_xp, mode=mode),
2805
+ xp.asarray(np_corr_result))
2806
+
2807
+ # See gh-5897
2808
+ if mode == 'valid':
2809
+ np_corr_result = np.correlate(b, a, mode=mode)
2810
+ assert_almost_equal(signal.correlate(b_xp, a_xp, mode=mode),
2811
+ xp.asarray(np_corr_result))
2812
+
2813
+ @skip_xp_backends(np_only=True) # XXX
2814
+ def test_consistency_correlate_funcs_2(self, xp):
2815
+ # Compare np.correlate, signal.correlate, signal.correlate2d
2816
+ a = np.arange(5)
2817
+ b = np.array([3.2, 1.4, 3])
2818
+ for mode in ['full', 'valid', 'same']:
2819
+ assert_almost_equal(np.squeeze(signal.correlate2d([a], [b],
2820
+ mode=mode)),
2821
+ signal.correlate(a, b, mode=mode))
2822
+
2823
+ # See gh-5897
2824
+ if mode == 'valid':
2825
+ assert_almost_equal(np.squeeze(signal.correlate2d([b], [a],
2826
+ mode=mode)),
2827
+ signal.correlate(b, a, mode=mode))
2828
+
2829
+
2830
+ @skip_xp_backends(np_only=True)
2831
+ def test_invalid_shapes(self, xp):
2832
+ # By "invalid," we mean that no one
2833
+ # array has dimensions that are all at
2834
+ # least as large as the corresponding
2835
+ # dimensions of the other array. This
2836
+ # setup should throw a ValueError.
2837
+ a = np.arange(1, 7).reshape((2, 3))
2838
+ b = np.arange(-6, 0).reshape((3, 2))
2839
+
2840
+ assert_raises(ValueError, signal.correlate2d, *(a, b), **{'mode': 'valid'})
2841
+ assert_raises(ValueError, signal.correlate2d, *(b, a), **{'mode': 'valid'})
2842
+
2843
+ def test_complex_input(self, xp):
2844
+ xp_assert_equal(signal.correlate2d(xp.asarray([[1]]), xp.asarray([[2j]])),
2845
+ xp.asarray([-2j]), check_shape=False, check_dtype=False)
2846
+ xp_assert_equal(signal.correlate2d(xp.asarray([[2j]]), xp.asarray([[3j]])),
2847
+ xp.asarray([6+0j]), check_shape=False, check_dtype=False)
2848
+ xp_assert_equal(signal.correlate2d(xp.asarray([[3j]]), xp.asarray([[4]])),
2849
+ xp.asarray([12j]), check_shape=False, check_dtype=False)
2850
+
2851
+
2852
+ @skip_xp_backends(cpu_only=True)
2853
+ @skip_xp_backends('cupy', reason='lfilter_zi is incompatible')
2854
+ class TestLFilterZI:
2855
+
2856
+ @skip_xp_backends(np_only=True, reason='list inputs are numpy specific')
2857
+ def test_array_like(self, xp):
2858
+ zi_expected = xp.asarray([5.0, -1.0])
2859
+ zi = lfilter_zi([1.0, 0.0, 2.0], [1.0, -1.0, 0.5])
2860
+ assert_array_almost_equal(zi, zi_expected)
2861
+
2862
+ def test_basic(self, xp):
2863
+ a = xp.asarray([1.0, -1.0, 0.5])
2864
+ b = xp.asarray([1.0, 0.0, 2.0])
2865
+ zi_expected = xp.asarray([5.0, -1.0])
2866
+ zi = lfilter_zi(b, a)
2867
+ assert_array_almost_equal(zi, zi_expected)
2868
+
2869
+ def test_scale_invariance(self, xp):
2870
+ # Regression test. There was a bug in which b was not correctly
2871
+ # rescaled when a[0] was nonzero.
2872
+ b = xp.asarray([2.0, 8, 5])
2873
+ a = xp.asarray([1.0, 1, 8])
2874
+ zi1 = lfilter_zi(b, a)
2875
+ zi2 = lfilter_zi(2*b, 2*a)
2876
+ xp_assert_close(zi2, zi1, rtol=1e-12)
2877
+
2878
+ @pytest.mark.parametrize('dtype', ['float32', 'float64'])
2879
+ def test_types(self, dtype, xp):
2880
+ dtype = getattr(xp, dtype)
2881
+ b = xp.zeros((8), dtype=dtype)
2882
+ a = xp.asarray([1], dtype=dtype)
2883
+ assert signal.lfilter_zi(b, a).dtype == dtype
2884
+
2885
+
2886
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
2887
+ class TestFiltFilt:
2888
+ filtfilt_kind = 'tf'
2889
+
2890
+ def filtfilt(self, zpk, x, axis=-1, padtype='odd', padlen=None,
2891
+ method='pad', irlen=None, xp=None):
2892
+ if self.filtfilt_kind == 'tf':
2893
+ b, a = zpk2tf(*zpk)
2894
+ b, a = xp.asarray(b), xp.asarray(a)
2895
+ return filtfilt(b, a, x, axis, padtype, padlen, method, irlen)
2896
+ elif self.filtfilt_kind == 'sos':
2897
+ sos = zpk2sos(*zpk)
2898
+ sos = xp.asarray(sos)
2899
+ return sosfiltfilt(sos, x, axis, padtype, padlen)
2900
+
2901
+ @skip_xp_backends('torch', reason='negative strides')
2902
+ def test_basic(self, xp):
2903
+ if is_jax(xp) and self.filtfilt_kind == 'sos':
2904
+ pytest.skip(reason='sosfilt works in-place')
2905
+
2906
+ zpk = tf2zpk([1, 2, 3], [1, 2, 3])
2907
+ out = self.filtfilt(zpk, xp.arange(12), xp=xp)
2908
+ atol= 3e-9 if is_cupy(xp) else 5.28e-11
2909
+ xp_assert_close(out, xp.arange(12, dtype=xp.float64), atol=atol)
2910
+
2911
+ @skip_xp_backends('torch', reason='negative strides')
2912
+ def test_sine(self, xp):
2913
+ if is_jax(xp) and self.filtfilt_kind == 'sos':
2914
+ pytest.skip(reason='sosfilt works in-place')
2915
+
2916
+ rate = 2000
2917
+ t = xp.linspace(0, 1.0, rate + 1)
2918
+ # A signal with low frequency and a high frequency.
2919
+ xlow = xp.sin(5 * 2 * np.pi * t)
2920
+ xhigh = xp.sin(250 * 2 * np.pi * t)
2921
+ x = xlow + xhigh
2922
+
2923
+ zpk = butter(8, 0.125, output='zpk')
2924
+ # r is the magnitude of the largest pole.
2925
+ r = np.abs(zpk[1]).max()
2926
+ eps = 1e-5
2927
+ # n estimates the number of steps for the
2928
+ # transient to decay by a factor of eps.
2929
+ n = int(np.ceil(np.log(eps) / np.log(r)))
2930
+
2931
+ # High order lowpass filter...
2932
+ y = self.filtfilt(zpk, x, padlen=n, xp=xp)
2933
+ # Result should be just xlow.
2934
+ err = np.abs(y - xlow).max()
2935
+ assert err < 1e-4
2936
+
2937
+ # A 2D case.
2938
+ x2d = xp.asarray(np.vstack([xlow, xlow + xhigh]))
2939
+ y2d = self.filtfilt(zpk, x2d, padlen=n, axis=1, xp=xp)
2940
+ assert y2d.shape == x2d.shape
2941
+ err = np.abs(y2d - xlow).max()
2942
+ assert err < 1e-4
2943
+
2944
+ # Use the previous result to check the use of the axis keyword.
2945
+ # (Regression test for ticket #1620)
2946
+ y2dt = self.filtfilt(zpk, x2d.T, padlen=n, axis=0, xp=xp)
2947
+ xp_assert_equal(y2d, y2dt.T)
2948
+
2949
+ @skip_xp_backends('torch', reason='negative strides')
2950
+ def test_axis(self, xp):
2951
+ if is_jax(xp) and self.filtfilt_kind == 'sos':
2952
+ pytest.skip(reason='sosfilt works in-place')
2953
+
2954
+ # Test the 'axis' keyword on a 3D array.
2955
+ x = np.arange(10.0 * 11.0 * 12.0).reshape(10, 11, 12)
2956
+ x = xp.asarray(x)
2957
+ zpk = butter(3, 0.125, output='zpk')
2958
+ y0 = self.filtfilt(zpk, x, padlen=0, axis=0, xp=xp)
2959
+ y1 = self.filtfilt(
2960
+ zpk, xp.asarray(np.swapaxes(x, 0, 1)), padlen=0, axis=1, xp=xp
2961
+ )
2962
+ xp_assert_equal(y0, xp.asarray(np.swapaxes(y1, 0, 1)))
2963
+ y2 = self.filtfilt(
2964
+ zpk, xp.asarray(np.swapaxes(x, 0, 2)), padlen=0, axis=2, xp=xp
2965
+ )
2966
+ xp_assert_equal(y0, xp.asarray(np.swapaxes(y2, 0, 2)))
2967
+
2968
+ @skip_xp_backends(np_only=True,
2969
+ reason='python scalars in array_namespace are np-only')
2970
+ def test_acoeff(self, xp):
2971
+ if self.filtfilt_kind != 'tf':
2972
+ return # only necessary for TF
2973
+ # test for 'a' coefficient as single number
2974
+ out = signal.filtfilt(
2975
+ xp.asarray([.5, .5]), 1, xp.arange(10, dtype=xp.float64)
2976
+ )
2977
+ xp_assert_close(out, xp.arange(10, dtype=xp.float64), rtol=1e-14, atol=1e-14)
2978
+
2979
+ @skip_xp_backends(np_only=True, reason='_filtfilt_gust is np-only')
2980
+ def test_gust_simple(self, xp):
2981
+ if self.filtfilt_kind != 'tf':
2982
+ pytest.skip('gust only implemented for TF systems')
2983
+ # The input array has length 2. The exact solution for this case
2984
+ # was computed "by hand".
2985
+ x = xp.asarray([1.0, 2.0])
2986
+ b = xp.asarray([0.5])
2987
+ a = xp.asarray([1.0, -0.5])
2988
+ y, z1, z2 = _filtfilt_gust(b, a, x)
2989
+ xp_assert_close(z1[0], 0.3*x[0] + 0.2*x[1])
2990
+ xp_assert_close(z2[0], 0.2*x[0] + 0.3*x[1])
2991
+ xp_assert_close(y,
2992
+ xp.asarray([z1[0] + 0.25*z2[0] + 0.25*x[0] + 0.125*x[1],
2993
+ 0.25*z1[0] + z2[0] + 0.125*x[0] + 0.25*x[1]])
2994
+ )
2995
+
2996
+ @skip_xp_backends(np_only=True,
2997
+ reason='python scalars in array_namespace are np-only')
2998
+ def test_gust_scalars(self, xp):
2999
+ if self.filtfilt_kind != 'tf':
3000
+ pytest.skip('gust only implemented for TF systems')
3001
+ # The filter coefficients are both scalars, so the filter simply
3002
+ # multiplies its input by b/a. When it is used in filtfilt, the
3003
+ # factor is (b/a)**2.
3004
+ x = xp.arange(12)
3005
+ b = 3.0
3006
+ a = 2.0
3007
+ y = filtfilt(b, a, x, method="gust")
3008
+ expected = (b/a)**2 * x
3009
+ xp_assert_close(y, expected)
3010
+
3011
+
3012
+ @skip_xp_backends(
3013
+ "dask.array", reason=(
3014
+ "sosfiltfilt directly sets shape attributes on arrays "
3015
+ "which dask doesn't like"
3016
+ )
3017
+ )
3018
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
3019
+ class TestSOSFiltFilt(TestFiltFilt):
3020
+ filtfilt_kind = 'sos'
3021
+
3022
+ @skip_xp_backends('jax.numpy', reason='sosfilt works in-place')
3023
+ @skip_xp_backends('torch', reason='negative strides')
3024
+ def test_equivalence(self, xp):
3025
+ """Test equivalence between sosfiltfilt and filtfilt"""
3026
+ x = np.random.RandomState(0).randn(1000)
3027
+ x = xp.asarray(x)
3028
+ for order in range(1, 6):
3029
+ zpk = signal.butter(order, 0.35, output='zpk')
3030
+ b, a = zpk2tf(*zpk)
3031
+ sos = zpk2sos(*zpk)
3032
+
3033
+ b, a, sos = map(xp.asarray, (b, a, sos))
3034
+ y = filtfilt(b, a, x)
3035
+ y_sos = sosfiltfilt(sos, x)
3036
+ xp_assert_close(y, y_sos, atol=1e-12, err_msg=f'order={order}')
3037
+
3038
+
3039
+ def filtfilt_gust_opt(b, a, x):
3040
+ """
3041
+ An alternative implementation of filtfilt with Gustafsson edges.
3042
+
3043
+ This function computes the same result as
3044
+ `scipy.signal._signaltools._filtfilt_gust`, but only 1-d arrays
3045
+ are accepted. The problem is solved using `fmin` from `scipy.optimize`.
3046
+ `_filtfilt_gust` is significantly faster than this implementation.
3047
+ """
3048
+ def filtfilt_gust_opt_func(ics, b, a, x):
3049
+ """Objective function used in filtfilt_gust_opt."""
3050
+ m = max(len(a), len(b)) - 1
3051
+ z0f = ics[:m]
3052
+ z0b = ics[m:]
3053
+ y_f = lfilter(b, a, x, zi=z0f)[0]
3054
+ y_fb = lfilter(b, a, y_f[::-1], zi=z0b)[0][::-1]
3055
+
3056
+ y_b = lfilter(b, a, x[::-1], zi=z0b)[0][::-1]
3057
+ y_bf = lfilter(b, a, y_b, zi=z0f)[0]
3058
+ value = np.sum((y_fb - y_bf)**2)
3059
+ return value
3060
+
3061
+ m = max(len(a), len(b)) - 1
3062
+ zi = lfilter_zi(b, a)
3063
+ ics = np.concatenate((x[:m].mean()*zi, x[-m:].mean()*zi))
3064
+ result = fmin(filtfilt_gust_opt_func, ics, args=(b, a, x),
3065
+ xtol=1e-10, ftol=1e-12,
3066
+ maxfun=10000, maxiter=10000,
3067
+ full_output=True, disp=False)
3068
+ opt, fopt, niter, funcalls, warnflag = result
3069
+ if warnflag > 0:
3070
+ raise RuntimeError(
3071
+ f"minimization failed in filtfilt_gust_opt: warnflag={warnflag}"
3072
+ )
3073
+ z0f = opt[:m]
3074
+ z0b = opt[m:]
3075
+
3076
+ # Apply the forward-backward filter using the computed initial
3077
+ # conditions.
3078
+ y_b = lfilter(b, a, x[::-1], zi=z0b)[0][::-1]
3079
+ y = lfilter(b, a, y_b, zi=z0f)[0]
3080
+
3081
+ return y, z0f, z0b
3082
+
3083
+
3084
+ def check_filtfilt_gust(b, a, shape, axis, irlen=None):
3085
+ # Generate x, the data to be filtered.
3086
+ np.random.seed(123)
3087
+ x = np.random.randn(*shape)
3088
+
3089
+ # Apply filtfilt to x. This is the main calculation to be checked.
3090
+ y = filtfilt(b, a, x, axis=axis, method="gust", irlen=irlen)
3091
+
3092
+ # Also call the private function so we can test the ICs.
3093
+ yg, zg1, zg2 = _filtfilt_gust(b, a, x, axis=axis, irlen=irlen)
3094
+
3095
+ # filtfilt_gust_opt is an independent implementation that gives the
3096
+ # expected result, but it only handles 1-D arrays, so use some looping
3097
+ # and reshaping shenanigans to create the expected output arrays.
3098
+ xx = np.swapaxes(x, axis, -1)
3099
+ out_shape = xx.shape[:-1]
3100
+ yo = np.empty_like(xx)
3101
+ m = max(len(a), len(b)) - 1
3102
+ zo1 = np.empty(out_shape + (m,))
3103
+ zo2 = np.empty(out_shape + (m,))
3104
+ for indx in product(*[range(d) for d in out_shape]):
3105
+ yo[indx], zo1[indx], zo2[indx] = filtfilt_gust_opt(b, a, xx[indx])
3106
+ yo = np.swapaxes(yo, -1, axis)
3107
+ zo1 = np.swapaxes(zo1, -1, axis)
3108
+ zo2 = np.swapaxes(zo2, -1, axis)
3109
+
3110
+ xp_assert_close(y, yo, rtol=1e-8, atol=1e-9)
3111
+ xp_assert_close(yg, yo, rtol=1e-8, atol=1e-9)
3112
+ xp_assert_close(zg1, zo1, rtol=1e-8, atol=1e-9)
3113
+ xp_assert_close(zg2, zo2, rtol=1e-8, atol=1e-9)
3114
+
3115
+
3116
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
3117
+ @skip_xp_backends(np_only=True)
3118
+ @pytest.mark.fail_slow(10)
3119
+ def test_choose_conv_method(xp):
3120
+ for mode in ['valid', 'same', 'full']:
3121
+ for ndim in [1, 2]:
3122
+ n, k, true_method = 8, 6, 'direct'
3123
+ x = np.random.randn(*((n,) * ndim))
3124
+ h = np.random.randn(*((k,) * ndim))
3125
+
3126
+ method = choose_conv_method(x, h, mode=mode)
3127
+ assert method == true_method
3128
+
3129
+ method_try, times = choose_conv_method(x, h, mode=mode, measure=True)
3130
+ assert method_try in {'fft', 'direct'}
3131
+ assert isinstance(times, dict)
3132
+ assert 'fft' in times.keys() and 'direct' in times.keys()
3133
+
3134
+ n = 10
3135
+ for not_fft_conv_supp in ["complex256", "complex192"]:
3136
+ if hasattr(np, not_fft_conv_supp):
3137
+ x = np.ones(n, dtype=not_fft_conv_supp)
3138
+ h = x.copy()
3139
+ assert choose_conv_method(x, h, mode=mode) == 'direct'
3140
+
3141
+ x = np.array([2**51], dtype=np.int64)
3142
+ h = x.copy()
3143
+ assert choose_conv_method(x, h, mode=mode) == 'direct'
3144
+
3145
+
3146
+ @pytest.mark.thread_unsafe
3147
+ @skip_xp_backends(np_only=True)
3148
+ def test_choose_conv_dtype_deprecation(xp):
3149
+ # gh-21211
3150
+ a = np.asarray([1, 2, 3, 6, 5, 3], dtype=object)
3151
+ b = np.asarray([2, 3, 4, 5, 3, 4, 2, 2, 1], dtype=object)
3152
+ with pytest.deprecated_call(match="dtype=object is not supported"):
3153
+ choose_conv_method(a, b)
3154
+
3155
+
3156
+ @skip_xp_backends(np_only=True)
3157
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
3158
+ def test_choose_conv_method_2(xp):
3159
+ for mode in ['valid', 'same', 'full']:
3160
+ x = [Decimal(3), Decimal(2)]
3161
+ h = [Decimal(1), Decimal(4)]
3162
+ assert choose_conv_method(x, h, mode=mode) == 'direct'
3163
+
3164
+ n = 10
3165
+ for not_fft_conv_supp in ["complex256", "complex192"]:
3166
+ if hasattr(np, not_fft_conv_supp):
3167
+ x = np.ones(n, dtype=not_fft_conv_supp)
3168
+ h = x.copy()
3169
+ assert choose_conv_method(x, h, mode=mode) == 'direct'
3170
+
3171
+
3172
+ @skip_xp_backends(np_only=True)
3173
+ @pytest.mark.fail_slow(10)
3174
+ def test_filtfilt_gust(xp):
3175
+ # Design a filter.
3176
+ z, p, k = signal.ellip(3, 0.01, 120, 0.0875, output='zpk')
3177
+
3178
+ # Find the approximate impulse response length of the filter.
3179
+ eps = 1e-10
3180
+ r = np.max(np.abs(p))
3181
+ approx_impulse_len = int(np.ceil(np.log(eps) / np.log(r)))
3182
+
3183
+ np.random.seed(123)
3184
+
3185
+ b, a = zpk2tf(z, p, k)
3186
+ for irlen in [None, approx_impulse_len]:
3187
+ signal_len = 5 * approx_impulse_len
3188
+
3189
+ # 1-d test case
3190
+ check_filtfilt_gust(b, a, (signal_len,), 0, irlen)
3191
+
3192
+ # 3-d test case; test each axis.
3193
+ for axis in range(3):
3194
+ shape = [2, 2, 2]
3195
+ shape[axis] = signal_len
3196
+ check_filtfilt_gust(b, a, shape, axis, irlen)
3197
+
3198
+ # Test case with length less than 2*approx_impulse_len.
3199
+ # In this case, `filtfilt_gust` should behave the same as if
3200
+ # `irlen=None` was given.
3201
+ length = 2*approx_impulse_len - 50
3202
+ check_filtfilt_gust(b, a, (length,), 0, approx_impulse_len)
3203
+
3204
+
3205
+ @skip_xp_backends(np_only=True)
3206
+ class TestDecimate:
3207
+ def test_bad_args(self, xp):
3208
+ x = np.arange(12)
3209
+ assert_raises(TypeError, signal.decimate, x, q=0.5, n=1)
3210
+ assert_raises(TypeError, signal.decimate, x, q=2, n=0.5)
3211
+
3212
+ def test_basic_IIR(self, xp):
3213
+ x = np.arange(12)
3214
+ y = signal.decimate(x, 2, n=1, ftype='iir', zero_phase=False).round()
3215
+ xp_assert_equal(y, x[::2].astype(float))
3216
+
3217
+ def test_basic_FIR(self, xp):
3218
+ x = np.arange(12)
3219
+ y = signal.decimate(x, 2, n=1, ftype='fir', zero_phase=False).round()
3220
+ xp_assert_equal(y, x[::2].astype(float))
3221
+
3222
+ def test_shape(self, xp):
3223
+ # Regression test for ticket #1480.
3224
+ z = np.zeros((30, 30))
3225
+ d0 = signal.decimate(z, 2, axis=0, zero_phase=False)
3226
+ assert d0.shape == (15, 30)
3227
+ d1 = signal.decimate(z, 2, axis=1, zero_phase=False)
3228
+ assert d1.shape == (30, 15)
3229
+
3230
+ def test_phaseshift_FIR(self, xp):
3231
+ with suppress_warnings() as sup:
3232
+ sup.filter(BadCoefficients, "Badly conditioned filter")
3233
+ self._test_phaseshift(method='fir', zero_phase=False)
3234
+
3235
+ def test_zero_phase_FIR(self, xp):
3236
+ with suppress_warnings() as sup:
3237
+ sup.filter(BadCoefficients, "Badly conditioned filter")
3238
+ self._test_phaseshift(method='fir', zero_phase=True)
3239
+
3240
+ def test_phaseshift_IIR(self, xp):
3241
+ self._test_phaseshift(method='iir', zero_phase=False)
3242
+
3243
+ def test_zero_phase_IIR(self, xp):
3244
+ self._test_phaseshift(method='iir', zero_phase=True)
3245
+
3246
+ def _test_phaseshift(self, method, zero_phase):
3247
+ rate = 120
3248
+ rates_to = [15, 20, 30, 40] # q = 8, 6, 4, 3
3249
+
3250
+ t_tot = 100 # Need to let antialiasing filters settle
3251
+ t = np.arange(rate*t_tot+1) / float(rate)
3252
+
3253
+ # Sinusoids at 0.8*nyquist, windowed to avoid edge artifacts
3254
+ freqs = np.array(rates_to) * 0.8 / 2
3255
+ d = (np.exp(1j * 2 * np.pi * freqs[:, np.newaxis] * t)
3256
+ * signal.windows.tukey(t.size, 0.1))
3257
+
3258
+ for rate_to in rates_to:
3259
+ q = rate // rate_to
3260
+ t_to = np.arange(rate_to*t_tot+1) / float(rate_to)
3261
+ d_tos = (np.exp(1j * 2 * np.pi * freqs[:, np.newaxis] * t_to)
3262
+ * signal.windows.tukey(t_to.size, 0.1))
3263
+
3264
+ # Set up downsampling filters, match v0.17 defaults
3265
+ if method == 'fir':
3266
+ n = 30
3267
+ system = signal.dlti(signal.firwin(n + 1, 1. / q,
3268
+ window='hamming'), 1.)
3269
+ elif method == 'iir':
3270
+ n = 8
3271
+ wc = 0.8*np.pi/q
3272
+ system = signal.dlti(*signal.cheby1(n, 0.05, wc/np.pi))
3273
+
3274
+ # Calculate expected phase response, as unit complex vector
3275
+ if zero_phase is False:
3276
+ _, h_resps = signal.freqz(system.num, system.den,
3277
+ freqs/rate*2*np.pi)
3278
+ h_resps /= np.abs(h_resps)
3279
+ else:
3280
+ h_resps = np.ones_like(freqs)
3281
+
3282
+ y_resamps = signal.decimate(d.real, q, n, ftype=system,
3283
+ zero_phase=zero_phase)
3284
+
3285
+ # Get phase from complex inner product, like CSD
3286
+ h_resamps = np.sum(d_tos.conj() * y_resamps, axis=-1)
3287
+ h_resamps /= np.abs(h_resamps)
3288
+ subnyq = freqs < 0.5*rate_to
3289
+
3290
+ # Complex vectors should be aligned, only compare below nyquist
3291
+ result = np.angle(h_resps.conj()*h_resamps)[subnyq]
3292
+ xp_assert_close(result, np.zeros_like(result),
3293
+ atol=1e-3, rtol=1e-3)
3294
+
3295
+ def test_auto_n(self, xp):
3296
+ # Test that our value of n is a reasonable choice (depends on
3297
+ # the downsampling factor)
3298
+ sfreq = 100.
3299
+ n = 1000
3300
+ t = np.arange(n) / sfreq
3301
+ # will alias for decimations (>= 15)
3302
+ x = np.sqrt(2. / n) * np.sin(2 * np.pi * (sfreq / 30.) * t)
3303
+ xp_assert_close(np.linalg.norm(x), 1., rtol=1e-3)
3304
+ x_out = signal.decimate(x, 30, ftype='fir')
3305
+ assert np.linalg.norm(x_out) < 0.01
3306
+
3307
+ def test_long_float32(self, xp):
3308
+ # regression: gh-15072. With 32-bit float and either lfilter
3309
+ # or filtfilt, this is numerically unstable
3310
+ x = signal.decimate(np.ones(10_000, dtype=np.float32), 10)
3311
+ assert not any(np.isnan(x))
3312
+
3313
+ def test_float16_upcast(self, xp):
3314
+ # float16 must be upcast to float64
3315
+ x = signal.decimate(np.ones(100, dtype=np.float16), 10)
3316
+ assert x.dtype.type == np.float64
3317
+
3318
+ def test_complex_iir_dlti(self, xp):
3319
+ # regression: gh-17845
3320
+ # centre frequency for filter [Hz]
3321
+ fcentre = 50
3322
+ # filter passband width [Hz]
3323
+ fwidth = 5
3324
+ # sample rate [Hz]
3325
+ fs = 1e3
3326
+
3327
+ z, p, k = signal.butter(2, 2*np.pi*fwidth/2, output='zpk', fs=fs)
3328
+ z = z.astype(complex) * np.exp(2j * np.pi * fcentre/fs)
3329
+ p = p.astype(complex) * np.exp(2j * np.pi * fcentre/fs)
3330
+ system = signal.dlti(z, p, k)
3331
+
3332
+ t = np.arange(200) / fs
3333
+
3334
+ # input
3335
+ u = (np.exp(2j * np.pi * fcentre * t)
3336
+ + 0.5 * np.exp(-2j * np.pi * fcentre * t))
3337
+
3338
+ ynzp = signal.decimate(u, 2, ftype=system, zero_phase=False)
3339
+ ynzpref = signal.lfilter(*signal.zpk2tf(z, p, k),
3340
+ u)[::2]
3341
+
3342
+ xp_assert_equal(ynzp, ynzpref)
3343
+
3344
+ yzp = signal.decimate(u, 2, ftype=system, zero_phase=True)
3345
+ yzpref = signal.filtfilt(*signal.zpk2tf(z, p, k),
3346
+ u)[::2]
3347
+
3348
+ xp_assert_close(yzp, yzpref, rtol=1e-10, atol=1e-13)
3349
+
3350
+ def test_complex_fir_dlti(self, xp):
3351
+ # centre frequency for filter [Hz]
3352
+ fcentre = 50
3353
+ # filter passband width [Hz]
3354
+ fwidth = 5
3355
+ # sample rate [Hz]
3356
+ fs = 1e3
3357
+ numtaps = 20
3358
+
3359
+ # FIR filter about 0Hz
3360
+ bbase = signal.firwin(numtaps, fwidth/2, fs=fs)
3361
+
3362
+ # rotate these to desired frequency
3363
+ zbase = np.roots(bbase)
3364
+ zrot = zbase * np.exp(2j * np.pi * fcentre/fs)
3365
+ # FIR filter about 50Hz, maintaining passband gain of 0dB
3366
+ bz = bbase[0] * np.poly(zrot)
3367
+
3368
+ system = signal.dlti(bz, 1)
3369
+
3370
+ t = np.arange(200) / fs
3371
+
3372
+ # input
3373
+ u = (np.exp(2j * np.pi * fcentre * t)
3374
+ + 0.5 * np.exp(-2j * np.pi * fcentre * t))
3375
+
3376
+ ynzp = signal.decimate(u, 2, ftype=system, zero_phase=False)
3377
+ ynzpref = signal.upfirdn(bz, u, up=1, down=2)[:100]
3378
+
3379
+ xp_assert_equal(ynzp, ynzpref)
3380
+
3381
+ yzp = signal.decimate(u, 2, ftype=system, zero_phase=True)
3382
+ yzpref = signal.resample_poly(u, 1, 2, window=bz)
3383
+
3384
+ xp_assert_equal(yzp, yzpref)
3385
+
3386
+
3387
+ @skip_xp_backends("jax.numpy",
3388
+ reason="jax arrays do not support item assignment")
3389
+ class TestHilbert:
3390
+
3391
+ def test_bad_args(self, xp):
3392
+ x = xp.asarray([1.0 + 0.0j])
3393
+ assert_raises(ValueError, hilbert, x)
3394
+ x = xp.arange(8.0)
3395
+ assert_raises(ValueError, hilbert, x, N=0)
3396
+
3397
+ def test_hilbert_theoretical(self, xp):
3398
+ # test cases by Ariel Rokem
3399
+ decimal = 14
3400
+
3401
+ pi = xp.pi
3402
+ t = xp.arange(0, 2 * pi, pi / 256, dtype=xp.float64)
3403
+ a0 = xp.sin(t)
3404
+ a1 = xp.cos(t)
3405
+ a2 = xp.sin(2 * t)
3406
+ a3 = xp.cos(2 * t)
3407
+ a = xp.stack([a0, a1, a2, a3])
3408
+
3409
+ h = hilbert(a)
3410
+ h_abs = xp.abs(h)
3411
+
3412
+ h_angle = xp.atan2(xp.imag(h), xp.real(h)) # np.angle(h)
3413
+ h_real = xp.real(h)
3414
+
3415
+ # The real part should be equal to the original signals:
3416
+ assert_almost_equal(h_real, a, decimal)
3417
+ # The absolute value should be one everywhere, for this input:
3418
+ assert_almost_equal(h_abs, xp.ones(a.shape), decimal)
3419
+ # For the 'slow' sine - the phase should go from -pi/2 to pi/2 in
3420
+ # the first 256 bins:
3421
+ assert_almost_equal(h_angle[0, :256],
3422
+ xp.arange(-pi / 2, pi / 2, pi / 256, dtype=xp.float64),
3423
+ decimal)
3424
+ # For the 'slow' cosine - the phase should go from 0 to pi in the
3425
+ # same interval:
3426
+ assert_almost_equal(
3427
+ h_angle[1, :256], xp.arange(0, pi, pi / 256, dtype=xp.float64), decimal)
3428
+ # The 'fast' sine should make this phase transition in half the time:
3429
+ assert_almost_equal(h_angle[2, :128],
3430
+ xp.arange(-pi / 2, pi / 2, pi / 128, dtype=xp.float64),
3431
+ decimal)
3432
+ # Ditto for the 'fast' cosine:
3433
+ assert_almost_equal(
3434
+ h_angle[3, :128], xp.arange(0, pi, pi / 128, dtype=xp.float64), decimal)
3435
+
3436
+ # The imaginary part of hilbert(cos(t)) = sin(t) Wikipedia
3437
+ assert_almost_equal(xp.imag(h[1, :]), a0, decimal)
3438
+
3439
+ def test_hilbert_axisN(self, xp):
3440
+ # tests for axis and N arguments
3441
+ a = xp.reshape(xp.arange(18, dtype=xp.float64), (3, 6))
3442
+ # test axis
3443
+ aa = hilbert(a, axis=-1)
3444
+ xp_assert_equal(hilbert(a.T, axis=0), aa.T)
3445
+ # test 1d
3446
+ assert_almost_equal(hilbert(a[0, :]), aa[0, :], 14)
3447
+
3448
+ # test N
3449
+ aan = hilbert(a, N=20, axis=-1)
3450
+ assert aan.shape == (3, 20)
3451
+ assert hilbert(a.T, N=20, axis=0).shape == (20, 3)
3452
+ # the next test is just a regression test,
3453
+ # no idea whether numbers make sense
3454
+ a0hilb = np.array([0.000000000000000e+00 - 1.72015830311905j,
3455
+ 1.000000000000000e+00 - 2.047794505137069j,
3456
+ 1.999999999999999e+00 - 2.244055555687583j,
3457
+ 3.000000000000000e+00 - 1.262750302935009j,
3458
+ 4.000000000000000e+00 - 1.066489252384493j,
3459
+ 5.000000000000000e+00 + 2.918022706971047j,
3460
+ 8.881784197001253e-17 + 3.845658908989067j,
3461
+ -9.444121133484362e-17 + 0.985044202202061j,
3462
+ -1.776356839400251e-16 + 1.332257797702019j,
3463
+ -3.996802888650564e-16 + 0.501905089898885j,
3464
+ 1.332267629550188e-16 + 0.668696078880782j,
3465
+ -1.192678053963799e-16 + 0.235487067862679j,
3466
+ -1.776356839400251e-16 + 0.286439612812121j,
3467
+ 3.108624468950438e-16 + 0.031676888064907j,
3468
+ 1.332267629550188e-16 - 0.019275656884536j,
3469
+ -2.360035624836702e-16 - 0.1652588660287j,
3470
+ 0.000000000000000e+00 - 0.332049855010597j,
3471
+ 3.552713678800501e-16 - 0.403810179797771j,
3472
+ 8.881784197001253e-17 - 0.751023775297729j,
3473
+ 9.444121133484362e-17 - 0.79252210110103j])
3474
+ a0hilb = xp.asarray(a0hilb)
3475
+ assert_almost_equal(aan[0, :], a0hilb, 14, err_msg='N regression')
3476
+
3477
+ @pytest.mark.parametrize('dtype', ['float32', 'float64'])
3478
+ def test_hilbert_types(self, dtype, xp):
3479
+ dtype = getattr(xp, dtype)
3480
+ in_typed = xp.zeros(8, dtype=dtype)
3481
+ assert xp.real(hilbert(in_typed)).dtype == dtype
3482
+
3483
+
3484
+ @skip_xp_backends("jax.numpy",
3485
+ reason="jax arrays do not support item assignment")
3486
+ class TestHilbert2:
3487
+
3488
+ @skip_xp_backends(np_only=True, reason='list inputs are numpy-specific')
3489
+ def test_array_like(self, xp):
3490
+ hilbert2([[1, 2, 3], [4, 5, 6]])
3491
+
3492
+ def test_bad_args(self, xp):
3493
+ # x must be real.
3494
+ x = xp.asarray([[1.0 + 0.0j]])
3495
+ assert_raises(ValueError, hilbert2, x)
3496
+
3497
+ # x must be rank 2.
3498
+ x = xp.reshape(xp.arange(24), (2, 3, 4))
3499
+ assert_raises(ValueError, hilbert2, x)
3500
+
3501
+ # Bad value for N.
3502
+ x = xp.reshape(xp.arange(16), (4, 4))
3503
+ assert_raises(ValueError, hilbert2, x, N=0)
3504
+ assert_raises(ValueError, hilbert2, x, N=(2, 0))
3505
+ assert_raises(ValueError, hilbert2, x, N=(2,))
3506
+
3507
+ @pytest.mark.parametrize('dtype', ['float32', 'float64'])
3508
+ def test_hilbert2_types(self, dtype, xp):
3509
+ dtype = getattr(xp, dtype)
3510
+ in_typed = xp.zeros((2, 32), dtype=dtype)
3511
+ assert xp.real(signal.hilbert2(in_typed)).dtype == dtype
3512
+
3513
+
3514
+ class TestEnvelope:
3515
+ """Unit tests for function `._signaltools.envelope()`. """
3516
+
3517
+ @staticmethod
3518
+ def assert_close(actual, desired, msg, xp):
3519
+ a_r_tol = ({'atol': 1e-12, 'rtol': 1e-12}
3520
+ if xp_default_dtype(xp) == xp.float64
3521
+ else {'atol': 1e-5, 'rtol': 1e-5}
3522
+ )
3523
+
3524
+ """Little helper to compare to arrays with proper tolerances"""
3525
+ xp_assert_close(actual, desired, **a_r_tol, err_msg=msg)
3526
+
3527
+ def test_envelope_invalid_parameters(self, xp):
3528
+ """For `envelope()` Raise all exceptions that are used to verify function
3529
+ parameters. """
3530
+ with pytest.raises(ValueError,
3531
+ match=r"Invalid parameter axis=2 for z.shape=.*"):
3532
+ envelope(np.ones(3), axis=2)
3533
+ with pytest.raises(ValueError,
3534
+ match=r"z.shape\[axis\] not > 0 for z.shape=.*"):
3535
+ envelope(xp.ones((3, 0)), axis=1)
3536
+ for bp_in in [(0, 1, 2), (0, 2.), (None, 2.)]:
3537
+ ts = ', '.join(map(str, bp_in))
3538
+ with pytest.raises(ValueError,
3539
+ match=rf"bp_in=\({ts}\) isn't a 2-tuple of.*"):
3540
+ # noinspection PyTypeChecker
3541
+ envelope(xp.ones(4), bp_in=bp_in)
3542
+ with pytest.raises(ValueError,
3543
+ match="n_out=10.0 is not a positive integer or.*"):
3544
+ # noinspection PyTypeChecker
3545
+ envelope(xp.ones(4), n_out=10.)
3546
+ for bp_in in [(-1, 3), (1, 1), (0, 10)]:
3547
+ with pytest.raises(ValueError,
3548
+ match=r"`-n//2 <= bp_in\[0\] < bp_in\[1\] <=.*"):
3549
+ envelope(xp.ones(4), bp_in=bp_in)
3550
+ with pytest.raises(ValueError, match="residual='undefined' not in .*"):
3551
+ # noinspection PyTypeChecker
3552
+ envelope(xp.ones(4), residual='undefined')
3553
+
3554
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
3555
+ def test_envelope_verify_parameters(self, xp):
3556
+ """Ensure that the various parametrizations produce compatible results. """
3557
+ dt_r = xp_default_dtype(xp)
3558
+ dt_c = xp.complex64 if dt_r == xp.float32 else xp.complex128
3559
+
3560
+ Z = xp.asarray([4.0, 2, 2, 3, 0], dtype=dt_r)
3561
+ Zr_a = xp.asarray([4.0, 0, 0, 6, 0, 0, 0, 0], dtype=dt_r)
3562
+ z = sp_fft.irfft(Z)
3563
+ n = z.shape[0]
3564
+
3565
+ # the reference envelope:
3566
+ ze2_0, zr_0 = xp.unstack(envelope(z, (1, 3), residual='all', squared=True))
3567
+ self.assert_close(sp_fft.rfft(ze2_0),
3568
+ xp.asarray([4, 2, 0, 0, 0], dtype=dt_c),
3569
+ msg="Envelope calculation error", xp=xp)
3570
+ self.assert_close(sp_fft.rfft(zr_0),
3571
+ xp.asarray([4, 0, 0, 3, 0], dtype=dt_c),
3572
+ msg="Residual calculation error", xp=xp)
3573
+
3574
+ ze_1, zr_1 = xp.unstack(envelope(z, (1, 3), residual='all', squared=False))
3575
+ self.assert_close(ze_1**2, ze2_0,
3576
+ msg="Unsquared versus Squared envelope calculation error",
3577
+ xp=xp)
3578
+ self.assert_close(zr_1, zr_0,
3579
+ msg="Unsquared versus Squared residual calculation error",
3580
+ xp=xp)
3581
+
3582
+ ze2_2, zr_2 = xp.unstack(
3583
+ envelope(z, (1, 3), residual='all', squared=True, n_out=3*n)
3584
+ )
3585
+ self.assert_close(ze2_2[::3], ze2_0,
3586
+ msg="3x up-sampled envelope calculation error", xp=xp)
3587
+ self.assert_close(zr_2[::3], zr_0,
3588
+ msg="3x up-sampled residual calculation error", xp=xp)
3589
+
3590
+ ze2_3, zr_3 = xp.unstack(envelope(z, (1, 3), residual='lowpass', squared=True))
3591
+ self.assert_close(ze2_3, ze2_0,
3592
+ msg="`residual='lowpass'` envelope calculation error", xp=xp)
3593
+ self.assert_close(sp_fft.rfft(zr_3),
3594
+ xp.asarray([4, 0, 0, 0, 0], dtype=dt_c),
3595
+ msg="`residual='lowpass'` residual calculation error", xp=xp)
3596
+
3597
+ ze2_4 = envelope(z, (1, 3), residual=None, squared=True)
3598
+ self.assert_close(ze2_4, ze2_0,
3599
+ msg="`residual=None` envelope calculation error", xp=xp)
3600
+
3601
+ # compare complex analytic signal to real version
3602
+ Z_a = xp.asarray(Z, copy=True)
3603
+ Z_a[1:] *= 2
3604
+ z_a = sp_fft.ifft(Z_a, n=n) # analytic signal of Z
3605
+ self.assert_close(xp.real(z_a), z,
3606
+ msg="Reference analytic signal error", xp=xp)
3607
+ ze2_a, zr_a = xp.unstack(envelope(z_a, (1, 3), residual='all', squared=True))
3608
+ self.assert_close(ze2_a, xp.astype(ze2_0, dt_c), # dtypes must match
3609
+ msg="Complex envelope calculation error", xp=xp)
3610
+ self.assert_close(sp_fft.fft(zr_a), xp.asarray(Zr_a, dtype=dt_c),
3611
+ msg="Complex residual calculation error", xp=xp)
3612
+
3613
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
3614
+ @pytest.mark.parametrize(
3615
+ " Z, bp_in, Ze2_desired, Zr_desired",
3616
+ [([1, 0, 2, 2, 0], (1, None), [4, 2, 0, 0, 0], [1, 0, 0, 0, 0]),
3617
+ ([4, 0, 2, 0, 0], (0, None), [4, 0, 2, 0, 0], [0, 0, 0, 0, 0]),
3618
+ ([4, 0, 0, 2, 0], (None, None), [4, 0, 0, 2, 0], [0, 0, 0, 0, 0]),
3619
+ ([0, 0, 2, 2, 0], (1, 3), [2, 0, 0, 0, 0], [0, 0, 0, 2, 0]),
3620
+ ([4, 0, 2, 2, 0], (-3, 3), [4, 0, 2, 0, 0], [0, 0, 0, 2, 0]),
3621
+ ([4, 0, 3, 4, 0], (None, 1), [2, 0, 0, 0, 0], [0, 0, 3, 4, 0]),
3622
+ ([4, 0, 3, 4, 0], (None, 0), [0, 0, 0, 0, 0], [4, 0, 3, 4, 0])])
3623
+ def test_envelope_real_signals(self, Z, bp_in, Ze2_desired, Zr_desired, xp):
3624
+ """Test envelope calculation with real-valued test signals.
3625
+
3626
+ The comparisons are performed in the Fourier space, since it makes evaluating
3627
+ the bandpass filter behavior straightforward. Note that also the squared
3628
+ envelope can be easily calculated by hand, if one recalls that coefficients of
3629
+ a complex-valued Fourier series representing the signal can be directly
3630
+ determined by an FFT and that the absolute square of a Fourier series is again
3631
+ a Fourier series.
3632
+ """
3633
+ Z = xp.asarray(Z, dtype=xp.float64)
3634
+ Ze2_desired = xp.asarray(Ze2_desired, dtype=xp.float64)
3635
+ Zr_desired = xp.asarray(Zr_desired, dtype=xp.float64)
3636
+
3637
+ z = sp_fft.irfft(Z)
3638
+ ze2, zr = xp.unstack(envelope(z, bp_in, residual='all', squared=True))
3639
+ ze2_lp, zr_lp = xp.unstack(envelope(z, bp_in, residual='lowpass', squared=True))
3640
+ Ze2, Zr, Ze2_lp, Zr_lp = (sp_fft.rfft(z_) for z_ in (ze2, zr, ze2_lp, zr_lp))
3641
+
3642
+ Ze2_desired = xp.asarray(Ze2_desired, dtype=xp.complex128)
3643
+ Zr_desired = xp.asarray(Zr_desired, dtype=xp.complex128)
3644
+ self.assert_close(Ze2, Ze2_desired,
3645
+ msg="Envelope calculation error (residual='all')", xp=xp)
3646
+ self.assert_close(Zr, Zr_desired,
3647
+ msg="Residual calculation error (residual='all')", xp=xp)
3648
+
3649
+ if bp_in[1] is not None:
3650
+ Zr_desired[bp_in[1]:] = 0
3651
+ self.assert_close(Ze2_lp, Ze2_desired,
3652
+ msg="Envelope calculation error (residual='lowpass')", xp=xp)
3653
+ self.assert_close(Zr_lp, Zr_desired,
3654
+ msg="Residual calculation error (residual='lowpass')", xp=xp)
3655
+
3656
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
3657
+ @pytest.mark.parametrize(
3658
+ " Z, bp_in, Ze2_desired, Zr_desired",
3659
+ [([0, 5, 0, 5, 0], (None, None), [5, 0, 10, 0, 5], [0, 0, 0, 0, 0]),
3660
+ ([1, 5, 0, 5, 2], (-1, 2), [5, 0, 10, 0, 5], [1, 0, 0, 0, 2]),
3661
+ ([1, 2, 6, 0, 6, 3], (-1, 2), [0, 6, 0, 12, 0, 6], [1, 2, 0, 0, 0, 3])
3662
+ ])
3663
+ def test_envelope_complex_signals(self, Z, bp_in, Ze2_desired, Zr_desired, xp):
3664
+ """Test envelope calculation with complex-valued test signals.
3665
+
3666
+ We only need to test for the complex envelope here, since the ``Nones``s in the
3667
+ bandpass filter were already tested in the previous test.
3668
+ """
3669
+ Z = xp.asarray(Z, dtype=xp.float64)
3670
+ Ze2_desired = xp.asarray(Ze2_desired, dtype=xp.complex128)
3671
+ Zr_desired = xp.asarray(Zr_desired, dtype=xp.complex128)
3672
+
3673
+ z = sp_fft.ifft(sp_fft.ifftshift(Z))
3674
+ ze2, zr = xp.unstack(envelope(z, bp_in, residual='all', squared=True))
3675
+ Ze2, Zr = (sp_fft.fftshift(sp_fft.fft(z_)) for z_ in (ze2, zr))
3676
+
3677
+ self.assert_close(Ze2, Ze2_desired,
3678
+ msg="Envelope calculation error", xp=xp)
3679
+ self.assert_close(Zr, Zr_desired,
3680
+ msg="Residual calculation error", xp=xp)
3681
+
3682
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
3683
+ def test_envelope_verify_axis_parameter(self, xp):
3684
+ """Test for multi-channel envelope calculations. """
3685
+ dt_r = xp_default_dtype(xp)
3686
+ dt_c = xp.complex64 if dt_r == xp.float32 else xp.complex128
3687
+
3688
+ z = sp_fft.irfft(xp.asarray([[1.0, 0, 2, 2, 0], [7, 0, 4, 4, 0]], dtype=dt_r))
3689
+ Ze2_desired = xp.asarray([[4, 2, 0, 0, 0], [16, 8, 0, 0, 0]],
3690
+ dtype=dt_c)
3691
+ Zr_desired = xp.asarray([[1, 0, 0, 0, 0], [7, 0, 0, 0, 0]], dtype=dt_c)
3692
+
3693
+ ze2, zr = xp.unstack(envelope(z, squared=True, axis=1))
3694
+ ye2T, yrT = xp.unstack(envelope(z.T, squared=True, axis=0))
3695
+ Ze2, Ye2, Zr, Yr = (sp_fft.rfft(z_) for z_ in (ze2, ye2T.T, zr, yrT.T))
3696
+
3697
+ self.assert_close(Ze2, Ze2_desired, msg="2d envelope calculation error", xp=xp)
3698
+ self.assert_close(Zr, Zr_desired, msg="2d residual calculation error", xp=xp)
3699
+ self.assert_close(
3700
+ Ye2, Ze2_desired, msg="Transposed 2d envelope calc. error", xp=xp
3701
+ )
3702
+ self.assert_close(
3703
+ Yr, Zr_desired, msg="Transposed 2d residual calc. error", xp=xp
3704
+ )
3705
+
3706
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
3707
+ def test_envelope_verify_axis_parameter_complex(self, xp):
3708
+ """Test for multi-channel envelope calculations with complex values. """
3709
+ dt_r = xp_default_dtype(xp)
3710
+ dt_c = xp.complex64 if dt_r == xp.float32 else xp.complex128
3711
+ inp = xp.asarray([[1.0, 5, 0, 5, 2], [1, 10, 0, 10, 2]], dtype=dt_r)
3712
+ z = sp_fft.ifft(sp_fft.ifftshift(inp, axes=1))
3713
+ Ze2_des = xp.asarray([[5, 0, 10, 0, 5], [20, 0, 40, 0, 20],], dtype=dt_c)
3714
+ Zr_des = xp.asarray([[1, 0, 0, 0, 2], [1, 0, 0, 0, 2]], dtype=dt_c)
3715
+
3716
+ kw = dict(bp_in=(-1, 2), residual='all', squared=True)
3717
+ ze2, zr = xp.unstack(envelope(z, axis=1, **kw))
3718
+ ye2T, yrT = xp.unstack(envelope(z.T, axis=0, **kw))
3719
+ Ze2, Ye2, Zr, Yr = (sp_fft.fftshift(sp_fft.fft(z_), axes=1)
3720
+ for z_ in (ze2, ye2T.T, zr, yrT.T))
3721
+
3722
+ self.assert_close(Ze2, Ze2_des, msg="2d envelope calculation error", xp=xp)
3723
+ self.assert_close(Zr, Zr_des, msg="2d residual calculation error", xp=xp)
3724
+ self.assert_close(
3725
+ Ye2, Ze2_des, msg="Transposed 2d envelope calc. error", xp=xp
3726
+ )
3727
+ self.assert_close(Yr, Zr_des, msg="Transposed 2d residual calc. error", xp=xp)
3728
+
3729
+ @skip_xp_backends("jax.numpy", reason="XXX: immutable arrays")
3730
+ @pytest.mark.parametrize('X', [[4, 0, 0, 1, 2], [4, 0, 0, 2, 1, 2]])
3731
+ def test_compare_envelope_hilbert(self, X, xp):
3732
+ """Compare output of `envelope()` and `hilbert()`. """
3733
+ X = xp.asarray(X, dtype=xp.float64)
3734
+ x = sp_fft.irfft(X)
3735
+ e_hil = xp.abs(hilbert(x))
3736
+ e_env = envelope(x, (None, None), residual=None)
3737
+ self.assert_close(e_hil, e_env, msg="Hilbert-Envelope comparison error", xp=xp)
3738
+
3739
+ def test_nyquist(self):
3740
+ """Test behavior when input is a cosine at the Nyquist frequency.
3741
+
3742
+ Resampling even length signals, requires accounting for unpaired bins at the
3743
+ Nyquist frequency (consults the source code of `resample`).
3744
+
3745
+ Since `envelope` excludes the Nyquist frequency from the envelope calculation,
3746
+ only the residues need to be investigated.
3747
+ """
3748
+ x4 = sp_fft.irfft([0, 0, 8]) # = [2, -2, 2, -2]
3749
+ x6 = signal.resample(x4, num=6) # = [2, -1, -1, 2, -1, -1]
3750
+ y6, y6_res = envelope(x4, n_out=6, residual='all') # real-valued case
3751
+ z6, z6_res = envelope(x4 + 0j, n_out=6, residual='all') # complex-valued case
3752
+
3753
+ xp_assert_close(y6, np.zeros(6), atol=1e-12)
3754
+ xp_assert_close(y6_res, x6, atol=1e-12)
3755
+
3756
+ xp_assert_close(z6, np.zeros(6, dtype=z6.dtype), atol=1e-12)
3757
+ xp_assert_close(z6_res, x6.astype(z6.dtype), atol=1e-12)
3758
+
3759
+ @skip_xp_backends(np_only=True)
3760
+ class TestPartialFractionExpansion:
3761
+ @staticmethod
3762
+ def assert_rp_almost_equal(r, p, r_true, p_true, decimal=7):
3763
+ r_true = np.asarray(r_true)
3764
+ p_true = np.asarray(p_true)
3765
+
3766
+ distance = np.hypot(abs(p[:, None] - p_true),
3767
+ abs(r[:, None] - r_true))
3768
+
3769
+ rows, cols = linear_sum_assignment(distance)
3770
+ assert_almost_equal(p[rows], p_true[cols], decimal=decimal)
3771
+ assert_almost_equal(r[rows], r_true[cols], decimal=decimal)
3772
+
3773
+ def test_compute_factors(self, xp):
3774
+ factors, poly = _compute_factors([1, 2, 3], [3, 2, 1])
3775
+ assert len(factors) == 3
3776
+ assert_almost_equal(factors[0], np.poly([2, 2, 3]))
3777
+ assert_almost_equal(factors[1], np.poly([1, 1, 1, 3]))
3778
+ assert_almost_equal(factors[2], np.poly([1, 1, 1, 2, 2]))
3779
+ assert_almost_equal(poly, np.poly([1, 1, 1, 2, 2, 3]))
3780
+
3781
+ factors, poly = _compute_factors([1, 2, 3], [3, 2, 1],
3782
+ include_powers=True)
3783
+ assert len(factors) == 6
3784
+ assert_almost_equal(factors[0], np.poly([1, 1, 2, 2, 3]))
3785
+ assert_almost_equal(factors[1], np.poly([1, 2, 2, 3]))
3786
+ assert_almost_equal(factors[2], np.poly([2, 2, 3]))
3787
+ assert_almost_equal(factors[3], np.poly([1, 1, 1, 2, 3]))
3788
+ assert_almost_equal(factors[4], np.poly([1, 1, 1, 3]))
3789
+ assert_almost_equal(factors[5], np.poly([1, 1, 1, 2, 2]))
3790
+ assert_almost_equal(poly, np.poly([1, 1, 1, 2, 2, 3]))
3791
+
3792
+ def test_group_poles(self, xp):
3793
+ unique, multiplicity = _group_poles(
3794
+ [1.0, 1.001, 1.003, 2.0, 2.003, 3.0], 0.1, 'min')
3795
+ xp_assert_close(unique, [1.0, 2.0, 3.0])
3796
+ xp_assert_close(multiplicity, [3, 2, 1])
3797
+
3798
+ def test_residue_general(self, xp):
3799
+ # Test are taken from issue #4464, note that poles in scipy are
3800
+ # in increasing by absolute value order, opposite to MATLAB.
3801
+ r, p, k = residue([5, 3, -2, 7], [-4, 0, 8, 3])
3802
+ assert_almost_equal(r, [1.3320, -0.6653, -1.4167], decimal=4)
3803
+ assert_almost_equal(p, [-0.4093, -1.1644, 1.5737], decimal=4)
3804
+ assert_almost_equal(k, [-1.2500], decimal=4)
3805
+
3806
+ r, p, k = residue([-4, 8], [1, 6, 8])
3807
+ assert_almost_equal(r, [8, -12])
3808
+ assert_almost_equal(p, [-2, -4])
3809
+ assert k.size == 0
3810
+
3811
+ r, p, k = residue([4, 1], [1, -1, -2])
3812
+ assert_almost_equal(r, [1, 3])
3813
+ assert_almost_equal(p, [-1, 2])
3814
+ assert k.size == 0
3815
+
3816
+ r, p, k = residue([4, 3], [2, -3.4, 1.98, -0.406])
3817
+ self.assert_rp_almost_equal(
3818
+ r, p, [-18.125 - 13.125j, -18.125 + 13.125j, 36.25],
3819
+ [0.5 - 0.2j, 0.5 + 0.2j, 0.7])
3820
+ assert k.size == 0
3821
+
3822
+ r, p, k = residue([2, 1], [1, 5, 8, 4])
3823
+ self.assert_rp_almost_equal(r, p, [-1, 1, 3], [-1, -2, -2])
3824
+ assert k.size == 0
3825
+
3826
+ r, p, k = residue([3, -1.1, 0.88, -2.396, 1.348],
3827
+ [1, -0.7, -0.14, 0.048])
3828
+ assert_almost_equal(r, [-3, 4, 1])
3829
+ assert_almost_equal(p, [0.2, -0.3, 0.8])
3830
+ assert_almost_equal(k, [3, 1])
3831
+
3832
+ r, p, k = residue([1], [1, 2, -3])
3833
+ assert_almost_equal(r, [0.25, -0.25])
3834
+ assert_almost_equal(p, [1, -3])
3835
+ assert k.size == 0
3836
+
3837
+ r, p, k = residue([1, 0, -5], [1, 0, 0, 0, -1])
3838
+ self.assert_rp_almost_equal(r, p,
3839
+ [1, 1.5j, -1.5j, -1], [-1, -1j, 1j, 1])
3840
+ assert k.size == 0
3841
+
3842
+ r, p, k = residue([3, 8, 6], [1, 3, 3, 1])
3843
+ self.assert_rp_almost_equal(r, p, [1, 2, 3], [-1, -1, -1])
3844
+ assert k.size == 0
3845
+
3846
+ r, p, k = residue([3, -1], [1, -3, 2])
3847
+ assert_almost_equal(r, [-2, 5])
3848
+ assert_almost_equal(p, [1, 2])
3849
+ assert k.size == 0
3850
+
3851
+ r, p, k = residue([2, 3, -1], [1, -3, 2])
3852
+ assert_almost_equal(r, [-4, 13])
3853
+ assert_almost_equal(p, [1, 2])
3854
+ assert_almost_equal(k, [2])
3855
+
3856
+ r, p, k = residue([7, 2, 3, -1], [1, -3, 2])
3857
+ assert_almost_equal(r, [-11, 69])
3858
+ assert_almost_equal(p, [1, 2])
3859
+ assert_almost_equal(k, [7, 23])
3860
+
3861
+ r, p, k = residue([2, 3, -1], [1, -3, 4, -2])
3862
+ self.assert_rp_almost_equal(r, p, [4, -1 + 3.5j, -1 - 3.5j],
3863
+ [1, 1 - 1j, 1 + 1j])
3864
+ assert k.size == 0
3865
+
3866
+ def test_residue_leading_zeros(self, xp):
3867
+ # Leading zeros in numerator or denominator must not affect the answer.
3868
+ r0, p0, k0 = residue([5, 3, -2, 7], [-4, 0, 8, 3])
3869
+ r1, p1, k1 = residue([0, 5, 3, -2, 7], [-4, 0, 8, 3])
3870
+ r2, p2, k2 = residue([5, 3, -2, 7], [0, -4, 0, 8, 3])
3871
+ r3, p3, k3 = residue([0, 0, 5, 3, -2, 7], [0, 0, 0, -4, 0, 8, 3])
3872
+ assert_almost_equal(r0, r1)
3873
+ assert_almost_equal(r0, r2)
3874
+ assert_almost_equal(r0, r3)
3875
+ assert_almost_equal(p0, p1)
3876
+ assert_almost_equal(p0, p2)
3877
+ assert_almost_equal(p0, p3)
3878
+ assert_almost_equal(k0, k1)
3879
+ assert_almost_equal(k0, k2)
3880
+ assert_almost_equal(k0, k3)
3881
+
3882
+ def test_resiude_degenerate(self, xp):
3883
+ # Several tests for zero numerator and denominator.
3884
+ r, p, k = residue([0, 0], [1, 6, 8])
3885
+ assert_almost_equal(r, [0, 0])
3886
+ assert_almost_equal(p, [-2, -4])
3887
+ assert k.size == 0
3888
+
3889
+ r, p, k = residue(0, 1)
3890
+ assert r.size == 0
3891
+ assert p.size == 0
3892
+ assert k.size == 0
3893
+
3894
+ with pytest.raises(ValueError, match="Denominator `a` is zero."):
3895
+ residue(1, 0)
3896
+
3897
+ def test_residuez_general(self, xp):
3898
+ r, p, k = residuez([1, 6, 6, 2], [1, -(2 + 1j), (1 + 2j), -1j])
3899
+ self.assert_rp_almost_equal(r, p, [-2+2.5j, 7.5+7.5j, -4.5-12j],
3900
+ [1j, 1, 1])
3901
+ assert_almost_equal(k, [2j])
3902
+
3903
+ r, p, k = residuez([1, 2, 1], [1, -1, 0.3561])
3904
+ self.assert_rp_almost_equal(r, p,
3905
+ [-0.9041 - 5.9928j, -0.9041 + 5.9928j],
3906
+ [0.5 + 0.3257j, 0.5 - 0.3257j],
3907
+ decimal=4)
3908
+ assert_almost_equal(k, [2.8082], decimal=4)
3909
+
3910
+ r, p, k = residuez([1, -1], [1, -5, 6])
3911
+ assert_almost_equal(r, [-1, 2])
3912
+ assert_almost_equal(p, [2, 3])
3913
+ assert k.size == 0
3914
+
3915
+ r, p, k = residuez([2, 3, 4], [1, 3, 3, 1])
3916
+ self.assert_rp_almost_equal(r, p, [4, -5, 3], [-1, -1, -1])
3917
+ assert k.size == 0
3918
+
3919
+ r, p, k = residuez([1, -10, -4, 4], [2, -2, -4])
3920
+ assert_almost_equal(r, [0.5, -1.5])
3921
+ assert_almost_equal(p, [-1, 2])
3922
+ assert_almost_equal(k, [1.5, -1])
3923
+
3924
+ r, p, k = residuez([18], [18, 3, -4, -1])
3925
+ self.assert_rp_almost_equal(r, p,
3926
+ [0.36, 0.24, 0.4], [0.5, -1/3, -1/3])
3927
+ assert k.size == 0
3928
+
3929
+ r, p, k = residuez([2, 3], np.polymul([1, -1/2], [1, 1/4]))
3930
+ assert_almost_equal(r, [-10/3, 16/3])
3931
+ assert_almost_equal(p, [-0.25, 0.5])
3932
+ assert k.size == 0
3933
+
3934
+ r, p, k = residuez([1, -2, 1], [1, -1])
3935
+ assert_almost_equal(r, [0])
3936
+ assert_almost_equal(p, [1])
3937
+ assert_almost_equal(k, [1, -1])
3938
+
3939
+ r, p, k = residuez(1, [1, -1j])
3940
+ assert_almost_equal(r, [1])
3941
+ assert_almost_equal(p, [1j])
3942
+ assert k.size == 0
3943
+
3944
+ r, p, k = residuez(1, [1, -1, 0.25])
3945
+ assert_almost_equal(r, [0, 1])
3946
+ assert_almost_equal(p, [0.5, 0.5])
3947
+ assert k.size == 0
3948
+
3949
+ r, p, k = residuez(1, [1, -0.75, .125])
3950
+ assert_almost_equal(r, [-1, 2])
3951
+ assert_almost_equal(p, [0.25, 0.5])
3952
+ assert k.size == 0
3953
+
3954
+ r, p, k = residuez([1, 6, 2], [1, -2, 1])
3955
+ assert_almost_equal(r, [-10, 9])
3956
+ assert_almost_equal(p, [1, 1])
3957
+ assert_almost_equal(k, [2])
3958
+
3959
+ r, p, k = residuez([6, 2], [1, -2, 1])
3960
+ assert_almost_equal(r, [-2, 8])
3961
+ assert_almost_equal(p, [1, 1])
3962
+ assert k.size == 0
3963
+
3964
+ r, p, k = residuez([1, 6, 6, 2], [1, -2, 1])
3965
+ assert_almost_equal(r, [-24, 15])
3966
+ assert_almost_equal(p, [1, 1])
3967
+ assert_almost_equal(k, [10, 2])
3968
+
3969
+ r, p, k = residuez([1, 0, 1], [1, 0, 0, 0, 0, -1])
3970
+ self.assert_rp_almost_equal(r, p,
3971
+ [0.2618 + 0.1902j, 0.2618 - 0.1902j,
3972
+ 0.4, 0.0382 - 0.1176j, 0.0382 + 0.1176j],
3973
+ [-0.8090 + 0.5878j, -0.8090 - 0.5878j,
3974
+ 1.0, 0.3090 + 0.9511j, 0.3090 - 0.9511j],
3975
+ decimal=4)
3976
+ assert k.size == 0
3977
+
3978
+ def test_residuez_trailing_zeros(self, xp):
3979
+ # Trailing zeros in numerator or denominator must not affect the
3980
+ # answer.
3981
+ r0, p0, k0 = residuez([5, 3, -2, 7], [-4, 0, 8, 3])
3982
+ r1, p1, k1 = residuez([5, 3, -2, 7, 0], [-4, 0, 8, 3])
3983
+ r2, p2, k2 = residuez([5, 3, -2, 7], [-4, 0, 8, 3, 0])
3984
+ r3, p3, k3 = residuez([5, 3, -2, 7, 0, 0], [-4, 0, 8, 3, 0, 0, 0])
3985
+ assert_almost_equal(r0, r1)
3986
+ assert_almost_equal(r0, r2)
3987
+ assert_almost_equal(r0, r3)
3988
+ assert_almost_equal(p0, p1)
3989
+ assert_almost_equal(p0, p2)
3990
+ assert_almost_equal(p0, p3)
3991
+ assert_almost_equal(k0, k1)
3992
+ assert_almost_equal(k0, k2)
3993
+ assert_almost_equal(k0, k3)
3994
+
3995
+ def test_residuez_degenerate(self, xp):
3996
+ r, p, k = residuez([0, 0], [1, 6, 8])
3997
+ assert_almost_equal(r, [0, 0])
3998
+ assert_almost_equal(p, [-2, -4])
3999
+ assert k.size == 0
4000
+
4001
+ r, p, k = residuez(0, 1)
4002
+ assert r.size == 0
4003
+ assert p.size == 0
4004
+ assert k.size == 0
4005
+
4006
+ with pytest.raises(ValueError, match="Denominator `a` is zero."):
4007
+ residuez(1, 0)
4008
+
4009
+ with pytest.raises(ValueError,
4010
+ match="First coefficient of determinant `a` must "
4011
+ "be non-zero."):
4012
+ residuez(1, [0, 1, 2, 3])
4013
+
4014
+ def test_inverse_unique_roots_different_rtypes(self, xp):
4015
+ # This test was inspired by github issue 2496.
4016
+ r = [3 / 10, -1 / 6, -2 / 15]
4017
+ p = [0, -2, -5]
4018
+ k = []
4019
+ b_expected = [0.0, 1, 3]
4020
+ a_expected = [1, 7, 10, 0]
4021
+
4022
+ # With the default tolerance, the rtype does not matter
4023
+ # for this example.
4024
+ for rtype in ('avg', 'mean', 'min', 'minimum', 'max', 'maximum'):
4025
+ b, a = invres(r, p, k, rtype=rtype)
4026
+ xp_assert_close(b, b_expected)
4027
+ xp_assert_close(a, a_expected, check_dtype=False)
4028
+
4029
+ b, a = invresz(r, p, k, rtype=rtype)
4030
+ xp_assert_close(b, b_expected)
4031
+ xp_assert_close(a, a_expected, check_dtype=False)
4032
+
4033
+ def test_inverse_repeated_roots_different_rtypes(self, xp):
4034
+ r = [3 / 20, -7 / 36, -1 / 6, 2 / 45]
4035
+ p = [0, -2, -2, -5]
4036
+ k = []
4037
+ b_expected = [0.0, 0, 1, 3]
4038
+ b_expected_z = [-1/6, -2/3, 11/6, 3]
4039
+ a_expected = [1, 9, 24, 20, 0]
4040
+
4041
+ for rtype in ('avg', 'mean', 'min', 'minimum', 'max', 'maximum'):
4042
+ b, a = invres(r, p, k, rtype=rtype)
4043
+ xp_assert_close(b, b_expected, atol=1e-14)
4044
+ xp_assert_close(a, a_expected, check_dtype=False)
4045
+
4046
+ b, a = invresz(r, p, k, rtype=rtype)
4047
+ xp_assert_close(b, b_expected_z, atol=1e-14)
4048
+ xp_assert_close(a, a_expected, check_dtype=False)
4049
+
4050
+ def test_inverse_bad_rtype(self, xp):
4051
+ r = [3 / 20, -7 / 36, -1 / 6, 2 / 45]
4052
+ p = [0, -2, -2, -5]
4053
+ k = []
4054
+ with pytest.raises(ValueError, match="`rtype` must be one of"):
4055
+ invres(r, p, k, rtype='median')
4056
+ with pytest.raises(ValueError, match="`rtype` must be one of"):
4057
+ invresz(r, p, k, rtype='median')
4058
+
4059
+ def test_invresz_one_coefficient_bug(self, xp):
4060
+ # Regression test for issue in gh-4646.
4061
+ r = [1]
4062
+ p = [2]
4063
+ k = [0]
4064
+ b, a = invresz(r, p, k)
4065
+ xp_assert_close(b, [1])
4066
+ xp_assert_close(a, [1.0, -2.0])
4067
+
4068
+ def test_invres(self, xp):
4069
+ b, a = invres([1], [1], [])
4070
+ assert_almost_equal(b, [1])
4071
+ assert_almost_equal(a, [1, -1])
4072
+
4073
+ b, a = invres([1 - 1j, 2, 0.5 - 3j], [1, 0.5j, 1 + 1j], [])
4074
+ assert_almost_equal(b, [3.5 - 4j, -8.5 + 0.25j, 3.5 + 3.25j])
4075
+ assert_almost_equal(a, [1, -2 - 1.5j, 0.5 + 2j, 0.5 - 0.5j])
4076
+
4077
+ b, a = invres([0.5, 1], [1 - 1j, 2 + 2j], [1, 2, 3])
4078
+ assert_almost_equal(b, [1, -1 - 1j, 1 - 2j, 0.5 - 3j, 10])
4079
+ assert_almost_equal(a, [1, -3 - 1j, 4])
4080
+
4081
+ b, a = invres([-1, 2, 1j, 3 - 1j, 4, -2],
4082
+ [-1, 2 - 1j, 2 - 1j, 3, 3, 3], [])
4083
+ assert_almost_equal(b, [4 - 1j, -28 + 16j, 40 - 62j, 100 + 24j,
4084
+ -292 + 219j, 192 - 268j])
4085
+ assert_almost_equal(a, [1, -12 + 2j, 53 - 20j, -96 + 68j, 27 - 72j,
4086
+ 108 - 54j, -81 + 108j])
4087
+
4088
+ b, a = invres([-1, 1j], [1, 1], [1, 2])
4089
+ assert_almost_equal(b, [1, 0, -4, 3 + 1j])
4090
+ assert_almost_equal(a, [1, -2, 1])
4091
+
4092
+ def test_invresz(self, xp):
4093
+ b, a = invresz([1], [1], [])
4094
+ assert_almost_equal(b, [1])
4095
+ assert_almost_equal(a, [1, -1])
4096
+
4097
+ b, a = invresz([1 - 1j, 2, 0.5 - 3j], [1, 0.5j, 1 + 1j], [])
4098
+ assert_almost_equal(b, [3.5 - 4j, -8.5 + 0.25j, 3.5 + 3.25j])
4099
+ assert_almost_equal(a, [1, -2 - 1.5j, 0.5 + 2j, 0.5 - 0.5j])
4100
+
4101
+ b, a = invresz([0.5, 1], [1 - 1j, 2 + 2j], [1, 2, 3])
4102
+ assert_almost_equal(b, [2.5, -3 - 1j, 1 - 2j, -1 - 3j, 12])
4103
+ assert_almost_equal(a, [1, -3 - 1j, 4])
4104
+
4105
+ b, a = invresz([-1, 2, 1j, 3 - 1j, 4, -2],
4106
+ [-1, 2 - 1j, 2 - 1j, 3, 3, 3], [])
4107
+ assert_almost_equal(b, [6, -50 + 11j, 100 - 72j, 80 + 58j,
4108
+ -354 + 228j, 234 - 297j])
4109
+ assert_almost_equal(a, [1, -12 + 2j, 53 - 20j, -96 + 68j, 27 - 72j,
4110
+ 108 - 54j, -81 + 108j])
4111
+
4112
+ b, a = invresz([-1, 1j], [1, 1], [1, 2])
4113
+ assert_almost_equal(b, [1j, 1, -3, 2])
4114
+ assert_almost_equal(a, [1, -2, 1])
4115
+
4116
+ def test_inverse_scalar_arguments(self, xp):
4117
+ b, a = invres(1, 1, 1)
4118
+ assert_almost_equal(b, [1, 0])
4119
+ assert_almost_equal(a, [1, -1])
4120
+
4121
+ b, a = invresz(1, 1, 1)
4122
+ assert_almost_equal(b, [2, -1])
4123
+ assert_almost_equal(a, [1, -1])
4124
+
4125
+
4126
+ class TestVectorstrength:
4127
+
4128
+ def test_single_1dperiod(self, xp):
4129
+ events = xp.asarray([.5])
4130
+ period = 5.
4131
+ targ_strength = 1.
4132
+ targ_phase = .1
4133
+
4134
+ strength, phase = vectorstrength(events, period)
4135
+
4136
+ assert strength.ndim == 0
4137
+ assert phase.ndim == 0
4138
+
4139
+ assert math.isclose(strength, targ_strength, abs_tol=1.5e-7)
4140
+ assert math.isclose(phase, 2 * math.pi * targ_phase, abs_tol=1.5e-7)
4141
+
4142
+ @xfail_xp_backends('torch', reason="phase modulo 2*pi")
4143
+ def test_single_2dperiod(self, xp):
4144
+ events = xp.asarray([.5])
4145
+ period = xp.asarray([1, 2, 5.])
4146
+ targ_strength = xp.asarray([1.] * 3)
4147
+ targ_phase = xp.asarray([.5, .25, .1])
4148
+
4149
+ strength, phase = vectorstrength(events, period)
4150
+
4151
+ assert strength.ndim == 1
4152
+ assert phase.ndim == 1
4153
+ assert_array_almost_equal(strength, targ_strength)
4154
+ assert_almost_equal(phase, 2 * xp.pi * targ_phase)
4155
+
4156
+ def test_equal_1dperiod(self, xp):
4157
+ events = xp.asarray([.25, .25, .25, .25, .25, .25])
4158
+ period = 2
4159
+ targ_strength = 1.
4160
+ targ_phase = .125
4161
+
4162
+ strength, phase = vectorstrength(events, period)
4163
+
4164
+ assert strength.ndim == 0
4165
+ assert phase.ndim == 0
4166
+
4167
+ assert math.isclose(strength, targ_strength, abs_tol=1.5e-7)
4168
+ assert math.isclose(phase, 2 * math.pi * targ_phase, abs_tol=1.5e-7)
4169
+
4170
+ def test_equal_2dperiod(self, xp):
4171
+ events = xp.asarray([.25, .25, .25, .25, .25, .25])
4172
+ period = xp.asarray([1, 2, ])
4173
+ targ_strength = xp.asarray([1.] * 2)
4174
+ targ_phase = xp.asarray([.25, .125])
4175
+
4176
+ strength, phase = vectorstrength(events, period)
4177
+
4178
+ assert strength.ndim == 1
4179
+ assert phase.ndim == 1
4180
+ assert_almost_equal(strength, targ_strength)
4181
+ assert_almost_equal(phase, 2 * xp.pi * targ_phase)
4182
+
4183
+ def test_spaced_1dperiod(self, xp):
4184
+ events = xp.asarray([.1, 1.1, 2.1, 4.1, 10.1])
4185
+ period = 1
4186
+ targ_strength = 1.
4187
+ targ_phase = .1
4188
+
4189
+ strength, phase = vectorstrength(events, period)
4190
+
4191
+ assert strength.ndim == 0
4192
+ assert phase.ndim == 0
4193
+
4194
+ assert math.isclose(strength, targ_strength, abs_tol=1.5e-7)
4195
+ assert math.isclose(phase, 2 * math.pi * targ_phase, abs_tol=1.5e-6)
4196
+
4197
+ def test_spaced_2dperiod(self, xp):
4198
+ events = xp.asarray([.1, 1.1, 2.1, 4.1, 10.1])
4199
+ period = xp.asarray([1, .5])
4200
+ targ_strength = xp.asarray([1.] * 2)
4201
+ targ_phase = xp.asarray([.1, .2])
4202
+
4203
+ strength, phase = vectorstrength(events, period)
4204
+
4205
+ assert strength.ndim == 1
4206
+ assert phase.ndim == 1
4207
+ assert_almost_equal(strength, targ_strength)
4208
+ rtol_kw = {'rtol': 2e-6} if xp_default_dtype(xp) == xp.float32 else {}
4209
+ xp_assert_close(phase, 2 * xp.pi * targ_phase, **rtol_kw)
4210
+
4211
+ def test_partial_1dperiod(self, xp):
4212
+ events = xp.asarray([.25, .5, .75])
4213
+ period = 1
4214
+ targ_strength = 1. / 3.
4215
+ targ_phase = .5
4216
+
4217
+ strength, phase = vectorstrength(events, period)
4218
+
4219
+ assert strength.ndim == 0
4220
+ assert phase.ndim == 0
4221
+
4222
+ assert math.isclose(strength, targ_strength)
4223
+ assert math.isclose(phase, 2 * math.pi * targ_phase)
4224
+
4225
+
4226
+ @xfail_xp_backends("torch", reason="phase modulo 2*pi")
4227
+ def test_partial_2dperiod(self, xp):
4228
+ events = xp.asarray([.25, .5, .75])
4229
+ period = xp.asarray([1., 1., 1., 1.])
4230
+ targ_strength = xp.asarray([1. / 3.] * 4)
4231
+ targ_phase = xp.asarray([.5, .5, .5, .5])
4232
+
4233
+ strength, phase = vectorstrength(events, period)
4234
+
4235
+ assert strength.ndim == 1
4236
+ assert phase.ndim == 1
4237
+ assert_almost_equal(strength, targ_strength)
4238
+ assert_almost_equal(phase, 2 * xp.pi * targ_phase)
4239
+
4240
+ def test_opposite_1dperiod(self, xp):
4241
+ events = xp.asarray([0, .25, .5, .75])
4242
+ period = 1.
4243
+ targ_strength = 0
4244
+
4245
+ strength, phase = vectorstrength(events, period)
4246
+
4247
+ assert strength.ndim == 0
4248
+ assert phase.ndim == 0
4249
+ assert math.isclose(strength, targ_strength, abs_tol=1.5e-7)
4250
+
4251
+ def test_opposite_2dperiod(self, xp):
4252
+ events = xp.asarray([0, .25, .5, .75])
4253
+ period = xp.asarray([1.] * 10)
4254
+ targ_strength = xp.asarray([0.] * 10)
4255
+
4256
+ strength, phase = vectorstrength(events, period)
4257
+
4258
+ assert strength.ndim == 1
4259
+ assert phase.ndim == 1
4260
+ assert_almost_equal(strength, targ_strength)
4261
+
4262
+ def test_2d_events_ValueError(self, xp):
4263
+ events = xp.asarray([[1, 2]])
4264
+ period = 1.
4265
+ assert_raises(ValueError, vectorstrength, events, period)
4266
+
4267
+ def test_2d_period_ValueError(self, xp):
4268
+ events = 1.
4269
+ period = xp.asarray([[1]])
4270
+ assert_raises(ValueError, vectorstrength, events, period)
4271
+
4272
+ def test_zero_period_ValueError(self, xp):
4273
+ events = 1.
4274
+ period = 0
4275
+ assert_raises(ValueError, vectorstrength, events, period)
4276
+
4277
+ def test_negative_period_ValueError(self, xp):
4278
+ events = 1.
4279
+ period = -1
4280
+ assert_raises(ValueError, vectorstrength, events, period)
4281
+
4282
+
4283
+ @pytest.mark.filterwarnings('ignore::DeprecationWarning')
4284
+ @skip_xp_backends(np_only=True)
4285
+ @pytest.mark.parametrize('func', (sosfilt, lfilter))
4286
+ def test_nonnumeric_dtypes(func, xp):
4287
+ x = [Decimal(1), Decimal(2), Decimal(3)]
4288
+ b = [Decimal(1), Decimal(2), Decimal(3)]
4289
+ a = [Decimal(1), Decimal(2), Decimal(3)]
4290
+ x = np.array(x)
4291
+ assert x.dtype.kind == 'O'
4292
+ desired = lfilter(np.array(b, float), np.array(a, float), x.astype(float))
4293
+ if func is sosfilt:
4294
+ actual = sosfilt([b + a], x)
4295
+ else:
4296
+ actual = lfilter(b, a, x)
4297
+ assert all(isinstance(x, Decimal) for x in actual)
4298
+ assert_allclose(actual.astype(float), desired.astype(float))
4299
+ # Degenerate cases
4300
+ if func is lfilter:
4301
+ args = [1., 1.]
4302
+ else:
4303
+ args = [tf2sos(1., 1.)]
4304
+
4305
+ with pytest.raises(ValueError, match='must be at least 1-D'):
4306
+ func(*args, x=1.)
4307
+
4308
+
4309
+ # XXX: restore testing on CuPy, where possible. Multiple issues in this test:
4310
+ # 1. _zi functions deliberately incompatible in cupy
4311
+ # (https://github.com/scipy/scipy/pull/21713#issuecomment-2417494443)
4312
+ # 2. a CuPy issue to be fixed in 14.0 only
4313
+ # (https://github.com/cupy/cupy/pull/8677)
4314
+ # 3. an issue with CuPy's __array__ not numpy-2.0 compatible
4315
+ @skip_xp_backends(cpu_only=True)
4316
+ @pytest.mark.parametrize('dt', ['float32', 'float64', 'complex64', 'complex128'])
4317
+ class TestSOSFilt:
4318
+
4319
+ # The test_rank* tests are pulled from _TestLinearFilter
4320
+ @skip_xp_backends('jax.numpy', reason='buffer array is read-only')
4321
+ def test_rank1(self, dt, xp):
4322
+ dt = getattr(xp, dt)
4323
+ x = xp.linspace(0, 5, 6, dtype=dt)
4324
+ b = xp.asarray([1, -1], dtype=dt)
4325
+ a = xp.asarray([0.5, -0.5], dtype=dt)
4326
+
4327
+ # Test simple IIR
4328
+ y_r = xp.asarray([0, 2, 4, 6, 8, 10.], dtype=dt)
4329
+ bb, aa = map(np.asarray, (b, a))
4330
+ sos = tf2sos(bb, aa)
4331
+ sos = xp.asarray(sos) # XXX while tf2sos is numpy only
4332
+ assert_array_almost_equal(sosfilt(sos, x), y_r)
4333
+
4334
+ # Test simple FIR
4335
+ b = xp.asarray([1, 1], dtype=dt)
4336
+ # NOTE: This was changed (rel. to TestLinear...) to add a pole @zero:
4337
+ a = xp.asarray([1, 0], dtype=dt)
4338
+ y_r = xp.asarray([0, 1, 3, 5, 7, 9.], dtype=dt)
4339
+ bb, aa = map(np.asarray, (b, a))
4340
+ sos = tf2sos(bb, aa)
4341
+ sos = xp.asarray(sos) # XXX while tf2sos is numpy only
4342
+ assert_array_almost_equal(sosfilt(sos, x), y_r)
4343
+
4344
+ b = xp.asarray([1.0, 1, 0])
4345
+ a = xp.asarray([1.0, 0, 0])
4346
+ x = xp.ones(8)
4347
+
4348
+ sos = xp.concat((b, a))
4349
+ sos = xp.reshape(sos, (1, 6))
4350
+ y = sosfilt(sos, x)
4351
+ xp_assert_close(y, xp.asarray([1.0, 2, 2, 2, 2, 2, 2, 2]))
4352
+
4353
+ @skip_xp_backends('jax.numpy', reason='buffer array is read-only')
4354
+ def test_rank2(self, dt, xp):
4355
+ dt = getattr(xp, dt)
4356
+ shape = (4, 3)
4357
+ prodshape = math.prod(shape)
4358
+ x = xp.linspace(0, prodshape - 1, prodshape, dtype=dt)
4359
+ x = xp.reshape(x, shape)
4360
+
4361
+ b = xp.asarray([1, -1], dtype=dt)
4362
+ a = xp.asarray([0.5, 0.5], dtype=dt)
4363
+
4364
+ y_r2_a0 = xp.asarray([[0, 2, 4], [6, 4, 2], [0, 2, 4], [6, 4, 2]],
4365
+ dtype=dt)
4366
+
4367
+ y_r2_a1 = xp.asarray([[0, 2, 0], [6, -4, 6], [12, -10, 12],
4368
+ [18, -16, 18]], dtype=dt)
4369
+
4370
+ bb, aa = map(np.asarray, (b, a))
4371
+ sos = tf2sos(bb, aa)
4372
+ sos = xp.asarray(sos) # XXX
4373
+ y = sosfilt(sos, x, axis=0)
4374
+ assert_array_almost_equal(y_r2_a0, y)
4375
+
4376
+ sos = tf2sos(bb, aa)
4377
+ sos = xp.asarray(sos) # XXX
4378
+ y = sosfilt(sos, x, axis=1)
4379
+ assert_array_almost_equal(y_r2_a1, y)
4380
+
4381
+ @skip_xp_backends('jax.numpy', reason='buffer array is read-only')
4382
+ def test_rank3(self, dt, xp):
4383
+ dt = getattr(xp, dt)
4384
+ shape = (4, 3, 2)
4385
+ prodshape = math.prod(shape)
4386
+ x = xp.linspace(0, prodshape - 1, prodshape)
4387
+ x = xp.reshape(x, shape)
4388
+
4389
+ b = xp.asarray([1, -1], dtype=dt)
4390
+ a = xp.asarray([0.5, 0.5], dtype=dt)
4391
+
4392
+ # Test last axis
4393
+ bb, aa = map(np.asarray, (b, a)) # XXX until tf2sos is array api compatible
4394
+ sos = tf2sos(bb, aa)
4395
+ sos = xp.asarray(sos) # XXX
4396
+ y = sosfilt(sos, x)
4397
+ for i in range(x.shape[0]):
4398
+ for j in range(x.shape[1]):
4399
+ assert_array_almost_equal(y[i, j, ...], lfilter(b, a, x[i, j, ...]))
4400
+
4401
+ def _get_ab_sos(self, xp):
4402
+ b1, a1 = signal.butter(2, 0.25, 'low')
4403
+ b2, a2 = signal.butter(2, 0.75, 'low')
4404
+ b3, a3 = signal.butter(2, 0.75, 'low')
4405
+ b = np.convolve(np.convolve(b1, b2), b3)
4406
+ a = np.convolve(np.convolve(a1, a2), a3)
4407
+ sos = np.array((np.r_[b1, a1], np.r_[b2, a2], np.r_[b3, a3]))
4408
+
4409
+ a, b, sos = map(xp.asarray, (a, b, sos))
4410
+ return a, b, sos
4411
+
4412
+ @skip_xp_backends('jax.numpy', reason='item assignment')
4413
+ def test_initial_conditions(self, dt, xp):
4414
+ a, b, sos = self._get_ab_sos(xp)
4415
+
4416
+ x = np.random.rand(50).astype(dt)
4417
+ x = xp.asarray(x)
4418
+
4419
+ dt = getattr(xp, dt)
4420
+
4421
+ # Stopping filtering and continuing
4422
+ y_true, zi = lfilter(b, a, x[:20], zi=xp.zeros(6))
4423
+ y_true = xp.concat((y_true, lfilter(b, a, x[20:], zi=zi)[0]))
4424
+ xp_assert_close(y_true, lfilter(b, a, x))
4425
+
4426
+ y_sos, zi = sosfilt(sos, x[:20], zi=xp.zeros((3, 2)))
4427
+ y_sos = xp.concat((y_sos, sosfilt(sos, x[20:], zi=zi)[0]))
4428
+ xp_assert_close(y_true, y_sos)
4429
+
4430
+ # Use a step function
4431
+ zi = sosfilt_zi(sos)
4432
+ x = xp.ones(8, dtype=dt)
4433
+ y, zf = sosfilt(sos, x, zi=zi)
4434
+
4435
+ xp_assert_close(y, xp.ones(8), check_dtype=False)
4436
+ xp_assert_close(zf, zi, check_dtype=False)
4437
+
4438
+ @skip_xp_backends('jax.numpy', reason='item assignment')
4439
+ @skip_xp_backends('array_api_strict', reason='fancy indexing not supported')
4440
+ def test_initial_conditions_2(self, dt, xp):
4441
+ dt = getattr(xp, dt)
4442
+ x = xp.ones(8, dtype=dt)
4443
+
4444
+ _, _, sos = self._get_ab_sos(xp)
4445
+ zi = sosfilt_zi(sos)
4446
+
4447
+ # Initial condition shape matching
4448
+ x = xp.reshape(x, (1, 1) + x.shape) # 3D
4449
+ with pytest.raises(ValueError):
4450
+ sosfilt(sos, x, zi=zi)
4451
+
4452
+ zi_nd = xp_copy(zi, xp=xp)
4453
+ zi_nd = xp.reshape(zi_nd, (zi.shape[0], 1, 1, zi.shape[-1]))
4454
+
4455
+ with pytest.raises(ValueError):
4456
+ sosfilt(sos, x, zi=zi_nd[:, :, :, [0, 1, 1]])
4457
+
4458
+ y, zf = sosfilt(sos, x, zi=zi_nd)
4459
+ xp_assert_close(y[0, 0], xp.ones(8), check_dtype=False)
4460
+ xp_assert_close(zf[:, 0, 0, :], zi, check_dtype=False)
4461
+
4462
+ @skip_xp_backends('jax.numpy', reason='item assignment')
4463
+ def test_initial_conditions_3d_axis1(self, dt, xp):
4464
+ # Test the use of zi when sosfilt is applied to axis 1 of a 3-d input.
4465
+
4466
+ # Input array is x.
4467
+ x = np.random.RandomState(159).randint(0, 5, size=(2, 15, 3))
4468
+ x = x.astype(dt)
4469
+ x = xp.asarray(x)
4470
+
4471
+ # Design a filter in ZPK format and convert to SOS
4472
+ zpk = signal.butter(6, 0.35, output='zpk')
4473
+ sos = zpk2sos(*zpk)
4474
+ sos = xp.asarray(sos) # XXX while zpk2sos is numpy-only
4475
+
4476
+ nsections = sos.shape[0]
4477
+
4478
+ # Filter along this axis.
4479
+ axis = 1
4480
+
4481
+ # Initial conditions, all zeros.
4482
+ shp = list(x.shape)
4483
+ shp[axis] = 2
4484
+ shp = tuple([nsections] + shp)
4485
+ z0 = xp.zeros(shp)
4486
+
4487
+ # Apply the filter to x.
4488
+ yf, zf = sosfilt(sos, x, axis=axis, zi=z0)
4489
+
4490
+ # Apply the filter to x in two stages.
4491
+ y1, z1 = sosfilt(sos, x[:, :5, :], axis=axis, zi=z0)
4492
+ y2, z2 = sosfilt(sos, x[:, 5:, :], axis=axis, zi=z1)
4493
+
4494
+ # y should equal yf, and z2 should equal zf.
4495
+ y = xp.concat((y1, y2), axis=axis)
4496
+ xp_assert_close(y, yf, rtol=1e-10, atol=1e-13)
4497
+ xp_assert_close(z2, zf, rtol=1e-10, atol=1e-13)
4498
+
4499
+ # let's try the "step" initial condition
4500
+ zi = sosfilt_zi(sos)
4501
+ zi = xp.reshape(zi, (nsections, 1, 2, 1))
4502
+ zi = zi * x[:, 0:1, :]
4503
+ y = sosfilt(sos, x, axis=axis, zi=zi)[0]
4504
+ # check it against the TF form
4505
+ b, a = zpk2tf(*zpk)
4506
+ b, a = xp.asarray(b), xp.asarray(a) # XXX while zpk2tf is numpy-only
4507
+
4508
+ zi = lfilter_zi(b, a)
4509
+ zi = xp.reshape(zi, (1, xp_size(zi), 1))
4510
+ zi = zi * x[:, 0:1, :]
4511
+ y_tf = lfilter(b, a, x, axis=axis, zi=zi)[0]
4512
+ xp_assert_close(y, y_tf, rtol=1e-10, atol=1e-13)
4513
+
4514
+ @skip_xp_backends('torch', reason='issues a RuntimeWarning')
4515
+ @skip_xp_backends('jax.numpy', reason='item assignment')
4516
+ def test_bad_zi_shape(self, dt, xp):
4517
+ dt = getattr(xp, dt)
4518
+ # The shape of zi is checked before using any values in the
4519
+ # arguments, so np.empty is fine for creating the arguments.
4520
+ x = xp.empty((3, 15, 3), dtype=dt)
4521
+ sos = xp.zeros((4, 6))
4522
+ zi = xp.empty((4, 3, 3, 2)) # Correct shape is (4, 3, 2, 3)
4523
+ with pytest.raises(ValueError, match='should be all ones'):
4524
+ sosfilt(sos, x, zi=zi, axis=1)
4525
+ sos[:, 3] = 1.
4526
+ with pytest.raises(ValueError, match='Invalid zi shape'):
4527
+ sosfilt(sos, x, zi=zi, axis=1)
4528
+
4529
+ @skip_xp_backends('jax.numpy', reason='item assignment')
4530
+ def test_sosfilt_zi(self, dt, xp):
4531
+ dt = getattr(xp, dt)
4532
+ sos = signal.butter(6, 0.2, output='sos')
4533
+ sos = xp.asarray(sos) # XXX while butter is np-only
4534
+ zi = sosfilt_zi(sos)
4535
+
4536
+ y, zf = sosfilt(sos, xp.ones(40, dtype=dt), zi=zi)
4537
+ xp_assert_close(zf, zi, rtol=1e-13, check_dtype=False)
4538
+
4539
+ # Expected steady state value of the step response of this filter:
4540
+ ss = xp.prod(xp.sum(sos[:, :3], axis=-1) / xp.sum(sos[:, 3:], axis=-1))
4541
+ xp_assert_close(y, ss * xp.ones_like(y), rtol=1e-13)
4542
+
4543
+ @skip_xp_backends(np_only=True)
4544
+ def test_sosfilt_zi_2(self, dt, xp):
4545
+ # zi as array-like
4546
+ dt = getattr(xp, dt)
4547
+ sos = signal.butter(6, 0.2, output='sos')
4548
+ sos = xp.asarray(sos) # XXX while butter is np-only
4549
+ zi = sosfilt_zi(sos)
4550
+
4551
+ _, zf = sosfilt(sos, xp.ones(40, dtype=dt), zi=zi.tolist())
4552
+ xp_assert_close(zf, zi, rtol=1e-13, check_dtype=False)
4553
+
4554
+ @pytest.mark.thread_unsafe
4555
+ @skip_xp_backends(np_only=True)
4556
+ def test_dtype_deprecation(self, dt, xp):
4557
+ # gh-21211
4558
+ sos = np.asarray([1, 2, 3, 1, 5, 3], dtype=object).reshape(1, 6)
4559
+ x = np.asarray([2, 3, 4, 5, 3, 4, 2, 2, 1], dtype=object)
4560
+ with pytest.deprecated_call(match="dtype=object is not supported"):
4561
+ sosfilt(sos, x)
4562
+
4563
+
4564
+ @skip_xp_backends(cpu_only=True, reason='lfilter is CPU-only compiled code')
4565
+ @skip_xp_backends('jax.numpy', reason='item assignment')
4566
+ class TestDeconvolve:
4567
+
4568
+ @skip_xp_backends(np_only=True, reason="list inputs are numpy-specific")
4569
+ def test_array_like(self, xp):
4570
+ # From docstring example: with lists
4571
+ original = [0.0, 1, 0, 0, 1, 1, 0, 0]
4572
+ impulse_response = [2, 1]
4573
+ recorded = xp.asarray([0.0, 2, 1, 0, 2, 3, 1, 0, 0])
4574
+ recovered, remainder = signal.deconvolve(recorded, impulse_response)
4575
+ xp_assert_close(recovered, original)
4576
+
4577
+ def test_basic(self, xp):
4578
+ # From docstring example
4579
+ original = xp.asarray([0.0, 1, 0, 0, 1, 1, 0, 0], dtype=xp.float64)
4580
+ impulse_response = xp.asarray([2, 1])
4581
+ recorded = xp.asarray([0.0, 2, 1, 0, 2, 3, 1, 0, 0])
4582
+ recovered, remainder = signal.deconvolve(recorded, impulse_response)
4583
+ xp_assert_close(recovered, original)
4584
+
4585
+ def test_n_dimensional_signal(self, xp):
4586
+ recorded = xp.asarray([[0, 0], [0, 0]])
4587
+ impulse_response = xp.asarray([0, 0])
4588
+ with pytest.raises(ValueError, match="signal must be 1-D."):
4589
+ quotient, remainder = signal.deconvolve(recorded, impulse_response)
4590
+
4591
+ def test_n_dimensional_divisor(self, xp):
4592
+ recorded = xp.asarray([0, 0])
4593
+ impulse_response = xp.asarray([[0, 0], [0, 0]])
4594
+ with pytest.raises(ValueError, match="divisor must be 1-D."):
4595
+ quotient, remainder = signal.deconvolve(recorded, impulse_response)
4596
+
4597
+
4598
+ @skip_xp_backends(cpu_only=True, exceptions=['cupy'])
4599
+ class TestDetrend:
4600
+
4601
+ def test_basic(self, xp):
4602
+ detrended = detrend(xp.asarray([1, 2, 3]))
4603
+ detrended_exact = xp.asarray([0, 0, 0])
4604
+ assert_array_almost_equal(detrended, detrended_exact)
4605
+
4606
+ @skip_xp_backends("jax.numpy", reason="overwrite_data not implemented")
4607
+ def test_copy(self, xp):
4608
+ x = xp.asarray([1, 1.2, 1.5, 1.6, 2.4])
4609
+ copy_array = detrend(x, overwrite_data=False)
4610
+ inplace = detrend(x, overwrite_data=True)
4611
+ assert_array_almost_equal(copy_array, inplace)
4612
+
4613
+ @pytest.mark.parametrize('kind', ['linear', 'constant'])
4614
+ @pytest.mark.parametrize('axis', [0, 1, 2])
4615
+ def test_axis(self, axis, kind, xp):
4616
+ data = xp.reshape(xp.arange(5*6*7), (5, 6, 7))
4617
+ detrended = detrend(data, type=kind, axis=axis)
4618
+ assert detrended.shape == data.shape
4619
+
4620
+ def test_bp(self, xp):
4621
+ data = [0, 1, 2] + [5, 0, -5, -10]
4622
+ data = xp.asarray(data)
4623
+ detrended = detrend(data, type='linear', bp=3)
4624
+ xp_assert_close(detrended, xp.zeros_like(detrended), atol=1e-14)
4625
+
4626
+ # repeat with ndim > 1 and axis
4627
+ data = xp.asarray(data)[None, :, None]
4628
+
4629
+ detrended = detrend(data, type="linear", bp=3, axis=1)
4630
+ xp_assert_close(detrended, xp.zeros_like(detrended), atol=1e-14)
4631
+
4632
+ # breakpoint index > shape[axis]: raises
4633
+ with assert_raises(ValueError):
4634
+ detrend(data, type="linear", bp=3)
4635
+
4636
+ @pytest.mark.parametrize('bp', [np.array([0, 2]), [0, 2]])
4637
+ def test_detrend_array_bp(self, bp, xp):
4638
+ # regression test for https://github.com/scipy/scipy/issues/18675
4639
+ rng = np.random.RandomState(12345)
4640
+ x = rng.rand(10)
4641
+ x = xp.asarray(x, dtype=xp_default_dtype(xp))
4642
+ if isinstance(bp, np.ndarray):
4643
+ bp = xp.asarray(bp)
4644
+ else:
4645
+ if not is_numpy(xp):
4646
+ pytest.skip("list bp is numpy-only")
4647
+
4648
+ res = detrend(x, bp=bp)
4649
+ res_scipy_191 = xp.asarray([-4.44089210e-16, -2.22044605e-16,
4650
+ -1.11128506e-01, -1.69470553e-01, 1.14710683e-01, 6.35468419e-02,
4651
+ 3.53533144e-01, -3.67877935e-02, -2.00417675e-02, -1.94362049e-01])
4652
+
4653
+ atol = 3e-7 if xp_default_dtype(xp) == xp.float32 else 1e-14
4654
+ xp_assert_close(res, res_scipy_191, atol=atol)
4655
+
4656
+
4657
+ @skip_xp_backends(np_only=True)
4658
+ class TestUniqueRoots:
4659
+ def test_real_no_repeat(self, xp):
4660
+ p = [-1.0, -0.5, 0.3, 1.2, 10.0]
4661
+ unique, multiplicity = unique_roots(p)
4662
+ assert_almost_equal(unique, p, decimal=15)
4663
+ xp_assert_equal(multiplicity, np.ones(len(p), dtype=int))
4664
+
4665
+ def test_real_repeat(self, xp):
4666
+ p = [-1.0, -0.95, -0.89, -0.8, 0.5, 1.0, 1.05]
4667
+
4668
+ unique, multiplicity = unique_roots(p, tol=1e-1, rtype='min')
4669
+ assert_almost_equal(unique, [-1.0, -0.89, 0.5, 1.0], decimal=15)
4670
+ xp_assert_equal(multiplicity, [2, 2, 1, 2])
4671
+
4672
+ unique, multiplicity = unique_roots(p, tol=1e-1, rtype='max')
4673
+ assert_almost_equal(unique, [-0.95, -0.8, 0.5, 1.05], decimal=15)
4674
+ xp_assert_equal(multiplicity, [2, 2, 1, 2])
4675
+
4676
+ unique, multiplicity = unique_roots(p, tol=1e-1, rtype='avg')
4677
+ assert_almost_equal(unique, [-0.975, -0.845, 0.5, 1.025], decimal=15)
4678
+ xp_assert_equal(multiplicity, [2, 2, 1, 2])
4679
+
4680
+ def test_complex_no_repeat(self, xp):
4681
+ p = [-1.0, 1.0j, 0.5 + 0.5j, -1.0 - 1.0j, 3.0 + 2.0j]
4682
+ unique, multiplicity = unique_roots(p)
4683
+ assert_almost_equal(unique, p, decimal=15)
4684
+ xp_assert_equal(multiplicity, np.ones(len(p), dtype=int))
4685
+
4686
+ def test_complex_repeat(self, xp):
4687
+ p = [-1.0, -1.0 + 0.05j, -0.95 + 0.15j, -0.90 + 0.15j, 0.0,
4688
+ 0.5 + 0.5j, 0.45 + 0.55j]
4689
+
4690
+ unique, multiplicity = unique_roots(p, tol=1e-1, rtype='min')
4691
+ assert_almost_equal(unique, [-1.0, -0.95 + 0.15j, 0.0, 0.45 + 0.55j],
4692
+ decimal=15)
4693
+ xp_assert_equal(multiplicity, [2, 2, 1, 2])
4694
+
4695
+ unique, multiplicity = unique_roots(p, tol=1e-1, rtype='max')
4696
+ assert_almost_equal(unique,
4697
+ [-1.0 + 0.05j, -0.90 + 0.15j, 0.0, 0.5 + 0.5j],
4698
+ decimal=15)
4699
+ xp_assert_equal(multiplicity, [2, 2, 1, 2])
4700
+
4701
+ unique, multiplicity = unique_roots(p, tol=1e-1, rtype='avg')
4702
+ assert_almost_equal(
4703
+ unique, [-1.0 + 0.025j, -0.925 + 0.15j, 0.0, 0.475 + 0.525j],
4704
+ decimal=15)
4705
+ xp_assert_equal(multiplicity, [2, 2, 1, 2])
4706
+
4707
+ def test_gh_4915(self, xp):
4708
+ p = np.roots(np.convolve(np.ones(5), np.ones(5)))
4709
+ true_roots = [-(-1)**(1/5), (-1)**(4/5), -(-1)**(3/5), (-1)**(2/5)]
4710
+
4711
+ unique, multiplicity = unique_roots(p)
4712
+ unique = np.sort(unique)
4713
+
4714
+ assert_almost_equal(np.sort(unique), true_roots, decimal=7)
4715
+ xp_assert_equal(multiplicity, [2, 2, 2, 2])
4716
+
4717
+ def test_complex_roots_extra(self, xp):
4718
+ unique, multiplicity = unique_roots([1.0, 1.0j, 1.0])
4719
+ assert_almost_equal(unique, [1.0, 1.0j], decimal=15)
4720
+ xp_assert_equal(multiplicity, [2, 1])
4721
+
4722
+ unique, multiplicity = unique_roots([1, 1 + 2e-9, 1e-9 + 1j], tol=0.1)
4723
+ assert_almost_equal(unique, [1.0, 1e-9 + 1.0j], decimal=15)
4724
+ xp_assert_equal(multiplicity, [2, 1])
4725
+
4726
+ def test_single_unique_root(self, xp):
4727
+ p = np.random.rand(100) + 1j * np.random.rand(100)
4728
+ unique, multiplicity = unique_roots(p, 2)
4729
+ assert_almost_equal(unique, [np.min(p)], decimal=15)
4730
+ xp_assert_equal(multiplicity, [100])
4731
+
4732
+
4733
+ def test_gh_22684():
4734
+ actual = signal.resample_poly(np.arange(2000, dtype=np.complex64), 6, 4)
4735
+ assert actual.dtype == np.complex64