scipy 1.16.0rc1__cp313-cp313-macosx_10_14_x86_64.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 (1417) hide show
  1. f/344fyF/362/335(/371/226fF/256/225|+/224/223/252R/316hUk/257?/024/337/347/247/342G/375/001/271/356i/313}/306/313.X/322/327/v +0 -0
  2. scipy/.dylibs/libgcc_s.1.1.dylib +0 -0
  3. scipy/.dylibs/libgfortran.5.dylib +0 -0
  4. scipy/.dylibs/libquadmath.0.dylib +0 -0
  5. scipy/.dylibs/libscipy_openblas.dylib +0 -0
  6. scipy/__config__.py +161 -0
  7. scipy/__init__.py +138 -0
  8. scipy/_cyutility.cpython-313-darwin.so +0 -0
  9. scipy/_distributor_init.py +18 -0
  10. scipy/_lib/__init__.py +14 -0
  11. scipy/_lib/_array_api.py +931 -0
  12. scipy/_lib/_array_api_compat_vendor.py +9 -0
  13. scipy/_lib/_array_api_no_0d.py +103 -0
  14. scipy/_lib/_bunch.py +229 -0
  15. scipy/_lib/_ccallback.py +251 -0
  16. scipy/_lib/_ccallback_c.cpython-313-darwin.so +0 -0
  17. scipy/_lib/_disjoint_set.py +254 -0
  18. scipy/_lib/_docscrape.py +761 -0
  19. scipy/_lib/_elementwise_iterative_method.py +346 -0
  20. scipy/_lib/_fpumode.cpython-313-darwin.so +0 -0
  21. scipy/_lib/_gcutils.py +105 -0
  22. scipy/_lib/_pep440.py +487 -0
  23. scipy/_lib/_sparse.py +41 -0
  24. scipy/_lib/_test_ccallback.cpython-313-darwin.so +0 -0
  25. scipy/_lib/_test_deprecation_call.cpython-313-darwin.so +0 -0
  26. scipy/_lib/_test_deprecation_def.cpython-313-darwin.so +0 -0
  27. scipy/_lib/_testutils.py +373 -0
  28. scipy/_lib/_threadsafety.py +58 -0
  29. scipy/_lib/_tmpdirs.py +86 -0
  30. scipy/_lib/_uarray/LICENSE +29 -0
  31. scipy/_lib/_uarray/__init__.py +116 -0
  32. scipy/_lib/_uarray/_backend.py +707 -0
  33. scipy/_lib/_uarray/_uarray.cpython-313-darwin.so +0 -0
  34. scipy/_lib/_util.py +1276 -0
  35. scipy/_lib/array_api_compat/__init__.py +22 -0
  36. scipy/_lib/array_api_compat/_internal.py +59 -0
  37. scipy/_lib/array_api_compat/common/__init__.py +1 -0
  38. scipy/_lib/array_api_compat/common/_aliases.py +727 -0
  39. scipy/_lib/array_api_compat/common/_fft.py +213 -0
  40. scipy/_lib/array_api_compat/common/_helpers.py +1058 -0
  41. scipy/_lib/array_api_compat/common/_linalg.py +232 -0
  42. scipy/_lib/array_api_compat/common/_typing.py +192 -0
  43. scipy/_lib/array_api_compat/cupy/__init__.py +13 -0
  44. scipy/_lib/array_api_compat/cupy/_aliases.py +156 -0
  45. scipy/_lib/array_api_compat/cupy/_info.py +336 -0
  46. scipy/_lib/array_api_compat/cupy/_typing.py +31 -0
  47. scipy/_lib/array_api_compat/cupy/fft.py +36 -0
  48. scipy/_lib/array_api_compat/cupy/linalg.py +49 -0
  49. scipy/_lib/array_api_compat/dask/__init__.py +0 -0
  50. scipy/_lib/array_api_compat/dask/array/__init__.py +12 -0
  51. scipy/_lib/array_api_compat/dask/array/_aliases.py +376 -0
  52. scipy/_lib/array_api_compat/dask/array/_info.py +416 -0
  53. scipy/_lib/array_api_compat/dask/array/fft.py +21 -0
  54. scipy/_lib/array_api_compat/dask/array/linalg.py +72 -0
  55. scipy/_lib/array_api_compat/numpy/__init__.py +28 -0
  56. scipy/_lib/array_api_compat/numpy/_aliases.py +190 -0
  57. scipy/_lib/array_api_compat/numpy/_info.py +366 -0
  58. scipy/_lib/array_api_compat/numpy/_typing.py +30 -0
  59. scipy/_lib/array_api_compat/numpy/fft.py +35 -0
  60. scipy/_lib/array_api_compat/numpy/linalg.py +143 -0
  61. scipy/_lib/array_api_compat/torch/__init__.py +22 -0
  62. scipy/_lib/array_api_compat/torch/_aliases.py +855 -0
  63. scipy/_lib/array_api_compat/torch/_info.py +369 -0
  64. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  65. scipy/_lib/array_api_compat/torch/fft.py +85 -0
  66. scipy/_lib/array_api_compat/torch/linalg.py +121 -0
  67. scipy/_lib/array_api_extra/__init__.py +38 -0
  68. scipy/_lib/array_api_extra/_delegation.py +171 -0
  69. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  70. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  71. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  72. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  73. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  74. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  75. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  76. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  77. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  78. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  79. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  80. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  81. scipy/_lib/array_api_extra/testing.py +359 -0
  82. scipy/_lib/cobyqa/__init__.py +20 -0
  83. scipy/_lib/cobyqa/framework.py +1240 -0
  84. scipy/_lib/cobyqa/main.py +1506 -0
  85. scipy/_lib/cobyqa/models.py +1529 -0
  86. scipy/_lib/cobyqa/problem.py +1296 -0
  87. scipy/_lib/cobyqa/settings.py +132 -0
  88. scipy/_lib/cobyqa/subsolvers/__init__.py +14 -0
  89. scipy/_lib/cobyqa/subsolvers/geometry.py +387 -0
  90. scipy/_lib/cobyqa/subsolvers/optim.py +1203 -0
  91. scipy/_lib/cobyqa/utils/__init__.py +18 -0
  92. scipy/_lib/cobyqa/utils/exceptions.py +22 -0
  93. scipy/_lib/cobyqa/utils/math.py +77 -0
  94. scipy/_lib/cobyqa/utils/versions.py +67 -0
  95. scipy/_lib/decorator.py +399 -0
  96. scipy/_lib/deprecation.py +274 -0
  97. scipy/_lib/doccer.py +366 -0
  98. scipy/_lib/messagestream.cpython-313-darwin.so +0 -0
  99. scipy/_lib/pyprima/__init__.py +212 -0
  100. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  101. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  102. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  103. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  104. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  105. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  106. scipy/_lib/pyprima/cobyla/update.py +289 -0
  107. scipy/_lib/pyprima/common/__init__.py +0 -0
  108. scipy/_lib/pyprima/common/_bounds.py +34 -0
  109. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  110. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  111. scipy/_lib/pyprima/common/_project.py +173 -0
  112. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  113. scipy/_lib/pyprima/common/consts.py +47 -0
  114. scipy/_lib/pyprima/common/evaluate.py +99 -0
  115. scipy/_lib/pyprima/common/history.py +38 -0
  116. scipy/_lib/pyprima/common/infos.py +30 -0
  117. scipy/_lib/pyprima/common/linalg.py +435 -0
  118. scipy/_lib/pyprima/common/message.py +290 -0
  119. scipy/_lib/pyprima/common/powalg.py +131 -0
  120. scipy/_lib/pyprima/common/preproc.py +277 -0
  121. scipy/_lib/pyprima/common/present.py +5 -0
  122. scipy/_lib/pyprima/common/ratio.py +54 -0
  123. scipy/_lib/pyprima/common/redrho.py +47 -0
  124. scipy/_lib/pyprima/common/selectx.py +296 -0
  125. scipy/_lib/tests/__init__.py +0 -0
  126. scipy/_lib/tests/test__gcutils.py +110 -0
  127. scipy/_lib/tests/test__pep440.py +67 -0
  128. scipy/_lib/tests/test__testutils.py +32 -0
  129. scipy/_lib/tests/test__threadsafety.py +51 -0
  130. scipy/_lib/tests/test__util.py +641 -0
  131. scipy/_lib/tests/test_array_api.py +322 -0
  132. scipy/_lib/tests/test_bunch.py +169 -0
  133. scipy/_lib/tests/test_ccallback.py +196 -0
  134. scipy/_lib/tests/test_config.py +45 -0
  135. scipy/_lib/tests/test_deprecation.py +10 -0
  136. scipy/_lib/tests/test_doccer.py +143 -0
  137. scipy/_lib/tests/test_import_cycles.py +18 -0
  138. scipy/_lib/tests/test_public_api.py +482 -0
  139. scipy/_lib/tests/test_scipy_version.py +28 -0
  140. scipy/_lib/tests/test_tmpdirs.py +48 -0
  141. scipy/_lib/tests/test_warnings.py +137 -0
  142. scipy/_lib/uarray.py +31 -0
  143. scipy/cluster/__init__.py +31 -0
  144. scipy/cluster/_hierarchy.cpython-313-darwin.so +0 -0
  145. scipy/cluster/_optimal_leaf_ordering.cpython-313-darwin.so +0 -0
  146. scipy/cluster/_vq.cpython-313-darwin.so +0 -0
  147. scipy/cluster/hierarchy.py +4348 -0
  148. scipy/cluster/tests/__init__.py +0 -0
  149. scipy/cluster/tests/hierarchy_test_data.py +145 -0
  150. scipy/cluster/tests/test_disjoint_set.py +202 -0
  151. scipy/cluster/tests/test_hierarchy.py +1238 -0
  152. scipy/cluster/tests/test_vq.py +434 -0
  153. scipy/cluster/vq.py +832 -0
  154. scipy/conftest.py +658 -0
  155. scipy/constants/__init__.py +358 -0
  156. scipy/constants/_codata.py +2266 -0
  157. scipy/constants/_constants.py +369 -0
  158. scipy/constants/codata.py +21 -0
  159. scipy/constants/constants.py +53 -0
  160. scipy/constants/tests/__init__.py +0 -0
  161. scipy/constants/tests/test_codata.py +78 -0
  162. scipy/constants/tests/test_constants.py +83 -0
  163. scipy/datasets/__init__.py +90 -0
  164. scipy/datasets/_download_all.py +71 -0
  165. scipy/datasets/_fetchers.py +225 -0
  166. scipy/datasets/_registry.py +26 -0
  167. scipy/datasets/_utils.py +81 -0
  168. scipy/datasets/tests/__init__.py +0 -0
  169. scipy/datasets/tests/test_data.py +128 -0
  170. scipy/differentiate/__init__.py +27 -0
  171. scipy/differentiate/_differentiate.py +1129 -0
  172. scipy/differentiate/tests/__init__.py +0 -0
  173. scipy/differentiate/tests/test_differentiate.py +694 -0
  174. scipy/fft/__init__.py +114 -0
  175. scipy/fft/_backend.py +196 -0
  176. scipy/fft/_basic.py +1650 -0
  177. scipy/fft/_basic_backend.py +197 -0
  178. scipy/fft/_debug_backends.py +22 -0
  179. scipy/fft/_fftlog.py +223 -0
  180. scipy/fft/_fftlog_backend.py +200 -0
  181. scipy/fft/_helper.py +348 -0
  182. scipy/fft/_pocketfft/LICENSE.md +25 -0
  183. scipy/fft/_pocketfft/__init__.py +9 -0
  184. scipy/fft/_pocketfft/basic.py +251 -0
  185. scipy/fft/_pocketfft/helper.py +249 -0
  186. scipy/fft/_pocketfft/pypocketfft.cpython-313-darwin.so +0 -0
  187. scipy/fft/_pocketfft/realtransforms.py +109 -0
  188. scipy/fft/_pocketfft/tests/__init__.py +0 -0
  189. scipy/fft/_pocketfft/tests/test_basic.py +1011 -0
  190. scipy/fft/_pocketfft/tests/test_real_transforms.py +505 -0
  191. scipy/fft/_realtransforms.py +706 -0
  192. scipy/fft/_realtransforms_backend.py +63 -0
  193. scipy/fft/tests/__init__.py +0 -0
  194. scipy/fft/tests/mock_backend.py +96 -0
  195. scipy/fft/tests/test_backend.py +98 -0
  196. scipy/fft/tests/test_basic.py +504 -0
  197. scipy/fft/tests/test_fftlog.py +215 -0
  198. scipy/fft/tests/test_helper.py +558 -0
  199. scipy/fft/tests/test_multithreading.py +84 -0
  200. scipy/fft/tests/test_real_transforms.py +247 -0
  201. scipy/fftpack/__init__.py +103 -0
  202. scipy/fftpack/_basic.py +428 -0
  203. scipy/fftpack/_helper.py +115 -0
  204. scipy/fftpack/_pseudo_diffs.py +554 -0
  205. scipy/fftpack/_realtransforms.py +598 -0
  206. scipy/fftpack/basic.py +20 -0
  207. scipy/fftpack/convolve.cpython-313-darwin.so +0 -0
  208. scipy/fftpack/helper.py +19 -0
  209. scipy/fftpack/pseudo_diffs.py +22 -0
  210. scipy/fftpack/realtransforms.py +19 -0
  211. scipy/fftpack/tests/__init__.py +0 -0
  212. scipy/fftpack/tests/fftw_double_ref.npz +0 -0
  213. scipy/fftpack/tests/fftw_longdouble_ref.npz +0 -0
  214. scipy/fftpack/tests/fftw_single_ref.npz +0 -0
  215. scipy/fftpack/tests/test.npz +0 -0
  216. scipy/fftpack/tests/test_basic.py +877 -0
  217. scipy/fftpack/tests/test_helper.py +54 -0
  218. scipy/fftpack/tests/test_import.py +33 -0
  219. scipy/fftpack/tests/test_pseudo_diffs.py +388 -0
  220. scipy/fftpack/tests/test_real_transforms.py +836 -0
  221. scipy/integrate/__init__.py +122 -0
  222. scipy/integrate/_bvp.py +1160 -0
  223. scipy/integrate/_cubature.py +729 -0
  224. scipy/integrate/_dop.cpython-313-darwin.so +0 -0
  225. scipy/integrate/_ivp/__init__.py +8 -0
  226. scipy/integrate/_ivp/base.py +290 -0
  227. scipy/integrate/_ivp/bdf.py +478 -0
  228. scipy/integrate/_ivp/common.py +451 -0
  229. scipy/integrate/_ivp/dop853_coefficients.py +193 -0
  230. scipy/integrate/_ivp/ivp.py +755 -0
  231. scipy/integrate/_ivp/lsoda.py +224 -0
  232. scipy/integrate/_ivp/radau.py +572 -0
  233. scipy/integrate/_ivp/rk.py +601 -0
  234. scipy/integrate/_ivp/tests/__init__.py +0 -0
  235. scipy/integrate/_ivp/tests/test_ivp.py +1287 -0
  236. scipy/integrate/_ivp/tests/test_rk.py +37 -0
  237. scipy/integrate/_lebedev.py +5450 -0
  238. scipy/integrate/_lsoda.cpython-313-darwin.so +0 -0
  239. scipy/integrate/_ode.py +1395 -0
  240. scipy/integrate/_odepack.cpython-313-darwin.so +0 -0
  241. scipy/integrate/_odepack_py.py +273 -0
  242. scipy/integrate/_quad_vec.py +674 -0
  243. scipy/integrate/_quadpack.cpython-313-darwin.so +0 -0
  244. scipy/integrate/_quadpack_py.py +1283 -0
  245. scipy/integrate/_quadrature.py +1336 -0
  246. scipy/integrate/_rules/__init__.py +12 -0
  247. scipy/integrate/_rules/_base.py +518 -0
  248. scipy/integrate/_rules/_gauss_kronrod.py +202 -0
  249. scipy/integrate/_rules/_gauss_legendre.py +62 -0
  250. scipy/integrate/_rules/_genz_malik.py +210 -0
  251. scipy/integrate/_tanhsinh.py +1385 -0
  252. scipy/integrate/_test_multivariate.cpython-313-darwin.so +0 -0
  253. scipy/integrate/_test_odeint_banded.cpython-313-darwin.so +0 -0
  254. scipy/integrate/_vode.cpython-313-darwin.so +0 -0
  255. scipy/integrate/dop.py +15 -0
  256. scipy/integrate/lsoda.py +15 -0
  257. scipy/integrate/odepack.py +17 -0
  258. scipy/integrate/quadpack.py +23 -0
  259. scipy/integrate/tests/__init__.py +0 -0
  260. scipy/integrate/tests/test__quad_vec.py +211 -0
  261. scipy/integrate/tests/test_banded_ode_solvers.py +305 -0
  262. scipy/integrate/tests/test_bvp.py +714 -0
  263. scipy/integrate/tests/test_cubature.py +1375 -0
  264. scipy/integrate/tests/test_integrate.py +840 -0
  265. scipy/integrate/tests/test_odeint_jac.py +74 -0
  266. scipy/integrate/tests/test_quadpack.py +680 -0
  267. scipy/integrate/tests/test_quadrature.py +730 -0
  268. scipy/integrate/tests/test_tanhsinh.py +1171 -0
  269. scipy/integrate/vode.py +15 -0
  270. scipy/interpolate/__init__.py +228 -0
  271. scipy/interpolate/_bary_rational.py +715 -0
  272. scipy/interpolate/_bsplines.py +2469 -0
  273. scipy/interpolate/_cubic.py +973 -0
  274. scipy/interpolate/_dfitpack.cpython-313-darwin.so +0 -0
  275. scipy/interpolate/_dierckx.cpython-313-darwin.so +0 -0
  276. scipy/interpolate/_fitpack.cpython-313-darwin.so +0 -0
  277. scipy/interpolate/_fitpack2.py +2397 -0
  278. scipy/interpolate/_fitpack_impl.py +811 -0
  279. scipy/interpolate/_fitpack_py.py +898 -0
  280. scipy/interpolate/_fitpack_repro.py +996 -0
  281. scipy/interpolate/_interpnd.cpython-313-darwin.so +0 -0
  282. scipy/interpolate/_interpolate.py +2266 -0
  283. scipy/interpolate/_ndbspline.py +415 -0
  284. scipy/interpolate/_ndgriddata.py +329 -0
  285. scipy/interpolate/_pade.py +67 -0
  286. scipy/interpolate/_polyint.py +1025 -0
  287. scipy/interpolate/_ppoly.cpython-313-darwin.so +0 -0
  288. scipy/interpolate/_rbf.py +290 -0
  289. scipy/interpolate/_rbfinterp.py +550 -0
  290. scipy/interpolate/_rbfinterp_pythran.cpython-313-darwin.so +0 -0
  291. scipy/interpolate/_rgi.py +764 -0
  292. scipy/interpolate/_rgi_cython.cpython-313-darwin.so +0 -0
  293. scipy/interpolate/dfitpack.py +24 -0
  294. scipy/interpolate/fitpack.py +31 -0
  295. scipy/interpolate/fitpack2.py +29 -0
  296. scipy/interpolate/interpnd.py +24 -0
  297. scipy/interpolate/interpolate.py +30 -0
  298. scipy/interpolate/ndgriddata.py +23 -0
  299. scipy/interpolate/polyint.py +24 -0
  300. scipy/interpolate/rbf.py +18 -0
  301. scipy/interpolate/tests/__init__.py +0 -0
  302. scipy/interpolate/tests/data/bug-1310.npz +0 -0
  303. scipy/interpolate/tests/data/estimate_gradients_hang.npy +0 -0
  304. scipy/interpolate/tests/data/gcvspl.npz +0 -0
  305. scipy/interpolate/tests/test_bary_rational.py +368 -0
  306. scipy/interpolate/tests/test_bsplines.py +3754 -0
  307. scipy/interpolate/tests/test_fitpack.py +519 -0
  308. scipy/interpolate/tests/test_fitpack2.py +1431 -0
  309. scipy/interpolate/tests/test_gil.py +64 -0
  310. scipy/interpolate/tests/test_interpnd.py +452 -0
  311. scipy/interpolate/tests/test_interpolate.py +2630 -0
  312. scipy/interpolate/tests/test_ndgriddata.py +308 -0
  313. scipy/interpolate/tests/test_pade.py +107 -0
  314. scipy/interpolate/tests/test_polyint.py +972 -0
  315. scipy/interpolate/tests/test_rbf.py +246 -0
  316. scipy/interpolate/tests/test_rbfinterp.py +534 -0
  317. scipy/interpolate/tests/test_rgi.py +1151 -0
  318. scipy/io/__init__.py +116 -0
  319. scipy/io/_fast_matrix_market/__init__.py +600 -0
  320. scipy/io/_fast_matrix_market/_fmm_core.cpython-313-darwin.so +0 -0
  321. scipy/io/_fortran.py +354 -0
  322. scipy/io/_harwell_boeing/__init__.py +7 -0
  323. scipy/io/_harwell_boeing/_fortran_format_parser.py +316 -0
  324. scipy/io/_harwell_boeing/hb.py +571 -0
  325. scipy/io/_harwell_boeing/tests/__init__.py +0 -0
  326. scipy/io/_harwell_boeing/tests/test_fortran_format.py +74 -0
  327. scipy/io/_harwell_boeing/tests/test_hb.py +70 -0
  328. scipy/io/_idl.py +917 -0
  329. scipy/io/_mmio.py +968 -0
  330. scipy/io/_netcdf.py +1104 -0
  331. scipy/io/_test_fortran.cpython-313-darwin.so +0 -0
  332. scipy/io/arff/__init__.py +28 -0
  333. scipy/io/arff/_arffread.py +873 -0
  334. scipy/io/arff/arffread.py +19 -0
  335. scipy/io/arff/tests/__init__.py +0 -0
  336. scipy/io/arff/tests/data/iris.arff +225 -0
  337. scipy/io/arff/tests/data/missing.arff +8 -0
  338. scipy/io/arff/tests/data/nodata.arff +11 -0
  339. scipy/io/arff/tests/data/quoted_nominal.arff +13 -0
  340. scipy/io/arff/tests/data/quoted_nominal_spaces.arff +13 -0
  341. scipy/io/arff/tests/data/test1.arff +10 -0
  342. scipy/io/arff/tests/data/test10.arff +8 -0
  343. scipy/io/arff/tests/data/test11.arff +11 -0
  344. scipy/io/arff/tests/data/test2.arff +15 -0
  345. scipy/io/arff/tests/data/test3.arff +6 -0
  346. scipy/io/arff/tests/data/test4.arff +11 -0
  347. scipy/io/arff/tests/data/test5.arff +26 -0
  348. scipy/io/arff/tests/data/test6.arff +12 -0
  349. scipy/io/arff/tests/data/test7.arff +15 -0
  350. scipy/io/arff/tests/data/test8.arff +12 -0
  351. scipy/io/arff/tests/data/test9.arff +14 -0
  352. scipy/io/arff/tests/test_arffread.py +421 -0
  353. scipy/io/harwell_boeing.py +17 -0
  354. scipy/io/idl.py +17 -0
  355. scipy/io/matlab/__init__.py +66 -0
  356. scipy/io/matlab/_byteordercodes.py +75 -0
  357. scipy/io/matlab/_mio.py +375 -0
  358. scipy/io/matlab/_mio4.py +632 -0
  359. scipy/io/matlab/_mio5.py +901 -0
  360. scipy/io/matlab/_mio5_params.py +281 -0
  361. scipy/io/matlab/_mio5_utils.cpython-313-darwin.so +0 -0
  362. scipy/io/matlab/_mio_utils.cpython-313-darwin.so +0 -0
  363. scipy/io/matlab/_miobase.py +435 -0
  364. scipy/io/matlab/_streams.cpython-313-darwin.so +0 -0
  365. scipy/io/matlab/byteordercodes.py +17 -0
  366. scipy/io/matlab/mio.py +16 -0
  367. scipy/io/matlab/mio4.py +17 -0
  368. scipy/io/matlab/mio5.py +19 -0
  369. scipy/io/matlab/mio5_params.py +18 -0
  370. scipy/io/matlab/mio5_utils.py +17 -0
  371. scipy/io/matlab/mio_utils.py +17 -0
  372. scipy/io/matlab/miobase.py +16 -0
  373. scipy/io/matlab/streams.py +16 -0
  374. scipy/io/matlab/tests/__init__.py +0 -0
  375. scipy/io/matlab/tests/data/bad_miuint32.mat +0 -0
  376. scipy/io/matlab/tests/data/bad_miutf8_array_name.mat +0 -0
  377. scipy/io/matlab/tests/data/big_endian.mat +0 -0
  378. scipy/io/matlab/tests/data/broken_utf8.mat +0 -0
  379. scipy/io/matlab/tests/data/corrupted_zlib_checksum.mat +0 -0
  380. scipy/io/matlab/tests/data/corrupted_zlib_data.mat +0 -0
  381. scipy/io/matlab/tests/data/debigged_m4.mat +0 -0
  382. scipy/io/matlab/tests/data/japanese_utf8.txt +5 -0
  383. scipy/io/matlab/tests/data/little_endian.mat +0 -0
  384. scipy/io/matlab/tests/data/logical_sparse.mat +0 -0
  385. scipy/io/matlab/tests/data/malformed1.mat +0 -0
  386. scipy/io/matlab/tests/data/miuint32_for_miint32.mat +0 -0
  387. scipy/io/matlab/tests/data/miutf8_array_name.mat +0 -0
  388. scipy/io/matlab/tests/data/nasty_duplicate_fieldnames.mat +0 -0
  389. scipy/io/matlab/tests/data/one_by_zero_char.mat +0 -0
  390. scipy/io/matlab/tests/data/parabola.mat +0 -0
  391. scipy/io/matlab/tests/data/single_empty_string.mat +0 -0
  392. scipy/io/matlab/tests/data/some_functions.mat +0 -0
  393. scipy/io/matlab/tests/data/sqr.mat +0 -0
  394. scipy/io/matlab/tests/data/test3dmatrix_6.1_SOL2.mat +0 -0
  395. scipy/io/matlab/tests/data/test3dmatrix_6.5.1_GLNX86.mat +0 -0
  396. scipy/io/matlab/tests/data/test3dmatrix_7.1_GLNX86.mat +0 -0
  397. scipy/io/matlab/tests/data/test3dmatrix_7.4_GLNX86.mat +0 -0
  398. scipy/io/matlab/tests/data/test_empty_struct.mat +0 -0
  399. scipy/io/matlab/tests/data/test_mat4_le_floats.mat +0 -0
  400. scipy/io/matlab/tests/data/test_skip_variable.mat +0 -0
  401. scipy/io/matlab/tests/data/testbool_8_WIN64.mat +0 -0
  402. scipy/io/matlab/tests/data/testcell_6.1_SOL2.mat +0 -0
  403. scipy/io/matlab/tests/data/testcell_6.5.1_GLNX86.mat +0 -0
  404. scipy/io/matlab/tests/data/testcell_7.1_GLNX86.mat +0 -0
  405. scipy/io/matlab/tests/data/testcell_7.4_GLNX86.mat +0 -0
  406. scipy/io/matlab/tests/data/testcellnest_6.1_SOL2.mat +0 -0
  407. scipy/io/matlab/tests/data/testcellnest_6.5.1_GLNX86.mat +0 -0
  408. scipy/io/matlab/tests/data/testcellnest_7.1_GLNX86.mat +0 -0
  409. scipy/io/matlab/tests/data/testcellnest_7.4_GLNX86.mat +0 -0
  410. scipy/io/matlab/tests/data/testcomplex_4.2c_SOL2.mat +0 -0
  411. scipy/io/matlab/tests/data/testcomplex_6.1_SOL2.mat +0 -0
  412. scipy/io/matlab/tests/data/testcomplex_6.5.1_GLNX86.mat +0 -0
  413. scipy/io/matlab/tests/data/testcomplex_7.1_GLNX86.mat +0 -0
  414. scipy/io/matlab/tests/data/testcomplex_7.4_GLNX86.mat +0 -0
  415. scipy/io/matlab/tests/data/testdouble_4.2c_SOL2.mat +0 -0
  416. scipy/io/matlab/tests/data/testdouble_6.1_SOL2.mat +0 -0
  417. scipy/io/matlab/tests/data/testdouble_6.5.1_GLNX86.mat +0 -0
  418. scipy/io/matlab/tests/data/testdouble_7.1_GLNX86.mat +0 -0
  419. scipy/io/matlab/tests/data/testdouble_7.4_GLNX86.mat +0 -0
  420. scipy/io/matlab/tests/data/testemptycell_5.3_SOL2.mat +0 -0
  421. scipy/io/matlab/tests/data/testemptycell_6.5.1_GLNX86.mat +0 -0
  422. scipy/io/matlab/tests/data/testemptycell_7.1_GLNX86.mat +0 -0
  423. scipy/io/matlab/tests/data/testemptycell_7.4_GLNX86.mat +0 -0
  424. scipy/io/matlab/tests/data/testfunc_7.4_GLNX86.mat +0 -0
  425. scipy/io/matlab/tests/data/testhdf5_7.4_GLNX86.mat +0 -0
  426. scipy/io/matlab/tests/data/testmatrix_4.2c_SOL2.mat +0 -0
  427. scipy/io/matlab/tests/data/testmatrix_6.1_SOL2.mat +0 -0
  428. scipy/io/matlab/tests/data/testmatrix_6.5.1_GLNX86.mat +0 -0
  429. scipy/io/matlab/tests/data/testmatrix_7.1_GLNX86.mat +0 -0
  430. scipy/io/matlab/tests/data/testmatrix_7.4_GLNX86.mat +0 -0
  431. scipy/io/matlab/tests/data/testminus_4.2c_SOL2.mat +0 -0
  432. scipy/io/matlab/tests/data/testminus_6.1_SOL2.mat +0 -0
  433. scipy/io/matlab/tests/data/testminus_6.5.1_GLNX86.mat +0 -0
  434. scipy/io/matlab/tests/data/testminus_7.1_GLNX86.mat +0 -0
  435. scipy/io/matlab/tests/data/testminus_7.4_GLNX86.mat +0 -0
  436. scipy/io/matlab/tests/data/testmulti_4.2c_SOL2.mat +0 -0
  437. scipy/io/matlab/tests/data/testmulti_7.1_GLNX86.mat +0 -0
  438. scipy/io/matlab/tests/data/testmulti_7.4_GLNX86.mat +0 -0
  439. scipy/io/matlab/tests/data/testobject_6.1_SOL2.mat +0 -0
  440. scipy/io/matlab/tests/data/testobject_6.5.1_GLNX86.mat +0 -0
  441. scipy/io/matlab/tests/data/testobject_7.1_GLNX86.mat +0 -0
  442. scipy/io/matlab/tests/data/testobject_7.4_GLNX86.mat +0 -0
  443. scipy/io/matlab/tests/data/testonechar_4.2c_SOL2.mat +0 -0
  444. scipy/io/matlab/tests/data/testonechar_6.1_SOL2.mat +0 -0
  445. scipy/io/matlab/tests/data/testonechar_6.5.1_GLNX86.mat +0 -0
  446. scipy/io/matlab/tests/data/testonechar_7.1_GLNX86.mat +0 -0
  447. scipy/io/matlab/tests/data/testonechar_7.4_GLNX86.mat +0 -0
  448. scipy/io/matlab/tests/data/testscalarcell_7.4_GLNX86.mat +0 -0
  449. scipy/io/matlab/tests/data/testsimplecell.mat +0 -0
  450. scipy/io/matlab/tests/data/testsparse_4.2c_SOL2.mat +0 -0
  451. scipy/io/matlab/tests/data/testsparse_6.1_SOL2.mat +0 -0
  452. scipy/io/matlab/tests/data/testsparse_6.5.1_GLNX86.mat +0 -0
  453. scipy/io/matlab/tests/data/testsparse_7.1_GLNX86.mat +0 -0
  454. scipy/io/matlab/tests/data/testsparse_7.4_GLNX86.mat +0 -0
  455. scipy/io/matlab/tests/data/testsparsecomplex_4.2c_SOL2.mat +0 -0
  456. scipy/io/matlab/tests/data/testsparsecomplex_6.1_SOL2.mat +0 -0
  457. scipy/io/matlab/tests/data/testsparsecomplex_6.5.1_GLNX86.mat +0 -0
  458. scipy/io/matlab/tests/data/testsparsecomplex_7.1_GLNX86.mat +0 -0
  459. scipy/io/matlab/tests/data/testsparsecomplex_7.4_GLNX86.mat +0 -0
  460. scipy/io/matlab/tests/data/testsparsefloat_7.4_GLNX86.mat +0 -0
  461. scipy/io/matlab/tests/data/teststring_4.2c_SOL2.mat +0 -0
  462. scipy/io/matlab/tests/data/teststring_6.1_SOL2.mat +0 -0
  463. scipy/io/matlab/tests/data/teststring_6.5.1_GLNX86.mat +0 -0
  464. scipy/io/matlab/tests/data/teststring_7.1_GLNX86.mat +0 -0
  465. scipy/io/matlab/tests/data/teststring_7.4_GLNX86.mat +0 -0
  466. scipy/io/matlab/tests/data/teststringarray_4.2c_SOL2.mat +0 -0
  467. scipy/io/matlab/tests/data/teststringarray_6.1_SOL2.mat +0 -0
  468. scipy/io/matlab/tests/data/teststringarray_6.5.1_GLNX86.mat +0 -0
  469. scipy/io/matlab/tests/data/teststringarray_7.1_GLNX86.mat +0 -0
  470. scipy/io/matlab/tests/data/teststringarray_7.4_GLNX86.mat +0 -0
  471. scipy/io/matlab/tests/data/teststruct_6.1_SOL2.mat +0 -0
  472. scipy/io/matlab/tests/data/teststruct_6.5.1_GLNX86.mat +0 -0
  473. scipy/io/matlab/tests/data/teststruct_7.1_GLNX86.mat +0 -0
  474. scipy/io/matlab/tests/data/teststruct_7.4_GLNX86.mat +0 -0
  475. scipy/io/matlab/tests/data/teststructarr_6.1_SOL2.mat +0 -0
  476. scipy/io/matlab/tests/data/teststructarr_6.5.1_GLNX86.mat +0 -0
  477. scipy/io/matlab/tests/data/teststructarr_7.1_GLNX86.mat +0 -0
  478. scipy/io/matlab/tests/data/teststructarr_7.4_GLNX86.mat +0 -0
  479. scipy/io/matlab/tests/data/teststructnest_6.1_SOL2.mat +0 -0
  480. scipy/io/matlab/tests/data/teststructnest_6.5.1_GLNX86.mat +0 -0
  481. scipy/io/matlab/tests/data/teststructnest_7.1_GLNX86.mat +0 -0
  482. scipy/io/matlab/tests/data/teststructnest_7.4_GLNX86.mat +0 -0
  483. scipy/io/matlab/tests/data/testunicode_7.1_GLNX86.mat +0 -0
  484. scipy/io/matlab/tests/data/testunicode_7.4_GLNX86.mat +0 -0
  485. scipy/io/matlab/tests/data/testvec_4_GLNX86.mat +0 -0
  486. scipy/io/matlab/tests/test_byteordercodes.py +29 -0
  487. scipy/io/matlab/tests/test_mio.py +1399 -0
  488. scipy/io/matlab/tests/test_mio5_utils.py +179 -0
  489. scipy/io/matlab/tests/test_mio_funcs.py +51 -0
  490. scipy/io/matlab/tests/test_mio_utils.py +45 -0
  491. scipy/io/matlab/tests/test_miobase.py +32 -0
  492. scipy/io/matlab/tests/test_pathological.py +33 -0
  493. scipy/io/matlab/tests/test_streams.py +232 -0
  494. scipy/io/mmio.py +17 -0
  495. scipy/io/netcdf.py +17 -0
  496. scipy/io/tests/__init__.py +0 -0
  497. scipy/io/tests/data/Transparent Busy.ani +0 -0
  498. scipy/io/tests/data/array_float32_1d.sav +0 -0
  499. scipy/io/tests/data/array_float32_2d.sav +0 -0
  500. scipy/io/tests/data/array_float32_3d.sav +0 -0
  501. scipy/io/tests/data/array_float32_4d.sav +0 -0
  502. scipy/io/tests/data/array_float32_5d.sav +0 -0
  503. scipy/io/tests/data/array_float32_6d.sav +0 -0
  504. scipy/io/tests/data/array_float32_7d.sav +0 -0
  505. scipy/io/tests/data/array_float32_8d.sav +0 -0
  506. scipy/io/tests/data/array_float32_pointer_1d.sav +0 -0
  507. scipy/io/tests/data/array_float32_pointer_2d.sav +0 -0
  508. scipy/io/tests/data/array_float32_pointer_3d.sav +0 -0
  509. scipy/io/tests/data/array_float32_pointer_4d.sav +0 -0
  510. scipy/io/tests/data/array_float32_pointer_5d.sav +0 -0
  511. scipy/io/tests/data/array_float32_pointer_6d.sav +0 -0
  512. scipy/io/tests/data/array_float32_pointer_7d.sav +0 -0
  513. scipy/io/tests/data/array_float32_pointer_8d.sav +0 -0
  514. scipy/io/tests/data/example_1.nc +0 -0
  515. scipy/io/tests/data/example_2.nc +0 -0
  516. scipy/io/tests/data/example_3_maskedvals.nc +0 -0
  517. scipy/io/tests/data/fortran-3x3d-2i.dat +0 -0
  518. scipy/io/tests/data/fortran-mixed.dat +0 -0
  519. scipy/io/tests/data/fortran-sf8-11x1x10.dat +0 -0
  520. scipy/io/tests/data/fortran-sf8-15x10x22.dat +0 -0
  521. scipy/io/tests/data/fortran-sf8-1x1x1.dat +0 -0
  522. scipy/io/tests/data/fortran-sf8-1x1x5.dat +0 -0
  523. scipy/io/tests/data/fortran-sf8-1x1x7.dat +0 -0
  524. scipy/io/tests/data/fortran-sf8-1x3x5.dat +0 -0
  525. scipy/io/tests/data/fortran-si4-11x1x10.dat +0 -0
  526. scipy/io/tests/data/fortran-si4-15x10x22.dat +0 -0
  527. scipy/io/tests/data/fortran-si4-1x1x1.dat +0 -0
  528. scipy/io/tests/data/fortran-si4-1x1x5.dat +0 -0
  529. scipy/io/tests/data/fortran-si4-1x1x7.dat +0 -0
  530. scipy/io/tests/data/fortran-si4-1x3x5.dat +0 -0
  531. scipy/io/tests/data/invalid_pointer.sav +0 -0
  532. scipy/io/tests/data/null_pointer.sav +0 -0
  533. scipy/io/tests/data/scalar_byte.sav +0 -0
  534. scipy/io/tests/data/scalar_byte_descr.sav +0 -0
  535. scipy/io/tests/data/scalar_complex32.sav +0 -0
  536. scipy/io/tests/data/scalar_complex64.sav +0 -0
  537. scipy/io/tests/data/scalar_float32.sav +0 -0
  538. scipy/io/tests/data/scalar_float64.sav +0 -0
  539. scipy/io/tests/data/scalar_heap_pointer.sav +0 -0
  540. scipy/io/tests/data/scalar_int16.sav +0 -0
  541. scipy/io/tests/data/scalar_int32.sav +0 -0
  542. scipy/io/tests/data/scalar_int64.sav +0 -0
  543. scipy/io/tests/data/scalar_string.sav +0 -0
  544. scipy/io/tests/data/scalar_uint16.sav +0 -0
  545. scipy/io/tests/data/scalar_uint32.sav +0 -0
  546. scipy/io/tests/data/scalar_uint64.sav +0 -0
  547. scipy/io/tests/data/struct_arrays.sav +0 -0
  548. scipy/io/tests/data/struct_arrays_byte_idl80.sav +0 -0
  549. scipy/io/tests/data/struct_arrays_replicated.sav +0 -0
  550. scipy/io/tests/data/struct_arrays_replicated_3d.sav +0 -0
  551. scipy/io/tests/data/struct_inherit.sav +0 -0
  552. scipy/io/tests/data/struct_pointer_arrays.sav +0 -0
  553. scipy/io/tests/data/struct_pointer_arrays_replicated.sav +0 -0
  554. scipy/io/tests/data/struct_pointer_arrays_replicated_3d.sav +0 -0
  555. scipy/io/tests/data/struct_pointers.sav +0 -0
  556. scipy/io/tests/data/struct_pointers_replicated.sav +0 -0
  557. scipy/io/tests/data/struct_pointers_replicated_3d.sav +0 -0
  558. scipy/io/tests/data/struct_scalars.sav +0 -0
  559. scipy/io/tests/data/struct_scalars_replicated.sav +0 -0
  560. scipy/io/tests/data/struct_scalars_replicated_3d.sav +0 -0
  561. scipy/io/tests/data/test-1234Hz-le-1ch-10S-20bit-extra.wav +0 -0
  562. scipy/io/tests/data/test-44100Hz-2ch-32bit-float-be.wav +0 -0
  563. scipy/io/tests/data/test-44100Hz-2ch-32bit-float-le.wav +0 -0
  564. scipy/io/tests/data/test-44100Hz-be-1ch-4bytes.wav +0 -0
  565. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof-no-data.wav +0 -0
  566. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-early-eof.wav +0 -0
  567. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-incomplete-chunk.wav +0 -0
  568. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes-rf64.wav +0 -0
  569. scipy/io/tests/data/test-44100Hz-le-1ch-4bytes.wav +0 -0
  570. scipy/io/tests/data/test-48000Hz-2ch-64bit-float-le-wavex.wav +0 -0
  571. scipy/io/tests/data/test-8000Hz-be-3ch-5S-24bit.wav +0 -0
  572. scipy/io/tests/data/test-8000Hz-le-1ch-1byte-ulaw.wav +0 -0
  573. scipy/io/tests/data/test-8000Hz-le-2ch-1byteu.wav +0 -0
  574. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-inconsistent.wav +0 -0
  575. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit-rf64.wav +0 -0
  576. scipy/io/tests/data/test-8000Hz-le-3ch-5S-24bit.wav +0 -0
  577. scipy/io/tests/data/test-8000Hz-le-3ch-5S-36bit.wav +0 -0
  578. scipy/io/tests/data/test-8000Hz-le-3ch-5S-45bit.wav +0 -0
  579. scipy/io/tests/data/test-8000Hz-le-3ch-5S-53bit.wav +0 -0
  580. scipy/io/tests/data/test-8000Hz-le-3ch-5S-64bit.wav +0 -0
  581. scipy/io/tests/data/test-8000Hz-le-4ch-9S-12bit.wav +0 -0
  582. scipy/io/tests/data/test-8000Hz-le-5ch-9S-5bit.wav +0 -0
  583. scipy/io/tests/data/various_compressed.sav +0 -0
  584. scipy/io/tests/test_fortran.py +264 -0
  585. scipy/io/tests/test_idl.py +483 -0
  586. scipy/io/tests/test_mmio.py +831 -0
  587. scipy/io/tests/test_netcdf.py +550 -0
  588. scipy/io/tests/test_paths.py +93 -0
  589. scipy/io/tests/test_wavfile.py +501 -0
  590. scipy/io/wavfile.py +938 -0
  591. scipy/linalg/__init__.pxd +1 -0
  592. scipy/linalg/__init__.py +236 -0
  593. scipy/linalg/_basic.py +2146 -0
  594. scipy/linalg/_blas_subroutines.h +164 -0
  595. scipy/linalg/_cythonized_array_utils.cpython-313-darwin.so +0 -0
  596. scipy/linalg/_cythonized_array_utils.pxd +40 -0
  597. scipy/linalg/_cythonized_array_utils.pyi +16 -0
  598. scipy/linalg/_decomp.py +1645 -0
  599. scipy/linalg/_decomp_cholesky.py +413 -0
  600. scipy/linalg/_decomp_cossin.py +236 -0
  601. scipy/linalg/_decomp_interpolative.cpython-313-darwin.so +0 -0
  602. scipy/linalg/_decomp_ldl.py +356 -0
  603. scipy/linalg/_decomp_lu.py +401 -0
  604. scipy/linalg/_decomp_lu_cython.cpython-313-darwin.so +0 -0
  605. scipy/linalg/_decomp_lu_cython.pyi +6 -0
  606. scipy/linalg/_decomp_polar.py +113 -0
  607. scipy/linalg/_decomp_qr.py +494 -0
  608. scipy/linalg/_decomp_qz.py +452 -0
  609. scipy/linalg/_decomp_schur.py +336 -0
  610. scipy/linalg/_decomp_svd.py +545 -0
  611. scipy/linalg/_decomp_update.cpython-313-darwin.so +0 -0
  612. scipy/linalg/_expm_frechet.py +417 -0
  613. scipy/linalg/_fblas.cpython-313-darwin.so +0 -0
  614. scipy/linalg/_flapack.cpython-313-darwin.so +0 -0
  615. scipy/linalg/_lapack_subroutines.h +1521 -0
  616. scipy/linalg/_linalg_pythran.cpython-313-darwin.so +0 -0
  617. scipy/linalg/_matfuncs.py +1050 -0
  618. scipy/linalg/_matfuncs_expm.cpython-313-darwin.so +0 -0
  619. scipy/linalg/_matfuncs_expm.pyi +6 -0
  620. scipy/linalg/_matfuncs_inv_ssq.py +886 -0
  621. scipy/linalg/_matfuncs_schur_sqrtm.cpython-313-darwin.so +0 -0
  622. scipy/linalg/_matfuncs_sqrtm.py +107 -0
  623. scipy/linalg/_matfuncs_sqrtm_triu.cpython-313-darwin.so +0 -0
  624. scipy/linalg/_misc.py +191 -0
  625. scipy/linalg/_procrustes.py +113 -0
  626. scipy/linalg/_sketches.py +189 -0
  627. scipy/linalg/_solve_toeplitz.cpython-313-darwin.so +0 -0
  628. scipy/linalg/_solvers.py +862 -0
  629. scipy/linalg/_special_matrices.py +1322 -0
  630. scipy/linalg/_testutils.py +65 -0
  631. scipy/linalg/basic.py +23 -0
  632. scipy/linalg/blas.py +484 -0
  633. scipy/linalg/cython_blas.cpython-313-darwin.so +0 -0
  634. scipy/linalg/cython_blas.pxd +169 -0
  635. scipy/linalg/cython_blas.pyx +1432 -0
  636. scipy/linalg/cython_lapack.cpython-313-darwin.so +0 -0
  637. scipy/linalg/cython_lapack.pxd +1528 -0
  638. scipy/linalg/cython_lapack.pyx +12045 -0
  639. scipy/linalg/decomp.py +23 -0
  640. scipy/linalg/decomp_cholesky.py +21 -0
  641. scipy/linalg/decomp_lu.py +21 -0
  642. scipy/linalg/decomp_qr.py +20 -0
  643. scipy/linalg/decomp_schur.py +21 -0
  644. scipy/linalg/decomp_svd.py +21 -0
  645. scipy/linalg/interpolative.py +989 -0
  646. scipy/linalg/lapack.py +1081 -0
  647. scipy/linalg/matfuncs.py +23 -0
  648. scipy/linalg/misc.py +21 -0
  649. scipy/linalg/special_matrices.py +22 -0
  650. scipy/linalg/tests/__init__.py +0 -0
  651. scipy/linalg/tests/_cython_examples/extending.pyx +23 -0
  652. scipy/linalg/tests/_cython_examples/meson.build +34 -0
  653. scipy/linalg/tests/data/carex_15_data.npz +0 -0
  654. scipy/linalg/tests/data/carex_18_data.npz +0 -0
  655. scipy/linalg/tests/data/carex_19_data.npz +0 -0
  656. scipy/linalg/tests/data/carex_20_data.npz +0 -0
  657. scipy/linalg/tests/data/carex_6_data.npz +0 -0
  658. scipy/linalg/tests/data/gendare_20170120_data.npz +0 -0
  659. scipy/linalg/tests/test_basic.py +2074 -0
  660. scipy/linalg/tests/test_batch.py +588 -0
  661. scipy/linalg/tests/test_blas.py +1127 -0
  662. scipy/linalg/tests/test_cython_blas.py +118 -0
  663. scipy/linalg/tests/test_cython_lapack.py +22 -0
  664. scipy/linalg/tests/test_cythonized_array_utils.py +130 -0
  665. scipy/linalg/tests/test_decomp.py +3189 -0
  666. scipy/linalg/tests/test_decomp_cholesky.py +268 -0
  667. scipy/linalg/tests/test_decomp_cossin.py +314 -0
  668. scipy/linalg/tests/test_decomp_ldl.py +137 -0
  669. scipy/linalg/tests/test_decomp_lu.py +308 -0
  670. scipy/linalg/tests/test_decomp_polar.py +110 -0
  671. scipy/linalg/tests/test_decomp_update.py +1701 -0
  672. scipy/linalg/tests/test_extending.py +46 -0
  673. scipy/linalg/tests/test_fblas.py +607 -0
  674. scipy/linalg/tests/test_interpolative.py +232 -0
  675. scipy/linalg/tests/test_lapack.py +3616 -0
  676. scipy/linalg/tests/test_matfuncs.py +1118 -0
  677. scipy/linalg/tests/test_matmul_toeplitz.py +136 -0
  678. scipy/linalg/tests/test_procrustes.py +214 -0
  679. scipy/linalg/tests/test_sketches.py +118 -0
  680. scipy/linalg/tests/test_solve_toeplitz.py +150 -0
  681. scipy/linalg/tests/test_solvers.py +844 -0
  682. scipy/linalg/tests/test_special_matrices.py +636 -0
  683. scipy/misc/__init__.py +6 -0
  684. scipy/misc/common.py +6 -0
  685. scipy/misc/doccer.py +6 -0
  686. scipy/ndimage/__init__.py +174 -0
  687. scipy/ndimage/_ctest.cpython-313-darwin.so +0 -0
  688. scipy/ndimage/_cytest.cpython-313-darwin.so +0 -0
  689. scipy/ndimage/_delegators.py +303 -0
  690. scipy/ndimage/_filters.py +2393 -0
  691. scipy/ndimage/_fourier.py +306 -0
  692. scipy/ndimage/_interpolation.py +1033 -0
  693. scipy/ndimage/_measurements.py +1689 -0
  694. scipy/ndimage/_morphology.py +2634 -0
  695. scipy/ndimage/_nd_image.cpython-313-darwin.so +0 -0
  696. scipy/ndimage/_ndimage_api.py +16 -0
  697. scipy/ndimage/_ni_docstrings.py +214 -0
  698. scipy/ndimage/_ni_label.cpython-313-darwin.so +0 -0
  699. scipy/ndimage/_ni_support.py +139 -0
  700. scipy/ndimage/_rank_filter_1d.cpython-313-darwin.so +0 -0
  701. scipy/ndimage/_support_alternative_backends.py +84 -0
  702. scipy/ndimage/filters.py +27 -0
  703. scipy/ndimage/fourier.py +21 -0
  704. scipy/ndimage/interpolation.py +22 -0
  705. scipy/ndimage/measurements.py +24 -0
  706. scipy/ndimage/morphology.py +27 -0
  707. scipy/ndimage/tests/__init__.py +12 -0
  708. scipy/ndimage/tests/data/label_inputs.txt +21 -0
  709. scipy/ndimage/tests/data/label_results.txt +294 -0
  710. scipy/ndimage/tests/data/label_strels.txt +42 -0
  711. scipy/ndimage/tests/dots.png +0 -0
  712. scipy/ndimage/tests/test_c_api.py +102 -0
  713. scipy/ndimage/tests/test_datatypes.py +67 -0
  714. scipy/ndimage/tests/test_filters.py +2998 -0
  715. scipy/ndimage/tests/test_fourier.py +187 -0
  716. scipy/ndimage/tests/test_interpolation.py +1491 -0
  717. scipy/ndimage/tests/test_measurements.py +1592 -0
  718. scipy/ndimage/tests/test_morphology.py +2950 -0
  719. scipy/ndimage/tests/test_ni_support.py +78 -0
  720. scipy/ndimage/tests/test_splines.py +70 -0
  721. scipy/odr/__init__.py +131 -0
  722. scipy/odr/__odrpack.cpython-313-darwin.so +0 -0
  723. scipy/odr/_add_newdocs.py +34 -0
  724. scipy/odr/_models.py +315 -0
  725. scipy/odr/_odrpack.py +1154 -0
  726. scipy/odr/models.py +20 -0
  727. scipy/odr/odrpack.py +21 -0
  728. scipy/odr/tests/__init__.py +0 -0
  729. scipy/odr/tests/test_odr.py +607 -0
  730. scipy/optimize/__init__.pxd +1 -0
  731. scipy/optimize/__init__.py +460 -0
  732. scipy/optimize/_basinhopping.py +741 -0
  733. scipy/optimize/_bglu_dense.cpython-313-darwin.so +0 -0
  734. scipy/optimize/_bracket.py +706 -0
  735. scipy/optimize/_chandrupatla.py +551 -0
  736. scipy/optimize/_cobyla_py.py +297 -0
  737. scipy/optimize/_cobyqa_py.py +72 -0
  738. scipy/optimize/_constraints.py +598 -0
  739. scipy/optimize/_dcsrch.py +728 -0
  740. scipy/optimize/_differentiable_functions.py +835 -0
  741. scipy/optimize/_differentialevolution.py +1970 -0
  742. scipy/optimize/_direct.cpython-313-darwin.so +0 -0
  743. scipy/optimize/_direct_py.py +280 -0
  744. scipy/optimize/_dual_annealing.py +732 -0
  745. scipy/optimize/_elementwise.py +798 -0
  746. scipy/optimize/_group_columns.cpython-313-darwin.so +0 -0
  747. scipy/optimize/_hessian_update_strategy.py +479 -0
  748. scipy/optimize/_highspy/__init__.py +0 -0
  749. scipy/optimize/_highspy/_core.cpython-313-darwin.so +0 -0
  750. scipy/optimize/_highspy/_highs_options.cpython-313-darwin.so +0 -0
  751. scipy/optimize/_highspy/_highs_wrapper.py +338 -0
  752. scipy/optimize/_isotonic.py +157 -0
  753. scipy/optimize/_lbfgsb.cpython-313-darwin.so +0 -0
  754. scipy/optimize/_lbfgsb_py.py +619 -0
  755. scipy/optimize/_linesearch.py +896 -0
  756. scipy/optimize/_linprog.py +733 -0
  757. scipy/optimize/_linprog_doc.py +1434 -0
  758. scipy/optimize/_linprog_highs.py +422 -0
  759. scipy/optimize/_linprog_ip.py +1141 -0
  760. scipy/optimize/_linprog_rs.py +572 -0
  761. scipy/optimize/_linprog_simplex.py +663 -0
  762. scipy/optimize/_linprog_util.py +1521 -0
  763. scipy/optimize/_lsap.cpython-313-darwin.so +0 -0
  764. scipy/optimize/_lsq/__init__.py +5 -0
  765. scipy/optimize/_lsq/bvls.py +183 -0
  766. scipy/optimize/_lsq/common.py +731 -0
  767. scipy/optimize/_lsq/dogbox.py +345 -0
  768. scipy/optimize/_lsq/givens_elimination.cpython-313-darwin.so +0 -0
  769. scipy/optimize/_lsq/least_squares.py +1044 -0
  770. scipy/optimize/_lsq/lsq_linear.py +361 -0
  771. scipy/optimize/_lsq/trf.py +587 -0
  772. scipy/optimize/_lsq/trf_linear.py +249 -0
  773. scipy/optimize/_milp.py +394 -0
  774. scipy/optimize/_minimize.py +1200 -0
  775. scipy/optimize/_minpack.cpython-313-darwin.so +0 -0
  776. scipy/optimize/_minpack_py.py +1178 -0
  777. scipy/optimize/_moduleTNC.cpython-313-darwin.so +0 -0
  778. scipy/optimize/_nnls.py +96 -0
  779. scipy/optimize/_nonlin.py +1634 -0
  780. scipy/optimize/_numdiff.py +963 -0
  781. scipy/optimize/_optimize.py +4169 -0
  782. scipy/optimize/_pava_pybind.cpython-313-darwin.so +0 -0
  783. scipy/optimize/_qap.py +760 -0
  784. scipy/optimize/_remove_redundancy.py +522 -0
  785. scipy/optimize/_root.py +732 -0
  786. scipy/optimize/_root_scalar.py +538 -0
  787. scipy/optimize/_shgo.py +1606 -0
  788. scipy/optimize/_shgo_lib/__init__.py +0 -0
  789. scipy/optimize/_shgo_lib/_complex.py +1225 -0
  790. scipy/optimize/_shgo_lib/_vertex.py +460 -0
  791. scipy/optimize/_slsqp_py.py +603 -0
  792. scipy/optimize/_slsqplib.cpython-313-darwin.so +0 -0
  793. scipy/optimize/_spectral.py +260 -0
  794. scipy/optimize/_tnc.py +438 -0
  795. scipy/optimize/_trlib/__init__.py +12 -0
  796. scipy/optimize/_trlib/_trlib.cpython-313-darwin.so +0 -0
  797. scipy/optimize/_trustregion.py +318 -0
  798. scipy/optimize/_trustregion_constr/__init__.py +6 -0
  799. scipy/optimize/_trustregion_constr/canonical_constraint.py +390 -0
  800. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +231 -0
  801. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +584 -0
  802. scipy/optimize/_trustregion_constr/projections.py +411 -0
  803. scipy/optimize/_trustregion_constr/qp_subproblem.py +637 -0
  804. scipy/optimize/_trustregion_constr/report.py +49 -0
  805. scipy/optimize/_trustregion_constr/tests/__init__.py +0 -0
  806. scipy/optimize/_trustregion_constr/tests/test_canonical_constraint.py +296 -0
  807. scipy/optimize/_trustregion_constr/tests/test_nested_minimize.py +39 -0
  808. scipy/optimize/_trustregion_constr/tests/test_projections.py +214 -0
  809. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +645 -0
  810. scipy/optimize/_trustregion_constr/tests/test_report.py +34 -0
  811. scipy/optimize/_trustregion_constr/tr_interior_point.py +361 -0
  812. scipy/optimize/_trustregion_dogleg.py +122 -0
  813. scipy/optimize/_trustregion_exact.py +437 -0
  814. scipy/optimize/_trustregion_krylov.py +65 -0
  815. scipy/optimize/_trustregion_ncg.py +126 -0
  816. scipy/optimize/_tstutils.py +972 -0
  817. scipy/optimize/_zeros.cpython-313-darwin.so +0 -0
  818. scipy/optimize/_zeros_py.py +1475 -0
  819. scipy/optimize/cobyla.py +19 -0
  820. scipy/optimize/cython_optimize/__init__.py +133 -0
  821. scipy/optimize/cython_optimize/_zeros.cpython-313-darwin.so +0 -0
  822. scipy/optimize/cython_optimize/_zeros.pxd +33 -0
  823. scipy/optimize/cython_optimize/c_zeros.pxd +26 -0
  824. scipy/optimize/cython_optimize.pxd +11 -0
  825. scipy/optimize/elementwise.py +38 -0
  826. scipy/optimize/lbfgsb.py +23 -0
  827. scipy/optimize/linesearch.py +18 -0
  828. scipy/optimize/minpack.py +27 -0
  829. scipy/optimize/minpack2.py +17 -0
  830. scipy/optimize/moduleTNC.py +19 -0
  831. scipy/optimize/nonlin.py +29 -0
  832. scipy/optimize/optimize.py +40 -0
  833. scipy/optimize/slsqp.py +22 -0
  834. scipy/optimize/tests/__init__.py +0 -0
  835. scipy/optimize/tests/_cython_examples/extending.pyx +43 -0
  836. scipy/optimize/tests/_cython_examples/meson.build +32 -0
  837. scipy/optimize/tests/test__basinhopping.py +535 -0
  838. scipy/optimize/tests/test__differential_evolution.py +1703 -0
  839. scipy/optimize/tests/test__dual_annealing.py +416 -0
  840. scipy/optimize/tests/test__linprog_clean_inputs.py +312 -0
  841. scipy/optimize/tests/test__numdiff.py +885 -0
  842. scipy/optimize/tests/test__remove_redundancy.py +228 -0
  843. scipy/optimize/tests/test__root.py +124 -0
  844. scipy/optimize/tests/test__shgo.py +1164 -0
  845. scipy/optimize/tests/test__spectral.py +226 -0
  846. scipy/optimize/tests/test_bracket.py +896 -0
  847. scipy/optimize/tests/test_chandrupatla.py +982 -0
  848. scipy/optimize/tests/test_cobyla.py +195 -0
  849. scipy/optimize/tests/test_cobyqa.py +252 -0
  850. scipy/optimize/tests/test_constraint_conversion.py +286 -0
  851. scipy/optimize/tests/test_constraints.py +255 -0
  852. scipy/optimize/tests/test_cython_optimize.py +92 -0
  853. scipy/optimize/tests/test_differentiable_functions.py +1025 -0
  854. scipy/optimize/tests/test_direct.py +321 -0
  855. scipy/optimize/tests/test_extending.py +28 -0
  856. scipy/optimize/tests/test_hessian_update_strategy.py +300 -0
  857. scipy/optimize/tests/test_isotonic_regression.py +167 -0
  858. scipy/optimize/tests/test_lbfgsb_hessinv.py +65 -0
  859. scipy/optimize/tests/test_lbfgsb_setulb.py +122 -0
  860. scipy/optimize/tests/test_least_squares.py +986 -0
  861. scipy/optimize/tests/test_linear_assignment.py +116 -0
  862. scipy/optimize/tests/test_linesearch.py +328 -0
  863. scipy/optimize/tests/test_linprog.py +2577 -0
  864. scipy/optimize/tests/test_lsq_common.py +297 -0
  865. scipy/optimize/tests/test_lsq_linear.py +287 -0
  866. scipy/optimize/tests/test_milp.py +459 -0
  867. scipy/optimize/tests/test_minimize_constrained.py +845 -0
  868. scipy/optimize/tests/test_minpack.py +1194 -0
  869. scipy/optimize/tests/test_nnls.py +469 -0
  870. scipy/optimize/tests/test_nonlin.py +572 -0
  871. scipy/optimize/tests/test_optimize.py +3335 -0
  872. scipy/optimize/tests/test_quadratic_assignment.py +455 -0
  873. scipy/optimize/tests/test_regression.py +40 -0
  874. scipy/optimize/tests/test_slsqp.py +645 -0
  875. scipy/optimize/tests/test_tnc.py +345 -0
  876. scipy/optimize/tests/test_trustregion.py +110 -0
  877. scipy/optimize/tests/test_trustregion_exact.py +351 -0
  878. scipy/optimize/tests/test_trustregion_krylov.py +170 -0
  879. scipy/optimize/tests/test_zeros.py +998 -0
  880. scipy/optimize/tnc.py +22 -0
  881. scipy/optimize/zeros.py +26 -0
  882. scipy/signal/__init__.py +316 -0
  883. scipy/signal/_arraytools.py +264 -0
  884. scipy/signal/_czt.py +575 -0
  885. scipy/signal/_delegators.py +568 -0
  886. scipy/signal/_filter_design.py +5881 -0
  887. scipy/signal/_fir_filter_design.py +1458 -0
  888. scipy/signal/_lti_conversion.py +534 -0
  889. scipy/signal/_ltisys.py +3546 -0
  890. scipy/signal/_max_len_seq.py +139 -0
  891. scipy/signal/_max_len_seq_inner.cpython-313-darwin.so +0 -0
  892. scipy/signal/_peak_finding.py +1310 -0
  893. scipy/signal/_peak_finding_utils.cpython-313-darwin.so +0 -0
  894. scipy/signal/_polyutils.py +172 -0
  895. scipy/signal/_savitzky_golay.py +357 -0
  896. scipy/signal/_short_time_fft.py +2187 -0
  897. scipy/signal/_signal_api.py +30 -0
  898. scipy/signal/_signaltools.py +5309 -0
  899. scipy/signal/_sigtools.cpython-313-darwin.so +0 -0
  900. scipy/signal/_sosfilt.cpython-313-darwin.so +0 -0
  901. scipy/signal/_spectral_py.py +2462 -0
  902. scipy/signal/_spline.cpython-313-darwin.so +0 -0
  903. scipy/signal/_spline.pyi +34 -0
  904. scipy/signal/_spline_filters.py +848 -0
  905. scipy/signal/_support_alternative_backends.py +73 -0
  906. scipy/signal/_upfirdn.py +219 -0
  907. scipy/signal/_upfirdn_apply.cpython-313-darwin.so +0 -0
  908. scipy/signal/_waveforms.py +687 -0
  909. scipy/signal/_wavelets.py +29 -0
  910. scipy/signal/bsplines.py +21 -0
  911. scipy/signal/filter_design.py +28 -0
  912. scipy/signal/fir_filter_design.py +21 -0
  913. scipy/signal/lti_conversion.py +20 -0
  914. scipy/signal/ltisys.py +25 -0
  915. scipy/signal/signaltools.py +27 -0
  916. scipy/signal/spectral.py +21 -0
  917. scipy/signal/spline.py +18 -0
  918. scipy/signal/tests/__init__.py +0 -0
  919. scipy/signal/tests/_scipy_spectral_test_shim.py +311 -0
  920. scipy/signal/tests/mpsig.py +122 -0
  921. scipy/signal/tests/test_array_tools.py +111 -0
  922. scipy/signal/tests/test_bsplines.py +365 -0
  923. scipy/signal/tests/test_cont2discrete.py +424 -0
  924. scipy/signal/tests/test_czt.py +221 -0
  925. scipy/signal/tests/test_dltisys.py +599 -0
  926. scipy/signal/tests/test_filter_design.py +4725 -0
  927. scipy/signal/tests/test_fir_filter_design.py +846 -0
  928. scipy/signal/tests/test_ltisys.py +1225 -0
  929. scipy/signal/tests/test_max_len_seq.py +71 -0
  930. scipy/signal/tests/test_peak_finding.py +915 -0
  931. scipy/signal/tests/test_result_type.py +51 -0
  932. scipy/signal/tests/test_savitzky_golay.py +363 -0
  933. scipy/signal/tests/test_short_time_fft.py +1098 -0
  934. scipy/signal/tests/test_signaltools.py +4729 -0
  935. scipy/signal/tests/test_spectral.py +2072 -0
  936. scipy/signal/tests/test_splines.py +427 -0
  937. scipy/signal/tests/test_upfirdn.py +322 -0
  938. scipy/signal/tests/test_waveforms.py +400 -0
  939. scipy/signal/tests/test_wavelets.py +59 -0
  940. scipy/signal/tests/test_windows.py +987 -0
  941. scipy/signal/waveforms.py +20 -0
  942. scipy/signal/wavelets.py +17 -0
  943. scipy/signal/windows/__init__.py +52 -0
  944. scipy/signal/windows/_windows.py +2513 -0
  945. scipy/signal/windows/windows.py +23 -0
  946. scipy/sparse/__init__.py +350 -0
  947. scipy/sparse/_base.py +1610 -0
  948. scipy/sparse/_bsr.py +880 -0
  949. scipy/sparse/_compressed.py +1328 -0
  950. scipy/sparse/_construct.py +1454 -0
  951. scipy/sparse/_coo.py +1581 -0
  952. scipy/sparse/_csc.py +367 -0
  953. scipy/sparse/_csparsetools.cpython-313-darwin.so +0 -0
  954. scipy/sparse/_csr.py +558 -0
  955. scipy/sparse/_data.py +569 -0
  956. scipy/sparse/_dia.py +677 -0
  957. scipy/sparse/_dok.py +669 -0
  958. scipy/sparse/_extract.py +178 -0
  959. scipy/sparse/_index.py +444 -0
  960. scipy/sparse/_lil.py +632 -0
  961. scipy/sparse/_matrix.py +169 -0
  962. scipy/sparse/_matrix_io.py +167 -0
  963. scipy/sparse/_sparsetools.cpython-313-darwin.so +0 -0
  964. scipy/sparse/_spfuncs.py +76 -0
  965. scipy/sparse/_sputils.py +632 -0
  966. scipy/sparse/base.py +24 -0
  967. scipy/sparse/bsr.py +22 -0
  968. scipy/sparse/compressed.py +20 -0
  969. scipy/sparse/construct.py +38 -0
  970. scipy/sparse/coo.py +23 -0
  971. scipy/sparse/csc.py +22 -0
  972. scipy/sparse/csgraph/__init__.py +210 -0
  973. scipy/sparse/csgraph/_flow.cpython-313-darwin.so +0 -0
  974. scipy/sparse/csgraph/_laplacian.py +563 -0
  975. scipy/sparse/csgraph/_matching.cpython-313-darwin.so +0 -0
  976. scipy/sparse/csgraph/_min_spanning_tree.cpython-313-darwin.so +0 -0
  977. scipy/sparse/csgraph/_reordering.cpython-313-darwin.so +0 -0
  978. scipy/sparse/csgraph/_shortest_path.cpython-313-darwin.so +0 -0
  979. scipy/sparse/csgraph/_tools.cpython-313-darwin.so +0 -0
  980. scipy/sparse/csgraph/_traversal.cpython-313-darwin.so +0 -0
  981. scipy/sparse/csgraph/_validation.py +66 -0
  982. scipy/sparse/csgraph/tests/__init__.py +0 -0
  983. scipy/sparse/csgraph/tests/test_connected_components.py +119 -0
  984. scipy/sparse/csgraph/tests/test_conversions.py +61 -0
  985. scipy/sparse/csgraph/tests/test_flow.py +209 -0
  986. scipy/sparse/csgraph/tests/test_graph_laplacian.py +368 -0
  987. scipy/sparse/csgraph/tests/test_matching.py +307 -0
  988. scipy/sparse/csgraph/tests/test_pydata_sparse.py +197 -0
  989. scipy/sparse/csgraph/tests/test_reordering.py +70 -0
  990. scipy/sparse/csgraph/tests/test_shortest_path.py +540 -0
  991. scipy/sparse/csgraph/tests/test_spanning_tree.py +66 -0
  992. scipy/sparse/csgraph/tests/test_traversal.py +148 -0
  993. scipy/sparse/csr.py +22 -0
  994. scipy/sparse/data.py +18 -0
  995. scipy/sparse/dia.py +22 -0
  996. scipy/sparse/dok.py +22 -0
  997. scipy/sparse/extract.py +23 -0
  998. scipy/sparse/lil.py +22 -0
  999. scipy/sparse/linalg/__init__.py +148 -0
  1000. scipy/sparse/linalg/_dsolve/__init__.py +71 -0
  1001. scipy/sparse/linalg/_dsolve/_add_newdocs.py +147 -0
  1002. scipy/sparse/linalg/_dsolve/_superlu.cpython-313-darwin.so +0 -0
  1003. scipy/sparse/linalg/_dsolve/linsolve.py +882 -0
  1004. scipy/sparse/linalg/_dsolve/tests/__init__.py +0 -0
  1005. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +928 -0
  1006. scipy/sparse/linalg/_eigen/__init__.py +22 -0
  1007. scipy/sparse/linalg/_eigen/_svds.py +540 -0
  1008. scipy/sparse/linalg/_eigen/_svds_doc.py +382 -0
  1009. scipy/sparse/linalg/_eigen/arpack/COPYING +45 -0
  1010. scipy/sparse/linalg/_eigen/arpack/__init__.py +20 -0
  1011. scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-313-darwin.so +0 -0
  1012. scipy/sparse/linalg/_eigen/arpack/arpack.py +1706 -0
  1013. scipy/sparse/linalg/_eigen/arpack/tests/__init__.py +0 -0
  1014. scipy/sparse/linalg/_eigen/arpack/tests/test_arpack.py +717 -0
  1015. scipy/sparse/linalg/_eigen/lobpcg/__init__.py +16 -0
  1016. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +1110 -0
  1017. scipy/sparse/linalg/_eigen/lobpcg/tests/__init__.py +0 -0
  1018. scipy/sparse/linalg/_eigen/lobpcg/tests/test_lobpcg.py +725 -0
  1019. scipy/sparse/linalg/_eigen/tests/__init__.py +0 -0
  1020. scipy/sparse/linalg/_eigen/tests/test_svds.py +886 -0
  1021. scipy/sparse/linalg/_expm_multiply.py +816 -0
  1022. scipy/sparse/linalg/_interface.py +920 -0
  1023. scipy/sparse/linalg/_isolve/__init__.py +20 -0
  1024. scipy/sparse/linalg/_isolve/_gcrotmk.py +503 -0
  1025. scipy/sparse/linalg/_isolve/iterative.py +1051 -0
  1026. scipy/sparse/linalg/_isolve/lgmres.py +230 -0
  1027. scipy/sparse/linalg/_isolve/lsmr.py +486 -0
  1028. scipy/sparse/linalg/_isolve/lsqr.py +589 -0
  1029. scipy/sparse/linalg/_isolve/minres.py +372 -0
  1030. scipy/sparse/linalg/_isolve/tests/__init__.py +0 -0
  1031. scipy/sparse/linalg/_isolve/tests/test_gcrotmk.py +183 -0
  1032. scipy/sparse/linalg/_isolve/tests/test_iterative.py +809 -0
  1033. scipy/sparse/linalg/_isolve/tests/test_lgmres.py +225 -0
  1034. scipy/sparse/linalg/_isolve/tests/test_lsmr.py +185 -0
  1035. scipy/sparse/linalg/_isolve/tests/test_lsqr.py +120 -0
  1036. scipy/sparse/linalg/_isolve/tests/test_minres.py +97 -0
  1037. scipy/sparse/linalg/_isolve/tests/test_utils.py +9 -0
  1038. scipy/sparse/linalg/_isolve/tfqmr.py +179 -0
  1039. scipy/sparse/linalg/_isolve/utils.py +121 -0
  1040. scipy/sparse/linalg/_matfuncs.py +940 -0
  1041. scipy/sparse/linalg/_norm.py +195 -0
  1042. scipy/sparse/linalg/_onenormest.py +467 -0
  1043. scipy/sparse/linalg/_propack/_cpropack.cpython-313-darwin.so +0 -0
  1044. scipy/sparse/linalg/_propack/_dpropack.cpython-313-darwin.so +0 -0
  1045. scipy/sparse/linalg/_propack/_spropack.cpython-313-darwin.so +0 -0
  1046. scipy/sparse/linalg/_propack/_zpropack.cpython-313-darwin.so +0 -0
  1047. scipy/sparse/linalg/_special_sparse_arrays.py +949 -0
  1048. scipy/sparse/linalg/_svdp.py +309 -0
  1049. scipy/sparse/linalg/dsolve.py +22 -0
  1050. scipy/sparse/linalg/eigen.py +21 -0
  1051. scipy/sparse/linalg/interface.py +20 -0
  1052. scipy/sparse/linalg/isolve.py +22 -0
  1053. scipy/sparse/linalg/matfuncs.py +18 -0
  1054. scipy/sparse/linalg/tests/__init__.py +0 -0
  1055. scipy/sparse/linalg/tests/propack_test_data.npz +0 -0
  1056. scipy/sparse/linalg/tests/test_expm_multiply.py +367 -0
  1057. scipy/sparse/linalg/tests/test_interface.py +561 -0
  1058. scipy/sparse/linalg/tests/test_matfuncs.py +592 -0
  1059. scipy/sparse/linalg/tests/test_norm.py +154 -0
  1060. scipy/sparse/linalg/tests/test_onenormest.py +252 -0
  1061. scipy/sparse/linalg/tests/test_propack.py +165 -0
  1062. scipy/sparse/linalg/tests/test_pydata_sparse.py +272 -0
  1063. scipy/sparse/linalg/tests/test_special_sparse_arrays.py +337 -0
  1064. scipy/sparse/sparsetools.py +17 -0
  1065. scipy/sparse/spfuncs.py +17 -0
  1066. scipy/sparse/sputils.py +17 -0
  1067. scipy/sparse/tests/__init__.py +0 -0
  1068. scipy/sparse/tests/data/csc_py2.npz +0 -0
  1069. scipy/sparse/tests/data/csc_py3.npz +0 -0
  1070. scipy/sparse/tests/test_arithmetic1d.py +341 -0
  1071. scipy/sparse/tests/test_array_api.py +561 -0
  1072. scipy/sparse/tests/test_base.py +5860 -0
  1073. scipy/sparse/tests/test_common1d.py +447 -0
  1074. scipy/sparse/tests/test_construct.py +872 -0
  1075. scipy/sparse/tests/test_coo.py +1119 -0
  1076. scipy/sparse/tests/test_csc.py +98 -0
  1077. scipy/sparse/tests/test_csr.py +214 -0
  1078. scipy/sparse/tests/test_dok.py +209 -0
  1079. scipy/sparse/tests/test_extract.py +51 -0
  1080. scipy/sparse/tests/test_indexing1d.py +603 -0
  1081. scipy/sparse/tests/test_matrix_io.py +109 -0
  1082. scipy/sparse/tests/test_minmax1d.py +128 -0
  1083. scipy/sparse/tests/test_sparsetools.py +344 -0
  1084. scipy/sparse/tests/test_spfuncs.py +97 -0
  1085. scipy/sparse/tests/test_sputils.py +424 -0
  1086. scipy/spatial/__init__.py +129 -0
  1087. scipy/spatial/_ckdtree.cpython-313-darwin.so +0 -0
  1088. scipy/spatial/_distance_pybind.cpython-313-darwin.so +0 -0
  1089. scipy/spatial/_distance_wrap.cpython-313-darwin.so +0 -0
  1090. scipy/spatial/_geometric_slerp.py +238 -0
  1091. scipy/spatial/_hausdorff.cpython-313-darwin.so +0 -0
  1092. scipy/spatial/_kdtree.py +920 -0
  1093. scipy/spatial/_plotutils.py +274 -0
  1094. scipy/spatial/_procrustes.py +132 -0
  1095. scipy/spatial/_qhull.cpython-313-darwin.so +0 -0
  1096. scipy/spatial/_qhull.pyi +213 -0
  1097. scipy/spatial/_spherical_voronoi.py +341 -0
  1098. scipy/spatial/_voronoi.cpython-313-darwin.so +0 -0
  1099. scipy/spatial/_voronoi.pyi +4 -0
  1100. scipy/spatial/ckdtree.py +18 -0
  1101. scipy/spatial/distance.py +3147 -0
  1102. scipy/spatial/distance.pyi +210 -0
  1103. scipy/spatial/kdtree.py +25 -0
  1104. scipy/spatial/qhull.py +25 -0
  1105. scipy/spatial/tests/__init__.py +0 -0
  1106. scipy/spatial/tests/data/cdist-X1.txt +10 -0
  1107. scipy/spatial/tests/data/cdist-X2.txt +20 -0
  1108. scipy/spatial/tests/data/degenerate_pointset.npz +0 -0
  1109. scipy/spatial/tests/data/iris.txt +150 -0
  1110. scipy/spatial/tests/data/pdist-boolean-inp.txt +20 -0
  1111. scipy/spatial/tests/data/pdist-chebyshev-ml-iris.txt +1 -0
  1112. scipy/spatial/tests/data/pdist-chebyshev-ml.txt +1 -0
  1113. scipy/spatial/tests/data/pdist-cityblock-ml-iris.txt +1 -0
  1114. scipy/spatial/tests/data/pdist-cityblock-ml.txt +1 -0
  1115. scipy/spatial/tests/data/pdist-correlation-ml-iris.txt +1 -0
  1116. scipy/spatial/tests/data/pdist-correlation-ml.txt +1 -0
  1117. scipy/spatial/tests/data/pdist-cosine-ml-iris.txt +1 -0
  1118. scipy/spatial/tests/data/pdist-cosine-ml.txt +1 -0
  1119. scipy/spatial/tests/data/pdist-double-inp.txt +20 -0
  1120. scipy/spatial/tests/data/pdist-euclidean-ml-iris.txt +1 -0
  1121. scipy/spatial/tests/data/pdist-euclidean-ml.txt +1 -0
  1122. scipy/spatial/tests/data/pdist-hamming-ml.txt +1 -0
  1123. scipy/spatial/tests/data/pdist-jaccard-ml.txt +1 -0
  1124. scipy/spatial/tests/data/pdist-jensenshannon-ml-iris.txt +1 -0
  1125. scipy/spatial/tests/data/pdist-jensenshannon-ml.txt +1 -0
  1126. scipy/spatial/tests/data/pdist-minkowski-3.2-ml-iris.txt +1 -0
  1127. scipy/spatial/tests/data/pdist-minkowski-3.2-ml.txt +1 -0
  1128. scipy/spatial/tests/data/pdist-minkowski-5.8-ml-iris.txt +1 -0
  1129. scipy/spatial/tests/data/pdist-seuclidean-ml-iris.txt +1 -0
  1130. scipy/spatial/tests/data/pdist-seuclidean-ml.txt +1 -0
  1131. scipy/spatial/tests/data/pdist-spearman-ml.txt +1 -0
  1132. scipy/spatial/tests/data/random-bool-data.txt +100 -0
  1133. scipy/spatial/tests/data/random-double-data.txt +100 -0
  1134. scipy/spatial/tests/data/random-int-data.txt +100 -0
  1135. scipy/spatial/tests/data/random-uint-data.txt +100 -0
  1136. scipy/spatial/tests/data/selfdual-4d-polytope.txt +27 -0
  1137. scipy/spatial/tests/test__plotutils.py +91 -0
  1138. scipy/spatial/tests/test__procrustes.py +116 -0
  1139. scipy/spatial/tests/test_distance.py +2376 -0
  1140. scipy/spatial/tests/test_hausdorff.py +199 -0
  1141. scipy/spatial/tests/test_kdtree.py +1536 -0
  1142. scipy/spatial/tests/test_qhull.py +1313 -0
  1143. scipy/spatial/tests/test_slerp.py +417 -0
  1144. scipy/spatial/tests/test_spherical_voronoi.py +358 -0
  1145. scipy/spatial/transform/__init__.py +31 -0
  1146. scipy/spatial/transform/_rigid_transform.cpython-313-darwin.so +0 -0
  1147. scipy/spatial/transform/_rotation.cpython-313-darwin.so +0 -0
  1148. scipy/spatial/transform/_rotation_groups.py +140 -0
  1149. scipy/spatial/transform/_rotation_spline.py +460 -0
  1150. scipy/spatial/transform/rotation.py +21 -0
  1151. scipy/spatial/transform/tests/__init__.py +0 -0
  1152. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  1153. scipy/spatial/transform/tests/test_rotation.py +2569 -0
  1154. scipy/spatial/transform/tests/test_rotation_groups.py +169 -0
  1155. scipy/spatial/transform/tests/test_rotation_spline.py +183 -0
  1156. scipy/special/__init__.pxd +1 -0
  1157. scipy/special/__init__.py +841 -0
  1158. scipy/special/_add_newdocs.py +9961 -0
  1159. scipy/special/_basic.py +3576 -0
  1160. scipy/special/_comb.cpython-313-darwin.so +0 -0
  1161. scipy/special/_ellip_harm.py +214 -0
  1162. scipy/special/_ellip_harm_2.cpython-313-darwin.so +0 -0
  1163. scipy/special/_gufuncs.cpython-313-darwin.so +0 -0
  1164. scipy/special/_input_validation.py +17 -0
  1165. scipy/special/_lambertw.py +149 -0
  1166. scipy/special/_logsumexp.py +426 -0
  1167. scipy/special/_mptestutils.py +453 -0
  1168. scipy/special/_multiufuncs.py +610 -0
  1169. scipy/special/_orthogonal.py +2592 -0
  1170. scipy/special/_orthogonal.pyi +330 -0
  1171. scipy/special/_precompute/__init__.py +0 -0
  1172. scipy/special/_precompute/cosine_cdf.py +17 -0
  1173. scipy/special/_precompute/expn_asy.py +54 -0
  1174. scipy/special/_precompute/gammainc_asy.py +116 -0
  1175. scipy/special/_precompute/gammainc_data.py +124 -0
  1176. scipy/special/_precompute/hyp2f1_data.py +484 -0
  1177. scipy/special/_precompute/lambertw.py +68 -0
  1178. scipy/special/_precompute/loggamma.py +43 -0
  1179. scipy/special/_precompute/struve_convergence.py +131 -0
  1180. scipy/special/_precompute/utils.py +38 -0
  1181. scipy/special/_precompute/wright_bessel.py +342 -0
  1182. scipy/special/_precompute/wright_bessel_data.py +152 -0
  1183. scipy/special/_precompute/wrightomega.py +41 -0
  1184. scipy/special/_precompute/zetac.py +27 -0
  1185. scipy/special/_sf_error.py +15 -0
  1186. scipy/special/_specfun.cpython-313-darwin.so +0 -0
  1187. scipy/special/_special_ufuncs.cpython-313-darwin.so +0 -0
  1188. scipy/special/_spfun_stats.py +106 -0
  1189. scipy/special/_spherical_bessel.py +397 -0
  1190. scipy/special/_support_alternative_backends.py +295 -0
  1191. scipy/special/_test_internal.cpython-313-darwin.so +0 -0
  1192. scipy/special/_test_internal.pyi +9 -0
  1193. scipy/special/_testutils.py +321 -0
  1194. scipy/special/_ufuncs.cpython-313-darwin.so +0 -0
  1195. scipy/special/_ufuncs.pyi +522 -0
  1196. scipy/special/_ufuncs.pyx +13173 -0
  1197. scipy/special/_ufuncs_cxx.cpython-313-darwin.so +0 -0
  1198. scipy/special/_ufuncs_cxx.pxd +142 -0
  1199. scipy/special/_ufuncs_cxx.pyx +427 -0
  1200. scipy/special/_ufuncs_cxx_defs.h +147 -0
  1201. scipy/special/_ufuncs_defs.h +57 -0
  1202. scipy/special/add_newdocs.py +15 -0
  1203. scipy/special/basic.py +87 -0
  1204. scipy/special/cython_special.cpython-313-darwin.so +0 -0
  1205. scipy/special/cython_special.pxd +259 -0
  1206. scipy/special/cython_special.pyi +3 -0
  1207. scipy/special/orthogonal.py +45 -0
  1208. scipy/special/sf_error.py +20 -0
  1209. scipy/special/specfun.py +24 -0
  1210. scipy/special/spfun_stats.py +17 -0
  1211. scipy/special/tests/__init__.py +0 -0
  1212. scipy/special/tests/_cython_examples/extending.pyx +12 -0
  1213. scipy/special/tests/_cython_examples/meson.build +34 -0
  1214. scipy/special/tests/data/__init__.py +0 -0
  1215. scipy/special/tests/data/boost.npz +0 -0
  1216. scipy/special/tests/data/gsl.npz +0 -0
  1217. scipy/special/tests/data/local.npz +0 -0
  1218. scipy/special/tests/test_basic.py +4815 -0
  1219. scipy/special/tests/test_bdtr.py +112 -0
  1220. scipy/special/tests/test_boost_ufuncs.py +64 -0
  1221. scipy/special/tests/test_boxcox.py +125 -0
  1222. scipy/special/tests/test_cdflib.py +712 -0
  1223. scipy/special/tests/test_cdft_asymptotic.py +49 -0
  1224. scipy/special/tests/test_cephes_intp_cast.py +29 -0
  1225. scipy/special/tests/test_cosine_distr.py +83 -0
  1226. scipy/special/tests/test_cython_special.py +363 -0
  1227. scipy/special/tests/test_data.py +719 -0
  1228. scipy/special/tests/test_dd.py +42 -0
  1229. scipy/special/tests/test_digamma.py +45 -0
  1230. scipy/special/tests/test_ellip_harm.py +278 -0
  1231. scipy/special/tests/test_erfinv.py +89 -0
  1232. scipy/special/tests/test_exponential_integrals.py +118 -0
  1233. scipy/special/tests/test_extending.py +28 -0
  1234. scipy/special/tests/test_faddeeva.py +85 -0
  1235. scipy/special/tests/test_gamma.py +12 -0
  1236. scipy/special/tests/test_gammainc.py +152 -0
  1237. scipy/special/tests/test_hyp2f1.py +2566 -0
  1238. scipy/special/tests/test_hypergeometric.py +234 -0
  1239. scipy/special/tests/test_iv_ratio.py +249 -0
  1240. scipy/special/tests/test_kolmogorov.py +491 -0
  1241. scipy/special/tests/test_lambertw.py +109 -0
  1242. scipy/special/tests/test_legendre.py +1518 -0
  1243. scipy/special/tests/test_log1mexp.py +85 -0
  1244. scipy/special/tests/test_loggamma.py +70 -0
  1245. scipy/special/tests/test_logit.py +162 -0
  1246. scipy/special/tests/test_logsumexp.py +469 -0
  1247. scipy/special/tests/test_mpmath.py +2293 -0
  1248. scipy/special/tests/test_nan_inputs.py +65 -0
  1249. scipy/special/tests/test_ndtr.py +77 -0
  1250. scipy/special/tests/test_ndtri_exp.py +94 -0
  1251. scipy/special/tests/test_orthogonal.py +821 -0
  1252. scipy/special/tests/test_orthogonal_eval.py +275 -0
  1253. scipy/special/tests/test_owens_t.py +53 -0
  1254. scipy/special/tests/test_pcf.py +24 -0
  1255. scipy/special/tests/test_pdtr.py +48 -0
  1256. scipy/special/tests/test_powm1.py +65 -0
  1257. scipy/special/tests/test_precompute_expn_asy.py +24 -0
  1258. scipy/special/tests/test_precompute_gammainc.py +108 -0
  1259. scipy/special/tests/test_precompute_utils.py +36 -0
  1260. scipy/special/tests/test_round.py +18 -0
  1261. scipy/special/tests/test_sf_error.py +146 -0
  1262. scipy/special/tests/test_sici.py +36 -0
  1263. scipy/special/tests/test_specfun.py +48 -0
  1264. scipy/special/tests/test_spence.py +32 -0
  1265. scipy/special/tests/test_spfun_stats.py +61 -0
  1266. scipy/special/tests/test_sph_harm.py +85 -0
  1267. scipy/special/tests/test_spherical_bessel.py +400 -0
  1268. scipy/special/tests/test_support_alternative_backends.py +248 -0
  1269. scipy/special/tests/test_trig.py +72 -0
  1270. scipy/special/tests/test_ufunc_signatures.py +46 -0
  1271. scipy/special/tests/test_wright_bessel.py +205 -0
  1272. scipy/special/tests/test_wrightomega.py +117 -0
  1273. scipy/special/tests/test_zeta.py +301 -0
  1274. scipy/stats/__init__.py +670 -0
  1275. scipy/stats/_ansari_swilk_statistics.cpython-313-darwin.so +0 -0
  1276. scipy/stats/_axis_nan_policy.py +700 -0
  1277. scipy/stats/_biasedurn.cpython-313-darwin.so +0 -0
  1278. scipy/stats/_biasedurn.pxd +27 -0
  1279. scipy/stats/_binned_statistic.py +795 -0
  1280. scipy/stats/_binomtest.py +375 -0
  1281. scipy/stats/_bws_test.py +177 -0
  1282. scipy/stats/_censored_data.py +459 -0
  1283. scipy/stats/_common.py +5 -0
  1284. scipy/stats/_constants.py +42 -0
  1285. scipy/stats/_continued_fraction.py +387 -0
  1286. scipy/stats/_continuous_distns.py +12483 -0
  1287. scipy/stats/_correlation.py +210 -0
  1288. scipy/stats/_covariance.py +636 -0
  1289. scipy/stats/_crosstab.py +204 -0
  1290. scipy/stats/_discrete_distns.py +2098 -0
  1291. scipy/stats/_distn_infrastructure.py +4201 -0
  1292. scipy/stats/_distr_params.py +299 -0
  1293. scipy/stats/_distribution_infrastructure.py +5730 -0
  1294. scipy/stats/_entropy.py +428 -0
  1295. scipy/stats/_finite_differences.py +145 -0
  1296. scipy/stats/_fit.py +1351 -0
  1297. scipy/stats/_hypotests.py +2060 -0
  1298. scipy/stats/_kde.py +732 -0
  1299. scipy/stats/_ksstats.py +600 -0
  1300. scipy/stats/_levy_stable/__init__.py +1231 -0
  1301. scipy/stats/_levy_stable/levyst.cpython-313-darwin.so +0 -0
  1302. scipy/stats/_mannwhitneyu.py +492 -0
  1303. scipy/stats/_mgc.py +550 -0
  1304. scipy/stats/_morestats.py +4626 -0
  1305. scipy/stats/_mstats_basic.py +3658 -0
  1306. scipy/stats/_mstats_extras.py +521 -0
  1307. scipy/stats/_multicomp.py +449 -0
  1308. scipy/stats/_multivariate.py +7281 -0
  1309. scipy/stats/_new_distributions.py +452 -0
  1310. scipy/stats/_odds_ratio.py +466 -0
  1311. scipy/stats/_page_trend_test.py +486 -0
  1312. scipy/stats/_probability_distribution.py +1964 -0
  1313. scipy/stats/_qmc.py +2956 -0
  1314. scipy/stats/_qmc_cy.cpython-313-darwin.so +0 -0
  1315. scipy/stats/_qmc_cy.pyi +54 -0
  1316. scipy/stats/_qmvnt.py +454 -0
  1317. scipy/stats/_qmvnt_cy.cpython-313-darwin.so +0 -0
  1318. scipy/stats/_quantile.py +335 -0
  1319. scipy/stats/_rcont/__init__.py +4 -0
  1320. scipy/stats/_rcont/rcont.cpython-313-darwin.so +0 -0
  1321. scipy/stats/_relative_risk.py +263 -0
  1322. scipy/stats/_resampling.py +2352 -0
  1323. scipy/stats/_result_classes.py +40 -0
  1324. scipy/stats/_sampling.py +1314 -0
  1325. scipy/stats/_sensitivity_analysis.py +713 -0
  1326. scipy/stats/_sobol.cpython-313-darwin.so +0 -0
  1327. scipy/stats/_sobol.pyi +54 -0
  1328. scipy/stats/_sobol_direction_numbers.npz +0 -0
  1329. scipy/stats/_stats.cpython-313-darwin.so +0 -0
  1330. scipy/stats/_stats.pxd +10 -0
  1331. scipy/stats/_stats_mstats_common.py +320 -0
  1332. scipy/stats/_stats_py.py +11089 -0
  1333. scipy/stats/_stats_pythran.cpython-313-darwin.so +0 -0
  1334. scipy/stats/_survival.py +683 -0
  1335. scipy/stats/_tukeylambda_stats.py +199 -0
  1336. scipy/stats/_unuran/__init__.py +0 -0
  1337. scipy/stats/_unuran/unuran_wrapper.cpython-313-darwin.so +0 -0
  1338. scipy/stats/_unuran/unuran_wrapper.pyi +179 -0
  1339. scipy/stats/_variation.py +126 -0
  1340. scipy/stats/_warnings_errors.py +38 -0
  1341. scipy/stats/_wilcoxon.py +265 -0
  1342. scipy/stats/biasedurn.py +16 -0
  1343. scipy/stats/contingency.py +521 -0
  1344. scipy/stats/distributions.py +24 -0
  1345. scipy/stats/kde.py +18 -0
  1346. scipy/stats/morestats.py +27 -0
  1347. scipy/stats/mstats.py +140 -0
  1348. scipy/stats/mstats_basic.py +42 -0
  1349. scipy/stats/mstats_extras.py +25 -0
  1350. scipy/stats/mvn.py +17 -0
  1351. scipy/stats/qmc.py +236 -0
  1352. scipy/stats/sampling.py +73 -0
  1353. scipy/stats/stats.py +41 -0
  1354. scipy/stats/tests/__init__.py +0 -0
  1355. scipy/stats/tests/common_tests.py +356 -0
  1356. scipy/stats/tests/data/_mvt.py +171 -0
  1357. scipy/stats/tests/data/fisher_exact_results_from_r.py +607 -0
  1358. scipy/stats/tests/data/jf_skew_t_gamlss_pdf_data.npy +0 -0
  1359. scipy/stats/tests/data/levy_stable/stable-Z1-cdf-sample-data.npy +0 -0
  1360. scipy/stats/tests/data/levy_stable/stable-Z1-pdf-sample-data.npy +0 -0
  1361. scipy/stats/tests/data/levy_stable/stable-loc-scale-sample-data.npy +0 -0
  1362. scipy/stats/tests/data/nist_anova/AtmWtAg.dat +108 -0
  1363. scipy/stats/tests/data/nist_anova/SiRstv.dat +85 -0
  1364. scipy/stats/tests/data/nist_anova/SmLs01.dat +249 -0
  1365. scipy/stats/tests/data/nist_anova/SmLs02.dat +1869 -0
  1366. scipy/stats/tests/data/nist_anova/SmLs03.dat +18069 -0
  1367. scipy/stats/tests/data/nist_anova/SmLs04.dat +249 -0
  1368. scipy/stats/tests/data/nist_anova/SmLs05.dat +1869 -0
  1369. scipy/stats/tests/data/nist_anova/SmLs06.dat +18069 -0
  1370. scipy/stats/tests/data/nist_anova/SmLs07.dat +249 -0
  1371. scipy/stats/tests/data/nist_anova/SmLs08.dat +1869 -0
  1372. scipy/stats/tests/data/nist_anova/SmLs09.dat +18069 -0
  1373. scipy/stats/tests/data/nist_linregress/Norris.dat +97 -0
  1374. scipy/stats/tests/data/rel_breitwigner_pdf_sample_data_ROOT.npy +0 -0
  1375. scipy/stats/tests/data/studentized_range_mpmath_ref.json +1499 -0
  1376. scipy/stats/tests/test_axis_nan_policy.py +1388 -0
  1377. scipy/stats/tests/test_binned_statistic.py +568 -0
  1378. scipy/stats/tests/test_censored_data.py +152 -0
  1379. scipy/stats/tests/test_contingency.py +294 -0
  1380. scipy/stats/tests/test_continued_fraction.py +173 -0
  1381. scipy/stats/tests/test_continuous.py +2198 -0
  1382. scipy/stats/tests/test_continuous_basic.py +1053 -0
  1383. scipy/stats/tests/test_continuous_fit_censored.py +683 -0
  1384. scipy/stats/tests/test_correlation.py +80 -0
  1385. scipy/stats/tests/test_crosstab.py +115 -0
  1386. scipy/stats/tests/test_discrete_basic.py +580 -0
  1387. scipy/stats/tests/test_discrete_distns.py +700 -0
  1388. scipy/stats/tests/test_distributions.py +10400 -0
  1389. scipy/stats/tests/test_entropy.py +322 -0
  1390. scipy/stats/tests/test_fast_gen_inversion.py +433 -0
  1391. scipy/stats/tests/test_fit.py +1090 -0
  1392. scipy/stats/tests/test_hypotests.py +1991 -0
  1393. scipy/stats/tests/test_kdeoth.py +676 -0
  1394. scipy/stats/tests/test_marray.py +289 -0
  1395. scipy/stats/tests/test_mgc.py +217 -0
  1396. scipy/stats/tests/test_morestats.py +3259 -0
  1397. scipy/stats/tests/test_mstats_basic.py +2071 -0
  1398. scipy/stats/tests/test_mstats_extras.py +172 -0
  1399. scipy/stats/tests/test_multicomp.py +405 -0
  1400. scipy/stats/tests/test_multivariate.py +4381 -0
  1401. scipy/stats/tests/test_odds_ratio.py +148 -0
  1402. scipy/stats/tests/test_qmc.py +1492 -0
  1403. scipy/stats/tests/test_quantile.py +199 -0
  1404. scipy/stats/tests/test_rank.py +345 -0
  1405. scipy/stats/tests/test_relative_risk.py +95 -0
  1406. scipy/stats/tests/test_resampling.py +2000 -0
  1407. scipy/stats/tests/test_sampling.py +1450 -0
  1408. scipy/stats/tests/test_sensitivity_analysis.py +310 -0
  1409. scipy/stats/tests/test_stats.py +9707 -0
  1410. scipy/stats/tests/test_survival.py +466 -0
  1411. scipy/stats/tests/test_tukeylambda_stats.py +85 -0
  1412. scipy/stats/tests/test_variation.py +216 -0
  1413. scipy/version.py +12 -0
  1414. scipy-1.16.0rc1.dist-info/LICENSE.txt +934 -0
  1415. scipy-1.16.0rc1.dist-info/METADATA +1082 -0
  1416. scipy-1.16.0rc1.dist-info/RECORD +1416 -0
  1417. scipy-1.16.0rc1.dist-info/WHEEL +6 -0
@@ -0,0 +1,4725 @@
1
+ import math
2
+ import cmath
3
+ import warnings
4
+
5
+ from itertools import product
6
+
7
+ from scipy._lib import _pep440
8
+ import numpy as np
9
+ from numpy.testing import (
10
+ assert_array_almost_equal_nulp, assert_warns, suppress_warnings
11
+ )
12
+ import pytest
13
+ from pytest import raises as assert_raises
14
+ from scipy._lib._array_api import (
15
+ xp_assert_close, xp_assert_equal,
16
+ assert_array_almost_equal, xp_size, xp_default_dtype, is_numpy
17
+ )
18
+
19
+ from numpy import array, spacing, sin, pi
20
+ from scipy.signal import (argrelextrema, BadCoefficients, bessel, besselap, bilinear,
21
+ buttap, butter, buttord, cheb1ap, cheb1ord, cheb2ap,
22
+ cheb2ord, cheby1, cheby2, ellip, ellipap, ellipord,
23
+ firwin, freqs_zpk, freqs, freqz, freqz_zpk,
24
+ gammatone, group_delay, iircomb, iirdesign, iirfilter,
25
+ iirnotch, iirpeak, lp2bp, lp2bs, lp2hp, lp2lp, normalize,
26
+ sos2tf, sos2zpk, sosfreqz, freqz_sos, tf2sos, tf2zpk, zpk2sos,
27
+ zpk2tf, bilinear_zpk, lp2lp_zpk, lp2hp_zpk, lp2bp_zpk,
28
+ lp2bs_zpk)
29
+ from scipy.signal._filter_design import (_cplxreal, _cplxpair, _norm_factor,
30
+ _bessel_poly, _bessel_zeros)
31
+ from scipy.signal._filter_design import _logspace
32
+ from scipy.signal import _polyutils as _pu
33
+ from scipy.signal._polyutils import _sort_cmplx
34
+
35
+ skip_xp_backends = pytest.mark.skip_xp_backends
36
+ xfail_xp_backends = pytest.mark.xfail_xp_backends
37
+
38
+
39
+ try:
40
+ import mpmath
41
+ except ImportError:
42
+ mpmath = None
43
+
44
+
45
+ def mpmath_check(min_ver):
46
+ return pytest.mark.skipif(
47
+ mpmath is None
48
+ or _pep440.parse(mpmath.__version__) < _pep440.Version(min_ver),
49
+ reason=f"mpmath version >= {min_ver} required",
50
+ )
51
+
52
+
53
+ class TestCplxPair:
54
+
55
+ def test_trivial_input(self):
56
+ assert _cplxpair([]).size == 0
57
+ assert _cplxpair(1) == 1
58
+
59
+ def test_output_order(self):
60
+ xp_assert_close(_cplxpair([1+1j, 1-1j]), [1-1j, 1+1j])
61
+
62
+ a = [1+1j, 1+1j, 1, 1-1j, 1-1j, 2]
63
+ b = [1-1j, 1+1j, 1-1j, 1+1j, 1, 2]
64
+ xp_assert_close(_cplxpair(a), b)
65
+
66
+ # points spaced around the unit circle
67
+ z = np.exp(2j*pi*array([4, 3, 5, 2, 6, 1, 0])/7)
68
+ z1 = np.copy(z)
69
+ np.random.shuffle(z)
70
+ xp_assert_close(_cplxpair(z), z1)
71
+ np.random.shuffle(z)
72
+ xp_assert_close(_cplxpair(z), z1)
73
+ np.random.shuffle(z)
74
+ xp_assert_close(_cplxpair(z), z1)
75
+
76
+ # Should be able to pair up all the conjugates
77
+ x = np.random.rand(10000) + 1j * np.random.rand(10000)
78
+ y = x.conj()
79
+ z = np.random.rand(10000)
80
+ x = np.concatenate((x, y, z))
81
+ np.random.shuffle(x)
82
+ c = _cplxpair(x)
83
+
84
+ # Every other element of head should be conjugates:
85
+ xp_assert_close(c[0:20000:2], np.conj(c[1:20000:2]))
86
+ # Real parts of head should be in sorted order:
87
+ xp_assert_close(c[0:20000:2].real, np.sort(c[0:20000:2].real))
88
+ # Tail should be sorted real numbers:
89
+ xp_assert_close(c[20000:], np.sort(c[20000:]))
90
+
91
+ def test_real_integer_input(self):
92
+ xp_assert_equal(_cplxpair([2, 0, 1]), [0, 1, 2])
93
+
94
+ def test_tolerances(self):
95
+ eps = spacing(1)
96
+ xp_assert_close(_cplxpair([1j, -1j, 1+1j*eps], tol=2*eps),
97
+ [-1j, 1j, 1+1j*eps])
98
+
99
+ # sorting close to 0
100
+ xp_assert_close(_cplxpair([-eps+1j, +eps-1j]), [-1j, +1j])
101
+ xp_assert_close(_cplxpair([+eps+1j, -eps-1j]), [-1j, +1j])
102
+ xp_assert_close(_cplxpair([+1j, -1j]), [-1j, +1j])
103
+
104
+ def test_unmatched_conjugates(self):
105
+ # 1+2j is unmatched
106
+ assert_raises(ValueError, _cplxpair, [1+3j, 1-3j, 1+2j])
107
+
108
+ # 1+2j and 1-3j are unmatched
109
+ assert_raises(ValueError, _cplxpair, [1+3j, 1-3j, 1+2j, 1-3j])
110
+
111
+ # 1+3j is unmatched
112
+ assert_raises(ValueError, _cplxpair, [1+3j, 1-3j, 1+3j])
113
+
114
+ # Not conjugates
115
+ assert_raises(ValueError, _cplxpair, [4+5j, 4+5j])
116
+ assert_raises(ValueError, _cplxpair, [1-7j, 1-7j])
117
+
118
+ # No pairs
119
+ assert_raises(ValueError, _cplxpair, [1+3j])
120
+ assert_raises(ValueError, _cplxpair, [1-3j])
121
+
122
+
123
+ class TestCplxReal:
124
+
125
+ def test_trivial_input(self):
126
+ assert all(x.size == 0 for x in _cplxreal([]))
127
+
128
+ x = _cplxreal(1)
129
+ assert x[0].size == 0
130
+ xp_assert_equal(x[1], np.asarray([1]))
131
+
132
+
133
+ def test_output_order(self):
134
+ zc, zr = _cplxreal(np.roots(array([1, 0, 0, 1])))
135
+ xp_assert_close(np.append(zc, zr), [1/2 + 1j*sin(pi/3), -1])
136
+
137
+ eps = spacing(1)
138
+
139
+ a = [0+1j, 0-1j, eps + 1j, eps - 1j, -eps + 1j, -eps - 1j,
140
+ 1, 4, 2, 3, 0, 0,
141
+ 2+3j, 2-3j,
142
+ 1-eps + 1j, 1+2j, 1-2j, 1+eps - 1j, # sorts out of order
143
+ 3+1j, 3+1j, 3+1j, 3-1j, 3-1j, 3-1j,
144
+ 2-3j, 2+3j]
145
+ zc, zr = _cplxreal(a)
146
+ xp_assert_close(zc, [1j, 1j, 1j, 1+1j, 1+2j, 2+3j, 2+3j, 3+1j, 3+1j,
147
+ 3+1j])
148
+ xp_assert_close(zr, [0.0, 0, 1, 2, 3, 4])
149
+
150
+ z = array([1-eps + 1j, 1+2j, 1-2j, 1+eps - 1j, 1+eps+3j, 1-2*eps-3j,
151
+ 0+1j, 0-1j, 2+4j, 2-4j, 2+3j, 2-3j, 3+7j, 3-7j, 4-eps+1j,
152
+ 4+eps-2j, 4-1j, 4-eps+2j])
153
+
154
+ zc, zr = _cplxreal(z)
155
+ xp_assert_close(zc, [1j, 1+1j, 1+2j, 1+3j, 2+3j, 2+4j, 3+7j, 4+1j,
156
+ 4+2j])
157
+ xp_assert_equal(zr, np.asarray([]))
158
+
159
+ def test_unmatched_conjugates(self):
160
+ # 1+2j is unmatched
161
+ assert_raises(ValueError, _cplxreal, [1+3j, 1-3j, 1+2j])
162
+
163
+ # 1+2j and 1-3j are unmatched
164
+ assert_raises(ValueError, _cplxreal, [1+3j, 1-3j, 1+2j, 1-3j])
165
+
166
+ # 1+3j is unmatched
167
+ assert_raises(ValueError, _cplxreal, [1+3j, 1-3j, 1+3j])
168
+
169
+ # No pairs
170
+ assert_raises(ValueError, _cplxreal, [1+3j])
171
+ assert_raises(ValueError, _cplxreal, [1-3j])
172
+
173
+ def test_real_integer_input(self):
174
+ zc, zr = _cplxreal([2, 0, 1, 4])
175
+ xp_assert_equal(zc, [])
176
+ xp_assert_equal(zr, [0, 1, 2, 4])
177
+
178
+
179
+ class TestTf2zpk:
180
+
181
+ @skip_xp_backends(
182
+ cpu_only=True, reason="XXX zpk2sos is numpy-only", exceptions=['cupy']
183
+ )
184
+ @skip_xp_backends("dask.array", reason="https://github.com/dask/dask/issues/11883")
185
+ @pytest.mark.parametrize('dt', ('float64', 'complex128'))
186
+ def test_simple(self, dt, xp):
187
+ dtyp = getattr(xp, dt)
188
+
189
+ z_r = xp.asarray([0.5, -0.5])
190
+ p_r = xp.asarray([1.j / math.sqrt(2), -1.j / math.sqrt(2)])
191
+ # Sort the zeros/poles so that we don't fail the test if the order
192
+ # changes
193
+ z_r = _sort_cmplx(z_r, xp=xp)
194
+ p_r = _sort_cmplx(p_r, xp=xp)
195
+
196
+ b = xp.astype(_pu.poly(z_r, xp=xp), dtyp)
197
+ a = xp.astype(_pu.poly(p_r, xp=xp), dtyp)
198
+
199
+ z, p, k = tf2zpk(b, a)
200
+ z = _sort_cmplx(z, xp=xp)
201
+ # The real part of `p` is ~0.0, so sort by imaginary part
202
+ p = p[xp.argsort(xp.imag(p))]
203
+
204
+ assert_array_almost_equal(z, z_r)
205
+ assert_array_almost_equal(p, p_r)
206
+ assert math.isclose(xp.real(k), 1.)
207
+ assert k.dtype == dtyp
208
+
209
+ def test_bad_filter(self):
210
+ # Regression test for #651: better handling of badly conditioned
211
+ # filter coefficients.
212
+ with suppress_warnings():
213
+ warnings.simplefilter("error", BadCoefficients)
214
+ assert_raises(BadCoefficients, tf2zpk, [1e-15], [1.0, 1.0])
215
+
216
+
217
+ class TestZpk2Tf:
218
+
219
+ def test_identity(self, xp):
220
+ """Test the identity transfer function."""
221
+ z = xp.asarray([])
222
+ p = xp.asarray([])
223
+ k = 1.
224
+ b, a = zpk2tf(z, p, k)
225
+ b_r = xp.asarray([1.]) # desired result
226
+ a_r = xp.asarray([1.]) # desired result
227
+ # The test for the *type* of the return values is a regression
228
+ # test for ticket #1095. In the case p=[], zpk2tf used to
229
+ # return the scalar 1.0 instead of array([1.0]).
230
+ xp_assert_equal(b, b_r)
231
+ xp_assert_equal(a, a_r)
232
+ if is_numpy(xp):
233
+ assert isinstance(b, np.ndarray)
234
+ assert isinstance(a, np.ndarray)
235
+
236
+ @skip_xp_backends("dask.array", reason="https://github.com/dask/dask/issues/11883")
237
+ @skip_xp_backends(cpu_only=True, reason="XXX zpk2sos is numpy-only")
238
+ def test_conj_pair(self, xp):
239
+ # conjugate pairs give real-coeff num & den
240
+ z = xp.asarray([1j, -1j, 2j, -2j])
241
+ # shouldn't need elements of pairs to be adjacent
242
+ p = xp.asarray([1+1j, 3-100j, 3+100j, 1-1j])
243
+ k = 23
244
+
245
+ # np.poly should do the right thing, but be explicit about
246
+ # taking real part
247
+ z_np, p_np = map(np.asarray, (z, p))
248
+ b_np = k * np.poly(z_np).real
249
+ a_np = np.poly(p_np).real
250
+ b, a = map(xp.asarray, (b_np, a_np))
251
+
252
+ bp, ap = zpk2tf(z, p, k)
253
+
254
+ xp_assert_close(b, bp)
255
+ xp_assert_close(a, ap)
256
+
257
+ assert xp.isdtype(bp.dtype, 'real floating')
258
+ assert xp.isdtype(ap.dtype, 'real floating')
259
+
260
+ @skip_xp_backends("dask.array", reason="https://github.com/dask/dask/issues/11883")
261
+ @skip_xp_backends(
262
+ cpu_only=True, reason="XXX zpk2sos is numpy-only", exceptions=['cupy']
263
+ )
264
+ def test_complexk(self, xp):
265
+ # regression: z, p real, k complex k gave real b, a
266
+ b, a = xp.asarray([1j, 1j]), xp.asarray([1.0, 2])
267
+ z, p, k = tf2zpk(b, a)
268
+ xp_assert_close(k, xp.asarray(1j), check_0d=False)
269
+ bp, ap = zpk2tf(z, p, k)
270
+ xp_assert_close(b, bp)
271
+ xp_assert_close(a, ap)
272
+
273
+
274
+ @skip_xp_backends("jax.numpy", reason='no eig in JAX on GPU.')
275
+ class TestSos2Zpk:
276
+
277
+ @skip_xp_backends("dask.array", reason="it https://github.com/dask/dask/issues/11883")
278
+ def test_basic(self, xp):
279
+ sos = [[1, 0, 1, 1, 0, -0.81],
280
+ [1, 0, 0, 1, 0, +0.49]]
281
+ sos = xp.asarray(sos)
282
+ z, p, k = sos2zpk(sos)
283
+ z2 = xp.asarray([1j, -1j, 0, 0])
284
+ p2 = xp.asarray([0.9, -0.9, 0.7j, -0.7j])
285
+ k2 = 1.
286
+ assert_array_almost_equal(_sort_cmplx(z, xp), _sort_cmplx(z2, xp), decimal=4)
287
+ assert_array_almost_equal(_sort_cmplx(p, xp), _sort_cmplx(p2, xp), decimal=4)
288
+ assert math.isclose(k, k2)
289
+
290
+ sos = [[1.00000, +0.61803, 1.0000, 1.00000, +0.60515, 0.95873],
291
+ [1.00000, -1.61803, 1.0000, 1.00000, -1.58430, 0.95873],
292
+ [1.00000, +1.00000, 0.0000, 1.00000, +0.97915, 0.00000]]
293
+ sos = xp.asarray(sos)
294
+ z, p, k = sos2zpk(sos)
295
+ z2 = [-0.3090 + 0.9511j, -0.3090 - 0.9511j, 0.8090 + 0.5878j,
296
+ 0.8090 - 0.5878j, -1.0000 + 0.0000j, 0]
297
+ p2 = [-0.3026 + 0.9312j, -0.3026 - 0.9312j, 0.7922 + 0.5755j,
298
+ 0.7922 - 0.5755j, -0.9791 + 0.0000j, 0]
299
+ z2 = xp.asarray(z2)
300
+ p2 = xp.asarray(p2)
301
+ k2 = 1
302
+ assert_array_almost_equal(_sort_cmplx(z, xp), _sort_cmplx(z2, xp), decimal=4)
303
+ assert_array_almost_equal(_sort_cmplx(p, xp), _sort_cmplx(p2, xp), decimal=4)
304
+
305
+ sos = array([[1, 2, 3, 1, 0.2, 0.3],
306
+ [4, 5, 6, 1, 0.4, 0.5]])
307
+ z = array([-1 - 1.41421356237310j, -1 + 1.41421356237310j,
308
+ -0.625 - 1.05326872164704j, -0.625 + 1.05326872164704j])
309
+ p = array([-0.2 - 0.678232998312527j, -0.2 + 0.678232998312527j,
310
+ -0.1 - 0.538516480713450j, -0.1 + 0.538516480713450j])
311
+ sos, z, p = map(xp.asarray, (sos, z, p))
312
+ k = 4
313
+ z2, p2, k2 = sos2zpk(sos)
314
+
315
+ xp_assert_close(_sort_cmplx(z2, xp=xp), _sort_cmplx(z, xp=xp))
316
+ xp_assert_close(_sort_cmplx(p2, xp=xp), _sort_cmplx(p, xp=xp))
317
+ assert k2 == k
318
+
319
+ @pytest.mark.thread_unsafe
320
+ def test_fewer_zeros(self, xp):
321
+ """Test not the expected number of p/z (effectively at origin)."""
322
+ sos = butter(3, 0.1, output='sos')
323
+ sos = xp.asarray(sos) # XXX convert butter
324
+ z, p, k = sos2zpk(sos)
325
+ assert z.shape[0] == 4
326
+ assert p.shape[0] == 4
327
+
328
+ sos = butter(12, [5., 30.], 'bandpass', fs=1200., analog=False,
329
+ output='sos')
330
+ xp = xp.asarray(sos)
331
+ with pytest.warns(BadCoefficients, match='Badly conditioned'):
332
+ z, p, k = sos2zpk(sos)
333
+ assert z.shape[0] == 24
334
+ assert p.shape[0] == 24
335
+
336
+
337
+ @skip_xp_backends(
338
+ cpu_only=True, reason="XXX zpk2sos is numpy-only", exceptions=['cupy']
339
+ )
340
+ class TestSos2Tf:
341
+
342
+ def test_basic(self, xp):
343
+ sos = [[1.0, 1, 1, 1, 0, -1],
344
+ [-2, 3, 1, 1, 10, 1]]
345
+ sos = xp.asarray(sos)
346
+ b, a = sos2tf(sos)
347
+ assert_array_almost_equal(b, xp.asarray([-2.0, 1, 2, 4, 1]))
348
+ assert_array_almost_equal(a, xp.asarray([1.0, 10, 0, -10, -1]))
349
+
350
+
351
+ @skip_xp_backends(cpu_only=True, reason="XXX zpk2sos is numpy-only")
352
+ class TestTf2Sos:
353
+
354
+ def test_basic(self, xp):
355
+ num = xp.asarray([2., 16, 44, 56, 32])
356
+ den = xp.asarray([3., 3, -15, 18, -12])
357
+ sos = tf2sos(num, den)
358
+ sos2 = [[0.6667, 4.0000, 5.3333, 1.0000, +2.0000, -4.0000],
359
+ [1.0000, 2.0000, 2.0000, 1.0000, -1.0000, +1.0000]]
360
+ sos2 = xp.asarray(sos2)
361
+ assert_array_almost_equal(sos, sos2, decimal=4)
362
+
363
+ b = xp.asarray([1.0, -3, 11, -27, 18])
364
+ a = xp.asarray([16.0, 12, 2, -4, -1])
365
+ sos = tf2sos(b, a)
366
+ sos2 = [[0.0625, -0.1875, 0.1250, 1.0000, -0.2500, -0.1250],
367
+ [1.0000, +0.0000, 9.0000, 1.0000, +1.0000, +0.5000]]
368
+ sos2 = xp.asarray(sos2)
369
+ #assert_array_almost_equal(sos, sos2, decimal=4)
370
+
371
+ @pytest.mark.parametrize('b, a, analog, sos',
372
+ [([1.0], [1.0], False, [[1., 0., 0., 1., 0., 0.]]),
373
+ ([1.0], [1.0], True, [[0., 0., 1., 0., 0., 1.]]),
374
+ ([1.0], [1., 0., -1.01, 0, 0.01], False,
375
+ [[1., 0., 0., 1., 0., -0.01],
376
+ [1., 0., 0., 1., 0., -1]]),
377
+ ([1.0], [1., 0., -1.01, 0, 0.01], True,
378
+ [[0., 0., 1., 1., 0., -1],
379
+ [0., 0., 1., 1., 0., -0.01]])])
380
+ def test_analog(self, b, a, analog, sos, xp):
381
+ b, a, sos = map(xp.asarray, (b, a, sos))
382
+ sos2 = tf2sos(b, a, analog=analog)
383
+ assert_array_almost_equal(sos, sos2, decimal=4)
384
+
385
+
386
+ @skip_xp_backends(
387
+ cpu_only=True, reason="XXX zpk2sos is numpy-only", exceptions=['cupy']
388
+ )
389
+ class TestZpk2Sos:
390
+
391
+ # @pytest.mark.parametrize('dt', 'fdgFDG')
392
+ # XXX: quietly remove float128 and complex256
393
+ @pytest.mark.parametrize('dt', ['float32', 'float64', 'complex64', 'complex128'])
394
+ @pytest.mark.parametrize('pairing, analog',
395
+ [('nearest', False),
396
+ ('keep_odd', False),
397
+ ('minimal', False),
398
+ ('minimal', True)])
399
+ def test_dtypes(self, dt, pairing, analog, xp):
400
+ dtype = getattr(xp, dt)
401
+ # the poles have to be complex
402
+ cdtype = (xp.empty(1, dtype=dtype) + 1j*xp.empty(1, dtype=dtype)).dtype
403
+
404
+ z = xp.asarray([-1, -1], dtype=dtype)
405
+ p = xp.asarray([0.57149 + 0.29360j, 0.57149 - 0.29360j], dtype=cdtype)
406
+ k = xp.asarray(1, dtype=dtype)
407
+ sos = zpk2sos(z, p, k, pairing=pairing, analog=analog)
408
+ # octave & MATLAB
409
+ sos2 = xp.asarray([[1, 2, 1, 1, -1.14298, 0.41280]], dtype=dtype)
410
+ assert_array_almost_equal(sos, sos2, decimal=4)
411
+
412
+ def test_basic(self, xp):
413
+ for pairing in ('nearest', 'keep_odd'):
414
+ #
415
+ # Cases that match octave
416
+ #
417
+
418
+ z = xp.asarray([-1.0, -1.0])
419
+ p = xp.asarray([0.57149 + 0.29360j, 0.57149 - 0.29360j])
420
+ k = 1
421
+ sos = zpk2sos(z, p, k, pairing=pairing)
422
+ sos2 = xp.asarray([[1, 2, 1, 1, -1.14298, 0.41280]]) # octave & MATLAB
423
+ assert_array_almost_equal(sos, sos2, decimal=4)
424
+
425
+ z = xp.asarray([1j, -1j])
426
+ p = xp.asarray([0.9, -0.9, 0.7j, -0.7j])
427
+ k = 1
428
+ sos = zpk2sos(z, p, k, pairing=pairing)
429
+ sos2 = [[1, 0, 1, 1, 0, +0.49],
430
+ [1, 0, 0, 1, 0, -0.81]] # octave
431
+ sos2 = xp.asarray(sos2)
432
+ # sos2 = [[0, 0, 1, 1, -0.9, 0],
433
+ # [1, 0, 1, 1, 0.9, 0]] # MATLAB
434
+ assert_array_almost_equal(sos, sos2, decimal=4)
435
+
436
+ z = xp.asarray([])
437
+ p = xp.asarray([0.8, -0.5+0.25j, -0.5-0.25j])
438
+ k = 1.
439
+ sos = zpk2sos(z, p, k, pairing=pairing)
440
+ sos2 = [[1., 0., 0., 1., 1., 0.3125],
441
+ [1., 0., 0., 1., -0.8, 0.]] # octave, MATLAB fails
442
+ sos2 = xp.asarray(sos2)
443
+ assert_array_almost_equal(sos, sos2, decimal=4)
444
+
445
+ z = xp.asarray([1., 1., 0.9j, -0.9j])
446
+ p = xp.asarray([0.99+0.01j, 0.99-0.01j, 0.1+0.9j, 0.1-0.9j])
447
+ k = 1
448
+ sos = zpk2sos(z, p, k, pairing=pairing)
449
+ sos2 = [[1, 0, 0.81, 1, -0.2, 0.82],
450
+ [1, -2, 1, 1, -1.98, 0.9802]] # octave
451
+ sos2 = xp.asarray(sos2)
452
+ # sos2 = [[1, -2, 1, 1, -0.2, 0.82],
453
+ # [1, 0, 0.81, 1, -1.98, 0.9802]] # MATLAB
454
+ assert_array_almost_equal(sos, sos2, decimal=4)
455
+
456
+ z = xp.asarray([0.9+0.1j, 0.9-0.1j, -0.9])
457
+ p = xp.asarray([0.75+0.25j, 0.75-0.25j, 0.9])
458
+ k = 1
459
+ sos = zpk2sos(z, p, k, pairing=pairing)
460
+ if pairing == 'keep_odd':
461
+ sos2 = [[1, -1.8, 0.82, 1, -1.5, 0.625],
462
+ [1, 0.9, 0, 1, -0.9, 0]] # octave; MATLAB fails
463
+ sos2 = xp.asarray(sos2)
464
+ assert_array_almost_equal(sos, sos2, decimal=4)
465
+ else: # pairing == 'nearest'
466
+ sos2 = [[1, 0.9, 0, 1, -1.5, 0.625],
467
+ [1, -1.8, 0.82, 1, -0.9, 0]] # our algorithm
468
+ sos2 = xp.asarray(sos2)
469
+ assert_array_almost_equal(sos, sos2, decimal=4)
470
+
471
+ #
472
+ # Cases that differ from octave:
473
+ #
474
+
475
+ z = [-0.3090 + 0.9511j, -0.3090 - 0.9511j, 0.8090 + 0.5878j,
476
+ +0.8090 - 0.5878j, -1.0000 + 0.0000j]
477
+ p = [-0.3026 + 0.9312j, -0.3026 - 0.9312j, 0.7922 + 0.5755j,
478
+ +0.7922 - 0.5755j, -0.9791 + 0.0000j]
479
+ z = xp.asarray(z)
480
+ p = xp.asarray(p)
481
+ k = 1
482
+ sos = zpk2sos(z, p, k, pairing=pairing)
483
+ # sos2 = [[1, 0.618, 1, 1, 0.6052, 0.95870],
484
+ # [1, -1.618, 1, 1, -1.5844, 0.95878],
485
+ # [1, 1, 0, 1, 0.9791, 0]] # octave, MATLAB fails
486
+ sos2 = [[1, 1, 0, 1, +0.97915, 0],
487
+ [1, 0.61803, 1, 1, +0.60515, 0.95873],
488
+ [1, -1.61803, 1, 1, -1.58430, 0.95873]]
489
+ sos2 = xp.asarray(sos2)
490
+ assert_array_almost_equal(sos, sos2, decimal=4)
491
+
492
+ z = [-1 - 1.4142j, -1 + 1.4142j,
493
+ -0.625 - 1.0533j, -0.625 + 1.0533j]
494
+ p = [-0.2 - 0.6782j, -0.2 + 0.6782j,
495
+ -0.1 - 0.5385j, -0.1 + 0.5385j]
496
+ z = xp.asarray(z)
497
+ p = xp.asarray(p)
498
+ k = 4
499
+ sos = zpk2sos(z, p, k, pairing=pairing)
500
+ sos2 = [[4, 8, 12, 1, 0.2, 0.3],
501
+ [1, 1.25, 1.5, 1, 0.4, 0.5]] # MATLAB
502
+ sos2 = xp.asarray(sos2, dtype=xp.float64)
503
+ # sos2 = [[4, 8, 12, 1, 0.4, 0.5],
504
+ # [1, 1.25, 1.5, 1, 0.2, 0.3]] # octave
505
+ xp_assert_close(sos, sos2, rtol=1e-4, atol=1e-4)
506
+
507
+ z = xp.asarray([])
508
+ p = xp.asarray([0.2, -0.5+0.25j, -0.5-0.25j])
509
+ k = 1.
510
+ sos = zpk2sos(z, p, k, pairing=pairing)
511
+ sos2 = [[1., 0., 0., 1., -0.2, 0.],
512
+ [1., 0., 0., 1., 1., 0.3125]]
513
+ sos2 = xp.asarray(sos2)
514
+ # sos2 = [[1., 0., 0., 1., 1., 0.3125],
515
+ # [1., 0., 0., 1., -0.2, 0]] # octave, MATLAB fails
516
+ assert_array_almost_equal(sos, sos2, decimal=4)
517
+
518
+ # The next two examples are adapted from Leland B. Jackson,
519
+ # "Digital Filters and Signal Processing (1995) p.400:
520
+ # http://books.google.com/books?id=VZ8uabI1pNMC&lpg=PA400&ots=gRD9pi8Jua&dq=Pole%2Fzero%20pairing%20for%20minimum%20roundoff%20noise%20in%20BSF.&pg=PA400#v=onepage&q=Pole%2Fzero%20pairing%20for%20minimum%20roundoff%20noise%20in%20BSF.&f=false
521
+
522
+ deg2rad = xp.pi / 180.
523
+ k = 1.
524
+
525
+ # first example
526
+ thetas = xp.asarray([22.5, 45, 77.5])
527
+ mags = xp.asarray([0.8, 0.6, 0.9])
528
+ z = xp.exp(1j * deg2rad * thetas)
529
+ z = xp.concat((z, xp.conj(z)))
530
+ p = xp.exp(1j * deg2rad * thetas) * mags
531
+ p = xp.concat((p, xp.conj(p)))
532
+ sos = zpk2sos(z, p, k)
533
+ # sos2 = [[1, -0.43288, 1, 1, -0.38959, 0.81], # octave,
534
+ # [1, -1.41421, 1, 1, -0.84853, 0.36], # MATLAB fails
535
+ # [1, -1.84776, 1, 1, -1.47821, 0.64]]
536
+ # Note that pole-zero pairing matches, but ordering is different
537
+ sos2 = [[1, -1.41421, 1, 1, -0.84853, 0.36],
538
+ [1, -1.84776, 1, 1, -1.47821, 0.64],
539
+ [1, -0.43288, 1, 1, -0.38959, 0.81]]
540
+ sos2 = xp.asarray(sos2)
541
+ assert_array_almost_equal(sos, sos2, decimal=4)
542
+
543
+ # second example
544
+ thetas = xp.asarray([85., 10.])
545
+ z = xp.exp(1j * deg2rad * thetas)
546
+ z = xp.concat((z, xp.conj(z), xp.asarray([1.0, -1.0])))
547
+ sos = zpk2sos(z, p, k)
548
+
549
+ # sos2 = [[1, -0.17431, 1, 1, -0.38959, 0.81], # octave "wrong",
550
+ # [1, -1.96962, 1, 1, -0.84853, 0.36], # MATLAB fails
551
+ # [1, 0, -1, 1, -1.47821, 0.64000]]
552
+ # Our pole-zero pairing matches the text, Octave does not
553
+ sos2 = [[1, 0, -1, 1, -0.84853, 0.36],
554
+ [1, -1.96962, 1, 1, -1.47821, 0.64],
555
+ [1, -0.17431, 1, 1, -0.38959, 0.81]]
556
+ sos2 = xp.asarray(sos2)
557
+ assert_array_almost_equal(sos, sos2, decimal=4)
558
+
559
+ # these examples are taken from the doc string, and show the
560
+ # effect of the 'pairing' argument
561
+ @pytest.mark.parametrize('pairing, sos',
562
+ [('nearest',
563
+ np.array([[1., 1., 0.5, 1., -0.75, 0.],
564
+ [1., 1., 0., 1., -1.6, 0.65]])),
565
+ ('keep_odd',
566
+ np.array([[1., 1., 0, 1., -0.75, 0.],
567
+ [1., 1., 0.5, 1., -1.6, 0.65]])),
568
+ ('minimal',
569
+ np.array([[0., 1., 1., 0., 1., -0.75],
570
+ [1., 1., 0.5, 1., -1.6, 0.65]]))])
571
+ def test_pairing(self, pairing, sos, xp):
572
+ sos = xp.asarray(sos)
573
+ z1 = xp.asarray([-1, -0.5-0.5j, -0.5+0.5j])
574
+ p1 = xp.asarray([0.75, 0.8+0.1j, 0.8-0.1j])
575
+ sos2 = zpk2sos(z1, p1, 1, pairing=pairing)
576
+ assert_array_almost_equal(sos, sos2, decimal=4)
577
+
578
+ @pytest.mark.parametrize('p, sos_dt',
579
+ [([-1, 1, -0.1, 0.1],
580
+ [[0., 0., 1., 1., 0., -0.01],
581
+ [0., 0., 1., 1., 0., -1]]),
582
+ ([-0.7071+0.7071j, -0.7071-0.7071j, -0.1j, 0.1j],
583
+ [[0., 0., 1., 1., 0., 0.01],
584
+ [0., 0., 1., 1., 1.4142, 1.]])])
585
+ def test_analog(self, p, sos_dt, xp):
586
+ # test `analog` argument
587
+ # for discrete time, poles closest to unit circle should appear last
588
+ # for cont. time, poles closest to imaginary axis should appear last
589
+ z, p = xp.asarray([]), xp.asarray(p)
590
+ sos_dt = xp.asarray(sos_dt)
591
+ sos2_dt = zpk2sos(z, p, 1, pairing='minimal', analog=False)
592
+ sos2_ct = zpk2sos(z, p, 1, pairing='minimal', analog=True)
593
+ assert_array_almost_equal(sos_dt, sos2_dt, decimal=4)
594
+ assert_array_almost_equal(xp.flip(sos_dt, axis=0), sos2_ct, decimal=4)
595
+
596
+ def test_bad_args(self):
597
+ with pytest.raises(ValueError, match=r'pairing must be one of'):
598
+ zpk2sos([1], [2], 1, pairing='no_such_pairing')
599
+
600
+ with pytest.raises(ValueError, match=r'.*pairing must be "minimal"'):
601
+ zpk2sos([1], [2], 1, pairing='keep_odd', analog=True)
602
+
603
+ with pytest.raises(ValueError,
604
+ match=r'.*must have len\(p\)>=len\(z\)'):
605
+ zpk2sos([1, 1], [2], 1, analog=True)
606
+
607
+ with pytest.raises(ValueError, match=r'k must be real'):
608
+ zpk2sos([1], [2], k=1j)
609
+
610
+
611
+ class TestFreqs:
612
+
613
+ def test_basic(self, xp):
614
+ _, h = freqs(xp.asarray([1.0]), xp.asarray([1.0]), worN=8)
615
+ assert_array_almost_equal(h, xp.ones(8))
616
+
617
+ def test_output(self, xp):
618
+ # 1st order low-pass filter: H(s) = 1 / (s + 1)
619
+ w = xp.asarray([0.1, 1, 10, 100])
620
+ num = xp.asarray([1.])
621
+ den = xp.asarray([1, 1.])
622
+ w, H = freqs(num, den, worN=w)
623
+ s = w * 1j
624
+ expected = 1 / (s + 1)
625
+ assert_array_almost_equal(xp.real(H), xp.real(expected))
626
+ assert_array_almost_equal(xp.imag(H), xp.imag(expected))
627
+
628
+ @skip_xp_backends("jax.numpy", reason="eigvals not available on CUDA")
629
+ def test_freq_range(self, xp):
630
+ # Test that freqresp() finds a reasonable frequency range.
631
+ # 1st order low-pass filter: H(s) = 1 / (s + 1)
632
+ # Expected range is from 0.01 to 10.
633
+ num = xp.asarray([1.])
634
+ den = xp.asarray([1, 1.])
635
+ n = 10
636
+ expected_w = _logspace(-2, 1, n, xp=xp)
637
+ w, H = freqs(num, den, worN=n)
638
+ assert_array_almost_equal(w, expected_w)
639
+
640
+ def test_plot(self, xp):
641
+
642
+ def plot(w, h):
643
+ assert_array_almost_equal(h, xp.ones(8))
644
+
645
+ with assert_raises(ZeroDivisionError):
646
+ freqs([1.0], [1.0], worN=8, plot=lambda w, h: 1 / 0)
647
+
648
+ freqs(xp.asarray([1.0]), xp.asarray([1.0]), worN=8, plot=plot)
649
+
650
+ def test_backward_compat(self, xp):
651
+ # For backward compatibility, test if None act as a wrapper for default
652
+ w1, h1 = freqs(xp.asarray([1.0]), xp.asarray([1.0]))
653
+ w2, h2 = freqs(xp.asarray([1.0]), xp.asarray([1.0]), None)
654
+ assert_array_almost_equal(w1, w2)
655
+ assert_array_almost_equal(h1, h2)
656
+
657
+ def test_w_or_N_types(self):
658
+ # Measure at 8 equally-spaced points
659
+ for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8),
660
+ np.array(8)):
661
+ w, h = freqs([1.0], [1.0], worN=N)
662
+ assert len(w) == 8
663
+ assert_array_almost_equal(h, np.ones(8))
664
+
665
+ # Measure at frequency 8 rad/sec
666
+ for w in (8.0, 8.0+0j):
667
+ w_out, h = freqs([1.0], [1.0], worN=w)
668
+ assert_array_almost_equal(w_out, [8])
669
+ assert_array_almost_equal(h, [1])
670
+
671
+
672
+ class TestFreqs_zpk:
673
+
674
+ def test_basic(self, xp):
675
+ _, h = freqs_zpk(
676
+ xp.asarray([1.0]), xp.asarray([1.0]), xp.asarray([1.0]), worN=8
677
+ )
678
+ assert_array_almost_equal(h, xp.ones(8))
679
+
680
+ def test_output(self, xp):
681
+ # 1st order low-pass filter: H(s) = 1 / (s + 1)
682
+ w = xp.asarray([0.1, 1, 10, 100])
683
+ z = xp.asarray([])
684
+ p = xp.asarray([-1.0])
685
+ k = 1
686
+ w, H = freqs_zpk(z, p, k, worN=w)
687
+ s = w * 1j
688
+ expected = 1 / (s + 1)
689
+ assert_array_almost_equal(xp.real(H), xp.real(expected))
690
+ assert_array_almost_equal(xp.imag(H), xp.imag(expected))
691
+
692
+ def test_freq_range(self, xp):
693
+ # Test that freqresp() finds a reasonable frequency range.
694
+ # 1st order low-pass filter: H(s) = 1 / (s + 1)
695
+ # Expected range is from 0.01 to 10.
696
+ z = xp.asarray([])
697
+ p = xp.asarray([-1.])
698
+ k = 1
699
+ n = 10
700
+ expected_w = _logspace(-2, 1, n, xp=xp)
701
+ w, H = freqs_zpk(z, p, k, worN=n)
702
+ assert_array_almost_equal(w, expected_w)
703
+
704
+ @skip_xp_backends("jax.numpy", reason="eigvals not available on CUDA")
705
+ def test_vs_freqs(self, xp):
706
+ b, a = cheby1(4, 5, 100, analog=True, output='ba')
707
+ z, p, k = cheby1(4, 5, 100, analog=True, output='zpk')
708
+
709
+ b, a = map(xp.asarray, (b, a)) # XXX convert cheby1
710
+ z, p = map(xp.asarray, (z, p))
711
+
712
+ w1, h1 = freqs(b, a)
713
+ w2, h2 = freqs_zpk(z, p, k)
714
+ xp_assert_close(w1, w2)
715
+ xp_assert_close(h1, h2, rtol=1e-6)
716
+
717
+ def test_backward_compat(self, xp):
718
+ # For backward compatibility, test if None act as a wrapper for default
719
+ # Also, keep testing `k` a length-one list: it is documented as a scalar,
720
+ # but the implementation was allowing for a one-element array-likes
721
+ w1, h1 = freqs_zpk(xp.asarray([1.0]), xp.asarray([1.0]), [1.0])
722
+ w2, h2 = freqs_zpk(xp.asarray([1.0]), xp.asarray([1.0]), [1.0], None)
723
+ assert_array_almost_equal(w1, w2)
724
+ assert_array_almost_equal(h1, h2)
725
+
726
+ def test_w_or_N_types(self):
727
+ # Measure at 8 equally-spaced points
728
+ for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8),
729
+ np.array(8)):
730
+ w, h = freqs_zpk([], [], 1, worN=N)
731
+ assert len(w) == 8
732
+ assert_array_almost_equal(h, np.ones(8))
733
+
734
+ # Measure at frequency 8 rad/sec
735
+ for w in (8.0, 8.0+0j):
736
+ w_out, h = freqs_zpk([], [], 1, worN=w)
737
+ assert_array_almost_equal(w_out, [8])
738
+ assert_array_almost_equal(h, [1])
739
+
740
+
741
+ class TestFreqz:
742
+
743
+ def test_ticket1441(self, xp):
744
+ """Regression test for ticket 1441."""
745
+ # Because freqz previously used arange instead of linspace,
746
+ # when N was large, it would return one more point than
747
+ # requested.
748
+ N = 100000
749
+ w, h = freqz(xp.asarray([1.0]), worN=N)
750
+ assert w.shape == (N,)
751
+
752
+ def test_gh_22886(self, xp):
753
+ w, h = freqz(xp.asarray([1.]), worN=xp.asarray([0., 0.1]))
754
+ xp_assert_equal(w, xp.asarray([0. , 0.1]))
755
+ xp_assert_equal(h, xp.asarray([1.+0.j, 1.+0.j]))
756
+
757
+ def test_basic(self, xp):
758
+ w, h = freqz(xp.asarray([1.0]), worN=8)
759
+ assert_array_almost_equal(w, xp.pi * xp.arange(8, dtype=w.dtype) / 8.)
760
+ assert_array_almost_equal(h, xp.ones(8))
761
+ w, h = freqz(xp.asarray([1.0]), worN=9)
762
+ assert_array_almost_equal(w, xp.pi * xp.arange(9, dtype=w.dtype) / 9.)
763
+ assert_array_almost_equal(h, xp.ones(9))
764
+
765
+ for a in [1, xp.ones(2)]:
766
+ w, h = freqz(xp.ones(2), a, worN=0)
767
+ assert w.shape == (0,)
768
+ assert h.shape == (0,)
769
+ hdt = xp.complex128 if xp_default_dtype(xp) == xp.float64 else xp.complex64
770
+ assert h.dtype == hdt
771
+
772
+ def test_basic2(self, xp):
773
+ t = xp.linspace(0, 1, 4, endpoint=False)
774
+ for b, a, h_whole in zip(
775
+ (xp.asarray([1., 0, 0, 0]), xp.sin(2 * xp.pi * t)),
776
+ (xp.asarray([1., 0, 0, 0]), xp.asarray([0.5, 0, 0, 0])),
777
+ (xp.asarray([1., 1., 1., 1.]), xp.asarray([0, -4j, 0, 4j]))
778
+ ):
779
+
780
+ w, h = freqz(b, a, worN=4, whole=True)
781
+ expected_w = xp.linspace(0, 2 * xp.pi, 4, endpoint=False)
782
+ assert_array_almost_equal(w, expected_w)
783
+ assert_array_almost_equal(h, h_whole)
784
+
785
+ # simultaneously check int-like support
786
+ w, h = freqz(b, a, worN=np.int32(4), whole=True)
787
+ assert_array_almost_equal(w, expected_w)
788
+ assert_array_almost_equal(h, h_whole)
789
+
790
+ w, h = freqz(b, a, worN=w, whole=True)
791
+ assert_array_almost_equal(w, expected_w)
792
+ assert_array_almost_equal(h, h_whole)
793
+
794
+ def test_basic3(self):
795
+ t = np.linspace(0, 1, 4, endpoint=False)
796
+ expected_w = np.linspace(0, 2 * np.pi, 4, endpoint=False)
797
+ for b, a, h_whole in zip(
798
+ (np.asarray([1., 0, 0, 0]), np.sin(2 * np.pi * t)),
799
+ (np.asarray([1., 0, 0, 0]), np.asarray([0.5, 0, 0, 0])),
800
+ (np.asarray([1., 1., 1., 1.]), np.asarray([0, -4j, 0, 4j]))
801
+ ):
802
+
803
+ w, h = freqz(b, a, worN=np.int32(4), whole=True)
804
+ assert_array_almost_equal(w, expected_w)
805
+ assert_array_almost_equal(h, h_whole)
806
+
807
+ w, h = freqz(b, a, worN=w, whole=True)
808
+ assert_array_almost_equal(w, expected_w)
809
+ assert_array_almost_equal(h, h_whole)
810
+
811
+ def test_basic_whole(self, xp):
812
+ w, h = freqz(xp.asarray([1.0]), worN=8, whole=True)
813
+ assert_array_almost_equal(w, 2 * xp.pi * xp.arange(8.0) / 8)
814
+ assert_array_almost_equal(h, xp.ones(8))
815
+
816
+ def test_plot(self, xp):
817
+
818
+ def plot(w, h):
819
+ assert_array_almost_equal(w, xp.pi * xp.arange(8.0) / 8)
820
+ assert_array_almost_equal(h, xp.ones(8))
821
+
822
+ with assert_raises(ZeroDivisionError):
823
+ freqz(xp.asarray([1.0]), worN=8, plot=lambda w, h: 1 / 0)
824
+
825
+ freqz(xp.asarray([1.0]), worN=8, plot=plot)
826
+
827
+ def test_fft_wrapping(self, xp):
828
+ # Some simple real FIR filters
829
+ bs = list() # filters
830
+ as_ = list()
831
+ hs_whole = list()
832
+ hs_half = list()
833
+ # 3 taps
834
+ t = xp.linspace(0, 1, 3, endpoint=False)
835
+ bs.append(xp.sin(2 * xp.pi * t))
836
+ as_.append(3.)
837
+ hs_whole.append(xp.asarray([0, -0.5j, 0.5j]))
838
+ hs_half.append(xp.asarray([0, math.sqrt(1./12.), -0.5j]))
839
+ # 4 taps
840
+ t = xp.linspace(0, 1, 4, endpoint=False)
841
+ bs.append(xp.sin(2 * xp.pi * t))
842
+ as_.append(0.5)
843
+ hs_whole.append(xp.asarray([0, -4j, 0, 4j]))
844
+ hs_half.append(xp.asarray([0, math.sqrt(8), -4j, -math.sqrt(8)]))
845
+ del t
846
+ for ii, b in enumerate(bs):
847
+ # whole
848
+ a = as_[ii]
849
+ expected_w = xp.linspace(0, 2 * xp.pi, b.shape[0], endpoint=False)
850
+ w, h = freqz(b, a, worN=expected_w, whole=True) # polyval
851
+ err_msg = f'b = {b}, a={a}'
852
+ assert_array_almost_equal(w, expected_w, err_msg=err_msg)
853
+ assert_array_almost_equal(h, hs_whole[ii], err_msg=err_msg)
854
+
855
+ w, h = freqz(b, a, worN=b.shape[0], whole=True) # FFT
856
+ assert_array_almost_equal(w, expected_w, err_msg=err_msg)
857
+ assert_array_almost_equal(h, hs_whole[ii], err_msg=err_msg)
858
+
859
+ # non-whole
860
+ expected_w = xp.linspace(0, xp.pi, b.shape[0], endpoint=False)
861
+ w, h = freqz(b, a, worN=expected_w, whole=False) # polyval
862
+ assert_array_almost_equal(w, expected_w, err_msg=err_msg)
863
+ assert_array_almost_equal(h, hs_half[ii], err_msg=err_msg)
864
+
865
+ w, h = freqz(b, a, worN=b.shape[0], whole=False) # FFT
866
+ assert_array_almost_equal(w, expected_w, err_msg=err_msg)
867
+ assert_array_almost_equal(h, hs_half[ii], err_msg=err_msg)
868
+
869
+ # some random FIR filters (real + complex)
870
+ # assume polyval is accurate
871
+ rng = np.random.RandomState(0)
872
+ for ii in range(2, 10): # number of taps
873
+ b = xp.asarray(rng.randn(ii))
874
+ for kk in range(2):
875
+ a = xp.asarray(rng.randn(1) if kk == 0 else rng.randn(3))
876
+ for jj in range(2):
877
+ if jj == 1:
878
+ b = b + xp.asarray(rng.randn(ii)) * 1j
879
+
880
+ # whole
881
+ expected_w = xp.linspace(0, 2 * xp.pi, ii, endpoint=False)
882
+ w, expected_h = freqz(b, a, worN=expected_w, whole=True)
883
+ assert_array_almost_equal(w, expected_w)
884
+ w, h = freqz(b, a, worN=ii, whole=True)
885
+ assert_array_almost_equal(w, expected_w)
886
+ assert_array_almost_equal(h, expected_h, decimal=4)
887
+
888
+ # half
889
+ expected_w = xp.linspace(0, xp.pi, ii, endpoint=False)
890
+ w, expected_h = freqz(b, a, worN=expected_w, whole=False)
891
+ assert_array_almost_equal(w, expected_w)
892
+ w, h = freqz(b, a, worN=ii, whole=False)
893
+ assert_array_almost_equal(w, expected_w)
894
+ assert_array_almost_equal(h, expected_h, decimal=4)
895
+
896
+ def test_broadcasting1(self, xp):
897
+ # Test broadcasting with worN an integer or a 1-D array,
898
+ # b and a are n-dimensional arrays.
899
+ np.random.seed(123)
900
+ b = np.random.rand(3, 5, 1)
901
+ a = np.random.rand(2, 1)
902
+ b, a = map(xp.asarray, (b, a))
903
+
904
+ for whole in [False, True]:
905
+ # Test with worN being integers (one fast for FFT and one not),
906
+ # a 1-D array, and an empty array.
907
+ for worN in [16, 17, xp.linspace(0, 1, 10), xp.asarray([])]:
908
+ w, h = freqz(b, a, worN=worN, whole=whole)
909
+ for k in range(b.shape[1]):
910
+ bk = b[:, k, 0]
911
+ ak = a[:, 0]
912
+ ww, hh = freqz(bk, ak, worN=worN, whole=whole)
913
+ xp_assert_close(ww, w)
914
+ xp_assert_close(hh, h[k, ...])
915
+
916
+ def test_broadcasting2(self, xp):
917
+ # Test broadcasting with worN an integer or a 1-D array,
918
+ # b is an n-dimensional array, and a is left at the default value.
919
+ np.random.seed(123)
920
+ b = np.random.rand(3, 5, 1)
921
+ b = xp.asarray(b)
922
+ for whole in [False, True]:
923
+ for worN in [16, 17, xp.linspace(0, 1, 10)]:
924
+ w, h = freqz(b, worN=worN, whole=whole)
925
+ for k in range(b.shape[1]):
926
+ bk = b[:, k, 0]
927
+ ww, hh = freqz(bk, worN=worN, whole=whole)
928
+ xp_assert_close(ww, w)
929
+ xp_assert_close(hh, h[k, :])
930
+
931
+ def test_broadcasting3(self, xp):
932
+ # Test broadcasting where b.shape[-1] is the same length
933
+ # as worN, and a is left at the default value.
934
+ np.random.seed(123)
935
+ N = 16
936
+ b = np.random.rand(3, N)
937
+ b = xp.asarray(b)
938
+ for whole in [False, True]:
939
+ for worN in [N, xp.linspace(0, 1, N)]:
940
+ w, h = freqz(b, worN=worN, whole=whole)
941
+ assert xp_size(w) == N
942
+ for k in range(N):
943
+ bk = b[:, k]
944
+ ww, hh = freqz(bk, worN=w[k], whole=whole)
945
+ xp_assert_close(ww, xp.asarray(w[k])[None])
946
+ xp_assert_close(hh, xp.asarray(h[k])[None])
947
+
948
+ def test_broadcasting4(self, xp):
949
+ # Test broadcasting with worN a 2-D array.
950
+ np.random.seed(123)
951
+ b = np.random.rand(4, 2, 1, 1)
952
+ a = np.random.rand(5, 2, 1, 1)
953
+ b, a = map(xp.asarray, (b, a))
954
+
955
+ for whole in [False, True]:
956
+ for worN in [np.random.rand(6, 7), np.empty((6, 0))]:
957
+ worN = xp.asarray(worN)
958
+ w, h = freqz(b, a, worN=worN, whole=whole)
959
+ xp_assert_close(w, worN, rtol=1e-14)
960
+ assert h.shape == (2,) + worN.shape
961
+ for k in range(2):
962
+ ww, hh = freqz(b[:, k, 0, 0], a[:, k, 0, 0],
963
+ worN=xp.reshape(worN, (-1,)),
964
+ whole=whole)
965
+ xp_assert_close(ww, xp.reshape(worN, (-1,)), rtol=1e-14)
966
+ xp_assert_close(hh, xp.reshape(h[k, :, :], (-1,)))
967
+
968
+ def test_backward_compat(self):
969
+ # For backward compatibility, test if None act as a wrapper for default
970
+ w1, h1 = freqz([1.0], 1)
971
+ w2, h2 = freqz([1.0], 1, None)
972
+ assert_array_almost_equal(w1, w2)
973
+ assert_array_almost_equal(h1, h2)
974
+
975
+ def test_fs_param(self, xp):
976
+ fs = 900
977
+ b = xp.asarray([0.039479155677484369, 0.11843746703245311, 0.11843746703245311,
978
+ 0.039479155677484369])
979
+ a = xp.asarray([1.0, -1.3199152021838287, 0.80341991081938424,
980
+ -0.16767146321568049])
981
+
982
+ # N = None, whole=False
983
+ w1, h1 = freqz(b, a, fs=fs)
984
+ w2, h2 = freqz(b, a)
985
+ xp_assert_close(h1, h2)
986
+ xp_assert_close(w1, xp.linspace(0, fs/2, 512, endpoint=False))
987
+
988
+ # N = None, whole=True
989
+ w1, h1 = freqz(b, a, whole=True, fs=fs)
990
+ w2, h2 = freqz(b, a, whole=True)
991
+ xp_assert_close(h1, h2)
992
+ xp_assert_close(w1, xp.linspace(0, fs, 512, endpoint=False))
993
+
994
+ # N = 5, whole=False
995
+ w1, h1 = freqz(b, a, 5, fs=fs)
996
+ w2, h2 = freqz(b, a, 5)
997
+ xp_assert_close(h1, h2)
998
+ xp_assert_close(w1, xp.linspace(0, fs/2, 5, endpoint=False))
999
+
1000
+ # N = 5, whole=True
1001
+ w1, h1 = freqz(b, a, 5, whole=True, fs=fs)
1002
+ w2, h2 = freqz(b, a, 5, whole=True)
1003
+ xp_assert_close(h1, h2)
1004
+ xp_assert_close(w1, xp.linspace(0, fs, 5, endpoint=False))
1005
+
1006
+ # w is an array_like
1007
+ for w in ([123], (123,), xp.asarray([123]), (50, 123, 230),
1008
+ xp.asarray([50, 123, 230])):
1009
+ w1, h1 = freqz(b, a, w, fs=fs)
1010
+ w2, h2 = freqz(b, a, 2*pi*xp.asarray(w, dtype=xp.float64)/ fs)
1011
+ xp_assert_close(h1, h2)
1012
+ xp_assert_close(w1, xp.asarray(w), check_dtype=False)
1013
+
1014
+ def test_w_or_N_types(self):
1015
+ # Measure at 7 (polyval) or 8 (fft) equally-spaced points
1016
+ for N in (7, np.int8(7), np.int16(7), np.int32(7), np.int64(7),
1017
+ np.array(7),
1018
+ 8, np.int8(8), np.int16(8), np.int32(8), np.int64(8),
1019
+ np.array(8)):
1020
+
1021
+ w, h = freqz([1.0], worN=N)
1022
+ assert_array_almost_equal(w, np.pi * np.arange(N) / N)
1023
+ assert_array_almost_equal(h, np.ones(N))
1024
+
1025
+ w, h = freqz([1.0], worN=N, fs=100)
1026
+ assert_array_almost_equal(w, np.linspace(0, 50, N, endpoint=False))
1027
+ assert_array_almost_equal(h, np.ones(N))
1028
+
1029
+ # Measure at frequency 8 Hz
1030
+ for w in (8.0, 8.0+0j):
1031
+ # Only makes sense when fs is specified
1032
+ w_out, h = freqz(np.asarray([1.0]), worN=w, fs=100)
1033
+ assert_array_almost_equal(w_out, np.asarray([8]))
1034
+ assert_array_almost_equal(h, np.asarray(1.), check_0d=False)
1035
+
1036
+ def test_nyquist(self, xp):
1037
+ w, h = freqz(xp.asarray([1.0]), worN=8, include_nyquist=True)
1038
+ assert_array_almost_equal(w, xp.pi * xp.arange(8, dtype=w.dtype) / 7.)
1039
+ assert_array_almost_equal(h, xp.ones(8))
1040
+ w, h = freqz(xp.asarray([1.0]), worN=9, include_nyquist=True)
1041
+ assert_array_almost_equal(w, xp.pi * xp.arange(9, dtype=w.dtype) / 8.)
1042
+ assert_array_almost_equal(h, xp.ones(9))
1043
+
1044
+ for a in [1, xp.ones(2)]:
1045
+ w, h = freqz(xp.ones(2), a, worN=0, include_nyquist=True)
1046
+ assert w.shape == (0,)
1047
+ assert h.shape == (0,)
1048
+ hdt = xp.complex128 if xp_default_dtype(xp) == xp.float64 else xp.complex64
1049
+ assert h.dtype == hdt
1050
+
1051
+ w1, h1 = freqz(xp.asarray([1.0]), worN=8, whole = True, include_nyquist=True)
1052
+ w2, h2 = freqz(xp.asarray([1.0]), worN=8, whole = True, include_nyquist=False)
1053
+ assert_array_almost_equal(w1, w2)
1054
+ assert_array_almost_equal(h1, h2)
1055
+
1056
+ # https://github.com/scipy/scipy/issues/17289
1057
+ # https://github.com/scipy/scipy/issues/15273
1058
+ @pytest.mark.parametrize('whole,nyquist,worN',
1059
+ [(False, False, 32),
1060
+ (False, True, 32),
1061
+ (True, False, 32),
1062
+ (True, True, 32),
1063
+ (False, False, 257),
1064
+ (False, True, 257),
1065
+ (True, False, 257),
1066
+ (True, True, 257)])
1067
+ def test_17289(self, whole, nyquist, worN, xp):
1068
+ d = xp.asarray([0.0, 1.0])
1069
+ w, Drfft = freqz(d, worN=32, whole=whole, include_nyquist=nyquist)
1070
+ _, Dpoly = freqz(d, worN=w)
1071
+ xp_assert_close(Drfft, Dpoly)
1072
+
1073
+ def test_fs_validation(self):
1074
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
1075
+ freqz([1.0], fs=np.array([10, 20]))
1076
+
1077
+ with pytest.raises(ValueError, match="Sampling.*be none."):
1078
+ freqz([1.0], fs=None)
1079
+
1080
+
1081
+ class TestFreqz_sos:
1082
+
1083
+ def test_freqz_sos_basic(self, xp):
1084
+ # Compare the results of freqz and freqz_sos for a low order
1085
+ # Butterworth filter.
1086
+
1087
+ N = 500
1088
+
1089
+ b, a = butter(4, 0.2)
1090
+ sos = butter(4, 0.2, output='sos')
1091
+ b, a, sos = map(xp.asarray, (b, a, sos)) # XXX until butter is converted
1092
+
1093
+ w, h = freqz(b, a, worN=N)
1094
+ w2, h2 = freqz_sos(sos, worN=N)
1095
+ xp_assert_equal(w2, w)
1096
+ xp_assert_close(h2, h, rtol=1e-10, atol=1e-14)
1097
+
1098
+ b, a = ellip(3, 1, 30, (0.2, 0.3), btype='bandpass')
1099
+ sos = ellip(3, 1, 30, (0.2, 0.3), btype='bandpass', output='sos')
1100
+ b, a, sos = map(xp.asarray, (b, a, sos)) # XXX until ellip is converted
1101
+
1102
+ w, h = freqz(b, a, worN=N)
1103
+ w2, h2 = freqz_sos(sos, worN=N)
1104
+ xp_assert_equal(w2, w)
1105
+ xp_assert_close(h2, h, rtol=1e-10, atol=1e-14)
1106
+
1107
+ # must have at least one section
1108
+ with assert_raises(ValueError):
1109
+ freqz_sos(sos[:0, ...])
1110
+
1111
+ def test_backward_compat(self, xp):
1112
+ # For backward compatibility, test if None act as a wrapper for default
1113
+ N = 500
1114
+
1115
+ sos = butter(4, 0.2, output='sos')
1116
+ sos = xp.asarray(sos) # XXX until butter is converted
1117
+ w1, h1 = freqz_sos(sos, worN=N)
1118
+ w2, h2 = sosfreqz(sos, worN=N)
1119
+ assert_array_almost_equal(w1, w2)
1120
+ assert_array_almost_equal(h1, h2)
1121
+
1122
+ @skip_xp_backends("dask.array", reason="float cannot be interpreted as in integer")
1123
+ def test_freqz_sos_design(self, xp):
1124
+ # Compare freqz_sos output against expected values for different
1125
+ # filter types
1126
+
1127
+ # from cheb2ord
1128
+ N, Wn = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60)
1129
+ sos = cheby2(N, 60, Wn, 'stop', output='sos')
1130
+ sos = xp.asarray(sos) # XXX
1131
+ zero = xp.asarray(0., dtype=xp.float64)
1132
+
1133
+ w, h = freqz_sos(sos)
1134
+ h = xp.abs(h)
1135
+ w = w / xp.pi
1136
+ xp_assert_close(20 * xp.log10(h[w <= 0.1]),
1137
+ zero, atol=3.01,
1138
+ check_shape=False)
1139
+ xp_assert_close(20 * xp.log10(h[w >= 0.6]),
1140
+ zero, atol=3.01,
1141
+ check_shape=False)
1142
+ xp_assert_close(h[(w >= 0.2) & (w <= 0.5)],
1143
+ zero, atol=1e-3,
1144
+ check_shape=False) # <= -60 dB
1145
+
1146
+ N, Wn = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 150)
1147
+ sos = cheby2(N, 150, Wn, 'stop', output='sos')
1148
+ sos = xp.asarray(sos)
1149
+
1150
+ w, h = freqz_sos(sos)
1151
+ dB = 20*xp.log10(xp.abs(h))
1152
+ w = w / xp.pi
1153
+ xp_assert_close(dB[w <= 0.1], zero, atol=3.01, check_shape=False)
1154
+ xp_assert_close(dB[w >= 0.6], zero, atol=3.01, check_shape=False)
1155
+ assert xp.all(dB[(w >= 0.2) & (w <= 0.5)] < -149.9)
1156
+
1157
+ # from cheb1ord
1158
+ N, Wn = cheb1ord(0.2, 0.3, 3, 40)
1159
+ sos = cheby1(N, 3, Wn, 'low', output='sos')
1160
+ sos = xp.asarray(sos)
1161
+
1162
+ w, h = freqz_sos(sos)
1163
+ h = xp.abs(h)
1164
+ w = w / xp.pi
1165
+ xp_assert_close(20 * xp.log10(h[w <= 0.2]), zero, atol=3.01,
1166
+ check_shape=False)
1167
+ xp_assert_close(h[w >= 0.3], zero, atol=1e-2,
1168
+ check_shape=False) # <= -40 dB
1169
+
1170
+ N, Wn = cheb1ord(0.2, 0.3, 1, 150)
1171
+ sos = cheby1(N, 1, Wn, 'low', output='sos')
1172
+ sos = xp.asarray(sos)
1173
+
1174
+ w, h = freqz_sos(sos)
1175
+ dB = 20*xp.log10(xp.abs(h))
1176
+ w /= np.pi
1177
+ xp_assert_close(dB[w <= 0.2], zero, atol=1.01, check_shape=False)
1178
+ assert xp.all(dB[w >= 0.3] < -149.9)
1179
+
1180
+ # adapted from ellipord
1181
+ N, Wn = ellipord(0.3, 0.2, 3, 60)
1182
+ sos = ellip(N, 0.3, 60, Wn, 'high', output='sos')
1183
+ sos = xp.asarray(sos)
1184
+
1185
+ w, h = freqz_sos(sos)
1186
+ h = xp.abs(h)
1187
+ w = w / xp.pi
1188
+ xp_assert_close(20 * xp.log10(h[w >= 0.3]), zero, atol=3.01,
1189
+ check_shape=False)
1190
+ xp_assert_close(h[w <= 0.1], zero, atol=1.5e-3,
1191
+ check_shape=False) # <= -60 dB (approx)
1192
+
1193
+ # adapted from buttord
1194
+ N, Wn = buttord([0.2, 0.5], [0.14, 0.6], 3, 40)
1195
+ sos = butter(N, Wn, 'band', output='sos')
1196
+ sos = xp.asarray(sos)
1197
+
1198
+ w, h = freqz_sos(sos)
1199
+ h = xp.abs(h)
1200
+ w = w / xp.pi
1201
+
1202
+ h014 = h[w <= 0.14]
1203
+ xp_assert_close(h014, xp.zeros_like(h014), atol=1e-2) # <= -40 dB
1204
+ h06 = h[w >= 0.6]
1205
+ xp_assert_close(h06, xp.zeros_like(h06), atol=1e-2) # <= -40 dB
1206
+ h0205 = 20 * xp.log10(h[(w >= 0.2) & (w <= 0.5)])
1207
+ xp_assert_close(h0205, xp.zeros_like(h0205), atol=3.01)
1208
+
1209
+ N, Wn = buttord([0.2, 0.5], [0.14, 0.6], 3, 100)
1210
+ sos = butter(N, Wn, 'band', output='sos')
1211
+ sos = xp.asarray(sos)
1212
+
1213
+ w, h = freqz_sos(sos)
1214
+ dB = 20*xp.log10(xp.maximum(xp.abs(h), xp.asarray(1e-10)))
1215
+ w = w / xp.pi
1216
+
1217
+ assert xp.all(dB[(w > 0) & (w <= 0.14)] < -99.9)
1218
+ assert xp.all(dB[w >= 0.6] < -99.9)
1219
+ db0205 = dB[(w >= 0.2) & (w <= 0.5)]
1220
+ xp_assert_close(db0205, xp.zeros_like(db0205), atol=3.01)
1221
+
1222
+ def test_freqz_sos_design_ellip(self, xp):
1223
+ N, Wn = ellipord(0.3, 0.1, 3, 60)
1224
+ sos = ellip(N, 0.3, 60, Wn, 'high', output='sos')
1225
+ sos = xp.asarray(sos)
1226
+
1227
+ w, h = freqz_sos(sos)
1228
+ h = xp.abs(h)
1229
+ w = w / xp.pi
1230
+
1231
+ h03 = 20 * xp.log10(h[w >= 0.3])
1232
+ xp_assert_close(h03, xp.zeros_like(h03), atol=3.01)
1233
+ h01 = h[w <= 0.1]
1234
+ xp_assert_close(h01, xp.zeros_like(h01), atol=1.5e-3) # <= -60 dB (approx)
1235
+
1236
+ N, Wn = ellipord(0.3, 0.2, .5, 150)
1237
+ sos = ellip(N, .5, 150, Wn, 'high', output='sos')
1238
+ sos = xp.asarray(sos)
1239
+
1240
+ w, h = freqz_sos(sos)
1241
+ dB = 20*xp.log10(xp.maximum(xp.abs(h), xp.asarray(1e-10)))
1242
+ w = w / xp.pi
1243
+
1244
+ db03 = dB[w >= 0.3]
1245
+ xp_assert_close(db03, xp.zeros_like(db03), atol=.55)
1246
+ # Allow some numerical slop in the upper bound -150, so this is
1247
+ # a check that dB[w <= 0.2] is less than or almost equal to -150.
1248
+ assert xp.max(dB[w <= 0.2]) < -150*(1 - 1e-12)
1249
+
1250
+ @mpmath_check("0.10")
1251
+ def test_freqz_sos_against_mp(self, xp):
1252
+ # Compare the result of freqz_sos applied to a high order Butterworth
1253
+ # filter against the result computed using mpmath. (signal.freqz fails
1254
+ # miserably with such high order filters.)
1255
+ from . import mpsig
1256
+ N = 500
1257
+ order = 25
1258
+ Wn = 0.15
1259
+ with mpmath.workdps(80):
1260
+ z_mp, p_mp, k_mp = mpsig.butter_lp(order, Wn)
1261
+ w_mp, h_mp = mpsig.zpkfreqz(z_mp, p_mp, k_mp, N)
1262
+ w_mp = xp.asarray([float(x) for x in w_mp], dtype=xp.float64)
1263
+ h_mp = xp.asarray([complex(x) for x in h_mp], dtype=xp.complex128)
1264
+
1265
+ sos = butter(order, Wn, output='sos')
1266
+ sos = xp.asarray(sos, dtype=xp.float64)
1267
+ w, h = freqz_sos(sos, worN=N)
1268
+ xp_assert_close(w, w_mp, rtol=1e-12, atol=1e-14)
1269
+ xp_assert_close(h, h_mp, rtol=1e-12, atol=1e-14)
1270
+
1271
+ def test_fs_param(self, xp):
1272
+ fs = 900
1273
+ sos = xp.asarray(
1274
+ [[0.03934683014103762, 0.07869366028207524, 0.03934683014103762,
1275
+ 1.0, -0.37256600288916636, 0.0],
1276
+ [1.0, 1.0, 0.0, 1.0, -0.9495739996946778, 0.45125966317124144]]
1277
+ )
1278
+
1279
+ # N = None, whole=False
1280
+ w1, h1 = freqz_sos(sos, fs=fs)
1281
+ w2, h2 = freqz_sos(sos)
1282
+ xp_assert_close(h1, h2)
1283
+ xp_assert_close(w1, xp.linspace(0, fs/2, 512, endpoint=False))
1284
+
1285
+ # N = None, whole=True
1286
+ w1, h1 = freqz_sos(sos, whole=True, fs=fs)
1287
+ w2, h2 = freqz_sos(sos, whole=True)
1288
+ xp_assert_close(h1, h2, atol=1e-27)
1289
+ xp_assert_close(w1, xp.linspace(0, fs, 512, endpoint=False))
1290
+
1291
+ # N = 5, whole=False
1292
+ w1, h1 = freqz_sos(sos, 5, fs=fs)
1293
+ w2, h2 = freqz_sos(sos, 5)
1294
+ xp_assert_close(h1, h2)
1295
+ xp_assert_close(w1, xp.linspace(0, fs/2, 5, endpoint=False))
1296
+
1297
+ # N = 5, whole=True
1298
+ w1, h1 = freqz_sos(sos, 5, whole=True, fs=fs)
1299
+ w2, h2 = freqz_sos(sos, 5, whole=True)
1300
+ xp_assert_close(h1, h2)
1301
+ xp_assert_close(w1, xp.linspace(0, fs, 5, endpoint=False))
1302
+
1303
+ @skip_xp_backends(np_only=True, reason="array-likes")
1304
+ def test_fs_param2(self, xp):
1305
+ fs = 900
1306
+ sos = xp.asarray(
1307
+ [[0.03934683014103762, 0.07869366028207524, 0.03934683014103762,
1308
+ 1.0, -0.37256600288916636, 0.0],
1309
+ [1.0, 1.0, 0.0, 1.0, -0.9495739996946778, 0.45125966317124144]]
1310
+ )
1311
+
1312
+ # w is an array_like
1313
+ for w in ([123], (123,), xp.asarray([123]), (50, 123, 230),
1314
+ xp.asarray([50, 123, 230])):
1315
+ w1, h1 = freqz_sos(sos, w, fs=fs)
1316
+ w1, h1 = map(xp.asarray, (w1, h1))
1317
+
1318
+ w2, h2 = freqz_sos(sos, 2*pi*xp.asarray(w, dtype=sos.dtype)/fs)
1319
+ xp_assert_close(h1, h2)
1320
+ xp_assert_close(w, w1, check_dtype=False)
1321
+
1322
+ def test_w_or_N_types(self):
1323
+ # Measure at 7 (polyval) or 8 (fft) equally-spaced points
1324
+ for N in (7, np.int8(7), np.int16(7), np.int32(7), np.int64(7),
1325
+ np.array(7),
1326
+ 8, np.int8(8), np.int16(8), np.int32(8), np.int64(8),
1327
+ np.array(8)):
1328
+
1329
+ w, h = freqz_sos([1, 0, 0, 1, 0, 0], worN=N)
1330
+ assert_array_almost_equal(w, np.pi * np.arange(N) / N)
1331
+ assert_array_almost_equal(h, np.ones(N))
1332
+
1333
+ w, h = freqz_sos([1, 0, 0, 1, 0, 0], worN=N, fs=100)
1334
+ assert_array_almost_equal(w, np.linspace(0, 50, N, endpoint=False))
1335
+ assert_array_almost_equal(h, np.ones(N))
1336
+
1337
+ # Measure at frequency 8 Hz
1338
+ for w in (8.0, 8.0+0j):
1339
+ # Only makes sense when fs is specified
1340
+ w_out, h = freqz_sos([1, 0, 0, 1, 0, 0], worN=w, fs=100)
1341
+ assert_array_almost_equal(w_out, [8])
1342
+ assert_array_almost_equal(h, [1])
1343
+
1344
+ def test_fs_validation(self):
1345
+ sos = butter(4, 0.2, output='sos')
1346
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
1347
+ freqz_sos(sos, fs=np.array([10, 20]))
1348
+
1349
+
1350
+ class TestFreqz_zpk:
1351
+
1352
+ def test_ticket1441(self, xp):
1353
+ """Regression test for ticket 1441."""
1354
+ # Because freqz previously used arange instead of linspace,
1355
+ # when N was large, it would return one more point than
1356
+ # requested.
1357
+ N = 100000
1358
+ w, h = freqz_zpk(xp.asarray([0.5]), xp.asarray([0.5]), 1.0, worN=N)
1359
+ assert w.shape == (N,)
1360
+
1361
+ def test_basic(self, xp):
1362
+ w, h = freqz_zpk(xp.asarray([0.5]), xp.asarray([0.5]), 1.0, worN=8)
1363
+ assert_array_almost_equal(w, xp.pi * xp.arange(8.0) / 8)
1364
+ assert_array_almost_equal(h, xp.ones(8))
1365
+
1366
+ def test_basic_whole(self, xp):
1367
+ w, h = freqz_zpk(xp.asarray([0.5]), xp.asarray([0.5]), 1.0, worN=8, whole=True)
1368
+ assert_array_almost_equal(w, 2 * xp.pi * xp.arange(8.0) / 8)
1369
+ assert_array_almost_equal(h, xp.ones(8))
1370
+
1371
+ def test_vs_freqz(self, xp):
1372
+ b, a = cheby1(4, 5, 0.5, analog=False, output='ba')
1373
+ z, p, k = cheby1(4, 5, 0.5, analog=False, output='zpk')
1374
+
1375
+ b, a = map(xp.asarray, (b, a)) # XXX convert cheby1
1376
+ z, p = map(xp.asarray, (z, p))
1377
+
1378
+ w1, h1 = freqz(b, a)
1379
+ w2, h2 = freqz_zpk(z, p, k)
1380
+ xp_assert_close(w1, w2)
1381
+ xp_assert_close(h1, h2, rtol=1e-6)
1382
+
1383
+ def test_backward_compat(self, xp):
1384
+ # For backward compatibility, test if None act as a wrapper for default
1385
+ w1, h1 = freqz_zpk(xp.asarray([0.5]), xp.asarray([0.5]), 1.0)
1386
+ w2, h2 = freqz_zpk(xp.asarray([0.5]), xp.asarray([0.5]), 1.0, None)
1387
+ assert_array_almost_equal(w1, w2)
1388
+ assert_array_almost_equal(h1, h2)
1389
+
1390
+ def test_fs_param(self, xp):
1391
+ fs = 900
1392
+ z = xp.asarray([-1, -1, -1.0])
1393
+ p = xp.asarray(
1394
+ [0.4747869998473389 + 0.4752230717749344j,
1395
+ 0.37256600288916636,
1396
+ 0.4747869998473389 - 0.4752230717749344j]
1397
+ )
1398
+ k = 0.03934683014103762
1399
+
1400
+ # N = None, whole=False
1401
+ w1, h1 = freqz_zpk(z, p, k, whole=False, fs=fs)
1402
+ w2, h2 = freqz_zpk(z, p, k, whole=False)
1403
+ xp_assert_close(h1, h2)
1404
+ xp_assert_close(w1, xp.linspace(0, fs/2, 512, endpoint=False))
1405
+
1406
+ # N = None, whole=True
1407
+ w1, h1 = freqz_zpk(z, p, k, whole=True, fs=fs)
1408
+ w2, h2 = freqz_zpk(z, p, k, whole=True)
1409
+ xp_assert_close(h1, h2)
1410
+ xp_assert_close(w1, xp.linspace(0, fs, 512, endpoint=False))
1411
+
1412
+ # N = 5, whole=False
1413
+ w1, h1 = freqz_zpk(z, p, k, 5, fs=fs)
1414
+ w2, h2 = freqz_zpk(z, p, k, 5)
1415
+ xp_assert_close(h1, h2)
1416
+ xp_assert_close(w1, xp.linspace(0, fs/2, 5, endpoint=False))
1417
+
1418
+ # N = 5, whole=True
1419
+ w1, h1 = freqz_zpk(z, p, k, 5, whole=True, fs=fs)
1420
+ w2, h2 = freqz_zpk(z, p, k, 5, whole=True)
1421
+ xp_assert_close(h1, h2)
1422
+ xp_assert_close(w1, xp.linspace(0, fs, 5, endpoint=False))
1423
+
1424
+ @skip_xp_backends(np_only=True, reason="array_likes")
1425
+ def test_fs_param2(self, xp):
1426
+ fs = 900
1427
+ z = xp.asarray([-1, -1, -1.0])
1428
+ p = xp.asarray(
1429
+ [0.4747869998473389 + 0.4752230717749344j,
1430
+ 0.37256600288916636,
1431
+ 0.4747869998473389 - 0.4752230717749344j]
1432
+ )
1433
+ k = 0.03934683014103762
1434
+
1435
+ # w is an array_like
1436
+ for w in ([123], (123,), xp.asarray([123]), (50, 123, 230),
1437
+ xp.asarray([50, 123, 230])):
1438
+ w1, h1 = freqz_zpk(z, p, k, w, fs=fs)
1439
+ w2, h2 = freqz_zpk(z, p, k, 2*pi*xp.asarray(w)/fs)
1440
+ xp_assert_close(h1, h2)
1441
+ xp_assert_close(w, w1, check_dtype=False)
1442
+
1443
+ def test_w_or_N_types(self):
1444
+ # Measure at 8 equally-spaced points
1445
+ for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8),
1446
+ np.array(8)):
1447
+
1448
+ w, h = freqz_zpk([], [], 1, worN=N)
1449
+ assert_array_almost_equal(w, np.pi * np.arange(8) / 8.)
1450
+ assert_array_almost_equal(h, np.ones(8))
1451
+
1452
+ w, h = freqz_zpk([], [], 1, worN=N, fs=100)
1453
+ assert_array_almost_equal(w, np.linspace(0, 50, 8, endpoint=False))
1454
+ assert_array_almost_equal(h, np.ones(8))
1455
+
1456
+ # Measure at frequency 8 Hz
1457
+ for w in (8.0, 8.0+0j):
1458
+ # Only makes sense when fs is specified
1459
+ w_out, h = freqz_zpk([], [], 1, worN=w, fs=100)
1460
+ assert_array_almost_equal(w_out, [8])
1461
+ assert_array_almost_equal(h, [1])
1462
+
1463
+ def test_fs_validation(self):
1464
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
1465
+ freqz_zpk([1.0], [1.0], [1.0], fs=np.array([10., 20]))
1466
+
1467
+ with pytest.raises(ValueError, match="Sampling.*be none."):
1468
+ freqz_zpk([1.0], [1.0], [1.0], fs=None)
1469
+
1470
+
1471
+ class TestNormalize:
1472
+
1473
+ def test_allclose(self):
1474
+ """Test for false positive on allclose in normalize() in
1475
+ filter_design.py"""
1476
+ # Test to make sure the allclose call within signal.normalize does not
1477
+ # choose false positives. Then check against a known output from MATLAB
1478
+ # to make sure the fix doesn't break anything.
1479
+
1480
+ # These are the coefficients returned from
1481
+ # `[b,a] = cheby1(8, 0.5, 0.048)'
1482
+ # in MATLAB. There are at least 15 significant figures in each
1483
+ # coefficient, so it makes sense to test for errors on the order of
1484
+ # 1e-13 (this can always be relaxed if different platforms have
1485
+ # different rounding errors)
1486
+ b_matlab = np.array([2.150733144728282e-11, 1.720586515782626e-10,
1487
+ 6.022052805239190e-10, 1.204410561047838e-09,
1488
+ 1.505513201309798e-09, 1.204410561047838e-09,
1489
+ 6.022052805239190e-10, 1.720586515782626e-10,
1490
+ 2.150733144728282e-11])
1491
+ a_matlab = np.array([1.000000000000000e+00, -7.782402035027959e+00,
1492
+ 2.654354569747454e+01, -5.182182531666387e+01,
1493
+ 6.334127355102684e+01, -4.963358186631157e+01,
1494
+ 2.434862182949389e+01, -6.836925348604676e+00,
1495
+ 8.412934944449140e-01])
1496
+
1497
+ # This is the input to signal.normalize after passing through the
1498
+ # equivalent steps in signal.iirfilter as was done for MATLAB
1499
+ b_norm_in = np.array([1.5543135865293012e-06, 1.2434508692234413e-05,
1500
+ 4.3520780422820447e-05, 8.7041560845640893e-05,
1501
+ 1.0880195105705122e-04, 8.7041560845640975e-05,
1502
+ 4.3520780422820447e-05, 1.2434508692234413e-05,
1503
+ 1.5543135865293012e-06])
1504
+ a_norm_in = np.array([7.2269025909127173e+04, -5.6242661430467968e+05,
1505
+ 1.9182761917308895e+06, -3.7451128364682454e+06,
1506
+ 4.5776121393762771e+06, -3.5869706138592605e+06,
1507
+ 1.7596511818472347e+06, -4.9409793515707983e+05,
1508
+ 6.0799461347219651e+04])
1509
+
1510
+ b_output, a_output = normalize(b_norm_in, a_norm_in)
1511
+
1512
+ # The test on b works for decimal=14 but the one for a does not. For
1513
+ # the sake of consistency, both of these are decimal=13. If something
1514
+ # breaks on another platform, it is probably fine to relax this lower.
1515
+ assert_array_almost_equal(b_matlab, b_output, decimal=13)
1516
+ assert_array_almost_equal(a_matlab, a_output, decimal=13)
1517
+
1518
+ def test_errors(self):
1519
+ """Test the error cases."""
1520
+ # all zero denominator
1521
+ assert_raises(ValueError, normalize, [1, 2], 0)
1522
+
1523
+ # denominator not 1 dimensional
1524
+ assert_raises(ValueError, normalize, [1, 2], [[1]])
1525
+
1526
+ # numerator too many dimensions
1527
+ assert_raises(ValueError, normalize, [[[1, 2]]], 1)
1528
+
1529
+
1530
+ class TestLp2lp:
1531
+
1532
+ def test_basic(self, xp):
1533
+ b = xp.asarray([1])
1534
+ a = xp.asarray([1, math.sqrt(2), 1])
1535
+ b_lp, a_lp = lp2lp(b, a, 0.38574256627112119)
1536
+ assert_array_almost_equal(b_lp, xp.asarray([0.1488]), decimal=4)
1537
+ assert_array_almost_equal(a_lp, xp.asarray([1, 0.5455, 0.1488]), decimal=4)
1538
+
1539
+
1540
+ class TestLp2hp:
1541
+
1542
+ @skip_xp_backends(eager_only=True, reason="in-place item assignment")
1543
+ def test_basic(self, xp):
1544
+ b = xp.asarray([0.25059432325190018])
1545
+ a = xp.asarray(
1546
+ [1, 0.59724041654134863, 0.92834805757524175, 0.25059432325190018]
1547
+ )
1548
+ b_hp, a_hp = lp2hp(b, a, 2*math.pi*5000)
1549
+ xp_assert_close(b_hp, xp.asarray([1.0, 0, 0, 0]))
1550
+ xp_assert_close(
1551
+ a_hp, xp.asarray([1, 1.1638e5, 2.3522e9, 1.2373e14]), rtol=1e-4
1552
+ )
1553
+
1554
+
1555
+ class TestLp2bp:
1556
+
1557
+ @skip_xp_backends(eager_only=True, reason="in-place item assignment")
1558
+ def test_basic(self, xp):
1559
+ b = xp.asarray([1])
1560
+ a = xp.asarray([1, 2, 2, 1])
1561
+ b_bp, a_bp = lp2bp(b, a, 2*math.pi*4000, 2*math.pi*2000)
1562
+ xp_assert_close(b_bp, xp.asarray([1.9844e12, 0, 0, 0]), rtol=1e-6)
1563
+ xp_assert_close(
1564
+ a_bp,
1565
+ xp.asarray([1, 2.5133e4, 2.2108e9, 3.3735e13,
1566
+ 1.3965e18, 1.0028e22, 2.5202e26]), rtol=1e-4
1567
+ )
1568
+
1569
+
1570
+ class TestLp2bs:
1571
+
1572
+ @skip_xp_backends(eager_only=True, reason="in-place item assignment")
1573
+ def test_basic(self, xp):
1574
+ b = xp.asarray([1])
1575
+ a = xp.asarray([1, 1])
1576
+ b_bs, a_bs = lp2bs(b, a, 0.41722257286366754, 0.18460575326152251)
1577
+ assert_array_almost_equal(b_bs, xp.asarray([1, 0, 0.17407]), decimal=5)
1578
+ assert_array_almost_equal(a_bs, xp.asarray([1, 0.18461, 0.17407]), decimal=5)
1579
+
1580
+
1581
+ class TestBilinear:
1582
+ """Tests for function `signal.bilinear`. """
1583
+
1584
+ def test_exceptions(self):
1585
+ """Raise all exceptions in `bilinear()`. """
1586
+ with pytest.raises(ValueError, match="Parameter a is not .*"):
1587
+ bilinear(1., np.array([[1, 2, 3]]))
1588
+ with pytest.raises(ValueError, match="Parameter b is not .*"):
1589
+ bilinear(np.ones((2,3)), 1. )
1590
+
1591
+ def test_basic(self):
1592
+ # reference output values computed with sympy
1593
+ b = [0.14879732743343033]
1594
+ a = [1, 0.54552236880522209, 0.14879732743343033]
1595
+ b_zref = [0.08782128175913713, 0.17564256351827426, 0.08782128175913713]
1596
+ a_zref = [1.0, -1.0047722097030667, 0.3560573367396151]
1597
+
1598
+ b_z, a_z = bilinear(b, a, 0.5)
1599
+
1600
+ assert_array_almost_equal_nulp(b_z, b_zref)
1601
+ assert_array_almost_equal_nulp(a_z, a_zref)
1602
+
1603
+ b = [1, 0, 0.17407467530697837]
1604
+ a = [1, 0.18460575326152251, 0.17407467530697837]
1605
+ b_zref = [0.8641286432189045, -1.2157757001964216, 0.8641286432189045]
1606
+ a_zref = [1.0, -1.2157757001964216, 0.7282572864378091]
1607
+
1608
+ b_z, a_z = bilinear(b, a, 0.5)
1609
+
1610
+ assert_array_almost_equal_nulp(b_z, b_zref)
1611
+ assert_array_almost_equal_nulp(a_z, a_zref)
1612
+
1613
+
1614
+ def test_ignore_leading_zeros(self):
1615
+ # regression for gh-6606
1616
+ # results shouldn't change when leading zeros are added to
1617
+ # input numerator or denominator
1618
+ b = [0.14879732743343033]
1619
+ a = [1, 0.54552236880522209, 0.14879732743343033]
1620
+
1621
+ b_zref = [0.08782128175913713, 0.17564256351827426, 0.08782128175913713]
1622
+ a_zref = [1.0, -1.0047722097030667, 0.3560573367396151]
1623
+
1624
+ for lzn, lzd in product(range(4), range(4)):
1625
+ b_z, a_z = bilinear(np.pad(b, (lzn, 0)),
1626
+ np.pad(a, (lzd, 0)),
1627
+ 0.5)
1628
+ assert_array_almost_equal_nulp(b_z, b_zref)
1629
+ assert_array_almost_equal_nulp(a_z, a_zref)
1630
+
1631
+
1632
+ def test_complex(self):
1633
+ # reference output values computed with sympy
1634
+ # this is an elliptical filter, 5Hz width, centered at +50Hz:
1635
+ # z, p, k = signal.ellip(2, 0.5, 20, 2*np.pi*5/2, output='zpk', analog=True)
1636
+ # z = z.astype(complex) + 2j * np.pi * 50
1637
+ # p = p.astype(complex) + 2j * np.pi * 50
1638
+ # b, a = signal.zpk2tf(z, p, k)
1639
+ b = [(0.09999999999999991+0j),
1640
+ -62.831853071795805j,
1641
+ (-9505.857007071314+0j)]
1642
+ a = [(1+0j),
1643
+ (21.09511000343942-628.3185307179587j),
1644
+ (-98310.74322875646-6627.2242613473845j)]
1645
+ # sample frequency
1646
+ fs = 1000
1647
+ b_zref = [(0.09905575106715676-0.00013441423112828688j),
1648
+ (-0.18834281923181084-0.06032810039049478j),
1649
+ (0.08054306669414343+0.05766172295523972j)]
1650
+ a_zref = [(1+0j),
1651
+ (-1.8839476369292854-0.606808151331815j),
1652
+ (0.7954687330018285+0.5717459398142481j)]
1653
+
1654
+ b_z, a_z = bilinear(b, a, fs)
1655
+
1656
+ # the 3 ulp difference determined from testing
1657
+ assert_array_almost_equal_nulp(b_z, b_zref, 3)
1658
+ assert_array_almost_equal_nulp(a_z, a_zref, 3)
1659
+
1660
+
1661
+ def test_fs_validation(self):
1662
+ b = [0.14879732743343033]
1663
+ a = [1, 0.54552236880522209, 0.14879732743343033]
1664
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
1665
+ bilinear(b, a, fs=np.array([10, 20]))
1666
+
1667
+ with pytest.raises(ValueError, match="Sampling.*be none"):
1668
+ bilinear(b, a, fs=None)
1669
+
1670
+
1671
+ class TestLp2lp_zpk:
1672
+
1673
+ @xfail_xp_backends(
1674
+ 'dask.array', reason='https://github.com/dask/dask/issues/11883'
1675
+ )
1676
+ def test_basic(self, xp):
1677
+ z = xp.asarray([])
1678
+ p = xp.asarray([(-1+1j) / math.sqrt(2), (-1-1j) / math.sqrt(2)])
1679
+ k = 1
1680
+ z_lp, p_lp, k_lp = lp2lp_zpk(z, p, k, 5)
1681
+ xp_assert_equal(z_lp, xp.asarray([]))
1682
+ xp_assert_close(_sort_cmplx(p_lp, xp=xp), _sort_cmplx(p, xp=xp) * 5)
1683
+ assert k_lp == 25.
1684
+
1685
+ # Pseudo-Chebyshev with both poles and zeros
1686
+ z = xp.asarray([-2j, +2j])
1687
+ p = xp.asarray([-0.75, -0.5-0.5j, -0.5+0.5j])
1688
+ k = 3
1689
+ z_lp, p_lp, k_lp = lp2lp_zpk(z, p, k, 20)
1690
+ xp_assert_close(
1691
+ _sort_cmplx(z_lp, xp=xp), _sort_cmplx([-40j, +40j], xp=xp)
1692
+ )
1693
+ xp_assert_close(
1694
+ _sort_cmplx(p_lp, xp=xp), _sort_cmplx([-15, -10-10j, -10+10j], xp=xp)
1695
+ )
1696
+ assert k_lp == 60.
1697
+
1698
+ def test_fs_validation(self):
1699
+ z = [-2j, +2j]
1700
+ p = [-0.75, -0.5 - 0.5j, -0.5 + 0.5j]
1701
+ k = 3
1702
+
1703
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
1704
+ bilinear_zpk(z, p, k, fs=np.array([10, 20]))
1705
+
1706
+ with pytest.raises(ValueError, match="Sampling.*be none"):
1707
+ bilinear_zpk(z, p, k, fs=None)
1708
+
1709
+
1710
+ class TestLp2hp_zpk:
1711
+
1712
+ @xfail_xp_backends(
1713
+ 'dask.array', reason='https://github.com/dask/dask/issues/11883'
1714
+ )
1715
+ def test_basic(self, xp):
1716
+ z = xp.asarray([])
1717
+ p = xp.asarray([(-1+1j) / math.sqrt(2), (-1-1j) / math.sqrt(2)])
1718
+ k = 1
1719
+
1720
+ z_hp, p_hp, k_hp = lp2hp_zpk(z, p, k, 5)
1721
+ xp_assert_equal(z_hp, xp.asarray([0.0, 0.0], dtype=z_hp.dtype))
1722
+ xp_assert_close(_sort_cmplx(p_hp, xp=xp), _sort_cmplx(p, xp=xp) * 5)
1723
+ assert math.isclose(k_hp, 1.0, rel_tol=4e-7)
1724
+
1725
+ z = xp.asarray([-2j, +2j])
1726
+ p = xp.asarray([-0.75, -0.5-0.5j, -0.5+0.5j])
1727
+ k = 3
1728
+ z_hp, p_hp, k_hp = lp2hp_zpk(z, p, k, 6)
1729
+ xp_assert_close(
1730
+ _sort_cmplx(z_hp, xp=xp), _sort_cmplx([-3j, 0, +3j], xp=xp)
1731
+ )
1732
+ xp_assert_close(
1733
+ _sort_cmplx(p_hp, xp=xp), _sort_cmplx([-8, -6-6j, -6+6j], xp=xp)
1734
+ )
1735
+ assert k_hp == 32.0
1736
+
1737
+
1738
+ class TestLp2bp_zpk:
1739
+
1740
+ @xfail_xp_backends(
1741
+ 'dask.array', reason='https://github.com/dask/dask/issues/11883'
1742
+ )
1743
+ def test_basic(self, xp):
1744
+ z = xp.asarray([-2j, +2j])
1745
+ p = xp.asarray([-0.75, -0.5-0.5j, -0.5+0.5j])
1746
+ k = 3
1747
+ z_bp, p_bp, k_bp = lp2bp_zpk(z, p, k, 15, 8)
1748
+ xp_assert_close(
1749
+ _sort_cmplx(z_bp, xp=xp),
1750
+ _sort_cmplx([-25j, -9j, 0, +9j, +25j], xp=xp), check_dtype=False
1751
+ )
1752
+ xp_assert_close(
1753
+ _sort_cmplx(p_bp, xp=xp),
1754
+ _sort_cmplx(
1755
+ [-3 + 6j*math.sqrt(6), -3 - 6j*math.sqrt(6),
1756
+ +2j + cmath.sqrt(-8j - 225) - 2, -2j + cmath.sqrt(+8j - 225) - 2,
1757
+ +2j - cmath.sqrt(-8j - 225) - 2, -2j - cmath.sqrt(+8j - 225) - 2
1758
+ ], xp=xp
1759
+ ), check_dtype=False
1760
+ )
1761
+ assert math.isclose(k_bp, 24.0)
1762
+
1763
+
1764
+ class TestLp2bs_zpk:
1765
+
1766
+ @xfail_xp_backends(
1767
+ 'dask.array', reason='https://github.com/dask/dask/issues/11883'
1768
+ )
1769
+ def test_basic(self, xp):
1770
+ z = xp.asarray([-2j, +2j])
1771
+ p = xp.asarray([-0.75, -0.5-0.5j, -0.5+0.5j])
1772
+ k = 3
1773
+
1774
+ z_bs, p_bs, k_bs = lp2bs_zpk(z, p, k, 35, 12)
1775
+
1776
+ xp_assert_close(
1777
+ _sort_cmplx(z_bs, xp=xp),
1778
+ _sort_cmplx([+35j, -35j,
1779
+ +3j + math.sqrt(1234)*1j,
1780
+ -3j + math.sqrt(1234)*1j,
1781
+ +3j - math.sqrt(1234)*1j,
1782
+ -3j - math.sqrt(1234)*1j], xp=xp), check_dtype=False
1783
+ )
1784
+ xp_assert_close(
1785
+ _sort_cmplx(p_bs, xp=xp),
1786
+ _sort_cmplx([+3j*math.sqrt(129) - 8,
1787
+ -3j*math.sqrt(129) - 8,
1788
+ (-6 + 6j) - cmath.sqrt(-1225 - 72j),
1789
+ (-6 - 6j) - cmath.sqrt(-1225 + 72j),
1790
+ (-6 + 6j) + cmath.sqrt(-1225 - 72j),
1791
+ (-6 - 6j) + cmath.sqrt(-1225 + 72j), ], xp=xp),
1792
+ check_dtype=False
1793
+ )
1794
+ assert math.isclose(k_bs, 32.0)
1795
+
1796
+
1797
+ class TestBilinear_zpk:
1798
+
1799
+ @xfail_xp_backends(
1800
+ 'dask.array', reason='https://github.com/dask/dask/issues/11883'
1801
+ )
1802
+ def test_basic(self, xp):
1803
+ z = xp.asarray([-2j, +2j])
1804
+ p = xp.asarray([-0.75, -0.5-0.5j, -0.5+0.5j])
1805
+ k = 3
1806
+
1807
+ z_d, p_d, k_d = bilinear_zpk(z, p, k, 10)
1808
+
1809
+ xp_assert_close(
1810
+ _sort_cmplx(z_d, xp=xp),
1811
+ _sort_cmplx([(20-2j) / (20+2j), (20+2j) / (20-2j), -1], xp=xp)
1812
+ )
1813
+ xp_assert_close(
1814
+ _sort_cmplx(p_d, xp=xp),
1815
+ _sort_cmplx(
1816
+ [77/83, (1j/2 + 39/2) / (41/2 - 1j/2), (39/2 - 1j/2) / (1j/2 + 41/2)],
1817
+ xp=xp
1818
+ )
1819
+ )
1820
+ assert math.isclose(k_d, 9696/69803, rel_tol=4e-7)
1821
+
1822
+
1823
+ class TestPrototypeType:
1824
+
1825
+ def test_output_type(self):
1826
+ # Prototypes should consistently output arrays, not lists
1827
+ # https://github.com/scipy/scipy/pull/441
1828
+ for func in (buttap,
1829
+ besselap,
1830
+ lambda N: cheb1ap(N, 1),
1831
+ lambda N: cheb2ap(N, 20),
1832
+ lambda N: ellipap(N, 1, 20)):
1833
+ for N in range(7):
1834
+ z, p, k = func(N)
1835
+ assert isinstance(z, np.ndarray)
1836
+ assert isinstance(p, np.ndarray)
1837
+
1838
+
1839
+ def dB(x):
1840
+ # Return magnitude in decibels, avoiding divide-by-zero warnings
1841
+ # (and deal with some "not less-ordered" errors when -inf shows up)
1842
+ return 20 * np.log10(np.maximum(np.abs(x), np.finfo(np.float64).tiny))
1843
+
1844
+
1845
+ class TestButtord:
1846
+
1847
+ def test_lowpass(self):
1848
+ wp = 0.2
1849
+ ws = 0.3
1850
+ rp = 3
1851
+ rs = 60
1852
+ N, Wn = buttord(wp, ws, rp, rs, False)
1853
+ b, a = butter(N, Wn, 'lowpass', False)
1854
+ w, h = freqz(b, a)
1855
+ w /= np.pi
1856
+ assert np.all(-rp < dB(h[w <= wp]))
1857
+ assert np.all(dB(h[ws <= w]) < -rs)
1858
+
1859
+ assert N == 16
1860
+ xp_assert_close(Wn,
1861
+ 2.0002776782743284e-01, rtol=1e-15)
1862
+
1863
+ def test_highpass(self):
1864
+ wp = 0.3
1865
+ ws = 0.2
1866
+ rp = 3
1867
+ rs = 70
1868
+ N, Wn = buttord(wp, ws, rp, rs, False)
1869
+ b, a = butter(N, Wn, 'highpass', False)
1870
+ w, h = freqz(b, a)
1871
+ w /= np.pi
1872
+ assert np.all(-rp < dB(h[wp <= w]))
1873
+ assert np.all(dB(h[w <= ws]) < -rs)
1874
+
1875
+ assert N == 18
1876
+ xp_assert_close(Wn,
1877
+ 2.9996603079132672e-01, rtol=1e-15)
1878
+
1879
+ def test_bandpass(self):
1880
+ wp = [0.2, 0.5]
1881
+ ws = [0.1, 0.6]
1882
+ rp = 3
1883
+ rs = 80
1884
+ N, Wn = buttord(wp, ws, rp, rs, False)
1885
+ b, a = butter(N, Wn, 'bandpass', False)
1886
+ w, h = freqz(b, a)
1887
+ w /= np.pi
1888
+
1889
+ assert np.all((-rp - 0.1) < dB(h[np.logical_and(wp[0] <= w, w <= wp[1])]))
1890
+
1891
+ assert np.all(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]) < (-rs + 0.1))
1892
+
1893
+ assert N == 18
1894
+ xp_assert_close(Wn, [1.9998742411409134e-01, 5.0002139595676276e-01],
1895
+ rtol=1e-15)
1896
+
1897
+ def test_bandstop(self):
1898
+ wp = [0.1, 0.6]
1899
+ ws = [0.2, 0.5]
1900
+ rp = 3
1901
+ rs = 90
1902
+ N, Wn = buttord(wp, ws, rp, rs, False)
1903
+ b, a = butter(N, Wn, 'bandstop', False)
1904
+ w, h = freqz(b, a)
1905
+ w /= np.pi
1906
+
1907
+ assert np.all(-rp < dB(h[np.logical_or(w <= wp[0], wp[1] <= w)]))
1908
+ assert np.all(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]) < -rs)
1909
+
1910
+ assert N == 20
1911
+ xp_assert_close(Wn, [1.4759432329294042e-01, 5.9997365985276407e-01],
1912
+ rtol=1e-6)
1913
+
1914
+ def test_analog(self):
1915
+ wp = 200
1916
+ ws = 600
1917
+ rp = 3
1918
+ rs = 60
1919
+ N, Wn = buttord(wp, ws, rp, rs, True)
1920
+ b, a = butter(N, Wn, 'lowpass', True)
1921
+ w, h = freqs(b, a)
1922
+ assert np.all(-rp < dB(h[w <= wp]))
1923
+ assert np.all(dB(h[ws <= w]) < -rs)
1924
+
1925
+ assert N == 7
1926
+ xp_assert_close(Wn, 2.0006785355671877e+02, rtol=1e-15)
1927
+
1928
+ n, Wn = buttord(1, 550/450, 1, 26, analog=True)
1929
+ assert n == 19
1930
+ xp_assert_close(Wn, 1.0361980524629517, rtol=1e-15)
1931
+
1932
+ xp_assert_equal(buttord(1, 1.2, 1, 80, analog=True)[0], 55)
1933
+
1934
+ def test_fs_param(self):
1935
+ wp = [4410, 11025]
1936
+ ws = [2205, 13230]
1937
+ rp = 3
1938
+ rs = 80
1939
+ fs = 44100
1940
+ N, Wn = buttord(wp, ws, rp, rs, False, fs=fs)
1941
+ b, a = butter(N, Wn, 'bandpass', False, fs=fs)
1942
+ w, h = freqz(b, a, fs=fs)
1943
+ assert np.all(-rp - 0.1 < dB(h[np.logical_and(wp[0] <= w, w <= wp[1])]))
1944
+ assert np.all(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]) < -rs + 0.1)
1945
+
1946
+ assert N == 18
1947
+ xp_assert_close(Wn, [4409.722701715714, 11025.47178084662],
1948
+ rtol=1e-15)
1949
+
1950
+ def test_invalid_input(self):
1951
+ with pytest.raises(ValueError) as exc_info:
1952
+ buttord([20, 50], [14, 60], 3, 2)
1953
+ assert "gpass should be smaller than gstop" in str(exc_info.value)
1954
+
1955
+ with pytest.raises(ValueError) as exc_info:
1956
+ buttord([20, 50], [14, 60], -1, 2)
1957
+ assert "gpass should be larger than 0.0" in str(exc_info.value)
1958
+
1959
+ with pytest.raises(ValueError) as exc_info:
1960
+ buttord([20, 50], [14, 60], 1, -2)
1961
+ assert "gstop should be larger than 0.0" in str(exc_info.value)
1962
+
1963
+ @pytest.mark.thread_unsafe
1964
+ def test_runtime_warnings(self):
1965
+ msg = "Order is zero.*|divide by zero encountered"
1966
+ with pytest.warns(RuntimeWarning, match=msg):
1967
+ buttord(0.0, 1.0, 3, 60)
1968
+
1969
+ def test_ellip_butter(self):
1970
+ # The purpose of the test is to compare to some known output from past
1971
+ # scipy versions. The values to compare to are generated with scipy
1972
+ # 1.9.1 (there is nothing special about this particular version though)
1973
+ n, wn = buttord([0.1, 0.6], [0.2, 0.5], 3, 60)
1974
+ assert n == 14
1975
+
1976
+ def test_fs_validation(self):
1977
+ wp = 0.2
1978
+ ws = 0.3
1979
+ rp = 3
1980
+ rs = 60
1981
+
1982
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
1983
+ buttord(wp, ws, rp, rs, False, fs=np.array([10, 20]))
1984
+
1985
+
1986
+ class TestCheb1ord:
1987
+
1988
+ def test_lowpass(self):
1989
+ wp = 0.2
1990
+ ws = 0.3
1991
+ rp = 3
1992
+ rs = 60
1993
+ N, Wn = cheb1ord(wp, ws, rp, rs, False)
1994
+ b, a = cheby1(N, rp, Wn, 'low', False)
1995
+ w, h = freqz(b, a)
1996
+ w /= np.pi
1997
+ assert np.all(-rp - 0.1 < dB(h[w <= wp]))
1998
+ assert np.all(dB(h[ws <= w]) < -rs + 0.1)
1999
+
2000
+ assert N == 8
2001
+ xp_assert_close(Wn, 0.2, rtol=1e-15)
2002
+
2003
+ def test_highpass(self):
2004
+ wp = 0.3
2005
+ ws = 0.2
2006
+ rp = 3
2007
+ rs = 70
2008
+ N, Wn = cheb1ord(wp, ws, rp, rs, False)
2009
+ b, a = cheby1(N, rp, Wn, 'high', False)
2010
+ w, h = freqz(b, a)
2011
+ w /= np.pi
2012
+ assert np.all(-rp - 0.1 < dB(h[wp <= w]))
2013
+ assert np.all(dB(h[w <= ws]) < -rs + 0.1)
2014
+
2015
+ assert N == 9
2016
+ xp_assert_close(Wn, 0.3, rtol=1e-15)
2017
+
2018
+ def test_bandpass(self):
2019
+ wp = [0.2, 0.5]
2020
+ ws = [0.1, 0.6]
2021
+ rp = 3
2022
+ rs = 80
2023
+ N, Wn = cheb1ord(wp, ws, rp, rs, False)
2024
+ b, a = cheby1(N, rp, Wn, 'band', False)
2025
+ w, h = freqz(b, a)
2026
+ w /= np.pi
2027
+ assert np.all(-rp - 0.1 < dB(h[np.logical_and(wp[0] <= w, w <= wp[1])]))
2028
+ assert np.all(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]) < -rs + 0.1)
2029
+
2030
+ assert N == 9
2031
+ xp_assert_close(Wn, [0.2, 0.5], rtol=1e-15)
2032
+
2033
+ def test_bandstop(self):
2034
+ wp = [0.1, 0.6]
2035
+ ws = [0.2, 0.5]
2036
+ rp = 3
2037
+ rs = 90
2038
+ N, Wn = cheb1ord(wp, ws, rp, rs, False)
2039
+ b, a = cheby1(N, rp, Wn, 'stop', False)
2040
+ w, h = freqz(b, a)
2041
+ w /= np.pi
2042
+ assert np.all(-rp - 0.1 < dB(h[np.logical_or(w <= wp[0], wp[1] <= w)]))
2043
+ assert np.all(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]) < -rs + 0.1)
2044
+
2045
+ assert N == 10
2046
+ xp_assert_close(Wn, [0.14758232569947785, 0.6], rtol=1e-5)
2047
+
2048
+ def test_analog(self):
2049
+ wp = 700
2050
+ ws = 100
2051
+ rp = 3
2052
+ rs = 70
2053
+ N, Wn = cheb1ord(wp, ws, rp, rs, True)
2054
+ b, a = cheby1(N, rp, Wn, 'high', True)
2055
+ w, h = freqs(b, a)
2056
+ assert np.all(-rp - 0.1 < dB(h[wp <= w]))
2057
+ assert np.all(dB(h[w <= ws]) < -rs + 0.1)
2058
+
2059
+ assert N == 4
2060
+ xp_assert_close(Wn, 700.0, rtol=1e-15)
2061
+
2062
+ xp_assert_equal(cheb1ord(1, 1.2, 1, 80, analog=True)[0], 17)
2063
+
2064
+ def test_fs_param(self):
2065
+ wp = 4800
2066
+ ws = 7200
2067
+ rp = 3
2068
+ rs = 60
2069
+ fs = 48000
2070
+ N, Wn = cheb1ord(wp, ws, rp, rs, False, fs=fs)
2071
+ b, a = cheby1(N, rp, Wn, 'low', False, fs=fs)
2072
+ w, h = freqz(b, a, fs=fs)
2073
+ assert np.all(-rp - 0.1 < dB(h[w <= wp]))
2074
+ assert np.all(dB(h[ws <= w]) < -rs + 0.1)
2075
+
2076
+ assert N == 8
2077
+ xp_assert_close(Wn, 4800.0, rtol=1e-15)
2078
+
2079
+ def test_invalid_input(self):
2080
+ with pytest.raises(ValueError) as exc_info:
2081
+ cheb1ord(0.2, 0.3, 3, 2)
2082
+ assert "gpass should be smaller than gstop" in str(exc_info.value)
2083
+
2084
+ with pytest.raises(ValueError) as exc_info:
2085
+ cheb1ord(0.2, 0.3, -1, 2)
2086
+ assert "gpass should be larger than 0.0" in str(exc_info.value)
2087
+
2088
+ with pytest.raises(ValueError) as exc_info:
2089
+ cheb1ord(0.2, 0.3, 1, -2)
2090
+ assert "gstop should be larger than 0.0" in str(exc_info.value)
2091
+
2092
+ def test_ellip_cheb1(self):
2093
+ # The purpose of the test is to compare to some known output from past
2094
+ # scipy versions. The values to compare to are generated with scipy
2095
+ # 1.9.1 (there is nothing special about this particular version though)
2096
+ n, wn = cheb1ord([0.1, 0.6], [0.2, 0.5], 3, 60)
2097
+ assert n == 7
2098
+
2099
+ n2, w2 = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60)
2100
+ assert not (wn == w2).all()
2101
+
2102
+ def test_fs_validation(self):
2103
+ wp = 0.2
2104
+ ws = 0.3
2105
+ rp = 3
2106
+ rs = 60
2107
+
2108
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
2109
+ cheb1ord(wp, ws, rp, rs, False, fs=np.array([10, 20]))
2110
+
2111
+
2112
+ class TestCheb2ord:
2113
+
2114
+ def test_lowpass(self):
2115
+ wp = 0.2
2116
+ ws = 0.3
2117
+ rp = 3
2118
+ rs = 60
2119
+ N, Wn = cheb2ord(wp, ws, rp, rs, False)
2120
+ b, a = cheby2(N, rs, Wn, 'lp', False)
2121
+ w, h = freqz(b, a)
2122
+ w /= np.pi
2123
+ assert np.all(-rp - 0.1 < dB(h[w <= wp]))
2124
+ assert np.all(dB(h[ws <= w]) < -rs + 0.1)
2125
+
2126
+ assert N == 8
2127
+ xp_assert_close(Wn, 0.28647639976553163, rtol=1e-15)
2128
+
2129
+ def test_highpass(self):
2130
+ wp = 0.3
2131
+ ws = 0.2
2132
+ rp = 3
2133
+ rs = 70
2134
+ N, Wn = cheb2ord(wp, ws, rp, rs, False)
2135
+ b, a = cheby2(N, rs, Wn, 'hp', False)
2136
+ w, h = freqz(b, a)
2137
+ w /= np.pi
2138
+ assert np.all(-rp - 0.1 < dB(h[wp <= w]))
2139
+ assert np.all(dB(h[w <= ws]) < -rs + 0.1)
2140
+
2141
+ assert N == 9
2142
+ xp_assert_close(Wn, 0.20697492182903282, rtol=1e-15)
2143
+
2144
+ def test_bandpass(self):
2145
+ wp = [0.2, 0.5]
2146
+ ws = [0.1, 0.6]
2147
+ rp = 3
2148
+ rs = 80
2149
+ N, Wn = cheb2ord(wp, ws, rp, rs, False)
2150
+ b, a = cheby2(N, rs, Wn, 'bp', False)
2151
+ w, h = freqz(b, a)
2152
+ w /= np.pi
2153
+ assert np.all(-rp - 0.1 < dB(h[np.logical_and(wp[0] <= w, w <= wp[1])]))
2154
+ assert np.all(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]) < -rs + 0.1)
2155
+
2156
+ assert N == 9
2157
+ xp_assert_close(Wn, [0.14876937565923479, 0.59748447842351482],
2158
+ rtol=1e-15)
2159
+
2160
+ def test_bandstop(self):
2161
+ wp = [0.1, 0.6]
2162
+ ws = [0.2, 0.5]
2163
+ rp = 3
2164
+ rs = 90
2165
+ N, Wn = cheb2ord(wp, ws, rp, rs, False)
2166
+ b, a = cheby2(N, rs, Wn, 'bs', False)
2167
+ w, h = freqz(b, a)
2168
+ w /= np.pi
2169
+ assert np.all(-rp - 0.1 < dB(h[np.logical_or(w <= wp[0], wp[1] <= w)]))
2170
+ assert np.all(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]) < -rs + 0.1)
2171
+
2172
+ assert N == 10
2173
+ xp_assert_close(Wn, [0.19926249974781743, 0.50125246585567362],
2174
+ rtol=1e-6)
2175
+
2176
+ def test_analog(self):
2177
+ wp = [20, 50]
2178
+ ws = [10, 60]
2179
+ rp = 3
2180
+ rs = 80
2181
+ N, Wn = cheb2ord(wp, ws, rp, rs, True)
2182
+ b, a = cheby2(N, rs, Wn, 'bp', True)
2183
+ w, h = freqs(b, a)
2184
+ assert np.all(-rp - 0.1 < dB(h[np.logical_and(wp[0] <= w, w <= wp[1])]))
2185
+ assert np.all(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]) < -rs + 0.1)
2186
+
2187
+ assert N == 11
2188
+ xp_assert_close(Wn, [1.673740595370124e+01, 5.974641487254268e+01],
2189
+ rtol=1e-15)
2190
+
2191
+ def test_fs_param(self):
2192
+ wp = 150
2193
+ ws = 100
2194
+ rp = 3
2195
+ rs = 70
2196
+ fs = 1000
2197
+ N, Wn = cheb2ord(wp, ws, rp, rs, False, fs=fs)
2198
+ b, a = cheby2(N, rs, Wn, 'hp', False, fs=fs)
2199
+ w, h = freqz(b, a, fs=fs)
2200
+ assert np.all(-rp - 0.1 < dB(h[wp <= w]))
2201
+ assert np.all(dB(h[w <= ws]) < -rs + 0.1)
2202
+
2203
+ assert N == 9
2204
+ xp_assert_close(Wn, 103.4874609145164, rtol=1e-15)
2205
+
2206
+ def test_invalid_input(self):
2207
+ with pytest.raises(ValueError) as exc_info:
2208
+ cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 2)
2209
+ assert "gpass should be smaller than gstop" in str(exc_info.value)
2210
+
2211
+ with pytest.raises(ValueError) as exc_info:
2212
+ cheb2ord([0.1, 0.6], [0.2, 0.5], -1, 2)
2213
+ assert "gpass should be larger than 0.0" in str(exc_info.value)
2214
+
2215
+ with pytest.raises(ValueError) as exc_info:
2216
+ cheb2ord([0.1, 0.6], [0.2, 0.5], 1, -2)
2217
+ assert "gstop should be larger than 0.0" in str(exc_info.value)
2218
+
2219
+ def test_ellip_cheb2(self):
2220
+ # The purpose of the test is to compare to some known output from past
2221
+ # scipy versions. The values to compare to are generated with scipy
2222
+ # 1.9.1 (there is nothing special about this particular version though)
2223
+ n, wn = cheb2ord([0.1, 0.6], [0.2, 0.5], 3, 60)
2224
+ assert n == 7
2225
+
2226
+ n1, w1 = cheb1ord([0.1, 0.6], [0.2, 0.5], 3, 60)
2227
+ assert not (wn == w1).all()
2228
+
2229
+ def test_fs_validation(self):
2230
+ wp = 0.2
2231
+ ws = 0.3
2232
+ rp = 3
2233
+ rs = 60
2234
+
2235
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
2236
+ cheb2ord(wp, ws, rp, rs, False, fs=np.array([10, 20]))
2237
+
2238
+
2239
+ class TestEllipord:
2240
+
2241
+ def test_lowpass(self):
2242
+ wp = 0.2
2243
+ ws = 0.3
2244
+ rp = 3
2245
+ rs = 60
2246
+ N, Wn = ellipord(wp, ws, rp, rs, False)
2247
+ b, a = ellip(N, rp, rs, Wn, 'lp', False)
2248
+ w, h = freqz(b, a)
2249
+ w /= np.pi
2250
+ assert np.all(-rp - 0.1 < dB(h[w <= wp]))
2251
+ assert np.all(dB(h[ws <= w]) < -rs + 0.1)
2252
+
2253
+ assert N == 5
2254
+ xp_assert_close(Wn, 0.2, rtol=1e-15)
2255
+
2256
+ def test_lowpass_1000dB(self):
2257
+ # failed when ellipkm1 wasn't used in ellipord and ellipap
2258
+ wp = 0.2
2259
+ ws = 0.3
2260
+ rp = 3
2261
+ rs = 1000
2262
+ N, Wn = ellipord(wp, ws, rp, rs, False)
2263
+ sos = ellip(N, rp, rs, Wn, 'lp', False, output='sos')
2264
+ w, h = freqz_sos(sos)
2265
+ w /= np.pi
2266
+ assert np.all(-rp - 0.1 < dB(h[w <= wp]))
2267
+ assert np.all(dB(h[ws <= w]) < -rs + 0.1)
2268
+
2269
+ def test_highpass(self):
2270
+ wp = 0.3
2271
+ ws = 0.2
2272
+ rp = 3
2273
+ rs = 70
2274
+ N, Wn = ellipord(wp, ws, rp, rs, False)
2275
+ b, a = ellip(N, rp, rs, Wn, 'hp', False)
2276
+ w, h = freqz(b, a)
2277
+ w /= np.pi
2278
+ assert np.all(-rp - 0.1 < dB(h[wp <= w]))
2279
+ assert np.all(dB(h[w <= ws]) < -rs + 0.1)
2280
+
2281
+ assert N == 6
2282
+ xp_assert_close(Wn, 0.3, rtol=1e-15)
2283
+
2284
+ def test_bandpass(self):
2285
+ wp = [0.2, 0.5]
2286
+ ws = [0.1, 0.6]
2287
+ rp = 3
2288
+ rs = 80
2289
+ N, Wn = ellipord(wp, ws, rp, rs, False)
2290
+ b, a = ellip(N, rp, rs, Wn, 'bp', False)
2291
+ w, h = freqz(b, a)
2292
+ w /= np.pi
2293
+ assert np.all(-rp - 0.1 < dB(h[np.logical_and(wp[0] <= w, w <= wp[1])]))
2294
+ assert np.all(dB(h[np.logical_or(w <= ws[0], ws[1] <= w)]) < -rs + 0.1)
2295
+
2296
+ assert N == 6
2297
+ xp_assert_close(Wn, [0.2, 0.5], rtol=1e-15)
2298
+
2299
+ def test_bandstop(self):
2300
+ wp = [0.1, 0.6]
2301
+ ws = [0.2, 0.5]
2302
+ rp = 3
2303
+ rs = 90
2304
+ N, Wn = ellipord(wp, ws, rp, rs, False)
2305
+ b, a = ellip(N, rp, rs, Wn, 'bs', False)
2306
+ w, h = freqz(b, a)
2307
+ w /= np.pi
2308
+ assert np.all(-rp - 0.1 < dB(h[np.logical_or(w <= wp[0], wp[1] <= w)]))
2309
+ assert np.all(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]) < -rs + 0.1)
2310
+
2311
+ assert N == 7
2312
+ xp_assert_close(Wn, [0.14758232794342988, 0.6], rtol=1e-5)
2313
+
2314
+ def test_analog(self):
2315
+ wp = [1000, 6000]
2316
+ ws = [2000, 5000]
2317
+ rp = 3
2318
+ rs = 90
2319
+ N, Wn = ellipord(wp, ws, rp, rs, True)
2320
+ b, a = ellip(N, rp, rs, Wn, 'bs', True)
2321
+ w, h = freqs(b, a)
2322
+ assert np.all(-rp - 0.1 < dB(h[np.logical_or(w <= wp[0], wp[1] <= w)]))
2323
+ assert np.all(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]) < -rs + 0.1)
2324
+
2325
+ assert N == 8
2326
+ xp_assert_close(Wn, [1666.6666, 6000])
2327
+
2328
+ assert ellipord(1, 1.2, 1, 80, analog=True)[0] == 9
2329
+
2330
+ def test_fs_param(self):
2331
+ wp = [400, 2400]
2332
+ ws = [800, 2000]
2333
+ rp = 3
2334
+ rs = 90
2335
+ fs = 8000
2336
+ N, Wn = ellipord(wp, ws, rp, rs, False, fs=fs)
2337
+ b, a = ellip(N, rp, rs, Wn, 'bs', False, fs=fs)
2338
+ w, h = freqz(b, a, fs=fs)
2339
+ assert np.all(-rp - 0.1 < dB(h[np.logical_or(w <= wp[0], wp[1] <= w)]))
2340
+ assert np.all(dB(h[np.logical_and(ws[0] <= w, w <= ws[1])]) < -rs + 0.1)
2341
+
2342
+ assert N == 7
2343
+ xp_assert_close(Wn, [590.3293117737195, 2400], rtol=1e-5)
2344
+
2345
+ def test_invalid_input(self):
2346
+ with pytest.raises(ValueError) as exc_info:
2347
+ ellipord(0.2, 0.5, 3, 2)
2348
+ assert "gpass should be smaller than gstop" in str(exc_info.value)
2349
+
2350
+ with pytest.raises(ValueError) as exc_info:
2351
+ ellipord(0.2, 0.5, -1, 2)
2352
+ assert "gpass should be larger than 0.0" in str(exc_info.value)
2353
+
2354
+ with pytest.raises(ValueError) as exc_info:
2355
+ ellipord(0.2, 0.5, 1, -2)
2356
+ assert "gstop should be larger than 0.0" in str(exc_info.value)
2357
+
2358
+ def test_ellip_butter(self):
2359
+ # The purpose of the test is to compare to some known output from past
2360
+ # scipy versions. The values to compare to are generated with scipy
2361
+ # 1.9.1 (there is nothing special about this particular version though)
2362
+ n, wn = ellipord([0.1, 0.6], [0.2, 0.5], 3, 60)
2363
+ assert n == 5
2364
+
2365
+ def test_fs_validation(self):
2366
+ wp = 0.2
2367
+ ws = 0.3
2368
+ rp = 3
2369
+ rs = 60
2370
+
2371
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
2372
+ ellipord(wp, ws, rp, rs, False, fs=np.array([10, 20]))
2373
+
2374
+
2375
+ class TestBessel:
2376
+
2377
+ def test_degenerate(self):
2378
+ for norm in ('delay', 'phase', 'mag'):
2379
+ # 0-order filter is just a passthrough
2380
+ b, a = bessel(0, 1, analog=True, norm=norm)
2381
+ xp_assert_equal(b, np.asarray([1.0]))
2382
+ xp_assert_equal(a, np.asarray([1.0]))
2383
+
2384
+ # 1-order filter is same for all types
2385
+ b, a = bessel(1, 1, analog=True, norm=norm)
2386
+ xp_assert_close(b, np.asarray([1.0]), rtol=1e-15)
2387
+ xp_assert_close(a, np.asarray([1.0, 1]), rtol=1e-15)
2388
+
2389
+ z, p, k = bessel(1, 0.3, analog=True, output='zpk', norm=norm)
2390
+ xp_assert_equal(z, np.asarray([]))
2391
+ xp_assert_close(p, np.asarray([-0.3+0j]), rtol=1e-14)
2392
+ xp_assert_close(k, 0.3, rtol=1e-14)
2393
+
2394
+ def test_high_order(self):
2395
+ # high even order, 'phase'
2396
+ z, p, k = bessel(24, 100, analog=True, output='zpk')
2397
+ z2 = []
2398
+ p2 = [
2399
+ -9.055312334014323e+01 + 4.844005815403969e+00j,
2400
+ -8.983105162681878e+01 + 1.454056170018573e+01j,
2401
+ -8.837357994162065e+01 + 2.426335240122282e+01j,
2402
+ -8.615278316179575e+01 + 3.403202098404543e+01j,
2403
+ -8.312326467067703e+01 + 4.386985940217900e+01j,
2404
+ -7.921695461084202e+01 + 5.380628489700191e+01j,
2405
+ -7.433392285433246e+01 + 6.388084216250878e+01j,
2406
+ -6.832565803501586e+01 + 7.415032695116071e+01j,
2407
+ -6.096221567378025e+01 + 8.470292433074425e+01j,
2408
+ -5.185914574820616e+01 + 9.569048385258847e+01j,
2409
+ -4.027853855197555e+01 + 1.074195196518679e+02j,
2410
+ -2.433481337524861e+01 + 1.207298683731973e+02j,
2411
+ ]
2412
+ k2 = 9.999999999999989e+47
2413
+ xp_assert_equal(z, z2)
2414
+ xp_assert_close(sorted(p, key=np.imag),
2415
+ sorted(np.union1d(p2, np.conj(p2)), key=np.imag))
2416
+ xp_assert_close(k, k2, rtol=1e-14)
2417
+
2418
+ # high odd order, 'phase'
2419
+ z, p, k = bessel(23, 1000, analog=True, output='zpk')
2420
+ z2 = []
2421
+ p2 = [
2422
+ -2.497697202208956e+02 + 1.202813187870698e+03j,
2423
+ -4.126986617510172e+02 + 1.065328794475509e+03j,
2424
+ -5.304922463809596e+02 + 9.439760364018479e+02j,
2425
+ -9.027564978975828e+02 + 1.010534334242318e+02j,
2426
+ -8.909283244406079e+02 + 2.023024699647598e+02j,
2427
+ -8.709469394347836e+02 + 3.039581994804637e+02j,
2428
+ -8.423805948131370e+02 + 4.062657947488952e+02j,
2429
+ -8.045561642249877e+02 + 5.095305912401127e+02j,
2430
+ -7.564660146766259e+02 + 6.141594859516342e+02j,
2431
+ -6.965966033906477e+02 + 7.207341374730186e+02j,
2432
+ -6.225903228776276e+02 + 8.301558302815096e+02j,
2433
+ -9.066732476324988e+02]
2434
+ k2 = 9.999999999999983e+68
2435
+ xp_assert_equal(z, z2)
2436
+ xp_assert_close(sorted(p, key=np.imag),
2437
+ sorted(np.union1d(p2, np.conj(p2)), key=np.imag))
2438
+ xp_assert_close(k, k2, rtol=1e-14)
2439
+
2440
+ # high even order, 'delay' (Orchard 1965 "The Roots of the
2441
+ # Maximally Flat-Delay Polynomials" Table 1)
2442
+ z, p, k = bessel(31, 1, analog=True, output='zpk', norm='delay')
2443
+ p2 = [-20.876706,
2444
+ -20.826543 + 1.735732j,
2445
+ -20.675502 + 3.473320j,
2446
+ -20.421895 + 5.214702j,
2447
+ -20.062802 + 6.961982j,
2448
+ -19.593895 + 8.717546j,
2449
+ -19.009148 + 10.484195j,
2450
+ -18.300400 + 12.265351j,
2451
+ -17.456663 + 14.065350j,
2452
+ -16.463032 + 15.889910j,
2453
+ -15.298849 + 17.746914j,
2454
+ -13.934466 + 19.647827j,
2455
+ -12.324914 + 21.610519j,
2456
+ -10.395893 + 23.665701j,
2457
+ - 8.005600 + 25.875019j,
2458
+ - 4.792045 + 28.406037j,
2459
+ ]
2460
+ xp_assert_close(sorted(p, key=np.imag),
2461
+ sorted(np.union1d(p2, np.conj(p2)), key=np.imag))
2462
+
2463
+ # high odd order, 'delay'
2464
+ z, p, k = bessel(30, 1, analog=True, output='zpk', norm='delay')
2465
+ p2 = [-20.201029 + 0.867750j,
2466
+ -20.097257 + 2.604235j,
2467
+ -19.888485 + 4.343721j,
2468
+ -19.572188 + 6.088363j,
2469
+ -19.144380 + 7.840570j,
2470
+ -18.599342 + 9.603147j,
2471
+ -17.929195 + 11.379494j,
2472
+ -17.123228 + 13.173901j,
2473
+ -16.166808 + 14.992008j,
2474
+ -15.039580 + 16.841580j,
2475
+ -13.712245 + 18.733902j,
2476
+ -12.140295 + 20.686563j,
2477
+ -10.250119 + 22.729808j,
2478
+ - 7.901170 + 24.924391j,
2479
+ - 4.734679 + 27.435615j,
2480
+ ]
2481
+ xp_assert_close(sorted(p, key=np.imag),
2482
+ sorted(np.union1d(p2, np.conj(p2)), key=np.imag))
2483
+
2484
+ def test_refs(self):
2485
+ # Compare to http://www.crbond.com/papers/bsf2.pdf
2486
+ # "Delay Normalized Bessel Polynomial Coefficients"
2487
+ bond_b = np.asarray([10395.0])
2488
+ bond_a = np.asarray([1.0, 21, 210, 1260, 4725, 10395, 10395])
2489
+ b, a = bessel(6, 1, norm='delay', analog=True)
2490
+ xp_assert_close(b, bond_b)
2491
+ xp_assert_close(a, bond_a)
2492
+
2493
+ # "Delay Normalized Bessel Pole Locations"
2494
+ bond_poles = {
2495
+ 1: [-1.0000000000],
2496
+ 2: [-1.5000000000 + 0.8660254038j],
2497
+ 3: [-1.8389073227 + 1.7543809598j, -2.3221853546],
2498
+ 4: [-2.1037893972 + 2.6574180419j, -2.8962106028 + 0.8672341289j],
2499
+ 5: [-2.3246743032 + 3.5710229203j, -3.3519563992 + 1.7426614162j,
2500
+ -3.6467385953],
2501
+ 6: [-2.5159322478 + 4.4926729537j, -3.7357083563 + 2.6262723114j,
2502
+ -4.2483593959 + 0.8675096732j],
2503
+ 7: [-2.6856768789 + 5.4206941307j, -4.0701391636 + 3.5171740477j,
2504
+ -4.7582905282 + 1.7392860611j, -4.9717868585],
2505
+ 8: [-2.8389839489 + 6.3539112986j, -4.3682892172 + 4.4144425005j,
2506
+ -5.2048407906 + 2.6161751526j, -5.5878860433 + 0.8676144454j],
2507
+ 9: [-2.9792607982 + 7.2914636883j, -4.6384398872 + 5.3172716754j,
2508
+ -5.6044218195 + 3.4981569179j, -6.1293679043 + 1.7378483835j,
2509
+ -6.2970191817],
2510
+ 10: [-3.1089162336 + 8.2326994591j, -4.8862195669 + 6.2249854825j,
2511
+ -5.9675283286 + 4.3849471889j, -6.6152909655 + 2.6115679208j,
2512
+ -6.9220449054 + 0.8676651955j]
2513
+ }
2514
+
2515
+ for N in range(1, 11):
2516
+ p1 = np.sort(bond_poles[N])
2517
+ p2 = np.sort(np.concatenate(_cplxreal(besselap(N, 'delay')[1])))
2518
+ assert_array_almost_equal(p1, p2, decimal=10)
2519
+
2520
+ # "Frequency Normalized Bessel Pole Locations"
2521
+ bond_poles = {
2522
+ 1: [-1.0000000000],
2523
+ 2: [-1.1016013306 + 0.6360098248j],
2524
+ 3: [-1.0474091610 + 0.9992644363j, -1.3226757999],
2525
+ 4: [-0.9952087644 + 1.2571057395j, -1.3700678306 + 0.4102497175j],
2526
+ 5: [-0.9576765486 + 1.4711243207j, -1.3808773259 + 0.7179095876j,
2527
+ -1.5023162714],
2528
+ 6: [-0.9306565229 + 1.6618632689j, -1.3818580976 + 0.9714718907j,
2529
+ -1.5714904036 + 0.3208963742j],
2530
+ 7: [-0.9098677806 + 1.8364513530j, -1.3789032168 + 1.1915667778j,
2531
+ -1.6120387662 + 0.5892445069j, -1.6843681793],
2532
+ 8: [-0.8928697188 + 1.9983258436j, -1.3738412176 + 1.3883565759j,
2533
+ -1.6369394181 + 0.8227956251j, -1.7574084004 + 0.2728675751j],
2534
+ 9: [-0.8783992762 + 2.1498005243j, -1.3675883098 + 1.5677337122j,
2535
+ -1.6523964846 + 1.0313895670j, -1.8071705350 + 0.5123837306j,
2536
+ -1.8566005012],
2537
+ 10: [-0.8657569017 + 2.2926048310j, -1.3606922784 + 1.7335057427j,
2538
+ -1.6618102414 + 1.2211002186j, -1.8421962445 + 0.7272575978j,
2539
+ -1.9276196914 + 0.2416234710j]
2540
+ }
2541
+
2542
+ for N in range(1, 11):
2543
+ p1 = np.sort(bond_poles[N])
2544
+ p2 = np.sort(np.concatenate(_cplxreal(besselap(N, 'mag')[1])))
2545
+ assert_array_almost_equal(p1, p2, decimal=10)
2546
+
2547
+ # Compare to https://www.ranecommercial.com/legacy/note147.html
2548
+ # "Table 1 - Bessel Crossovers of Second, Third, and Fourth-Order"
2549
+ a = np.asarray([1, 1, 1/3])
2550
+ b2, a2 = bessel(2, 1, norm='delay', analog=True)
2551
+ xp_assert_close(a[::-1], a2/b2)
2552
+
2553
+ a = np.asarray([1, 1, 2/5, 1/15])
2554
+ b2, a2 = bessel(3, 1, norm='delay', analog=True)
2555
+ xp_assert_close(a[::-1], a2/b2)
2556
+
2557
+ a = np.asarray([1, 1, 9/21, 2/21, 1/105])
2558
+ b2, a2 = bessel(4, 1, norm='delay', analog=True)
2559
+ xp_assert_close(a[::-1], a2/b2)
2560
+
2561
+ a = np.asarray([1, np.sqrt(3), 1])
2562
+ b2, a2 = bessel(2, 1, norm='phase', analog=True)
2563
+ xp_assert_close(a[::-1], a2/b2)
2564
+
2565
+ # TODO: Why so inaccurate? Is reference flawed?
2566
+ a = np.asarray([1, 2.481, 2.463, 1.018])
2567
+ b2, a2 = bessel(3, 1, norm='phase', analog=True)
2568
+ assert_array_almost_equal(a[::-1], a2/b2, decimal=1)
2569
+
2570
+ # TODO: Why so inaccurate? Is reference flawed?
2571
+ a = np.asarray([1, 3.240, 4.5, 3.240, 1.050])
2572
+ b2, a2 = bessel(4, 1, norm='phase', analog=True)
2573
+ assert_array_almost_equal(a[::-1], a2/b2, decimal=1)
2574
+
2575
+ # Table of -3 dB factors:
2576
+ N, scale = 2, np.asarray([1.272, 1.272], dtype=np.complex128)
2577
+ scale2 = besselap(N, 'mag')[1] / besselap(N, 'phase')[1]
2578
+ assert_array_almost_equal(scale2, scale, decimal=3)
2579
+
2580
+ # TODO: Why so inaccurate? Is reference flawed?
2581
+ N, scale = 3, np.asarray([1.413, 1.413, 1.413], dtype=np.complex128)
2582
+ scale2 = besselap(N, 'mag')[1] / besselap(N, 'phase')[1]
2583
+ assert_array_almost_equal(scale2, scale, decimal=2)
2584
+
2585
+ # TODO: Why so inaccurate? Is reference flawed?
2586
+ N, scale = 4, np.asarray([1.533]*4, dtype=np.complex128)
2587
+ scale2 = besselap(N, 'mag')[1] / besselap(N, 'phase')[1]
2588
+ assert_array_almost_equal(scale, scale2, decimal=1)
2589
+
2590
+ def test_hardcoded(self):
2591
+ # Compare to values from original hardcoded implementation
2592
+ originals = {
2593
+ 0: [],
2594
+ 1: [-1],
2595
+ 2: [-.8660254037844386467637229 + .4999999999999999999999996j],
2596
+ 3: [-.9416000265332067855971980,
2597
+ -.7456403858480766441810907 + .7113666249728352680992154j],
2598
+ 4: [-.6572111716718829545787788 + .8301614350048733772399715j,
2599
+ -.9047587967882449459642624 + .2709187330038746636700926j],
2600
+ 5: [-.9264420773877602247196260,
2601
+ -.8515536193688395541722677 + .4427174639443327209850002j,
2602
+ -.5905759446119191779319432 + .9072067564574549539291747j],
2603
+ 6: [-.9093906830472271808050953 + .1856964396793046769246397j,
2604
+ -.7996541858328288520243325 + .5621717346937317988594118j,
2605
+ -.5385526816693109683073792 + .9616876881954277199245657j],
2606
+ 7: [-.9194871556490290014311619,
2607
+ -.8800029341523374639772340 + .3216652762307739398381830j,
2608
+ -.7527355434093214462291616 + .6504696305522550699212995j,
2609
+ -.4966917256672316755024763 + 1.002508508454420401230220j],
2610
+ 8: [-.9096831546652910216327629 + .1412437976671422927888150j,
2611
+ -.8473250802359334320103023 + .4259017538272934994996429j,
2612
+ -.7111381808485399250796172 + .7186517314108401705762571j,
2613
+ -.4621740412532122027072175 + 1.034388681126901058116589j],
2614
+ 9: [-.9154957797499037686769223,
2615
+ -.8911217017079759323183848 + .2526580934582164192308115j,
2616
+ -.8148021112269012975514135 + .5085815689631499483745341j,
2617
+ -.6743622686854761980403401 + .7730546212691183706919682j,
2618
+ -.4331415561553618854685942 + 1.060073670135929666774323j],
2619
+ 10: [-.9091347320900502436826431 + .1139583137335511169927714j,
2620
+ -.8688459641284764527921864 + .3430008233766309973110589j,
2621
+ -.7837694413101441082655890 + .5759147538499947070009852j,
2622
+ -.6417513866988316136190854 + .8175836167191017226233947j,
2623
+ -.4083220732868861566219785 + 1.081274842819124562037210j],
2624
+ 11: [-.9129067244518981934637318,
2625
+ -.8963656705721166099815744 + .2080480375071031919692341j,
2626
+ -.8453044014712962954184557 + .4178696917801248292797448j,
2627
+ -.7546938934722303128102142 + .6319150050721846494520941j,
2628
+ -.6126871554915194054182909 + .8547813893314764631518509j,
2629
+ -.3868149510055090879155425 + 1.099117466763120928733632j],
2630
+ 12: [-.9084478234140682638817772 + 95506365213450398415258360e-27j,
2631
+ -.8802534342016826507901575 + .2871779503524226723615457j,
2632
+ -.8217296939939077285792834 + .4810212115100676440620548j,
2633
+ -.7276681615395159454547013 + .6792961178764694160048987j,
2634
+ -.5866369321861477207528215 + .8863772751320727026622149j,
2635
+ -.3679640085526312839425808 + 1.114373575641546257595657j],
2636
+ 13: [-.9110914665984182781070663,
2637
+ -.8991314665475196220910718 + .1768342956161043620980863j,
2638
+ -.8625094198260548711573628 + .3547413731172988997754038j,
2639
+ -.7987460692470972510394686 + .5350752120696801938272504j,
2640
+ -.7026234675721275653944062 + .7199611890171304131266374j,
2641
+ -.5631559842430199266325818 + .9135900338325109684927731j,
2642
+ -.3512792323389821669401925 + 1.127591548317705678613239j],
2643
+ 14: [-.9077932138396487614720659 + 82196399419401501888968130e-27j,
2644
+ -.8869506674916445312089167 + .2470079178765333183201435j,
2645
+ -.8441199160909851197897667 + .4131653825102692595237260j,
2646
+ -.7766591387063623897344648 + .5819170677377608590492434j,
2647
+ -.6794256425119233117869491 + .7552857305042033418417492j,
2648
+ -.5418766775112297376541293 + .9373043683516919569183099j,
2649
+ -.3363868224902037330610040 + 1.139172297839859991370924j],
2650
+ 15: [-.9097482363849064167228581,
2651
+ -.9006981694176978324932918 + .1537681197278439351298882j,
2652
+ -.8731264620834984978337843 + .3082352470564267657715883j,
2653
+ -.8256631452587146506294553 + .4642348752734325631275134j,
2654
+ -.7556027168970728127850416 + .6229396358758267198938604j,
2655
+ -.6579196593110998676999362 + .7862895503722515897065645j,
2656
+ -.5224954069658330616875186 + .9581787261092526478889345j,
2657
+ -.3229963059766444287113517 + 1.149416154583629539665297j],
2658
+ 16: [-.9072099595087001356491337 + 72142113041117326028823950e-27j,
2659
+ -.8911723070323647674780132 + .2167089659900576449410059j,
2660
+ -.8584264231521330481755780 + .3621697271802065647661080j,
2661
+ -.8074790293236003885306146 + .5092933751171800179676218j,
2662
+ -.7356166304713115980927279 + .6591950877860393745845254j,
2663
+ -.6379502514039066715773828 + .8137453537108761895522580j,
2664
+ -.5047606444424766743309967 + .9767137477799090692947061j,
2665
+ -.3108782755645387813283867 + 1.158552841199330479412225j],
2666
+ 17: [-.9087141161336397432860029,
2667
+ -.9016273850787285964692844 + .1360267995173024591237303j,
2668
+ -.8801100704438627158492165 + .2725347156478803885651973j,
2669
+ -.8433414495836129204455491 + .4100759282910021624185986j,
2670
+ -.7897644147799708220288138 + .5493724405281088674296232j,
2671
+ -.7166893842372349049842743 + .6914936286393609433305754j,
2672
+ -.6193710717342144521602448 + .8382497252826992979368621j,
2673
+ -.4884629337672704194973683 + .9932971956316781632345466j,
2674
+ -.2998489459990082015466971 + 1.166761272925668786676672j],
2675
+ 18: [-.9067004324162775554189031 + 64279241063930693839360680e-27j,
2676
+ -.8939764278132455733032155 + .1930374640894758606940586j,
2677
+ -.8681095503628830078317207 + .3224204925163257604931634j,
2678
+ -.8281885016242836608829018 + .4529385697815916950149364j,
2679
+ -.7726285030739558780127746 + .5852778162086640620016316j,
2680
+ -.6987821445005273020051878 + .7204696509726630531663123j,
2681
+ -.6020482668090644386627299 + .8602708961893664447167418j,
2682
+ -.4734268069916151511140032 + 1.008234300314801077034158j,
2683
+ -.2897592029880489845789953 + 1.174183010600059128532230j],
2684
+ 19: [-.9078934217899404528985092,
2685
+ -.9021937639390660668922536 + .1219568381872026517578164j,
2686
+ -.8849290585034385274001112 + .2442590757549818229026280j,
2687
+ -.8555768765618421591093993 + .3672925896399872304734923j,
2688
+ -.8131725551578197705476160 + .4915365035562459055630005j,
2689
+ -.7561260971541629355231897 + .6176483917970178919174173j,
2690
+ -.6818424412912442033411634 + .7466272357947761283262338j,
2691
+ -.5858613321217832644813602 + .8801817131014566284786759j,
2692
+ -.4595043449730988600785456 + 1.021768776912671221830298j,
2693
+ -.2804866851439370027628724 + 1.180931628453291873626003j],
2694
+ 20: [-.9062570115576771146523497 + 57961780277849516990208850e-27j,
2695
+ -.8959150941925768608568248 + .1740317175918705058595844j,
2696
+ -.8749560316673332850673214 + .2905559296567908031706902j,
2697
+ -.8427907479956670633544106 + .4078917326291934082132821j,
2698
+ -.7984251191290606875799876 + .5264942388817132427317659j,
2699
+ -.7402780309646768991232610 + .6469975237605228320268752j,
2700
+ -.6658120544829934193890626 + .7703721701100763015154510j,
2701
+ -.5707026806915714094398061 + .8982829066468255593407161j,
2702
+ -.4465700698205149555701841 + 1.034097702560842962315411j,
2703
+ -.2719299580251652601727704 + 1.187099379810885886139638j],
2704
+ 21: [-.9072262653142957028884077,
2705
+ -.9025428073192696303995083 + .1105252572789856480992275j,
2706
+ -.8883808106664449854431605 + .2213069215084350419975358j,
2707
+ -.8643915813643204553970169 + .3326258512522187083009453j,
2708
+ -.8299435470674444100273463 + .4448177739407956609694059j,
2709
+ -.7840287980408341576100581 + .5583186348022854707564856j,
2710
+ -.7250839687106612822281339 + .6737426063024382240549898j,
2711
+ -.6506315378609463397807996 + .7920349342629491368548074j,
2712
+ -.5564766488918562465935297 + .9148198405846724121600860j,
2713
+ -.4345168906815271799687308 + 1.045382255856986531461592j,
2714
+ -.2640041595834031147954813 + 1.192762031948052470183960j],
2715
+ 22: [-.9058702269930872551848625 + 52774908289999045189007100e-27j,
2716
+ -.8972983138153530955952835 + .1584351912289865608659759j,
2717
+ -.8799661455640176154025352 + .2644363039201535049656450j,
2718
+ -.8534754036851687233084587 + .3710389319482319823405321j,
2719
+ -.8171682088462720394344996 + .4785619492202780899653575j,
2720
+ -.7700332930556816872932937 + .5874255426351153211965601j,
2721
+ -.7105305456418785989070935 + .6982266265924524000098548j,
2722
+ -.6362427683267827226840153 + .8118875040246347267248508j,
2723
+ -.5430983056306302779658129 + .9299947824439872998916657j,
2724
+ -.4232528745642628461715044 + 1.055755605227545931204656j,
2725
+ -.2566376987939318038016012 + 1.197982433555213008346532j],
2726
+ 23: [-.9066732476324988168207439,
2727
+ -.9027564979912504609412993 + .1010534335314045013252480j,
2728
+ -.8909283242471251458653994 + .2023024699381223418195228j,
2729
+ -.8709469395587416239596874 + .3039581993950041588888925j,
2730
+ -.8423805948021127057054288 + .4062657948237602726779246j,
2731
+ -.8045561642053176205623187 + .5095305912227258268309528j,
2732
+ -.7564660146829880581478138 + .6141594859476032127216463j,
2733
+ -.6965966033912705387505040 + .7207341374753046970247055j,
2734
+ -.6225903228771341778273152 + .8301558302812980678845563j,
2735
+ -.5304922463810191698502226 + .9439760364018300083750242j,
2736
+ -.4126986617510148836149955 + 1.065328794475513585531053j,
2737
+ -.2497697202208956030229911 + 1.202813187870697831365338j],
2738
+ 24: [-.9055312363372773709269407 + 48440066540478700874836350e-27j,
2739
+ -.8983105104397872954053307 + .1454056133873610120105857j,
2740
+ -.8837358034555706623131950 + .2426335234401383076544239j,
2741
+ -.8615278304016353651120610 + .3403202112618624773397257j,
2742
+ -.8312326466813240652679563 + .4386985933597305434577492j,
2743
+ -.7921695462343492518845446 + .5380628490968016700338001j,
2744
+ -.7433392285088529449175873 + .6388084216222567930378296j,
2745
+ -.6832565803536521302816011 + .7415032695091650806797753j,
2746
+ -.6096221567378335562589532 + .8470292433077202380020454j,
2747
+ -.5185914574820317343536707 + .9569048385259054576937721j,
2748
+ -.4027853855197518014786978 + 1.074195196518674765143729j,
2749
+ -.2433481337524869675825448 + 1.207298683731972524975429j],
2750
+ 25: [-.9062073871811708652496104,
2751
+ -.9028833390228020537142561 + 93077131185102967450643820e-27j,
2752
+ -.8928551459883548836774529 + .1863068969804300712287138j,
2753
+ -.8759497989677857803656239 + .2798521321771408719327250j,
2754
+ -.8518616886554019782346493 + .3738977875907595009446142j,
2755
+ -.8201226043936880253962552 + .4686668574656966589020580j,
2756
+ -.7800496278186497225905443 + .5644441210349710332887354j,
2757
+ -.7306549271849967721596735 + .6616149647357748681460822j,
2758
+ -.6704827128029559528610523 + .7607348858167839877987008j,
2759
+ -.5972898661335557242320528 + .8626676330388028512598538j,
2760
+ -.5073362861078468845461362 + .9689006305344868494672405j,
2761
+ -.3934529878191079606023847 + 1.082433927173831581956863j,
2762
+ -.2373280669322028974199184 + 1.211476658382565356579418j],
2763
+ }
2764
+ for N in originals:
2765
+ p1 = sorted(np.union1d(originals[N],
2766
+ np.conj(originals[N])), key=np.imag)
2767
+ p2 = sorted(besselap(N)[1], key=np.imag)
2768
+ xp_assert_close(p1,
2769
+ p2, rtol=1e-14, check_dtype=False)
2770
+
2771
+ def test_norm_phase(self):
2772
+ # Test some orders and frequencies and see that they have the right
2773
+ # phase at w0
2774
+ for N in (1, 2, 3, 4, 5, 51, 72):
2775
+ for w0 in (1, 100):
2776
+ b, a = bessel(N, w0, analog=True, norm='phase')
2777
+ w = np.linspace(0, w0, 100)
2778
+ w, h = freqs(b, a, w)
2779
+ phase = np.unwrap(np.angle(h))
2780
+ xp_assert_close(phase[[0, -1]], (0, -N*pi/4), rtol=1e-1)
2781
+
2782
+ def test_norm_mag(self):
2783
+ # Test some orders and frequencies and see that they have the right
2784
+ # mag at w0
2785
+ for N in (1, 2, 3, 4, 5, 51, 72):
2786
+ for w0 in (1, 100):
2787
+ b, a = bessel(N, w0, analog=True, norm='mag')
2788
+ w = (0, w0)
2789
+ w, h = freqs(b, a, w)
2790
+ mag = abs(h)
2791
+ xp_assert_close(mag, (1, 1/np.sqrt(2)))
2792
+
2793
+ def test_norm_delay(self):
2794
+ # Test some orders and frequencies and see that they have the right
2795
+ # delay at DC
2796
+ for N in (1, 2, 3, 4, 5, 51, 72):
2797
+ for w0 in (1, 100):
2798
+ b, a = bessel(N, w0, analog=True, norm='delay')
2799
+ w = np.linspace(0, 10*w0, 1000)
2800
+ w, h = freqs(b, a, w)
2801
+ delay = -np.diff(np.unwrap(np.angle(h)))/np.diff(w)
2802
+ xp_assert_close(delay[0], 1/w0, rtol=1e-4)
2803
+
2804
+ def test_norm_factor(self):
2805
+ mpmath_values = {
2806
+ 1: 1.0, 2: 1.361654128716130520, 3: 1.755672368681210649,
2807
+ 4: 2.113917674904215843, 5: 2.427410702152628137,
2808
+ 6: 2.703395061202921876, 7: 2.951722147038722771,
2809
+ 8: 3.179617237510651330, 9: 3.391693138911660101,
2810
+ 10: 3.590980594569163482, 11: 3.779607416439620092,
2811
+ 12: 3.959150821144285315, 13: 4.130825499383535980,
2812
+ 14: 4.295593409533637564, 15: 4.454233021624377494,
2813
+ 16: 4.607385465472647917, 17: 4.755586548961147727,
2814
+ 18: 4.899289677284488007, 19: 5.038882681488207605,
2815
+ 20: 5.174700441742707423, 21: 5.307034531360917274,
2816
+ 22: 5.436140703250035999, 23: 5.562244783787878196,
2817
+ 24: 5.685547371295963521, 25: 5.806227623775418541,
2818
+ 50: 8.268963160013226298, 51: 8.352374541546012058,
2819
+ }
2820
+ for N in mpmath_values:
2821
+ z, p, k = besselap(N, 'delay')
2822
+ xp_assert_close(mpmath_values[N], _norm_factor(p, k), rtol=1e-13)
2823
+
2824
+ def test_bessel_poly(self):
2825
+ xp_assert_equal(_bessel_poly(5), [945, 945, 420, 105, 15, 1])
2826
+ xp_assert_equal(_bessel_poly(4, True), [1, 10, 45, 105, 105])
2827
+
2828
+ def test_bessel_zeros(self):
2829
+ xp_assert_equal(_bessel_zeros(0), [])
2830
+
2831
+ def test_invalid(self):
2832
+ assert_raises(ValueError, besselap, 5, 'nonsense')
2833
+ assert_raises(ValueError, besselap, -5)
2834
+ assert_raises(ValueError, besselap, 3.2)
2835
+ assert_raises(ValueError, _bessel_poly, -3)
2836
+ assert_raises(ValueError, _bessel_poly, 3.3)
2837
+
2838
+ @pytest.mark.fail_slow(10)
2839
+ def test_fs_param(self):
2840
+ for norm in ('phase', 'mag', 'delay'):
2841
+ for fs in (900, 900.1, 1234.567):
2842
+ for N in (0, 1, 2, 3, 10):
2843
+ for fc in (100, 100.1, 432.12345):
2844
+ for btype in ('lp', 'hp'):
2845
+ ba1 = bessel(N, fc, btype, norm=norm, fs=fs)
2846
+ ba2 = bessel(N, fc/(fs/2), btype, norm=norm)
2847
+ for ba1_, ba2_ in zip(ba1, ba2):
2848
+ xp_assert_close(ba1_, ba2_)
2849
+ for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)):
2850
+ for btype in ('bp', 'bs'):
2851
+ ba1 = bessel(N, fc, btype, norm=norm, fs=fs)
2852
+ for seq in (list, tuple, array):
2853
+ fcnorm = seq([f/(fs/2) for f in fc])
2854
+ ba2 = bessel(N, fcnorm, btype, norm=norm)
2855
+ for ba1_, ba2_ in zip(ba1, ba2):
2856
+ xp_assert_close(ba1_, ba2_)
2857
+
2858
+
2859
+ class TestButter:
2860
+
2861
+ def test_degenerate(self):
2862
+ # 0-order filter is just a passthrough
2863
+ b, a = butter(0, 1, analog=True)
2864
+ xp_assert_equal(b, np.asarray([1.0]))
2865
+ xp_assert_equal(a, np.asarray([1.0]))
2866
+
2867
+ # 1-order filter is same for all types
2868
+ b, a = butter(1, 1, analog=True)
2869
+ assert_array_almost_equal(b, [1])
2870
+ assert_array_almost_equal(a, [1, 1])
2871
+
2872
+ z, p, k = butter(1, 0.3, output='zpk')
2873
+ xp_assert_equal(z, np.asarray([-1.0]))
2874
+ xp_assert_close(p, [3.249196962329063e-01 + 0j], rtol=1e-14)
2875
+ xp_assert_close(k, 3.375401518835469e-01, rtol=1e-14)
2876
+
2877
+ def test_basic(self):
2878
+ # analog s-plane
2879
+ for N in range(25):
2880
+ wn = 0.01
2881
+ z, p, k = butter(N, wn, 'low', analog=True, output='zpk')
2882
+ assert_array_almost_equal([], z)
2883
+ assert len(p) == N
2884
+ # All poles should be at distance wn from origin
2885
+ assert_array_almost_equal(abs(p), np.asarray(wn))
2886
+ assert all(np.real(p) <= 0) # No poles in right half of S-plane
2887
+ assert_array_almost_equal(wn**N, k)
2888
+
2889
+ # digital z-plane
2890
+ for N in range(25):
2891
+ wn = 0.01
2892
+ z, p, k = butter(N, wn, 'high', analog=False, output='zpk')
2893
+ xp_assert_equal(np.ones(N), z) # All zeros exactly at DC
2894
+ assert all(np.abs(p) <= 1) # No poles outside unit circle
2895
+
2896
+ b1, a1 = butter(2, 1, analog=True)
2897
+ assert_array_almost_equal(b1, [1])
2898
+ assert_array_almost_equal(a1, [1, np.sqrt(2), 1])
2899
+
2900
+ b2, a2 = butter(5, 1, analog=True)
2901
+ assert_array_almost_equal(b2, [1])
2902
+ assert_array_almost_equal(a2, [1, 3.2361, 5.2361,
2903
+ 5.2361, 3.2361, 1], decimal=4)
2904
+
2905
+ b3, a3 = butter(10, 1, analog=True)
2906
+ assert_array_almost_equal(b3, [1])
2907
+ assert_array_almost_equal(a3, [1, 6.3925, 20.4317, 42.8021, 64.8824,
2908
+ 74.2334, 64.8824, 42.8021, 20.4317,
2909
+ 6.3925, 1], decimal=4)
2910
+
2911
+ b2, a2 = butter(19, 1.0441379169150726, analog=True)
2912
+ assert_array_almost_equal(b2, [2.2720], decimal=4)
2913
+ assert_array_almost_equal(a2, 1.0e+004 * np.array([
2914
+ 0.0001, 0.0013, 0.0080, 0.0335, 0.1045, 0.2570,
2915
+ 0.5164, 0.8669, 1.2338, 1.5010, 1.5672, 1.4044,
2916
+ 1.0759, 0.6986, 0.3791, 0.1681, 0.0588, 0.0153,
2917
+ 0.0026, 0.0002]), decimal=0)
2918
+
2919
+ b, a = butter(5, 0.4)
2920
+ assert_array_almost_equal(b, [0.0219, 0.1097, 0.2194,
2921
+ 0.2194, 0.1097, 0.0219], decimal=4)
2922
+ assert_array_almost_equal(a, [1.0000, -0.9853, 0.9738,
2923
+ -0.3864, 0.1112, -0.0113], decimal=4)
2924
+
2925
+ def test_highpass(self):
2926
+ # highpass, high even order
2927
+ z, p, k = butter(28, 0.43, 'high', output='zpk')
2928
+ z2 = np.ones(28)
2929
+ p2 = [
2930
+ 2.068257195514592e-01 + 9.238294351481734e-01j,
2931
+ 2.068257195514592e-01 - 9.238294351481734e-01j,
2932
+ 1.874933103892023e-01 + 8.269455076775277e-01j,
2933
+ 1.874933103892023e-01 - 8.269455076775277e-01j,
2934
+ 1.717435567330153e-01 + 7.383078571194629e-01j,
2935
+ 1.717435567330153e-01 - 7.383078571194629e-01j,
2936
+ 1.588266870755982e-01 + 6.564623730651094e-01j,
2937
+ 1.588266870755982e-01 - 6.564623730651094e-01j,
2938
+ 1.481881532502603e-01 + 5.802343458081779e-01j,
2939
+ 1.481881532502603e-01 - 5.802343458081779e-01j,
2940
+ 1.394122576319697e-01 + 5.086609000582009e-01j,
2941
+ 1.394122576319697e-01 - 5.086609000582009e-01j,
2942
+ 1.321840881809715e-01 + 4.409411734716436e-01j,
2943
+ 1.321840881809715e-01 - 4.409411734716436e-01j,
2944
+ 1.262633413354405e-01 + 3.763990035551881e-01j,
2945
+ 1.262633413354405e-01 - 3.763990035551881e-01j,
2946
+ 1.214660449478046e-01 + 3.144545234797277e-01j,
2947
+ 1.214660449478046e-01 - 3.144545234797277e-01j,
2948
+ 1.104868766650320e-01 + 2.771505404367791e-02j,
2949
+ 1.104868766650320e-01 - 2.771505404367791e-02j,
2950
+ 1.111768629525075e-01 + 8.331369153155753e-02j,
2951
+ 1.111768629525075e-01 - 8.331369153155753e-02j,
2952
+ 1.125740630842972e-01 + 1.394219509611784e-01j,
2953
+ 1.125740630842972e-01 - 1.394219509611784e-01j,
2954
+ 1.147138487992747e-01 + 1.963932363793666e-01j,
2955
+ 1.147138487992747e-01 - 1.963932363793666e-01j,
2956
+ 1.176516491045901e-01 + 2.546021573417188e-01j,
2957
+ 1.176516491045901e-01 - 2.546021573417188e-01j,
2958
+ ]
2959
+ k2 = 1.446671081817286e-06
2960
+ xp_assert_equal(z, z2)
2961
+ xp_assert_close(sorted(p, key=np.imag),
2962
+ sorted(p2, key=np.imag), rtol=1e-7)
2963
+ xp_assert_close(k, k2, rtol=1e-10)
2964
+
2965
+ # highpass, high odd order
2966
+ z, p, k = butter(27, 0.56, 'high', output='zpk')
2967
+ z2 = np.ones(27)
2968
+ p2 = [
2969
+ -1.772572785680147e-01 + 9.276431102995948e-01j,
2970
+ -1.772572785680147e-01 - 9.276431102995948e-01j,
2971
+ -1.600766565322114e-01 + 8.264026279893268e-01j,
2972
+ -1.600766565322114e-01 - 8.264026279893268e-01j,
2973
+ -1.461948419016121e-01 + 7.341841939120078e-01j,
2974
+ -1.461948419016121e-01 - 7.341841939120078e-01j,
2975
+ -1.348975284762046e-01 + 6.493235066053785e-01j,
2976
+ -1.348975284762046e-01 - 6.493235066053785e-01j,
2977
+ -1.256628210712206e-01 + 5.704921366889227e-01j,
2978
+ -1.256628210712206e-01 - 5.704921366889227e-01j,
2979
+ -1.181038235962314e-01 + 4.966120551231630e-01j,
2980
+ -1.181038235962314e-01 - 4.966120551231630e-01j,
2981
+ -1.119304913239356e-01 + 4.267938916403775e-01j,
2982
+ -1.119304913239356e-01 - 4.267938916403775e-01j,
2983
+ -1.069237739782691e-01 + 3.602914879527338e-01j,
2984
+ -1.069237739782691e-01 - 3.602914879527338e-01j,
2985
+ -1.029178030691416e-01 + 2.964677964142126e-01j,
2986
+ -1.029178030691416e-01 - 2.964677964142126e-01j,
2987
+ -9.978747500816100e-02 + 2.347687643085738e-01j,
2988
+ -9.978747500816100e-02 - 2.347687643085738e-01j,
2989
+ -9.743974496324025e-02 + 1.747028739092479e-01j,
2990
+ -9.743974496324025e-02 - 1.747028739092479e-01j,
2991
+ -9.580754551625957e-02 + 1.158246860771989e-01j,
2992
+ -9.580754551625957e-02 - 1.158246860771989e-01j,
2993
+ -9.484562207782568e-02 + 5.772118357151691e-02j,
2994
+ -9.484562207782568e-02 - 5.772118357151691e-02j,
2995
+ -9.452783117928215e-02
2996
+ ]
2997
+ k2 = 9.585686688851069e-09
2998
+ xp_assert_equal(z, z2)
2999
+ xp_assert_close(sorted(p, key=np.imag),
3000
+ sorted(p2, key=np.imag), rtol=1e-8)
3001
+ xp_assert_close(k, k2)
3002
+
3003
+ def test_bandpass(self):
3004
+ z, p, k = butter(8, [0.25, 0.33], 'band', output='zpk')
3005
+ z2 = [1, 1, 1, 1, 1, 1, 1, 1,
3006
+ -1, -1, -1, -1, -1, -1, -1, -1]
3007
+ p2 = [
3008
+ 4.979909925436156e-01 + 8.367609424799387e-01j,
3009
+ 4.979909925436156e-01 - 8.367609424799387e-01j,
3010
+ 4.913338722555539e-01 + 7.866774509868817e-01j,
3011
+ 4.913338722555539e-01 - 7.866774509868817e-01j,
3012
+ 5.035229361778706e-01 + 7.401147376726750e-01j,
3013
+ 5.035229361778706e-01 - 7.401147376726750e-01j,
3014
+ 5.307617160406101e-01 + 7.029184459442954e-01j,
3015
+ 5.307617160406101e-01 - 7.029184459442954e-01j,
3016
+ 5.680556159453138e-01 + 6.788228792952775e-01j,
3017
+ 5.680556159453138e-01 - 6.788228792952775e-01j,
3018
+ 6.100962560818854e-01 + 6.693849403338664e-01j,
3019
+ 6.100962560818854e-01 - 6.693849403338664e-01j,
3020
+ 6.904694312740631e-01 + 6.930501690145245e-01j,
3021
+ 6.904694312740631e-01 - 6.930501690145245e-01j,
3022
+ 6.521767004237027e-01 + 6.744414640183752e-01j,
3023
+ 6.521767004237027e-01 - 6.744414640183752e-01j,
3024
+ ]
3025
+ k2 = 3.398854055800844e-08
3026
+ xp_assert_equal(z, z2, check_dtype=False)
3027
+ xp_assert_close(sorted(p, key=np.imag),
3028
+ sorted(p2, key=np.imag), rtol=1e-13)
3029
+ xp_assert_close(k, k2, rtol=1e-13)
3030
+
3031
+ # bandpass analog
3032
+ z, p, k = butter(4, [90.5, 110.5], 'bp', analog=True, output='zpk')
3033
+ z2 = np.zeros(4, dtype=z.dtype)
3034
+ p2 = [
3035
+ -4.179137760733086e+00 + 1.095935899082837e+02j,
3036
+ -4.179137760733086e+00 - 1.095935899082837e+02j,
3037
+ -9.593598668443835e+00 + 1.034745398029734e+02j,
3038
+ -9.593598668443835e+00 - 1.034745398029734e+02j,
3039
+ -8.883991981781929e+00 + 9.582087115567160e+01j,
3040
+ -8.883991981781929e+00 - 9.582087115567160e+01j,
3041
+ -3.474530886568715e+00 + 9.111599925805801e+01j,
3042
+ -3.474530886568715e+00 - 9.111599925805801e+01j,
3043
+ ]
3044
+ k2 = 1.600000000000001e+05
3045
+ xp_assert_equal(z, z2)
3046
+ xp_assert_close(sorted(p, key=np.imag),
3047
+ sorted(p2, key=np.imag))
3048
+ xp_assert_close(k, k2, rtol=1e-15)
3049
+
3050
+ def test_bandstop(self):
3051
+ z, p, k = butter(7, [0.45, 0.56], 'stop', output='zpk')
3052
+ z2 = [-1.594474531383421e-02 + 9.998728744679880e-01j,
3053
+ -1.594474531383421e-02 - 9.998728744679880e-01j,
3054
+ -1.594474531383421e-02 + 9.998728744679880e-01j,
3055
+ -1.594474531383421e-02 - 9.998728744679880e-01j,
3056
+ -1.594474531383421e-02 + 9.998728744679880e-01j,
3057
+ -1.594474531383421e-02 - 9.998728744679880e-01j,
3058
+ -1.594474531383421e-02 + 9.998728744679880e-01j,
3059
+ -1.594474531383421e-02 - 9.998728744679880e-01j,
3060
+ -1.594474531383421e-02 + 9.998728744679880e-01j,
3061
+ -1.594474531383421e-02 - 9.998728744679880e-01j,
3062
+ -1.594474531383421e-02 + 9.998728744679880e-01j,
3063
+ -1.594474531383421e-02 - 9.998728744679880e-01j,
3064
+ -1.594474531383421e-02 + 9.998728744679880e-01j,
3065
+ -1.594474531383421e-02 - 9.998728744679880e-01j]
3066
+ p2 = [-1.766850742887729e-01 + 9.466951258673900e-01j,
3067
+ -1.766850742887729e-01 - 9.466951258673900e-01j,
3068
+ 1.467897662432886e-01 + 9.515917126462422e-01j,
3069
+ 1.467897662432886e-01 - 9.515917126462422e-01j,
3070
+ -1.370083529426906e-01 + 8.880376681273993e-01j,
3071
+ -1.370083529426906e-01 - 8.880376681273993e-01j,
3072
+ 1.086774544701390e-01 + 8.915240810704319e-01j,
3073
+ 1.086774544701390e-01 - 8.915240810704319e-01j,
3074
+ -7.982704457700891e-02 + 8.506056315273435e-01j,
3075
+ -7.982704457700891e-02 - 8.506056315273435e-01j,
3076
+ 5.238812787110331e-02 + 8.524011102699969e-01j,
3077
+ 5.238812787110331e-02 - 8.524011102699969e-01j,
3078
+ -1.357545000491310e-02 + 8.382287744986582e-01j,
3079
+ -1.357545000491310e-02 - 8.382287744986582e-01j]
3080
+ k2 = 4.577122512960063e-01
3081
+ xp_assert_close(sorted(z, key=np.imag),
3082
+ sorted(z2, key=np.imag))
3083
+ xp_assert_close(sorted(p, key=np.imag),
3084
+ sorted(p2, key=np.imag))
3085
+ xp_assert_close(k, k2, rtol=1e-14)
3086
+
3087
+ def test_ba_output(self):
3088
+ b, a = butter(4, [100, 300], 'bandpass', analog=True)
3089
+ b2 = [1.6e+09, 0, 0, 0, 0]
3090
+ a2 = [1.000000000000000e+00, 5.226251859505511e+02,
3091
+ 2.565685424949238e+05, 6.794127417357160e+07,
3092
+ 1.519411254969542e+10, 2.038238225207147e+12,
3093
+ 2.309116882454312e+14, 1.411088002066486e+16,
3094
+ 8.099999999999991e+17]
3095
+ xp_assert_close(b, b2, rtol=1e-14)
3096
+ xp_assert_close(a, a2, rtol=1e-14)
3097
+
3098
+ def test_fs_param(self):
3099
+ for fs in (900, 900.1, 1234.567):
3100
+ for N in (0, 1, 2, 3, 10):
3101
+ for fc in (100, 100.1, 432.12345):
3102
+ for btype in ('lp', 'hp'):
3103
+ ba1 = butter(N, fc, btype, fs=fs)
3104
+ ba2 = butter(N, fc/(fs/2), btype)
3105
+ for ba1_, ba2_ in zip(ba1, ba2):
3106
+ xp_assert_close(ba1_, ba2_)
3107
+ for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)):
3108
+ for btype in ('bp', 'bs'):
3109
+ ba1 = butter(N, fc, btype, fs=fs)
3110
+ for seq in (list, tuple, array):
3111
+ fcnorm = seq([f/(fs/2) for f in fc])
3112
+ ba2 = butter(N, fcnorm, btype)
3113
+ for ba1_, ba2_ in zip(ba1, ba2):
3114
+ xp_assert_close(ba1_, ba2_)
3115
+
3116
+
3117
+ class TestCheby1:
3118
+
3119
+ def test_degenerate(self):
3120
+ # 0-order filter is just a passthrough
3121
+ # Even-order filters have DC gain of -rp dB
3122
+ b, a = cheby1(0, 10*np.log10(2), 1, analog=True)
3123
+ assert_array_almost_equal(b, [1/np.sqrt(2)])
3124
+ xp_assert_equal(a, np.asarray([1.0]))
3125
+
3126
+ # 1-order filter is same for all types
3127
+ b, a = cheby1(1, 10*np.log10(2), 1, analog=True)
3128
+ assert_array_almost_equal(b, [1])
3129
+ assert_array_almost_equal(a, [1, 1])
3130
+
3131
+ z, p, k = cheby1(1, 0.1, 0.3, output='zpk')
3132
+ xp_assert_equal(z, np.asarray([-1.0]))
3133
+ xp_assert_close(p, [-5.390126972799615e-01 + 0j], rtol=1e-14)
3134
+ xp_assert_close(k, 7.695063486399808e-01, rtol=1e-14)
3135
+
3136
+ def test_basic(self):
3137
+ for N in range(25):
3138
+ wn = 0.01
3139
+ z, p, k = cheby1(N, 1, wn, 'low', analog=True, output='zpk')
3140
+ assert_array_almost_equal([], z)
3141
+ assert len(p) == N
3142
+ assert all(np.real(p) <= 0) # No poles in right half of S-plane
3143
+
3144
+ for N in range(25):
3145
+ wn = 0.01
3146
+ z, p, k = cheby1(N, 1, wn, 'high', analog=False, output='zpk')
3147
+ xp_assert_equal(np.ones(N), z) # All zeros exactly at DC
3148
+ assert all(np.abs(p) <= 1) # No poles outside unit circle
3149
+
3150
+ # Same test as TestNormalize
3151
+ b, a = cheby1(8, 0.5, 0.048)
3152
+ assert_array_almost_equal(b, [
3153
+ 2.150733144728282e-11, 1.720586515782626e-10,
3154
+ 6.022052805239190e-10, 1.204410561047838e-09,
3155
+ 1.505513201309798e-09, 1.204410561047838e-09,
3156
+ 6.022052805239190e-10, 1.720586515782626e-10,
3157
+ 2.150733144728282e-11], decimal=14)
3158
+ assert_array_almost_equal(a, [
3159
+ 1.000000000000000e+00, -7.782402035027959e+00,
3160
+ 2.654354569747454e+01, -5.182182531666387e+01,
3161
+ 6.334127355102684e+01, -4.963358186631157e+01,
3162
+ 2.434862182949389e+01, -6.836925348604676e+00,
3163
+ 8.412934944449140e-01], decimal=14)
3164
+
3165
+ b, a = cheby1(4, 1, [0.4, 0.7], btype='band')
3166
+ assert_array_almost_equal(b, [0.0084, 0, -0.0335, 0, 0.0502, 0,
3167
+ -0.0335, 0, 0.0084], decimal=4)
3168
+ assert_array_almost_equal(a, [1.0, 1.1191, 2.862, 2.2986, 3.4137,
3169
+ 1.8653, 1.8982, 0.5676, 0.4103],
3170
+ decimal=4)
3171
+
3172
+ b2, a2 = cheby1(5, 3, 1, analog=True)
3173
+ assert_array_almost_equal(b2, [0.0626], decimal=4)
3174
+ assert_array_almost_equal(a2, [1, 0.5745, 1.4150, 0.5489, 0.4080,
3175
+ 0.0626], decimal=4)
3176
+
3177
+ b, a = cheby1(8, 0.5, 0.1)
3178
+ assert_array_almost_equal(b, 1.0e-006 * np.array([
3179
+ 0.00703924326028, 0.05631394608227, 0.19709881128793,
3180
+ 0.39419762257586, 0.49274702821983, 0.39419762257586,
3181
+ 0.19709881128793, 0.05631394608227, 0.00703924326028]),
3182
+ decimal=13)
3183
+ assert_array_almost_equal(a, [
3184
+ 1.00000000000000, -7.44912258934158, 24.46749067762108,
3185
+ -46.27560200466141, 55.11160187999928, -42.31640010161038,
3186
+ 20.45543300484147, -5.69110270561444, 0.69770374759022],
3187
+ decimal=13)
3188
+
3189
+ b, a = cheby1(8, 0.5, 0.25)
3190
+ assert_array_almost_equal(b, 1.0e-003 * np.array([
3191
+ 0.00895261138923, 0.07162089111382, 0.25067311889837,
3192
+ 0.50134623779673, 0.62668279724591, 0.50134623779673,
3193
+ 0.25067311889837, 0.07162089111382, 0.00895261138923]),
3194
+ decimal=13)
3195
+ assert_array_almost_equal(a, [1.00000000000000, -5.97529229188545,
3196
+ 16.58122329202101, -27.71423273542923,
3197
+ 30.39509758355313, -22.34729670426879,
3198
+ 10.74509800434910, -3.08924633697497,
3199
+ 0.40707685889802], decimal=13)
3200
+
3201
+ def test_highpass(self):
3202
+ # high even order
3203
+ z, p, k = cheby1(24, 0.7, 0.2, 'high', output='zpk')
3204
+ z2 = np.ones(24)
3205
+ p2 = [-6.136558509657073e-01 + 2.700091504942893e-01j,
3206
+ -6.136558509657073e-01 - 2.700091504942893e-01j,
3207
+ -3.303348340927516e-01 + 6.659400861114254e-01j,
3208
+ -3.303348340927516e-01 - 6.659400861114254e-01j,
3209
+ 8.779713780557169e-03 + 8.223108447483040e-01j,
3210
+ 8.779713780557169e-03 - 8.223108447483040e-01j,
3211
+ 2.742361123006911e-01 + 8.356666951611864e-01j,
3212
+ 2.742361123006911e-01 - 8.356666951611864e-01j,
3213
+ 4.562984557158206e-01 + 7.954276912303594e-01j,
3214
+ 4.562984557158206e-01 - 7.954276912303594e-01j,
3215
+ 5.777335494123628e-01 + 7.435821817961783e-01j,
3216
+ 5.777335494123628e-01 - 7.435821817961783e-01j,
3217
+ 6.593260977749194e-01 + 6.955390907990932e-01j,
3218
+ 6.593260977749194e-01 - 6.955390907990932e-01j,
3219
+ 7.149590948466562e-01 + 6.559437858502012e-01j,
3220
+ 7.149590948466562e-01 - 6.559437858502012e-01j,
3221
+ 7.532432388188739e-01 + 6.256158042292060e-01j,
3222
+ 7.532432388188739e-01 - 6.256158042292060e-01j,
3223
+ 7.794365244268271e-01 + 6.042099234813333e-01j,
3224
+ 7.794365244268271e-01 - 6.042099234813333e-01j,
3225
+ 7.967253874772997e-01 + 5.911966597313203e-01j,
3226
+ 7.967253874772997e-01 - 5.911966597313203e-01j,
3227
+ 8.069756417293870e-01 + 5.862214589217275e-01j,
3228
+ 8.069756417293870e-01 - 5.862214589217275e-01j]
3229
+ k2 = 6.190427617192018e-04
3230
+ xp_assert_equal(z, z2)
3231
+ xp_assert_close(sorted(p, key=np.imag),
3232
+ sorted(p2, key=np.imag), rtol=1e-10)
3233
+ xp_assert_close(k, k2, rtol=1e-10)
3234
+
3235
+ # high odd order
3236
+ z, p, k = cheby1(23, 0.8, 0.3, 'high', output='zpk')
3237
+ z2 = np.ones(23)
3238
+ p2 = [-7.676400532011010e-01,
3239
+ -6.754621070166477e-01 + 3.970502605619561e-01j,
3240
+ -6.754621070166477e-01 - 3.970502605619561e-01j,
3241
+ -4.528880018446727e-01 + 6.844061483786332e-01j,
3242
+ -4.528880018446727e-01 - 6.844061483786332e-01j,
3243
+ -1.986009130216447e-01 + 8.382285942941594e-01j,
3244
+ -1.986009130216447e-01 - 8.382285942941594e-01j,
3245
+ 2.504673931532608e-02 + 8.958137635794080e-01j,
3246
+ 2.504673931532608e-02 - 8.958137635794080e-01j,
3247
+ 2.001089429976469e-01 + 9.010678290791480e-01j,
3248
+ 2.001089429976469e-01 - 9.010678290791480e-01j,
3249
+ 3.302410157191755e-01 + 8.835444665962544e-01j,
3250
+ 3.302410157191755e-01 - 8.835444665962544e-01j,
3251
+ 4.246662537333661e-01 + 8.594054226449009e-01j,
3252
+ 4.246662537333661e-01 - 8.594054226449009e-01j,
3253
+ 4.919620928120296e-01 + 8.366772762965786e-01j,
3254
+ 4.919620928120296e-01 - 8.366772762965786e-01j,
3255
+ 5.385746917494749e-01 + 8.191616180796720e-01j,
3256
+ 5.385746917494749e-01 - 8.191616180796720e-01j,
3257
+ 5.855636993537203e-01 + 8.060680937701062e-01j,
3258
+ 5.855636993537203e-01 - 8.060680937701062e-01j,
3259
+ 5.688812849391721e-01 + 8.086497795114683e-01j,
3260
+ 5.688812849391721e-01 - 8.086497795114683e-01j]
3261
+ k2 = 1.941697029206324e-05
3262
+ xp_assert_equal(z, z2)
3263
+ xp_assert_close(sorted(p, key=np.imag),
3264
+ sorted(p2, key=np.imag), rtol=1e-10)
3265
+ xp_assert_close(k, k2, rtol=1e-10)
3266
+
3267
+ z, p, k = cheby1(10, 1, 1000, 'high', analog=True, output='zpk')
3268
+ z2 = np.zeros(10)
3269
+ p2 = [-3.144743169501551e+03 + 3.511680029092744e+03j,
3270
+ -3.144743169501551e+03 - 3.511680029092744e+03j,
3271
+ -5.633065604514602e+02 + 2.023615191183945e+03j,
3272
+ -5.633065604514602e+02 - 2.023615191183945e+03j,
3273
+ -1.946412183352025e+02 + 1.372309454274755e+03j,
3274
+ -1.946412183352025e+02 - 1.372309454274755e+03j,
3275
+ -7.987162953085479e+01 + 1.105207708045358e+03j,
3276
+ -7.987162953085479e+01 - 1.105207708045358e+03j,
3277
+ -2.250315039031946e+01 + 1.001723931471477e+03j,
3278
+ -2.250315039031946e+01 - 1.001723931471477e+03j]
3279
+ k2 = 8.912509381337453e-01
3280
+ xp_assert_equal(z, z2)
3281
+ xp_assert_close(sorted(p, key=np.imag),
3282
+ sorted(p2, key=np.imag), rtol=1e-13)
3283
+ xp_assert_close(k, k2, rtol=1e-15)
3284
+
3285
+ def test_bandpass(self):
3286
+ z, p, k = cheby1(8, 1, [0.3, 0.4], 'bp', output='zpk')
3287
+ z2 = [1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1]
3288
+ p2 = [3.077784854851463e-01 + 9.453307017592942e-01j,
3289
+ 3.077784854851463e-01 - 9.453307017592942e-01j,
3290
+ 3.280567400654425e-01 + 9.272377218689016e-01j,
3291
+ 3.280567400654425e-01 - 9.272377218689016e-01j,
3292
+ 3.677912763284301e-01 + 9.038008865279966e-01j,
3293
+ 3.677912763284301e-01 - 9.038008865279966e-01j,
3294
+ 4.194425632520948e-01 + 8.769407159656157e-01j,
3295
+ 4.194425632520948e-01 - 8.769407159656157e-01j,
3296
+ 4.740921994669189e-01 + 8.496508528630974e-01j,
3297
+ 4.740921994669189e-01 - 8.496508528630974e-01j,
3298
+ 5.234866481897429e-01 + 8.259608422808477e-01j,
3299
+ 5.234866481897429e-01 - 8.259608422808477e-01j,
3300
+ 5.844717632289875e-01 + 8.052901363500210e-01j,
3301
+ 5.844717632289875e-01 - 8.052901363500210e-01j,
3302
+ 5.615189063336070e-01 + 8.100667803850766e-01j,
3303
+ 5.615189063336070e-01 - 8.100667803850766e-01j]
3304
+ k2 = 5.007028718074307e-09
3305
+ xp_assert_equal(z, z2, check_dtype=False)
3306
+ xp_assert_close(sorted(p, key=np.imag),
3307
+ sorted(p2, key=np.imag), rtol=1e-13)
3308
+ xp_assert_close(k, k2, rtol=1e-13)
3309
+
3310
+ def test_bandstop(self):
3311
+ z, p, k = cheby1(7, 1, [0.5, 0.6], 'stop', output='zpk')
3312
+ z2 = [-1.583844403245361e-01 + 9.873775210440450e-01j,
3313
+ -1.583844403245361e-01 - 9.873775210440450e-01j,
3314
+ -1.583844403245361e-01 + 9.873775210440450e-01j,
3315
+ -1.583844403245361e-01 - 9.873775210440450e-01j,
3316
+ -1.583844403245361e-01 + 9.873775210440450e-01j,
3317
+ -1.583844403245361e-01 - 9.873775210440450e-01j,
3318
+ -1.583844403245361e-01 + 9.873775210440450e-01j,
3319
+ -1.583844403245361e-01 - 9.873775210440450e-01j,
3320
+ -1.583844403245361e-01 + 9.873775210440450e-01j,
3321
+ -1.583844403245361e-01 - 9.873775210440450e-01j,
3322
+ -1.583844403245361e-01 + 9.873775210440450e-01j,
3323
+ -1.583844403245361e-01 - 9.873775210440450e-01j,
3324
+ -1.583844403245361e-01 + 9.873775210440450e-01j,
3325
+ -1.583844403245361e-01 - 9.873775210440450e-01j]
3326
+ p2 = [-8.942974551472813e-02 + 3.482480481185926e-01j,
3327
+ -8.942974551472813e-02 - 3.482480481185926e-01j,
3328
+ 1.293775154041798e-01 + 8.753499858081858e-01j,
3329
+ 1.293775154041798e-01 - 8.753499858081858e-01j,
3330
+ 3.399741945062013e-02 + 9.690316022705607e-01j,
3331
+ 3.399741945062013e-02 - 9.690316022705607e-01j,
3332
+ 4.167225522796539e-04 + 9.927338161087488e-01j,
3333
+ 4.167225522796539e-04 - 9.927338161087488e-01j,
3334
+ -3.912966549550960e-01 + 8.046122859255742e-01j,
3335
+ -3.912966549550960e-01 - 8.046122859255742e-01j,
3336
+ -3.307805547127368e-01 + 9.133455018206508e-01j,
3337
+ -3.307805547127368e-01 - 9.133455018206508e-01j,
3338
+ -3.072658345097743e-01 + 9.443589759799366e-01j,
3339
+ -3.072658345097743e-01 - 9.443589759799366e-01j]
3340
+ k2 = 3.619438310405028e-01
3341
+ xp_assert_close(sorted(z, key=np.imag),
3342
+ sorted(z2, key=np.imag), rtol=1e-13)
3343
+ xp_assert_close(sorted(p, key=np.imag),
3344
+ sorted(p2, key=np.imag), rtol=1e-13)
3345
+ xp_assert_close(k, k2, rtol=0, atol=5e-16)
3346
+
3347
+ def test_ba_output(self):
3348
+ # with transfer function conversion, without digital conversion
3349
+ b, a = cheby1(5, 0.9, [210, 310], 'stop', analog=True)
3350
+ b2 = [1.000000000000006e+00, 0,
3351
+ 3.255000000000020e+05, 0,
3352
+ 4.238010000000026e+10, 0,
3353
+ 2.758944510000017e+15, 0,
3354
+ 8.980364380050052e+19, 0,
3355
+ 1.169243442282517e+24
3356
+ ]
3357
+ a2 = [1.000000000000000e+00, 4.630555945694342e+02,
3358
+ 4.039266454794788e+05, 1.338060988610237e+08,
3359
+ 5.844333551294591e+10, 1.357346371637638e+13,
3360
+ 3.804661141892782e+15, 5.670715850340080e+17,
3361
+ 1.114411200988328e+20, 8.316815934908471e+21,
3362
+ 1.169243442282517e+24
3363
+ ]
3364
+ xp_assert_close(b, b2, rtol=1e-14)
3365
+ xp_assert_close(a, a2, rtol=1e-14)
3366
+
3367
+ def test_fs_param(self):
3368
+ for fs in (900, 900.1, 1234.567):
3369
+ for N in (0, 1, 2, 3, 10):
3370
+ for fc in (100, 100.1, 432.12345):
3371
+ for btype in ('lp', 'hp'):
3372
+ ba1 = cheby1(N, 1, fc, btype, fs=fs)
3373
+ ba2 = cheby1(N, 1, fc/(fs/2), btype)
3374
+ for ba1_, ba2_ in zip(ba1, ba2):
3375
+ xp_assert_close(ba1_, ba2_)
3376
+ for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)):
3377
+ for btype in ('bp', 'bs'):
3378
+ ba1 = cheby1(N, 1, fc, btype, fs=fs)
3379
+ for seq in (list, tuple, array):
3380
+ fcnorm = seq([f/(fs/2) for f in fc])
3381
+ ba2 = cheby1(N, 1, fcnorm, btype)
3382
+ for ba1_, ba2_ in zip(ba1, ba2):
3383
+ xp_assert_close(ba1_, ba2_)
3384
+
3385
+ class TestCheby2:
3386
+
3387
+ def test_degenerate(self):
3388
+ # 0-order filter is just a passthrough
3389
+ # Stopband ripple factor doesn't matter
3390
+ b, a = cheby2(0, 123.456, 1, analog=True)
3391
+ xp_assert_equal(b, np.asarray([1.0]))
3392
+ xp_assert_equal(a, np.asarray([1.0]))
3393
+
3394
+ # 1-order filter is same for all types
3395
+ b, a = cheby2(1, 10*np.log10(2), 1, analog=True)
3396
+ assert_array_almost_equal(b, [1])
3397
+ assert_array_almost_equal(a, [1, 1])
3398
+
3399
+ z, p, k = cheby2(1, 50, 0.3, output='zpk')
3400
+ xp_assert_equal(z, np.asarray([-1], dtype=np.complex128))
3401
+ xp_assert_close(p, [9.967826460175649e-01 + 0j], rtol=1e-14)
3402
+ xp_assert_close(k, 1.608676991217512e-03, rtol=1e-14)
3403
+
3404
+ def test_basic(self):
3405
+ for N in range(25):
3406
+ wn = 0.01
3407
+ z, p, k = cheby2(N, 40, wn, 'low', analog=True, output='zpk')
3408
+ assert len(p) == N
3409
+ assert all(np.real(p) <= 0) # No poles in right half of S-plane
3410
+
3411
+ for N in range(25):
3412
+ wn = 0.01
3413
+ z, p, k = cheby2(N, 40, wn, 'high', analog=False, output='zpk')
3414
+ assert all(np.abs(p) <= 1) # No poles outside unit circle
3415
+
3416
+ B, A = cheby2(18, 100, 0.5)
3417
+ assert_array_almost_equal(B, [
3418
+ 0.00167583914216, 0.01249479541868, 0.05282702120282,
3419
+ 0.15939804265706, 0.37690207631117, 0.73227013789108,
3420
+ 1.20191856962356, 1.69522872823393, 2.07598674519837,
3421
+ 2.21972389625291, 2.07598674519838, 1.69522872823395,
3422
+ 1.20191856962359, 0.73227013789110, 0.37690207631118,
3423
+ 0.15939804265707, 0.05282702120282, 0.01249479541868,
3424
+ 0.00167583914216], decimal=13)
3425
+ assert_array_almost_equal(A, [
3426
+ 1.00000000000000, -0.27631970006174, 3.19751214254060,
3427
+ -0.15685969461355, 4.13926117356269, 0.60689917820044,
3428
+ 2.95082770636540, 0.89016501910416, 1.32135245849798,
3429
+ 0.51502467236824, 0.38906643866660, 0.15367372690642,
3430
+ 0.07255803834919, 0.02422454070134, 0.00756108751837,
3431
+ 0.00179848550988, 0.00033713574499, 0.00004258794833,
3432
+ 0.00000281030149], decimal=13)
3433
+
3434
+ def test_highpass(self):
3435
+ # high even order
3436
+ z, p, k = cheby2(26, 60, 0.3, 'high', output='zpk')
3437
+ z2 = [9.981088955489852e-01 + 6.147058341984388e-02j,
3438
+ 9.981088955489852e-01 - 6.147058341984388e-02j,
3439
+ 9.832702870387426e-01 + 1.821525257215483e-01j,
3440
+ 9.832702870387426e-01 - 1.821525257215483e-01j,
3441
+ 9.550760158089112e-01 + 2.963609353922882e-01j,
3442
+ 9.550760158089112e-01 - 2.963609353922882e-01j,
3443
+ 9.162054748821922e-01 + 4.007087817803773e-01j,
3444
+ 9.162054748821922e-01 - 4.007087817803773e-01j,
3445
+ 8.700619897368064e-01 + 4.929423232136168e-01j,
3446
+ 8.700619897368064e-01 - 4.929423232136168e-01j,
3447
+ 5.889791753434985e-01 + 8.081482110427953e-01j,
3448
+ 5.889791753434985e-01 - 8.081482110427953e-01j,
3449
+ 5.984900456570295e-01 + 8.011302423760501e-01j,
3450
+ 5.984900456570295e-01 - 8.011302423760501e-01j,
3451
+ 6.172880888914629e-01 + 7.867371958365343e-01j,
3452
+ 6.172880888914629e-01 - 7.867371958365343e-01j,
3453
+ 6.448899971038180e-01 + 7.642754030030161e-01j,
3454
+ 6.448899971038180e-01 - 7.642754030030161e-01j,
3455
+ 6.804845629637927e-01 + 7.327624168637228e-01j,
3456
+ 6.804845629637927e-01 - 7.327624168637228e-01j,
3457
+ 8.202619107108660e-01 + 5.719881098737678e-01j,
3458
+ 8.202619107108660e-01 - 5.719881098737678e-01j,
3459
+ 7.228410452536148e-01 + 6.910143437705678e-01j,
3460
+ 7.228410452536148e-01 - 6.910143437705678e-01j,
3461
+ 7.702121399578629e-01 + 6.377877856007792e-01j,
3462
+ 7.702121399578629e-01 - 6.377877856007792e-01j]
3463
+ p2 = [7.365546198286450e-01 + 4.842085129329526e-02j,
3464
+ 7.365546198286450e-01 - 4.842085129329526e-02j,
3465
+ 7.292038510962885e-01 + 1.442201672097581e-01j,
3466
+ 7.292038510962885e-01 - 1.442201672097581e-01j,
3467
+ 7.151293788040354e-01 + 2.369925800458584e-01j,
3468
+ 7.151293788040354e-01 - 2.369925800458584e-01j,
3469
+ 6.955051820787286e-01 + 3.250341363856910e-01j,
3470
+ 6.955051820787286e-01 - 3.250341363856910e-01j,
3471
+ 6.719122956045220e-01 + 4.070475750638047e-01j,
3472
+ 6.719122956045220e-01 - 4.070475750638047e-01j,
3473
+ 6.461722130611300e-01 + 4.821965916689270e-01j,
3474
+ 6.461722130611300e-01 - 4.821965916689270e-01j,
3475
+ 5.528045062872224e-01 + 8.162920513838372e-01j,
3476
+ 5.528045062872224e-01 - 8.162920513838372e-01j,
3477
+ 5.464847782492791e-01 + 7.869899955967304e-01j,
3478
+ 5.464847782492791e-01 - 7.869899955967304e-01j,
3479
+ 5.488033111260949e-01 + 7.520442354055579e-01j,
3480
+ 5.488033111260949e-01 - 7.520442354055579e-01j,
3481
+ 6.201874719022955e-01 + 5.500894392527353e-01j,
3482
+ 6.201874719022955e-01 - 5.500894392527353e-01j,
3483
+ 5.586478152536709e-01 + 7.112676877332921e-01j,
3484
+ 5.586478152536709e-01 - 7.112676877332921e-01j,
3485
+ 5.958145844148228e-01 + 6.107074340842115e-01j,
3486
+ 5.958145844148228e-01 - 6.107074340842115e-01j,
3487
+ 5.747812938519067e-01 + 6.643001536914696e-01j,
3488
+ 5.747812938519067e-01 - 6.643001536914696e-01j]
3489
+ k2 = 9.932997786497189e-02
3490
+ xp_assert_close(sorted(z, key=np.angle),
3491
+ sorted(z2, key=np.angle), rtol=1e-13)
3492
+ xp_assert_close(sorted(p, key=np.angle),
3493
+ sorted(p2, key=np.angle), rtol=1e-12)
3494
+ xp_assert_close(k, k2, rtol=1e-11)
3495
+
3496
+ # high odd order
3497
+ z, p, k = cheby2(25, 80, 0.5, 'high', output='zpk')
3498
+ z2 = [9.690690376586687e-01 + 2.467897896011971e-01j,
3499
+ 9.690690376586687e-01 - 2.467897896011971e-01j,
3500
+ 9.999999999999492e-01,
3501
+ 8.835111277191199e-01 + 4.684101698261429e-01j,
3502
+ 8.835111277191199e-01 - 4.684101698261429e-01j,
3503
+ 7.613142857900539e-01 + 6.483830335935022e-01j,
3504
+ 7.613142857900539e-01 - 6.483830335935022e-01j,
3505
+ 6.232625173626231e-01 + 7.820126817709752e-01j,
3506
+ 6.232625173626231e-01 - 7.820126817709752e-01j,
3507
+ 4.864456563413621e-01 + 8.737108351316745e-01j,
3508
+ 4.864456563413621e-01 - 8.737108351316745e-01j,
3509
+ 3.618368136816749e-01 + 9.322414495530347e-01j,
3510
+ 3.618368136816749e-01 - 9.322414495530347e-01j,
3511
+ 2.549486883466794e-01 + 9.669545833752675e-01j,
3512
+ 2.549486883466794e-01 - 9.669545833752675e-01j,
3513
+ 1.676175432109457e-01 + 9.858520980390212e-01j,
3514
+ 1.676175432109457e-01 - 9.858520980390212e-01j,
3515
+ 1.975218468277521e-03 + 9.999980492540941e-01j,
3516
+ 1.975218468277521e-03 - 9.999980492540941e-01j,
3517
+ 1.786959496651858e-02 + 9.998403260399917e-01j,
3518
+ 1.786959496651858e-02 - 9.998403260399917e-01j,
3519
+ 9.967933660557139e-02 + 9.950196127985684e-01j,
3520
+ 9.967933660557139e-02 - 9.950196127985684e-01j,
3521
+ 5.013970951219547e-02 + 9.987422137518890e-01j,
3522
+ 5.013970951219547e-02 - 9.987422137518890e-01j]
3523
+ p2 = [4.218866331906864e-01,
3524
+ 4.120110200127552e-01 + 1.361290593621978e-01j,
3525
+ 4.120110200127552e-01 - 1.361290593621978e-01j,
3526
+ 3.835890113632530e-01 + 2.664910809911026e-01j,
3527
+ 3.835890113632530e-01 - 2.664910809911026e-01j,
3528
+ 3.399195570456499e-01 + 3.863983538639875e-01j,
3529
+ 3.399195570456499e-01 - 3.863983538639875e-01j,
3530
+ 2.855977834508353e-01 + 4.929444399540688e-01j,
3531
+ 2.855977834508353e-01 - 4.929444399540688e-01j,
3532
+ 2.255765441339322e-01 + 5.851631870205766e-01j,
3533
+ 2.255765441339322e-01 - 5.851631870205766e-01j,
3534
+ 1.644087535815792e-01 + 6.637356937277153e-01j,
3535
+ 1.644087535815792e-01 - 6.637356937277153e-01j,
3536
+ -7.293633845273095e-02 + 9.739218252516307e-01j,
3537
+ -7.293633845273095e-02 - 9.739218252516307e-01j,
3538
+ 1.058259206358626e-01 + 7.304739464862978e-01j,
3539
+ 1.058259206358626e-01 - 7.304739464862978e-01j,
3540
+ -5.703971947785402e-02 + 9.291057542169088e-01j,
3541
+ -5.703971947785402e-02 - 9.291057542169088e-01j,
3542
+ 5.263875132656864e-02 + 7.877974334424453e-01j,
3543
+ 5.263875132656864e-02 - 7.877974334424453e-01j,
3544
+ -3.007943405982616e-02 + 8.846331716180016e-01j,
3545
+ -3.007943405982616e-02 - 8.846331716180016e-01j,
3546
+ 6.857277464483946e-03 + 8.383275456264492e-01j,
3547
+ 6.857277464483946e-03 - 8.383275456264492e-01j]
3548
+ k2 = 6.507068761705037e-03
3549
+ xp_assert_close(sorted(z, key=np.angle),
3550
+ sorted(z2, key=np.angle), rtol=1e-13)
3551
+ xp_assert_close(sorted(p, key=np.angle),
3552
+ sorted(p2, key=np.angle), rtol=1e-12)
3553
+ xp_assert_close(k, k2, rtol=1e-11)
3554
+
3555
+ def test_bandpass(self):
3556
+ z, p, k = cheby2(9, 40, [0.07, 0.2], 'pass', output='zpk')
3557
+ z2 = [-9.999999999999999e-01,
3558
+ 3.676588029658514e-01 + 9.299607543341383e-01j,
3559
+ 3.676588029658514e-01 - 9.299607543341383e-01j,
3560
+ 7.009689684982283e-01 + 7.131917730894889e-01j,
3561
+ 7.009689684982283e-01 - 7.131917730894889e-01j,
3562
+ 7.815697973765858e-01 + 6.238178033919218e-01j,
3563
+ 7.815697973765858e-01 - 6.238178033919218e-01j,
3564
+ 8.063793628819866e-01 + 5.913986160941200e-01j,
3565
+ 8.063793628819866e-01 - 5.913986160941200e-01j,
3566
+ 1.000000000000001e+00,
3567
+ 9.944493019920448e-01 + 1.052168511576739e-01j,
3568
+ 9.944493019920448e-01 - 1.052168511576739e-01j,
3569
+ 9.854674703367308e-01 + 1.698642543566085e-01j,
3570
+ 9.854674703367308e-01 - 1.698642543566085e-01j,
3571
+ 9.762751735919308e-01 + 2.165335665157851e-01j,
3572
+ 9.762751735919308e-01 - 2.165335665157851e-01j,
3573
+ 9.792277171575134e-01 + 2.027636011479496e-01j,
3574
+ 9.792277171575134e-01 - 2.027636011479496e-01j]
3575
+ p2 = [8.143803410489621e-01 + 5.411056063397541e-01j,
3576
+ 8.143803410489621e-01 - 5.411056063397541e-01j,
3577
+ 7.650769827887418e-01 + 5.195412242095543e-01j,
3578
+ 7.650769827887418e-01 - 5.195412242095543e-01j,
3579
+ 6.096241204063443e-01 + 3.568440484659796e-01j,
3580
+ 6.096241204063443e-01 - 3.568440484659796e-01j,
3581
+ 6.918192770246239e-01 + 4.770463577106911e-01j,
3582
+ 6.918192770246239e-01 - 4.770463577106911e-01j,
3583
+ 6.986241085779207e-01 + 1.146512226180060e-01j,
3584
+ 6.986241085779207e-01 - 1.146512226180060e-01j,
3585
+ 8.654645923909734e-01 + 1.604208797063147e-01j,
3586
+ 8.654645923909734e-01 - 1.604208797063147e-01j,
3587
+ 9.164831670444591e-01 + 1.969181049384918e-01j,
3588
+ 9.164831670444591e-01 - 1.969181049384918e-01j,
3589
+ 9.630425777594550e-01 + 2.317513360702271e-01j,
3590
+ 9.630425777594550e-01 - 2.317513360702271e-01j,
3591
+ 9.438104703725529e-01 + 2.193509900269860e-01j,
3592
+ 9.438104703725529e-01 - 2.193509900269860e-01j]
3593
+ k2 = 9.345352824659604e-03
3594
+ xp_assert_close(sorted(z, key=np.angle),
3595
+ sorted(z2, key=np.angle), rtol=1e-13)
3596
+ xp_assert_close(sorted(p, key=np.angle),
3597
+ sorted(p2, key=np.angle), rtol=1e-13)
3598
+ xp_assert_close(k, k2, rtol=1e-11)
3599
+
3600
+ def test_bandstop(self):
3601
+ z, p, k = cheby2(6, 55, [0.1, 0.9], 'stop', output='zpk')
3602
+ z2 = [6.230544895101009e-01 + 7.821784343111114e-01j,
3603
+ 6.230544895101009e-01 - 7.821784343111114e-01j,
3604
+ 9.086608545660115e-01 + 4.175349702471991e-01j,
3605
+ 9.086608545660115e-01 - 4.175349702471991e-01j,
3606
+ 9.478129721465802e-01 + 3.188268649763867e-01j,
3607
+ 9.478129721465802e-01 - 3.188268649763867e-01j,
3608
+ -6.230544895100982e-01 + 7.821784343111109e-01j,
3609
+ -6.230544895100982e-01 - 7.821784343111109e-01j,
3610
+ -9.086608545660116e-01 + 4.175349702472088e-01j,
3611
+ -9.086608545660116e-01 - 4.175349702472088e-01j,
3612
+ -9.478129721465784e-01 + 3.188268649763897e-01j,
3613
+ -9.478129721465784e-01 - 3.188268649763897e-01j]
3614
+ p2 = [-9.464094036167638e-01 + 1.720048695084344e-01j,
3615
+ -9.464094036167638e-01 - 1.720048695084344e-01j,
3616
+ -8.715844103386737e-01 + 1.370665039509297e-01j,
3617
+ -8.715844103386737e-01 - 1.370665039509297e-01j,
3618
+ -8.078751204586425e-01 + 5.729329866682983e-02j,
3619
+ -8.078751204586425e-01 - 5.729329866682983e-02j,
3620
+ 9.464094036167665e-01 + 1.720048695084332e-01j,
3621
+ 9.464094036167665e-01 - 1.720048695084332e-01j,
3622
+ 8.078751204586447e-01 + 5.729329866683007e-02j,
3623
+ 8.078751204586447e-01 - 5.729329866683007e-02j,
3624
+ 8.715844103386721e-01 + 1.370665039509331e-01j,
3625
+ 8.715844103386721e-01 - 1.370665039509331e-01j]
3626
+ k2 = 2.917823332763358e-03
3627
+ xp_assert_close(sorted(z, key=np.angle),
3628
+ sorted(z2, key=np.angle), rtol=1e-13)
3629
+ xp_assert_close(sorted(p, key=np.angle),
3630
+ sorted(p2, key=np.angle), rtol=1e-13)
3631
+ xp_assert_close(k, k2, rtol=1e-11)
3632
+
3633
+ def test_ba_output(self):
3634
+ # with transfer function conversion, without digital conversion
3635
+ b, a = cheby2(5, 20, [2010, 2100], 'stop', True)
3636
+ b2 = [1.000000000000000e+00, 0, # Matlab: 6.683253076978249e-12,
3637
+ 2.111512500000000e+07, 0, # Matlab: 1.134325604589552e-04,
3638
+ 1.782966433781250e+14, 0, # Matlab: 7.216787944356781e+02,
3639
+ 7.525901316990656e+20, 0, # Matlab: 2.039829265789886e+09,
3640
+ 1.587960565565748e+27, 0, # Matlab: 2.161236218626134e+15,
3641
+ 1.339913493808585e+33]
3642
+ a2 = [1.000000000000000e+00, 1.849550755473371e+02,
3643
+ 2.113222918998538e+07, 3.125114149732283e+09,
3644
+ 1.785133457155609e+14, 1.979158697776348e+16,
3645
+ 7.535048322653831e+20, 5.567966191263037e+22,
3646
+ 1.589246884221346e+27, 5.871210648525566e+28,
3647
+ 1.339913493808590e+33]
3648
+ xp_assert_close(b, b2, rtol=1e-14)
3649
+ xp_assert_close(a, a2, rtol=1e-14)
3650
+
3651
+ def test_fs_param(self):
3652
+ for fs in (900, 900.1, 1234.567):
3653
+ for N in (0, 1, 2, 3, 10):
3654
+ for fc in (100, 100.1, 432.12345):
3655
+ for btype in ('lp', 'hp'):
3656
+ ba1 = cheby2(N, 20, fc, btype, fs=fs)
3657
+ ba2 = cheby2(N, 20, fc/(fs/2), btype)
3658
+ for ba1_, ba2_ in zip(ba1, ba2):
3659
+ xp_assert_close(ba1_, ba2_)
3660
+ for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)):
3661
+ for btype in ('bp', 'bs'):
3662
+ ba1 = cheby2(N, 20, fc, btype, fs=fs)
3663
+ for seq in (list, tuple, array):
3664
+ fcnorm = seq([f/(fs/2) for f in fc])
3665
+ ba2 = cheby2(N, 20, fcnorm, btype)
3666
+ for ba1_, ba2_ in zip(ba1, ba2):
3667
+ xp_assert_close(ba1_, ba2_)
3668
+
3669
+ class TestEllip:
3670
+
3671
+ def test_degenerate(self):
3672
+ # 0-order filter is just a passthrough
3673
+ # Even-order filters have DC gain of -rp dB
3674
+ # Stopband ripple factor doesn't matter
3675
+ b, a = ellip(0, 10*np.log10(2), 123.456, 1, analog=True)
3676
+ assert_array_almost_equal(b, [1/np.sqrt(2)])
3677
+ xp_assert_equal(a, np.asarray([1.0]))
3678
+
3679
+ # 1-order filter is same for all types
3680
+ b, a = ellip(1, 10*np.log10(2), 1, 1, analog=True)
3681
+ assert_array_almost_equal(b, [1])
3682
+ assert_array_almost_equal(a, [1, 1])
3683
+
3684
+ z, p, k = ellip(1, 1, 55, 0.3, output='zpk')
3685
+ xp_assert_close(z, [-9.999999999999998e-01], rtol=1e-14)
3686
+ xp_assert_close(p, [-6.660721153525525e-04], rtol=1e-10)
3687
+ xp_assert_close(k, 5.003330360576763e-01, rtol=1e-14)
3688
+
3689
+ def test_basic(self):
3690
+ for N in range(25):
3691
+ wn = 0.01
3692
+ z, p, k = ellip(N, 1, 40, wn, 'low', analog=True, output='zpk')
3693
+ assert len(p) == N
3694
+ assert all(np.real(p) <= 0) # No poles in right half of S-plane
3695
+
3696
+ for N in range(25):
3697
+ wn = 0.01
3698
+ z, p, k = ellip(N, 1, 40, wn, 'high', analog=False, output='zpk')
3699
+ assert all(np.abs(p) <= 1) # No poles outside unit circle
3700
+
3701
+ b3, a3 = ellip(5, 3, 26, 1, analog=True)
3702
+ assert_array_almost_equal(b3, [0.1420, 0, 0.3764, 0,
3703
+ 0.2409], decimal=4)
3704
+ assert_array_almost_equal(a3, [1, 0.5686, 1.8061, 0.8017, 0.8012,
3705
+ 0.2409], decimal=4)
3706
+
3707
+ b, a = ellip(3, 1, 60, [0.4, 0.7], 'stop')
3708
+ assert_array_almost_equal(b, [0.3310, 0.3469, 1.1042, 0.7044, 1.1042,
3709
+ 0.3469, 0.3310], decimal=4)
3710
+ assert_array_almost_equal(a, [1.0000, 0.6973, 1.1441, 0.5878, 0.7323,
3711
+ 0.1131, -0.0060], decimal=4)
3712
+
3713
+ def test_highpass(self):
3714
+ # high even order
3715
+ z, p, k = ellip(24, 1, 80, 0.3, 'high', output='zpk')
3716
+ z2 = [9.761875332501075e-01 + 2.169283290099910e-01j,
3717
+ 9.761875332501075e-01 - 2.169283290099910e-01j,
3718
+ 8.413503353963494e-01 + 5.404901600661900e-01j,
3719
+ 8.413503353963494e-01 - 5.404901600661900e-01j,
3720
+ 7.160082576305009e-01 + 6.980918098681732e-01j,
3721
+ 7.160082576305009e-01 - 6.980918098681732e-01j,
3722
+ 6.456533638965329e-01 + 7.636306264739803e-01j,
3723
+ 6.456533638965329e-01 - 7.636306264739803e-01j,
3724
+ 6.127321820971366e-01 + 7.902906256703928e-01j,
3725
+ 6.127321820971366e-01 - 7.902906256703928e-01j,
3726
+ 5.983607817490196e-01 + 8.012267936512676e-01j,
3727
+ 5.983607817490196e-01 - 8.012267936512676e-01j,
3728
+ 5.922577552594799e-01 + 8.057485658286990e-01j,
3729
+ 5.922577552594799e-01 - 8.057485658286990e-01j,
3730
+ 5.896952092563588e-01 + 8.076258788449631e-01j,
3731
+ 5.896952092563588e-01 - 8.076258788449631e-01j,
3732
+ 5.886248765538837e-01 + 8.084063054565607e-01j,
3733
+ 5.886248765538837e-01 - 8.084063054565607e-01j,
3734
+ 5.881802711123132e-01 + 8.087298490066037e-01j,
3735
+ 5.881802711123132e-01 - 8.087298490066037e-01j,
3736
+ 5.879995719101164e-01 + 8.088612386766461e-01j,
3737
+ 5.879995719101164e-01 - 8.088612386766461e-01j,
3738
+ 5.879354086709576e-01 + 8.089078780868164e-01j,
3739
+ 5.879354086709576e-01 - 8.089078780868164e-01j]
3740
+ p2 = [-3.184805259081650e-01 + 4.206951906775851e-01j,
3741
+ -3.184805259081650e-01 - 4.206951906775851e-01j,
3742
+ 1.417279173459985e-01 + 7.903955262836452e-01j,
3743
+ 1.417279173459985e-01 - 7.903955262836452e-01j,
3744
+ 4.042881216964651e-01 + 8.309042239116594e-01j,
3745
+ 4.042881216964651e-01 - 8.309042239116594e-01j,
3746
+ 5.128964442789670e-01 + 8.229563236799665e-01j,
3747
+ 5.128964442789670e-01 - 8.229563236799665e-01j,
3748
+ 5.569614712822724e-01 + 8.155957702908510e-01j,
3749
+ 5.569614712822724e-01 - 8.155957702908510e-01j,
3750
+ 5.750478870161392e-01 + 8.118633973883931e-01j,
3751
+ 5.750478870161392e-01 - 8.118633973883931e-01j,
3752
+ 5.825314018170804e-01 + 8.101960910679270e-01j,
3753
+ 5.825314018170804e-01 - 8.101960910679270e-01j,
3754
+ 5.856397379751872e-01 + 8.094825218722543e-01j,
3755
+ 5.856397379751872e-01 - 8.094825218722543e-01j,
3756
+ 5.869326035251949e-01 + 8.091827531557583e-01j,
3757
+ 5.869326035251949e-01 - 8.091827531557583e-01j,
3758
+ 5.874697218855733e-01 + 8.090593298213502e-01j,
3759
+ 5.874697218855733e-01 - 8.090593298213502e-01j,
3760
+ 5.876904783532237e-01 + 8.090127161018823e-01j,
3761
+ 5.876904783532237e-01 - 8.090127161018823e-01j,
3762
+ 5.877753105317594e-01 + 8.090050577978136e-01j,
3763
+ 5.877753105317594e-01 - 8.090050577978136e-01j]
3764
+ k2 = 4.918081266957108e-02
3765
+ xp_assert_close(sorted(z, key=np.angle),
3766
+ sorted(z2, key=np.angle), rtol=1e-4)
3767
+ xp_assert_close(sorted(p, key=np.angle),
3768
+ sorted(p2, key=np.angle), rtol=1e-4)
3769
+ xp_assert_close(k, k2, rtol=1e-3)
3770
+
3771
+ # high odd order
3772
+ z, p, k = ellip(23, 1, 70, 0.5, 'high', output='zpk')
3773
+ z2 = [9.999999999998661e-01,
3774
+ 6.603717261750994e-01 + 7.509388678638675e-01j,
3775
+ 6.603717261750994e-01 - 7.509388678638675e-01j,
3776
+ 2.788635267510325e-01 + 9.603307416968041e-01j,
3777
+ 2.788635267510325e-01 - 9.603307416968041e-01j,
3778
+ 1.070215532544218e-01 + 9.942567008268131e-01j,
3779
+ 1.070215532544218e-01 - 9.942567008268131e-01j,
3780
+ 4.049427369978163e-02 + 9.991797705105507e-01j,
3781
+ 4.049427369978163e-02 - 9.991797705105507e-01j,
3782
+ 1.531059368627931e-02 + 9.998827859909265e-01j,
3783
+ 1.531059368627931e-02 - 9.998827859909265e-01j,
3784
+ 5.808061438534933e-03 + 9.999831330689181e-01j,
3785
+ 5.808061438534933e-03 - 9.999831330689181e-01j,
3786
+ 2.224277847754599e-03 + 9.999975262909676e-01j,
3787
+ 2.224277847754599e-03 - 9.999975262909676e-01j,
3788
+ 8.731857107534554e-04 + 9.999996187732845e-01j,
3789
+ 8.731857107534554e-04 - 9.999996187732845e-01j,
3790
+ 3.649057346914968e-04 + 9.999999334218996e-01j,
3791
+ 3.649057346914968e-04 - 9.999999334218996e-01j,
3792
+ 1.765538109802615e-04 + 9.999999844143768e-01j,
3793
+ 1.765538109802615e-04 - 9.999999844143768e-01j,
3794
+ 1.143655290967426e-04 + 9.999999934602630e-01j,
3795
+ 1.143655290967426e-04 - 9.999999934602630e-01j]
3796
+ p2 = [-6.322017026545028e-01,
3797
+ -4.648423756662754e-01 + 5.852407464440732e-01j,
3798
+ -4.648423756662754e-01 - 5.852407464440732e-01j,
3799
+ -2.249233374627773e-01 + 8.577853017985717e-01j,
3800
+ -2.249233374627773e-01 - 8.577853017985717e-01j,
3801
+ -9.234137570557621e-02 + 9.506548198678851e-01j,
3802
+ -9.234137570557621e-02 - 9.506548198678851e-01j,
3803
+ -3.585663561241373e-02 + 9.821494736043981e-01j,
3804
+ -3.585663561241373e-02 - 9.821494736043981e-01j,
3805
+ -1.363917242312723e-02 + 9.933844128330656e-01j,
3806
+ -1.363917242312723e-02 - 9.933844128330656e-01j,
3807
+ -5.131505238923029e-03 + 9.975221173308673e-01j,
3808
+ -5.131505238923029e-03 - 9.975221173308673e-01j,
3809
+ -1.904937999259502e-03 + 9.990680819857982e-01j,
3810
+ -1.904937999259502e-03 - 9.990680819857982e-01j,
3811
+ -6.859439885466834e-04 + 9.996492201426826e-01j,
3812
+ -6.859439885466834e-04 - 9.996492201426826e-01j,
3813
+ -2.269936267937089e-04 + 9.998686250679161e-01j,
3814
+ -2.269936267937089e-04 - 9.998686250679161e-01j,
3815
+ -5.687071588789117e-05 + 9.999527573294513e-01j,
3816
+ -5.687071588789117e-05 - 9.999527573294513e-01j,
3817
+ -6.948417068525226e-07 + 9.999882737700173e-01j,
3818
+ -6.948417068525226e-07 - 9.999882737700173e-01j]
3819
+ k2 = 1.220910020289434e-02
3820
+ xp_assert_close(sorted(z, key=np.angle),
3821
+ sorted(z2, key=np.angle), rtol=1e-4)
3822
+ xp_assert_close(sorted(p, key=np.angle),
3823
+ sorted(p2, key=np.angle), rtol=1e-4)
3824
+ xp_assert_close(k, k2, rtol=1e-3)
3825
+
3826
+ def test_bandpass(self):
3827
+ z, p, k = ellip(7, 1, 40, [0.07, 0.2], 'pass', output='zpk')
3828
+ z2 = [-9.999999999999991e-01,
3829
+ 6.856610961780020e-01 + 7.279209168501619e-01j,
3830
+ 6.856610961780020e-01 - 7.279209168501619e-01j,
3831
+ 7.850346167691289e-01 + 6.194518952058737e-01j,
3832
+ 7.850346167691289e-01 - 6.194518952058737e-01j,
3833
+ 7.999038743173071e-01 + 6.001281461922627e-01j,
3834
+ 7.999038743173071e-01 - 6.001281461922627e-01j,
3835
+ 9.999999999999999e-01,
3836
+ 9.862938983554124e-01 + 1.649980183725925e-01j,
3837
+ 9.862938983554124e-01 - 1.649980183725925e-01j,
3838
+ 9.788558330548762e-01 + 2.045513580850601e-01j,
3839
+ 9.788558330548762e-01 - 2.045513580850601e-01j,
3840
+ 9.771155231720003e-01 + 2.127093189691258e-01j,
3841
+ 9.771155231720003e-01 - 2.127093189691258e-01j]
3842
+ p2 = [8.063992755498643e-01 + 5.858071374778874e-01j,
3843
+ 8.063992755498643e-01 - 5.858071374778874e-01j,
3844
+ 8.050395347071724e-01 + 5.639097428109795e-01j,
3845
+ 8.050395347071724e-01 - 5.639097428109795e-01j,
3846
+ 8.113124936559144e-01 + 4.855241143973142e-01j,
3847
+ 8.113124936559144e-01 - 4.855241143973142e-01j,
3848
+ 8.665595314082394e-01 + 3.334049560919331e-01j,
3849
+ 8.665595314082394e-01 - 3.334049560919331e-01j,
3850
+ 9.412369011968871e-01 + 2.457616651325908e-01j,
3851
+ 9.412369011968871e-01 - 2.457616651325908e-01j,
3852
+ 9.679465190411238e-01 + 2.228772501848216e-01j,
3853
+ 9.679465190411238e-01 - 2.228772501848216e-01j,
3854
+ 9.747235066273385e-01 + 2.178937926146544e-01j,
3855
+ 9.747235066273385e-01 - 2.178937926146544e-01j]
3856
+ k2 = 8.354782670263239e-03
3857
+ xp_assert_close(sorted(z, key=np.angle),
3858
+ sorted(z2, key=np.angle), rtol=1e-4)
3859
+ xp_assert_close(sorted(p, key=np.angle),
3860
+ sorted(p2, key=np.angle), rtol=1e-4)
3861
+ xp_assert_close(k, k2, rtol=1e-3)
3862
+
3863
+ z, p, k = ellip(5, 1, 75, [90.5, 110.5], 'pass', True, 'zpk')
3864
+ z2 = [-5.583607317695175e-14 + 1.433755965989225e+02j,
3865
+ -5.583607317695175e-14 - 1.433755965989225e+02j,
3866
+ 5.740106416459296e-14 + 1.261678754570291e+02j,
3867
+ 5.740106416459296e-14 - 1.261678754570291e+02j,
3868
+ -2.199676239638652e-14 + 6.974861996895196e+01j,
3869
+ -2.199676239638652e-14 - 6.974861996895196e+01j,
3870
+ -3.372595657044283e-14 + 7.926145989044531e+01j,
3871
+ -3.372595657044283e-14 - 7.926145989044531e+01j,
3872
+ 0]
3873
+ p2 = [-8.814960004852743e-01 + 1.104124501436066e+02j,
3874
+ -8.814960004852743e-01 - 1.104124501436066e+02j,
3875
+ -2.477372459140184e+00 + 1.065638954516534e+02j,
3876
+ -2.477372459140184e+00 - 1.065638954516534e+02j,
3877
+ -3.072156842945799e+00 + 9.995404870405324e+01j,
3878
+ -3.072156842945799e+00 - 9.995404870405324e+01j,
3879
+ -2.180456023925693e+00 + 9.379206865455268e+01j,
3880
+ -2.180456023925693e+00 - 9.379206865455268e+01j,
3881
+ -7.230484977485752e-01 + 9.056598800801140e+01j,
3882
+ -7.230484977485752e-01 - 9.056598800801140e+01j]
3883
+ k2 = 3.774571622827070e-02
3884
+ xp_assert_close(sorted(z, key=np.imag),
3885
+ sorted(z2, key=np.imag), rtol=1e-4)
3886
+ xp_assert_close(sorted(p, key=np.imag),
3887
+ sorted(p2, key=np.imag), rtol=1e-6)
3888
+ xp_assert_close(k, k2, rtol=1e-3)
3889
+
3890
+ def test_bandstop(self):
3891
+ z, p, k = ellip(8, 1, 65, [0.2, 0.4], 'stop', output='zpk')
3892
+ z2 = [3.528578094286510e-01 + 9.356769561794296e-01j,
3893
+ 3.528578094286510e-01 - 9.356769561794296e-01j,
3894
+ 3.769716042264783e-01 + 9.262248159096587e-01j,
3895
+ 3.769716042264783e-01 - 9.262248159096587e-01j,
3896
+ 4.406101783111199e-01 + 8.976985411420985e-01j,
3897
+ 4.406101783111199e-01 - 8.976985411420985e-01j,
3898
+ 5.539386470258847e-01 + 8.325574907062760e-01j,
3899
+ 5.539386470258847e-01 - 8.325574907062760e-01j,
3900
+ 6.748464963023645e-01 + 7.379581332490555e-01j,
3901
+ 6.748464963023645e-01 - 7.379581332490555e-01j,
3902
+ 7.489887970285254e-01 + 6.625826604475596e-01j,
3903
+ 7.489887970285254e-01 - 6.625826604475596e-01j,
3904
+ 7.913118471618432e-01 + 6.114127579150699e-01j,
3905
+ 7.913118471618432e-01 - 6.114127579150699e-01j,
3906
+ 7.806804740916381e-01 + 6.249303940216475e-01j,
3907
+ 7.806804740916381e-01 - 6.249303940216475e-01j]
3908
+
3909
+ p2 = [-1.025299146693730e-01 + 5.662682444754943e-01j,
3910
+ -1.025299146693730e-01 - 5.662682444754943e-01j,
3911
+ 1.698463595163031e-01 + 8.926678667070186e-01j,
3912
+ 1.698463595163031e-01 - 8.926678667070186e-01j,
3913
+ 2.750532687820631e-01 + 9.351020170094005e-01j,
3914
+ 2.750532687820631e-01 - 9.351020170094005e-01j,
3915
+ 3.070095178909486e-01 + 9.457373499553291e-01j,
3916
+ 3.070095178909486e-01 - 9.457373499553291e-01j,
3917
+ 7.695332312152288e-01 + 2.792567212705257e-01j,
3918
+ 7.695332312152288e-01 - 2.792567212705257e-01j,
3919
+ 8.083818999225620e-01 + 4.990723496863960e-01j,
3920
+ 8.083818999225620e-01 - 4.990723496863960e-01j,
3921
+ 8.066158014414928e-01 + 5.649811440393374e-01j,
3922
+ 8.066158014414928e-01 - 5.649811440393374e-01j,
3923
+ 8.062787978834571e-01 + 5.855780880424964e-01j,
3924
+ 8.062787978834571e-01 - 5.855780880424964e-01j]
3925
+ k2 = 2.068622545291259e-01
3926
+ xp_assert_close(sorted(z, key=np.angle),
3927
+ sorted(z2, key=np.angle), rtol=1e-6)
3928
+ xp_assert_close(sorted(p, key=np.angle),
3929
+ sorted(p2, key=np.angle), rtol=1e-5)
3930
+ xp_assert_close(k, k2, rtol=1e-5)
3931
+
3932
+ def test_ba_output(self):
3933
+ # with transfer function conversion, without digital conversion
3934
+ b, a = ellip(5, 1, 40, [201, 240], 'stop', True)
3935
+ b2 = [
3936
+ 1.000000000000000e+00, 0, # Matlab: 1.743506051190569e-13,
3937
+ 2.426561778314366e+05, 0, # Matlab: 3.459426536825722e-08,
3938
+ 2.348218683400168e+10, 0, # Matlab: 2.559179747299313e-03,
3939
+ 1.132780692872241e+15, 0, # Matlab: 8.363229375535731e+01,
3940
+ 2.724038554089566e+19, 0, # Matlab: 1.018700994113120e+06,
3941
+ 2.612380874940186e+23
3942
+ ]
3943
+ a2 = [
3944
+ 1.000000000000000e+00, 1.337266601804649e+02,
3945
+ 2.486725353510667e+05, 2.628059713728125e+07,
3946
+ 2.436169536928770e+10, 1.913554568577315e+12,
3947
+ 1.175208184614438e+15, 6.115751452473410e+16,
3948
+ 2.791577695211466e+19, 7.241811142725384e+20,
3949
+ 2.612380874940182e+23
3950
+ ]
3951
+ xp_assert_close(b, b2, rtol=1e-6)
3952
+ xp_assert_close(a, a2, rtol=1e-4)
3953
+
3954
+ def test_fs_param(self):
3955
+ for fs in (900, 900.1, 1234.567):
3956
+ for N in (0, 1, 2, 3, 10):
3957
+ for fc in (100, 100.1, 432.12345):
3958
+ for btype in ('lp', 'hp'):
3959
+ ba1 = ellip(N, 1, 20, fc, btype, fs=fs)
3960
+ ba2 = ellip(N, 1, 20, fc/(fs/2), btype)
3961
+ for ba1_, ba2_ in zip(ba1, ba2):
3962
+ xp_assert_close(ba1_, ba2_)
3963
+ for fc in ((100, 200), (100.1, 200.2), (321.123, 432.123)):
3964
+ for btype in ('bp', 'bs'):
3965
+ ba1 = ellip(N, 1, 20, fc, btype, fs=fs)
3966
+ for seq in (list, tuple, array):
3967
+ fcnorm = seq([f/(fs/2) for f in fc])
3968
+ ba2 = ellip(N, 1, 20, fcnorm, btype)
3969
+ for ba1_, ba2_ in zip(ba1, ba2):
3970
+ xp_assert_close(ba1_, ba2_)
3971
+
3972
+ def test_fs_validation(self):
3973
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
3974
+ iirnotch(0.06, 30, fs=np.array([10, 20]))
3975
+
3976
+ with pytest.raises(ValueError, match="Sampling.*be none"):
3977
+ iirnotch(0.06, 30, fs=None)
3978
+
3979
+
3980
+ def test_sos_consistency():
3981
+ # Consistency checks of output='sos' for the specialized IIR filter
3982
+ # design functions.
3983
+ design_funcs = [(bessel, (0.1,)),
3984
+ (butter, (0.1,)),
3985
+ (cheby1, (45.0, 0.1)),
3986
+ (cheby2, (0.087, 0.1)),
3987
+ (ellip, (0.087, 45, 0.1))]
3988
+ for func, args in design_funcs:
3989
+ name = func.__name__
3990
+
3991
+ b, a = func(2, *args, output='ba')
3992
+ sos = func(2, *args, output='sos')
3993
+ xp_assert_close(sos, [np.hstack((b, a))], err_msg=f"{name}(2,...)")
3994
+
3995
+ zpk = func(3, *args, output='zpk')
3996
+ sos = func(3, *args, output='sos')
3997
+ xp_assert_close(sos, zpk2sos(*zpk), err_msg=f"{name}(3,...)")
3998
+
3999
+ zpk = func(4, *args, output='zpk')
4000
+ sos = func(4, *args, output='sos')
4001
+ xp_assert_close(sos, zpk2sos(*zpk), err_msg=f"{name}(4,...)")
4002
+
4003
+
4004
+ class TestIIRNotch:
4005
+
4006
+ def test_ba_output(self):
4007
+ # Compare coefficients with Matlab ones
4008
+ # for the equivalent input:
4009
+ b, a = iirnotch(0.06, 30)
4010
+ b2 = [
4011
+ 9.9686824e-01, -1.9584219e+00,
4012
+ 9.9686824e-01
4013
+ ]
4014
+ a2 = [
4015
+ 1.0000000e+00, -1.9584219e+00,
4016
+ 9.9373647e-01
4017
+ ]
4018
+
4019
+ xp_assert_close(b, b2, rtol=1e-8)
4020
+ xp_assert_close(a, a2, rtol=1e-8)
4021
+
4022
+ def test_frequency_response(self):
4023
+ # Get filter coefficients
4024
+ b, a = iirnotch(0.3, 30)
4025
+
4026
+ # Get frequency response
4027
+ w, h = freqz(b, a, 1000)
4028
+
4029
+ # Pick 5 point
4030
+ p = [200, # w0 = 0.200
4031
+ 295, # w0 = 0.295
4032
+ 300, # w0 = 0.300
4033
+ 305, # w0 = 0.305
4034
+ 400] # w0 = 0.400
4035
+
4036
+ # Get frequency response correspondent to each of those points
4037
+ hp = h[p]
4038
+
4039
+ # Check if the frequency response fulfill the specifications:
4040
+ # hp[0] and hp[4] correspond to frequencies distant from
4041
+ # w0 = 0.3 and should be close to 1
4042
+ xp_assert_close(abs(hp[0]), np.asarray(1.), rtol=1e-2, check_0d=False)
4043
+ xp_assert_close(abs(hp[4]), np.asarray(1.), rtol=1e-2, check_0d=False)
4044
+
4045
+ # hp[1] and hp[3] correspond to frequencies approximately
4046
+ # on the edges of the passband and should be close to -3dB
4047
+ xp_assert_close(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2)
4048
+ xp_assert_close(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2)
4049
+
4050
+ # hp[2] correspond to the frequency that should be removed
4051
+ # the frequency response should be very close to 0
4052
+ xp_assert_close(abs(hp[2]), np.asarray(0.0), atol=1e-10, check_0d=False)
4053
+
4054
+ def test_errors(self):
4055
+ # Exception should be raised if w0 > 1 or w0 <0
4056
+ assert_raises(ValueError, iirnotch, w0=2, Q=30)
4057
+ assert_raises(ValueError, iirnotch, w0=-1, Q=30)
4058
+
4059
+ # Exception should be raised if any of the parameters
4060
+ # are not float (or cannot be converted to one)
4061
+ assert_raises(ValueError, iirnotch, w0="blabla", Q=30)
4062
+ assert_raises(TypeError, iirnotch, w0=-1, Q=[1, 2, 3])
4063
+
4064
+ def test_fs_param(self):
4065
+ # Get filter coefficients
4066
+ b, a = iirnotch(1500, 30, fs=10000)
4067
+
4068
+ # Get frequency response
4069
+ w, h = freqz(b, a, 1000, fs=10000)
4070
+
4071
+ # Pick 5 point
4072
+ p = [200, # w0 = 1000
4073
+ 295, # w0 = 1475
4074
+ 300, # w0 = 1500
4075
+ 305, # w0 = 1525
4076
+ 400] # w0 = 2000
4077
+
4078
+ # Get frequency response correspondent to each of those points
4079
+ hp = h[p]
4080
+
4081
+ # Check if the frequency response fulfill the specifications:
4082
+ # hp[0] and hp[4] correspond to frequencies distant from
4083
+ # w0 = 1500 and should be close to 1
4084
+ xp_assert_close(abs(hp[0]), np.ones_like(abs(hp[0])), rtol=1e-2,
4085
+ check_0d=False)
4086
+ xp_assert_close(abs(hp[4]), np.ones_like(abs(hp[4])), rtol=1e-2,
4087
+ check_0d=False)
4088
+
4089
+ # hp[1] and hp[3] correspond to frequencies approximately
4090
+ # on the edges of the passband and should be close to -3dB
4091
+ xp_assert_close(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2)
4092
+ xp_assert_close(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2)
4093
+
4094
+ # hp[2] correspond to the frequency that should be removed
4095
+ # the frequency response should be very close to 0
4096
+ xp_assert_close(abs(hp[2]), np.asarray(0.0), atol=1e-10, check_0d=False)
4097
+
4098
+
4099
+ class TestIIRPeak:
4100
+
4101
+ def test_ba_output(self):
4102
+ # Compare coefficients with Matlab ones
4103
+ # for the equivalent input:
4104
+ b, a = iirpeak(0.06, 30)
4105
+ b2 = [
4106
+ 3.131764229e-03, 0,
4107
+ -3.131764229e-03
4108
+ ]
4109
+ a2 = [
4110
+ 1.0000000e+00, -1.958421917e+00,
4111
+ 9.9373647e-01
4112
+ ]
4113
+ xp_assert_close(b, b2, rtol=1e-8)
4114
+ xp_assert_close(a, a2, rtol=1e-8)
4115
+
4116
+ def test_frequency_response(self):
4117
+ # Get filter coefficients
4118
+ b, a = iirpeak(0.3, 30)
4119
+
4120
+ # Get frequency response
4121
+ w, h = freqz(b, a, 1000)
4122
+
4123
+ # Pick 5 point
4124
+ p = [30, # w0 = 0.030
4125
+ 295, # w0 = 0.295
4126
+ 300, # w0 = 0.300
4127
+ 305, # w0 = 0.305
4128
+ 800] # w0 = 0.800
4129
+
4130
+ # Get frequency response correspondent to each of those points
4131
+ hp = h[p]
4132
+
4133
+ # Check if the frequency response fulfill the specifications:
4134
+ # hp[0] and hp[4] correspond to frequencies distant from
4135
+ # w0 = 0.3 and should be close to 0
4136
+ xp_assert_close(abs(hp[0]),
4137
+ np.zeros_like(abs(hp[0])), atol=1e-2, check_0d=False)
4138
+ xp_assert_close(abs(hp[4]),
4139
+ np.zeros_like(abs(hp[4])), atol=1e-2, check_0d=False)
4140
+
4141
+ # hp[1] and hp[3] correspond to frequencies approximately
4142
+ # on the edges of the passband and should be close to 10**(-3/20)
4143
+ xp_assert_close(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2)
4144
+ xp_assert_close(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2)
4145
+
4146
+ # hp[2] correspond to the frequency that should be retained and
4147
+ # the frequency response should be very close to 1
4148
+ xp_assert_close(abs(hp[2]), np.asarray(1.0), rtol=1e-10, check_0d=False)
4149
+
4150
+ def test_errors(self):
4151
+ # Exception should be raised if w0 > 1 or w0 <0
4152
+ assert_raises(ValueError, iirpeak, w0=2, Q=30)
4153
+ assert_raises(ValueError, iirpeak, w0=-1, Q=30)
4154
+
4155
+ # Exception should be raised if any of the parameters
4156
+ # are not float (or cannot be converted to one)
4157
+ assert_raises(ValueError, iirpeak, w0="blabla", Q=30)
4158
+ assert_raises(TypeError, iirpeak, w0=-1, Q=[1, 2, 3])
4159
+
4160
+ def test_fs_param(self):
4161
+ # Get filter coefficients
4162
+ b, a = iirpeak(1200, 30, fs=8000)
4163
+
4164
+ # Get frequency response
4165
+ w, h = freqz(b, a, 1000, fs=8000)
4166
+
4167
+ # Pick 5 point
4168
+ p = [30, # w0 = 120
4169
+ 295, # w0 = 1180
4170
+ 300, # w0 = 1200
4171
+ 305, # w0 = 1220
4172
+ 800] # w0 = 3200
4173
+
4174
+ # Get frequency response correspondent to each of those points
4175
+ hp = h[p]
4176
+
4177
+ # Check if the frequency response fulfill the specifications:
4178
+ # hp[0] and hp[4] correspond to frequencies distant from
4179
+ # w0 = 1200 and should be close to 0
4180
+ xp_assert_close(abs(hp[0]),
4181
+ np.zeros_like(abs(hp[0])), atol=1e-2, check_0d=False)
4182
+ xp_assert_close(abs(hp[4]),
4183
+ np.zeros_like(abs(hp[4])), atol=1e-2, check_0d=False)
4184
+
4185
+ # hp[1] and hp[3] correspond to frequencies approximately
4186
+ # on the edges of the passband and should be close to 10**(-3/20)
4187
+ xp_assert_close(abs(hp[1]), 1/np.sqrt(2), rtol=1e-2)
4188
+ xp_assert_close(abs(hp[3]), 1/np.sqrt(2), rtol=1e-2)
4189
+
4190
+ # hp[2] correspond to the frequency that should be retained and
4191
+ # the frequency response should be very close to 1
4192
+ xp_assert_close(abs(hp[2]),
4193
+ np.ones_like(abs(hp[2])), rtol=1e-10, check_0d=False)
4194
+
4195
+
4196
+ class TestIIRComb:
4197
+ # Test erroneous input cases
4198
+ def test_invalid_input(self):
4199
+ # w0 is <= 0 or >= fs / 2
4200
+ fs = 1000
4201
+ for args in [(-fs, 30), (0, 35), (fs / 2, 40), (fs, 35)]:
4202
+ with pytest.raises(ValueError, match='w0 must be between '):
4203
+ iircomb(*args, fs=fs)
4204
+
4205
+ # fs is not divisible by w0
4206
+ for args in [(120, 30), (157, 35)]:
4207
+ with pytest.raises(ValueError, match='fs must be divisible '):
4208
+ iircomb(*args, fs=fs)
4209
+
4210
+ # https://github.com/scipy/scipy/issues/14043#issuecomment-1107349140
4211
+ # Previously, fs=44100, w0=49.999 was rejected, but fs=2,
4212
+ # w0=49.999/int(44100/2) was accepted. Now it is rejected, too.
4213
+ with pytest.raises(ValueError, match='fs must be divisible '):
4214
+ iircomb(w0=49.999/int(44100/2), Q=30)
4215
+
4216
+ with pytest.raises(ValueError, match='fs must be divisible '):
4217
+ iircomb(w0=49.999, Q=30, fs=44100)
4218
+
4219
+ # Filter type is not notch or peak
4220
+ for args in [(0.2, 30, 'natch'), (0.5, 35, 'comb')]:
4221
+ with pytest.raises(ValueError, match='ftype must be '):
4222
+ iircomb(*args)
4223
+
4224
+ # Verify that the filter's frequency response contains a
4225
+ # notch at the cutoff frequency
4226
+ @pytest.mark.parametrize('ftype', ('notch', 'peak'))
4227
+ def test_frequency_response(self, ftype):
4228
+ # Create a notching or peaking comb filter at 1000 Hz
4229
+ b, a = iircomb(1000, 30, ftype=ftype, fs=10000)
4230
+
4231
+ # Compute the frequency response
4232
+ freqs, response = freqz(b, a, 1000, fs=10000)
4233
+
4234
+ # Find the notch using argrelextrema
4235
+ comb_points = argrelextrema(abs(response), np.less)[0]
4236
+
4237
+ # Verify that the first notch sits at 1000 Hz
4238
+ comb1 = comb_points[0]
4239
+ xp_assert_close(freqs[comb1], np.asarray(1000.), check_0d=False)
4240
+
4241
+ # Verify pass_zero parameter
4242
+ @pytest.mark.parametrize('ftype,pass_zero,peak,notch',
4243
+ [('peak', True, 123.45, 61.725),
4244
+ ('peak', False, 61.725, 123.45),
4245
+ ('peak', None, 61.725, 123.45),
4246
+ ('notch', None, 61.725, 123.45),
4247
+ ('notch', True, 123.45, 61.725),
4248
+ ('notch', False, 61.725, 123.45)])
4249
+ def test_pass_zero(self, ftype, pass_zero, peak, notch):
4250
+ # Create a notching or peaking comb filter
4251
+ b, a = iircomb(123.45, 30, ftype=ftype, fs=1234.5, pass_zero=pass_zero)
4252
+
4253
+ # Compute the frequency response
4254
+ freqs, response = freqz(b, a, [peak, notch], fs=1234.5)
4255
+
4256
+ # Verify that expected notches are notches and peaks are peaks
4257
+ assert abs(response[0]) > 0.99
4258
+ assert abs(response[1]) < 1e-10
4259
+
4260
+ # All built-in IIR filters are real, so should have perfectly
4261
+ # symmetrical poles and zeros. Then ba representation (using
4262
+ # numpy.poly) will be purely real instead of having negligible
4263
+ # imaginary parts.
4264
+ def test_iir_symmetry(self):
4265
+ b, a = iircomb(400, 30, fs=24000)
4266
+ z, p, k = tf2zpk(b, a)
4267
+ xp_assert_equal(sorted(z), sorted(z.conj()))
4268
+ xp_assert_equal(sorted(p), sorted(p.conj()))
4269
+ xp_assert_equal(k, np.real(k))
4270
+
4271
+ assert issubclass(b.dtype.type, np.floating)
4272
+ assert issubclass(a.dtype.type, np.floating)
4273
+
4274
+ # Verify filter coefficients with MATLAB's iircomb function
4275
+ def test_ba_output(self):
4276
+ b_notch, a_notch = iircomb(60, 35, ftype='notch', fs=600)
4277
+ b_notch2 = [0.957020174408697, 0.0, 0.0, 0.0, 0.0, 0.0,
4278
+ 0.0, 0.0, 0.0, 0.0, -0.957020174408697]
4279
+ a_notch2 = [1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
4280
+ 0.0, 0.0, 0.0, 0.0, -0.914040348817395]
4281
+ xp_assert_close(b_notch, b_notch2)
4282
+ xp_assert_close(a_notch, a_notch2)
4283
+
4284
+ b_peak, a_peak = iircomb(60, 35, ftype='peak', fs=600)
4285
+ b_peak2 = [0.0429798255913026, 0.0, 0.0, 0.0, 0.0, 0.0,
4286
+ 0.0, 0.0, 0.0, 0.0, -0.0429798255913026]
4287
+ a_peak2 = [1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
4288
+ 0.0, 0.0, 0.0, 0.0, 0.914040348817395]
4289
+ xp_assert_close(b_peak, b_peak2)
4290
+ xp_assert_close(a_peak, a_peak2)
4291
+
4292
+ # Verify that https://github.com/scipy/scipy/issues/14043 is fixed
4293
+ def test_nearest_divisor(self):
4294
+ # Create a notching comb filter
4295
+ b, a = iircomb(50/int(44100/2), 50.0, ftype='notch')
4296
+
4297
+ # Compute the frequency response at an upper harmonic of 50
4298
+ freqs, response = freqz(b, a, [22000], fs=44100)
4299
+
4300
+ # Before bug fix, this would produce N = 881, so that 22 kHz was ~0 dB.
4301
+ # Now N = 882 correctly and 22 kHz should be a notch <-220 dB
4302
+ assert abs(response[0]) < 1e-10
4303
+
4304
+ def test_fs_validation(self):
4305
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
4306
+ iircomb(1000, 30, fs=np.array([10, 20]))
4307
+
4308
+ with pytest.raises(ValueError, match="Sampling.*be none"):
4309
+ iircomb(1000, 30, fs=None)
4310
+
4311
+
4312
+ class TestIIRDesign:
4313
+
4314
+ def test_exceptions(self):
4315
+ with pytest.raises(ValueError, match="the same shape"):
4316
+ iirdesign(0.2, [0.1, 0.3], 1, 40)
4317
+ with pytest.raises(ValueError, match="the same shape"):
4318
+ iirdesign(np.array([[0.3, 0.6], [0.3, 0.6]]),
4319
+ np.array([[0.4, 0.5], [0.4, 0.5]]), 1, 40)
4320
+
4321
+ # discrete filter with non-positive frequency
4322
+ with pytest.raises(ValueError, match="must be greater than 0"):
4323
+ iirdesign(0, 0.5, 1, 40)
4324
+ with pytest.raises(ValueError, match="must be greater than 0"):
4325
+ iirdesign(-0.1, 0.5, 1, 40)
4326
+ with pytest.raises(ValueError, match="must be greater than 0"):
4327
+ iirdesign(0.1, 0, 1, 40)
4328
+ with pytest.raises(ValueError, match="must be greater than 0"):
4329
+ iirdesign(0.1, -0.5, 1, 40)
4330
+ with pytest.raises(ValueError, match="must be greater than 0"):
4331
+ iirdesign([0, 0.3], [0.1, 0.5], 1, 40)
4332
+ with pytest.raises(ValueError, match="must be greater than 0"):
4333
+ iirdesign([-0.1, 0.3], [0.1, 0.5], 1, 40)
4334
+ with pytest.raises(ValueError, match="must be greater than 0"):
4335
+ iirdesign([0.1, 0], [0.1, 0.5], 1, 40)
4336
+ with pytest.raises(ValueError, match="must be greater than 0"):
4337
+ iirdesign([0.1, -0.3], [0.1, 0.5], 1, 40)
4338
+ with pytest.raises(ValueError, match="must be greater than 0"):
4339
+ iirdesign([0.1, 0.3], [0, 0.5], 1, 40)
4340
+ with pytest.raises(ValueError, match="must be greater than 0"):
4341
+ iirdesign([0.1, 0.3], [-0.1, 0.5], 1, 40)
4342
+ with pytest.raises(ValueError, match="must be greater than 0"):
4343
+ iirdesign([0.1, 0.3], [0.1, 0], 1, 40)
4344
+ with pytest.raises(ValueError, match="must be greater than 0"):
4345
+ iirdesign([0.1, 0.3], [0.1, -0.5], 1, 40)
4346
+
4347
+ # analog filter with negative frequency
4348
+ with pytest.raises(ValueError, match="must be greater than 0"):
4349
+ iirdesign(-0.1, 0.5, 1, 40, analog=True)
4350
+ with pytest.raises(ValueError, match="must be greater than 0"):
4351
+ iirdesign(0.1, -0.5, 1, 40, analog=True)
4352
+ with pytest.raises(ValueError, match="must be greater than 0"):
4353
+ iirdesign([-0.1, 0.3], [0.1, 0.5], 1, 40, analog=True)
4354
+ with pytest.raises(ValueError, match="must be greater than 0"):
4355
+ iirdesign([0.1, -0.3], [0.1, 0.5], 1, 40, analog=True)
4356
+ with pytest.raises(ValueError, match="must be greater than 0"):
4357
+ iirdesign([0.1, 0.3], [-0.1, 0.5], 1, 40, analog=True)
4358
+ with pytest.raises(ValueError, match="must be greater than 0"):
4359
+ iirdesign([0.1, 0.3], [0.1, -0.5], 1, 40, analog=True)
4360
+
4361
+ # discrete filter with fs=None, freq > 1
4362
+ with pytest.raises(ValueError, match="must be less than 1"):
4363
+ iirdesign(1, 0.5, 1, 40)
4364
+ with pytest.raises(ValueError, match="must be less than 1"):
4365
+ iirdesign(1.1, 0.5, 1, 40)
4366
+ with pytest.raises(ValueError, match="must be less than 1"):
4367
+ iirdesign(0.1, 1, 1, 40)
4368
+ with pytest.raises(ValueError, match="must be less than 1"):
4369
+ iirdesign(0.1, 1.5, 1, 40)
4370
+ with pytest.raises(ValueError, match="must be less than 1"):
4371
+ iirdesign([1, 0.3], [0.1, 0.5], 1, 40)
4372
+ with pytest.raises(ValueError, match="must be less than 1"):
4373
+ iirdesign([1.1, 0.3], [0.1, 0.5], 1, 40)
4374
+ with pytest.raises(ValueError, match="must be less than 1"):
4375
+ iirdesign([0.1, 1], [0.1, 0.5], 1, 40)
4376
+ with pytest.raises(ValueError, match="must be less than 1"):
4377
+ iirdesign([0.1, 1.1], [0.1, 0.5], 1, 40)
4378
+ with pytest.raises(ValueError, match="must be less than 1"):
4379
+ iirdesign([0.1, 0.3], [1, 0.5], 1, 40)
4380
+ with pytest.raises(ValueError, match="must be less than 1"):
4381
+ iirdesign([0.1, 0.3], [1.1, 0.5], 1, 40)
4382
+ with pytest.raises(ValueError, match="must be less than 1"):
4383
+ iirdesign([0.1, 0.3], [0.1, 1], 1, 40)
4384
+ with pytest.raises(ValueError, match="must be less than 1"):
4385
+ iirdesign([0.1, 0.3], [0.1, 1.5], 1, 40)
4386
+
4387
+ # discrete filter with fs>2, wp, ws < fs/2 must pass
4388
+ iirdesign(100, 500, 1, 40, fs=2000)
4389
+ iirdesign(500, 100, 1, 40, fs=2000)
4390
+ iirdesign([200, 400], [100, 500], 1, 40, fs=2000)
4391
+ iirdesign([100, 500], [200, 400], 1, 40, fs=2000)
4392
+
4393
+ # discrete filter with fs>2, freq > fs/2: this must raise
4394
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4395
+ iirdesign(1000, 400, 1, 40, fs=2000)
4396
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4397
+ iirdesign(1100, 500, 1, 40, fs=2000)
4398
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4399
+ iirdesign(100, 1000, 1, 40, fs=2000)
4400
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4401
+ iirdesign(100, 1100, 1, 40, fs=2000)
4402
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4403
+ iirdesign([1000, 400], [100, 500], 1, 40, fs=2000)
4404
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4405
+ iirdesign([1100, 400], [100, 500], 1, 40, fs=2000)
4406
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4407
+ iirdesign([200, 1000], [100, 500], 1, 40, fs=2000)
4408
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4409
+ iirdesign([200, 1100], [100, 500], 1, 40, fs=2000)
4410
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4411
+ iirdesign([200, 400], [1000, 500], 1, 40, fs=2000)
4412
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4413
+ iirdesign([200, 400], [1100, 500], 1, 40, fs=2000)
4414
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4415
+ iirdesign([200, 400], [100, 1000], 1, 40, fs=2000)
4416
+ with pytest.raises(ValueError, match="must be less than fs/2"):
4417
+ iirdesign([200, 400], [100, 1100], 1, 40, fs=2000)
4418
+
4419
+ with pytest.raises(ValueError, match="strictly inside stopband"):
4420
+ iirdesign([0.1, 0.4], [0.5, 0.6], 1, 40)
4421
+ with pytest.raises(ValueError, match="strictly inside stopband"):
4422
+ iirdesign([0.5, 0.6], [0.1, 0.4], 1, 40)
4423
+ with pytest.raises(ValueError, match="strictly inside stopband"):
4424
+ iirdesign([0.3, 0.6], [0.4, 0.7], 1, 40)
4425
+ with pytest.raises(ValueError, match="strictly inside stopband"):
4426
+ iirdesign([0.4, 0.7], [0.3, 0.6], 1, 40)
4427
+
4428
+ def test_fs_validation(self):
4429
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
4430
+ iirfilter(1, 1, btype="low", fs=np.array([10, 20]))
4431
+
4432
+
4433
+ class TestIIRFilter:
4434
+
4435
+ def test_symmetry(self):
4436
+ # All built-in IIR filters are real, so should have perfectly
4437
+ # symmetrical poles and zeros. Then ba representation (using
4438
+ # numpy.poly) will be purely real instead of having negligible
4439
+ # imaginary parts.
4440
+ for N in np.arange(1, 26):
4441
+ for ftype in ('butter', 'bessel', 'cheby1', 'cheby2', 'ellip'):
4442
+ z, p, k = iirfilter(N, 1.1, 1, 20, 'low', analog=True,
4443
+ ftype=ftype, output='zpk')
4444
+ xp_assert_equal(sorted(z),
4445
+ sorted(z.conj()))
4446
+ xp_assert_equal(sorted(p),
4447
+ sorted(p.conj()))
4448
+ xp_assert_equal(k, np.real(k))
4449
+
4450
+ b, a = iirfilter(N, 1.1, 1, 20, 'low', analog=True,
4451
+ ftype=ftype, output='ba')
4452
+ assert issubclass(b.dtype.type, np.floating)
4453
+ assert issubclass(a.dtype.type, np.floating)
4454
+
4455
+ def test_int_inputs(self):
4456
+ # Using integer frequency arguments and large N should not produce
4457
+ # numpy integers that wraparound to negative numbers
4458
+ k = iirfilter(24, 100, btype='low', analog=True, ftype='bessel',
4459
+ output='zpk')[2]
4460
+ k2 = 9.999999999999989e+47
4461
+ xp_assert_close(np.asarray(k), np.asarray(k2))
4462
+ # if fs is specified then the normalization of Wn to have
4463
+ # 0 <= Wn <= 1 should not cause an integer overflow
4464
+ # the following line should not raise an exception
4465
+ iirfilter(20, [1000000000, 1100000000], btype='bp',
4466
+ analog=False, fs=6250000000)
4467
+
4468
+ def test_invalid_wn_size(self):
4469
+ # low and high have 1 Wn, band and stop have 2 Wn
4470
+ assert_raises(ValueError, iirfilter, 1, [0.1, 0.9], btype='low')
4471
+ assert_raises(ValueError, iirfilter, 1, [0.2, 0.5], btype='high')
4472
+ assert_raises(ValueError, iirfilter, 1, 0.2, btype='bp')
4473
+ assert_raises(ValueError, iirfilter, 1, 400, btype='bs', analog=True)
4474
+
4475
+ def test_invalid_wn_range(self):
4476
+ # For digital filters, 0 <= Wn <= 1
4477
+ assert_raises(ValueError, iirfilter, 1, 2, btype='low')
4478
+ assert_raises(ValueError, iirfilter, 1, [0.5, 1], btype='band')
4479
+ assert_raises(ValueError, iirfilter, 1, [0., 0.5], btype='band')
4480
+ assert_raises(ValueError, iirfilter, 1, -1, btype='high')
4481
+ assert_raises(ValueError, iirfilter, 1, [1, 2], btype='band')
4482
+ assert_raises(ValueError, iirfilter, 1, [10, 20], btype='stop')
4483
+
4484
+ # analog=True with non-positive critical frequencies
4485
+ with pytest.raises(ValueError, match="must be greater than 0"):
4486
+ iirfilter(2, 0, btype='low', analog=True)
4487
+ with pytest.raises(ValueError, match="must be greater than 0"):
4488
+ iirfilter(2, -1, btype='low', analog=True)
4489
+ with pytest.raises(ValueError, match="must be greater than 0"):
4490
+ iirfilter(2, [0, 100], analog=True)
4491
+ with pytest.raises(ValueError, match="must be greater than 0"):
4492
+ iirfilter(2, [-1, 100], analog=True)
4493
+ with pytest.raises(ValueError, match="must be greater than 0"):
4494
+ iirfilter(2, [10, 0], analog=True)
4495
+ with pytest.raises(ValueError, match="must be greater than 0"):
4496
+ iirfilter(2, [10, -1], analog=True)
4497
+
4498
+ def test_analog_sos(self):
4499
+ # first order Butterworth filter with Wn = 1 has tf 1/(s+1)
4500
+ sos = [[0., 0., 1., 0., 1., 1.]]
4501
+ sos2 = iirfilter(N=1, Wn=1, btype='low', analog=True, output='sos')
4502
+ assert_array_almost_equal(sos, sos2)
4503
+
4504
+ def test_wn1_ge_wn0(self):
4505
+ # gh-15773: should raise error if Wn[0] >= Wn[1]
4506
+ with pytest.raises(ValueError,
4507
+ match=r"Wn\[0\] must be less than Wn\[1\]"):
4508
+ iirfilter(2, [0.5, 0.5])
4509
+ with pytest.raises(ValueError,
4510
+ match=r"Wn\[0\] must be less than Wn\[1\]"):
4511
+ iirfilter(2, [0.6, 0.5])
4512
+
4513
+
4514
+ class TestGroupDelay:
4515
+ def test_identity_filter(self):
4516
+ w, gd = group_delay((1, 1))
4517
+ assert_array_almost_equal(w, pi * np.arange(512) / 512)
4518
+ assert_array_almost_equal(gd, np.zeros(512))
4519
+ w, gd = group_delay((1, 1), whole=True)
4520
+ assert_array_almost_equal(w, 2 * pi * np.arange(512) / 512)
4521
+ assert_array_almost_equal(gd, np.zeros(512))
4522
+
4523
+ def test_fir(self):
4524
+ # Let's design linear phase FIR and check that the group delay
4525
+ # is constant.
4526
+ N = 100
4527
+ b = firwin(N + 1, 0.1)
4528
+ w, gd = group_delay((b, 1))
4529
+ xp_assert_close(gd, np.ones_like(gd)*(0.5 * N))
4530
+
4531
+ def test_iir(self):
4532
+ # Let's design Butterworth filter and test the group delay at
4533
+ # some points against MATLAB answer.
4534
+ b, a = butter(4, 0.1)
4535
+ w = np.linspace(0, pi, num=10, endpoint=False)
4536
+ w, gd = group_delay((b, a), w=w)
4537
+ matlab_gd = np.array([8.249313898506037, 11.958947880907104,
4538
+ 2.452325615326005, 1.048918665702008,
4539
+ 0.611382575635897, 0.418293269460578,
4540
+ 0.317932917836572, 0.261371844762525,
4541
+ 0.229038045801298, 0.212185774208521])
4542
+ assert_array_almost_equal(gd, matlab_gd)
4543
+
4544
+ @pytest.mark.thread_unsafe
4545
+ def test_singular(self):
4546
+ # Let's create a filter with zeros and poles on the unit circle and
4547
+ # check if warnings are raised at those frequencies.
4548
+ z1 = np.exp(1j * 0.1 * pi)
4549
+ z2 = np.exp(1j * 0.25 * pi)
4550
+ p1 = np.exp(1j * 0.5 * pi)
4551
+ p2 = np.exp(1j * 0.8 * pi)
4552
+ b = np.convolve([1, -z1], [1, -z2])
4553
+ a = np.convolve([1, -p1], [1, -p2])
4554
+ w = np.array([0.1 * pi, 0.25 * pi, -0.5 * pi, -0.8 * pi])
4555
+
4556
+ w, gd = assert_warns(UserWarning, group_delay, (b, a), w=w)
4557
+
4558
+ def test_backward_compat(self):
4559
+ # For backward compatibility, test if None act as a wrapper for default
4560
+ w1, gd1 = group_delay((1, 1))
4561
+ w2, gd2 = group_delay((1, 1), None)
4562
+ assert_array_almost_equal(w1, w2)
4563
+ assert_array_almost_equal(gd1, gd2)
4564
+
4565
+ def test_fs_param(self):
4566
+ # Let's design Butterworth filter and test the group delay at
4567
+ # some points against the normalized frequency answer.
4568
+ b, a = butter(4, 4800, fs=96000)
4569
+ w = np.linspace(0, 96000/2, num=10, endpoint=False)
4570
+ w, gd = group_delay((b, a), w=w, fs=96000)
4571
+ norm_gd = np.array([8.249313898506037, 11.958947880907104,
4572
+ 2.452325615326005, 1.048918665702008,
4573
+ 0.611382575635897, 0.418293269460578,
4574
+ 0.317932917836572, 0.261371844762525,
4575
+ 0.229038045801298, 0.212185774208521])
4576
+ assert_array_almost_equal(gd, norm_gd)
4577
+
4578
+ def test_w_or_N_types(self):
4579
+ # Measure at 8 equally-spaced points
4580
+ for N in (8, np.int8(8), np.int16(8), np.int32(8), np.int64(8),
4581
+ np.array(8)):
4582
+ w, gd = group_delay((1, 1), N)
4583
+ assert_array_almost_equal(w, pi * np.arange(8) / 8)
4584
+ assert_array_almost_equal(gd, np.zeros(8))
4585
+
4586
+ # Measure at frequency 8 rad/sec
4587
+ for w in (8.0, 8.0+0j):
4588
+ w_out, gd = group_delay((1, 1), w)
4589
+ assert_array_almost_equal(w_out, [8])
4590
+ assert_array_almost_equal(gd, [0])
4591
+
4592
+ def test_complex_coef(self):
4593
+ # gh-19586: handle complex coef TFs
4594
+ #
4595
+ # for g(z) = (alpha*z+1)/(1+conjugate(alpha)), group delay is
4596
+ # given by function below.
4597
+ #
4598
+ # def gd_expr(w, alpha):
4599
+ # num = 1j*(abs(alpha)**2-1)*np.exp(1j*w)
4600
+ # den = (alpha*np.exp(1j*w)+1)*(np.exp(1j*w)+np.conj(alpha))
4601
+ # return -np.imag(num/den)
4602
+
4603
+ # arbitrary non-real alpha
4604
+ alpha = -0.6143077933232609+0.3355978770229421j
4605
+ # 8 points from from -pi to pi
4606
+ wref = np.array([-3.141592653589793 ,
4607
+ -2.356194490192345 ,
4608
+ -1.5707963267948966,
4609
+ -0.7853981633974483,
4610
+ 0. ,
4611
+ 0.7853981633974483,
4612
+ 1.5707963267948966,
4613
+ 2.356194490192345 ])
4614
+ gdref = array([0.18759548150354619,
4615
+ 0.17999770352712252,
4616
+ 0.23598047471879877,
4617
+ 0.46539443069907194,
4618
+ 1.9511492420564165 ,
4619
+ 3.478129975138865 ,
4620
+ 0.6228594960517333 ,
4621
+ 0.27067831839471224])
4622
+ b = [alpha,1]
4623
+ a = [1, np.conjugate(alpha)]
4624
+ gdtest = group_delay((b,a), wref)[1]
4625
+ # need nulp=14 for macOS arm64 wheel builds; added 2 for some
4626
+ # robustness on other platforms.
4627
+ assert_array_almost_equal_nulp(gdtest, gdref, nulp=16)
4628
+
4629
+ def test_fs_validation(self):
4630
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
4631
+ group_delay((1, 1), fs=np.array([10, 20]))
4632
+
4633
+ with pytest.raises(ValueError, match="Sampling.*be none"):
4634
+ group_delay((1, 1), fs=None)
4635
+
4636
+
4637
+ class TestGammatone:
4638
+ # Test erroneous input cases.
4639
+ def test_invalid_input(self):
4640
+ # Cutoff frequency is <= 0 or >= fs / 2.
4641
+ fs = 16000
4642
+ for args in [(-fs, 'iir'), (0, 'fir'), (fs / 2, 'iir'), (fs, 'fir')]:
4643
+ with pytest.raises(ValueError, match='The frequency must be '
4644
+ 'between '):
4645
+ gammatone(*args, fs=fs)
4646
+
4647
+ # Filter type is not fir or iir
4648
+ for args in [(440, 'fie'), (220, 'it')]:
4649
+ with pytest.raises(ValueError, match='ftype must be '):
4650
+ gammatone(*args, fs=fs)
4651
+
4652
+ # Order is <= 0 or > 24 for FIR filter.
4653
+ for args in [(440, 'fir', -50), (220, 'fir', 0), (110, 'fir', 25),
4654
+ (55, 'fir', 50)]:
4655
+ with pytest.raises(ValueError, match='Invalid order: '):
4656
+ gammatone(*args, numtaps=None, fs=fs)
4657
+
4658
+ # Verify that the filter's frequency response is approximately
4659
+ # 1 at the cutoff frequency.
4660
+ def test_frequency_response(self):
4661
+ fs = 16000
4662
+ ftypes = ['fir', 'iir']
4663
+ for ftype in ftypes:
4664
+ # Create a gammatone filter centered at 1000 Hz.
4665
+ b, a = gammatone(1000, ftype, fs=fs)
4666
+
4667
+ # Calculate the frequency response.
4668
+ freqs, response = freqz(b, a)
4669
+
4670
+ # Determine peak magnitude of the response
4671
+ # and corresponding frequency.
4672
+ response_max = np.max(np.abs(response))
4673
+ freq_hz = freqs[np.argmax(np.abs(response))] / ((2 * np.pi) / fs)
4674
+
4675
+ # Check that the peak magnitude is 1 and the frequency is 1000 Hz.
4676
+ xp_assert_close(response_max,
4677
+ np.ones_like(response_max), rtol=1e-2, check_0d=False)
4678
+ xp_assert_close(freq_hz,
4679
+ 1000*np.ones_like(freq_hz), rtol=1e-2, check_0d=False)
4680
+
4681
+ # All built-in IIR filters are real, so should have perfectly
4682
+ # symmetrical poles and zeros. Then ba representation (using
4683
+ # numpy.poly) will be purely real instead of having negligible
4684
+ # imaginary parts.
4685
+ def test_iir_symmetry(self):
4686
+ b, a = gammatone(440, 'iir', fs=24000)
4687
+ z, p, k = tf2zpk(b, a)
4688
+ xp_assert_equal(sorted(z), sorted(z.conj()))
4689
+ xp_assert_equal(sorted(p), sorted(p.conj()))
4690
+ xp_assert_equal(k, np.real(k))
4691
+
4692
+ assert issubclass(b.dtype.type, np.floating)
4693
+ assert issubclass(a.dtype.type, np.floating)
4694
+
4695
+ # Verify FIR filter coefficients with the paper's
4696
+ # Mathematica implementation
4697
+ def test_fir_ba_output(self):
4698
+ b, _ = gammatone(15, 'fir', fs=1000)
4699
+ b2 = [0.0, 2.2608075649884e-04,
4700
+ 1.5077903981357e-03, 4.2033687753998e-03,
4701
+ 8.1508962726503e-03, 1.2890059089154e-02,
4702
+ 1.7833890391666e-02, 2.2392613558564e-02,
4703
+ 2.6055195863104e-02, 2.8435872863284e-02,
4704
+ 2.9293319149544e-02, 2.852976858014e-02,
4705
+ 2.6176557156294e-02, 2.2371510270395e-02,
4706
+ 1.7332485267759e-02]
4707
+ xp_assert_close(b, b2)
4708
+
4709
+ # Verify IIR filter coefficients with the paper's MATLAB implementation
4710
+ def test_iir_ba_output(self):
4711
+ b, a = gammatone(440, 'iir', fs=16000)
4712
+ b2 = [1.31494461367464e-06, -5.03391196645395e-06,
4713
+ 7.00649426000897e-06, -4.18951968419854e-06,
4714
+ 9.02614910412011e-07]
4715
+ a2 = [1.0, -7.65646235454218,
4716
+ 25.7584699322366, -49.7319214483238,
4717
+ 60.2667361289181, -46.9399590980486,
4718
+ 22.9474798808461, -6.43799381299034,
4719
+ 0.793651554625368]
4720
+ xp_assert_close(b, b2)
4721
+ xp_assert_close(a, a2)
4722
+
4723
+ def test_fs_validation(self):
4724
+ with pytest.raises(ValueError, match="Sampling.*single scalar"):
4725
+ gammatone(440, 'iir', fs=np.array([10, 20]))