scipy 1.15.3__cp312-cp312-musllinux_1_2_aarch64.whl → 1.16.0rc2__cp312-cp312-musllinux_1_2_aarch64.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 (641) hide show
  1. scipy/__config__.py +10 -10
  2. scipy/__init__.py +3 -6
  3. scipy/_cyutility.cpython-312-aarch64-linux-musl.so +0 -0
  4. scipy/_lib/_array_api.py +486 -161
  5. scipy/_lib/_array_api_compat_vendor.py +9 -0
  6. scipy/_lib/_bunch.py +4 -0
  7. scipy/_lib/_ccallback_c.cpython-312-aarch64-linux-musl.so +0 -0
  8. scipy/_lib/_docscrape.py +1 -1
  9. scipy/_lib/_elementwise_iterative_method.py +15 -26
  10. scipy/_lib/_fpumode.cpython-312-aarch64-linux-musl.so +0 -0
  11. scipy/_lib/_sparse.py +41 -0
  12. scipy/_lib/_test_ccallback.cpython-312-aarch64-linux-musl.so +0 -0
  13. scipy/_lib/_test_deprecation_call.cpython-312-aarch64-linux-musl.so +0 -0
  14. scipy/_lib/_test_deprecation_def.cpython-312-aarch64-linux-musl.so +0 -0
  15. scipy/_lib/_testutils.py +6 -2
  16. scipy/_lib/_uarray/_uarray.cpython-312-aarch64-linux-musl.so +0 -0
  17. scipy/_lib/_util.py +222 -125
  18. scipy/_lib/array_api_compat/__init__.py +4 -4
  19. scipy/_lib/array_api_compat/_internal.py +19 -6
  20. scipy/_lib/array_api_compat/common/__init__.py +1 -1
  21. scipy/_lib/array_api_compat/common/_aliases.py +365 -193
  22. scipy/_lib/array_api_compat/common/_fft.py +94 -64
  23. scipy/_lib/array_api_compat/common/_helpers.py +413 -180
  24. scipy/_lib/array_api_compat/common/_linalg.py +116 -40
  25. scipy/_lib/array_api_compat/common/_typing.py +179 -10
  26. scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
  27. scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
  28. scipy/_lib/array_api_compat/cupy/_info.py +16 -6
  29. scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
  30. scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
  31. scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
  32. scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
  33. scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
  34. scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
  35. scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
  36. scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
  37. scipy/_lib/array_api_compat/numpy/_info.py +36 -16
  38. scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
  39. scipy/_lib/array_api_compat/numpy/fft.py +11 -5
  40. scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
  41. scipy/_lib/array_api_compat/torch/__init__.py +3 -5
  42. scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
  43. scipy/_lib/array_api_compat/torch/_info.py +27 -16
  44. scipy/_lib/array_api_compat/torch/_typing.py +3 -0
  45. scipy/_lib/array_api_compat/torch/fft.py +17 -18
  46. scipy/_lib/array_api_compat/torch/linalg.py +16 -16
  47. scipy/_lib/array_api_extra/__init__.py +26 -3
  48. scipy/_lib/array_api_extra/_delegation.py +171 -0
  49. scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
  50. scipy/_lib/array_api_extra/_lib/_at.py +463 -0
  51. scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
  52. scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
  53. scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
  54. scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
  55. scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
  56. scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
  57. scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
  58. scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
  59. scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
  60. scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
  61. scipy/_lib/array_api_extra/testing.py +359 -0
  62. scipy/_lib/decorator.py +2 -2
  63. scipy/_lib/doccer.py +1 -7
  64. scipy/_lib/messagestream.cpython-312-aarch64-linux-musl.so +0 -0
  65. scipy/_lib/pyprima/__init__.py +212 -0
  66. scipy/_lib/pyprima/cobyla/__init__.py +0 -0
  67. scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
  68. scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
  69. scipy/_lib/pyprima/cobyla/geometry.py +226 -0
  70. scipy/_lib/pyprima/cobyla/initialize.py +215 -0
  71. scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
  72. scipy/_lib/pyprima/cobyla/update.py +289 -0
  73. scipy/_lib/pyprima/common/__init__.py +0 -0
  74. scipy/_lib/pyprima/common/_bounds.py +34 -0
  75. scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
  76. scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
  77. scipy/_lib/pyprima/common/_project.py +173 -0
  78. scipy/_lib/pyprima/common/checkbreak.py +93 -0
  79. scipy/_lib/pyprima/common/consts.py +47 -0
  80. scipy/_lib/pyprima/common/evaluate.py +99 -0
  81. scipy/_lib/pyprima/common/history.py +38 -0
  82. scipy/_lib/pyprima/common/infos.py +30 -0
  83. scipy/_lib/pyprima/common/linalg.py +435 -0
  84. scipy/_lib/pyprima/common/message.py +290 -0
  85. scipy/_lib/pyprima/common/powalg.py +131 -0
  86. scipy/_lib/pyprima/common/preproc.py +277 -0
  87. scipy/_lib/pyprima/common/present.py +5 -0
  88. scipy/_lib/pyprima/common/ratio.py +54 -0
  89. scipy/_lib/pyprima/common/redrho.py +47 -0
  90. scipy/_lib/pyprima/common/selectx.py +296 -0
  91. scipy/_lib/tests/test__util.py +105 -121
  92. scipy/_lib/tests/test_array_api.py +166 -35
  93. scipy/_lib/tests/test_bunch.py +7 -0
  94. scipy/_lib/tests/test_ccallback.py +2 -10
  95. scipy/_lib/tests/test_public_api.py +13 -0
  96. scipy/cluster/_hierarchy.cpython-312-aarch64-linux-musl.so +0 -0
  97. scipy/cluster/_optimal_leaf_ordering.cpython-312-aarch64-linux-musl.so +0 -0
  98. scipy/cluster/_vq.cpython-312-aarch64-linux-musl.so +0 -0
  99. scipy/cluster/hierarchy.py +393 -223
  100. scipy/cluster/tests/test_hierarchy.py +273 -335
  101. scipy/cluster/tests/test_vq.py +45 -61
  102. scipy/cluster/vq.py +39 -35
  103. scipy/conftest.py +263 -157
  104. scipy/constants/_constants.py +4 -1
  105. scipy/constants/tests/test_codata.py +2 -2
  106. scipy/constants/tests/test_constants.py +11 -18
  107. scipy/datasets/_download_all.py +15 -1
  108. scipy/datasets/_fetchers.py +7 -1
  109. scipy/datasets/_utils.py +1 -1
  110. scipy/differentiate/_differentiate.py +25 -25
  111. scipy/differentiate/tests/test_differentiate.py +24 -25
  112. scipy/fft/_basic.py +20 -0
  113. scipy/fft/_helper.py +3 -34
  114. scipy/fft/_pocketfft/helper.py +29 -1
  115. scipy/fft/_pocketfft/pypocketfft.cpython-312-aarch64-linux-musl.so +0 -0
  116. scipy/fft/_pocketfft/tests/test_basic.py +2 -4
  117. scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
  118. scipy/fft/_realtransforms.py +13 -0
  119. scipy/fft/tests/test_basic.py +27 -25
  120. scipy/fft/tests/test_fftlog.py +16 -7
  121. scipy/fft/tests/test_helper.py +18 -34
  122. scipy/fft/tests/test_real_transforms.py +8 -10
  123. scipy/fftpack/convolve.cpython-312-aarch64-linux-musl.so +0 -0
  124. scipy/fftpack/tests/test_basic.py +2 -4
  125. scipy/fftpack/tests/test_real_transforms.py +8 -9
  126. scipy/integrate/_bvp.py +9 -3
  127. scipy/integrate/_cubature.py +3 -2
  128. scipy/integrate/_dop.cpython-312-aarch64-linux-musl.so +0 -0
  129. scipy/integrate/_lsoda.cpython-312-aarch64-linux-musl.so +0 -0
  130. scipy/integrate/_ode.py +9 -2
  131. scipy/integrate/_odepack.cpython-312-aarch64-linux-musl.so +0 -0
  132. scipy/integrate/_quad_vec.py +21 -29
  133. scipy/integrate/_quadpack.cpython-312-aarch64-linux-musl.so +0 -0
  134. scipy/integrate/_quadpack_py.py +11 -7
  135. scipy/integrate/_quadrature.py +3 -3
  136. scipy/integrate/_rules/_base.py +2 -2
  137. scipy/integrate/_tanhsinh.py +48 -47
  138. scipy/integrate/_test_multivariate.cpython-312-aarch64-linux-musl.so +0 -0
  139. scipy/integrate/_test_odeint_banded.cpython-312-aarch64-linux-musl.so +0 -0
  140. scipy/integrate/_vode.cpython-312-aarch64-linux-musl.so +0 -0
  141. scipy/integrate/tests/test__quad_vec.py +0 -6
  142. scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
  143. scipy/integrate/tests/test_cubature.py +21 -35
  144. scipy/integrate/tests/test_quadrature.py +6 -8
  145. scipy/integrate/tests/test_tanhsinh.py +56 -48
  146. scipy/interpolate/__init__.py +70 -58
  147. scipy/interpolate/_bary_rational.py +22 -22
  148. scipy/interpolate/_bsplines.py +119 -66
  149. scipy/interpolate/_cubic.py +65 -50
  150. scipy/interpolate/_dfitpack.cpython-312-aarch64-linux-musl.so +0 -0
  151. scipy/interpolate/_dierckx.cpython-312-aarch64-linux-musl.so +0 -0
  152. scipy/interpolate/_fitpack.cpython-312-aarch64-linux-musl.so +0 -0
  153. scipy/interpolate/_fitpack2.py +9 -6
  154. scipy/interpolate/_fitpack_impl.py +32 -26
  155. scipy/interpolate/_fitpack_repro.py +23 -19
  156. scipy/interpolate/_interpnd.cpython-312-aarch64-linux-musl.so +0 -0
  157. scipy/interpolate/_interpolate.py +30 -12
  158. scipy/interpolate/_ndbspline.py +13 -18
  159. scipy/interpolate/_ndgriddata.py +5 -8
  160. scipy/interpolate/_polyint.py +95 -31
  161. scipy/interpolate/_ppoly.cpython-312-aarch64-linux-musl.so +0 -0
  162. scipy/interpolate/_rbf.py +2 -2
  163. scipy/interpolate/_rbfinterp.py +1 -1
  164. scipy/interpolate/_rbfinterp_pythran.cpython-312-aarch64-linux-musl.so +0 -0
  165. scipy/interpolate/_rgi.py +31 -26
  166. scipy/interpolate/_rgi_cython.cpython-312-aarch64-linux-musl.so +0 -0
  167. scipy/interpolate/dfitpack.py +0 -20
  168. scipy/interpolate/interpnd.py +1 -2
  169. scipy/interpolate/tests/test_bary_rational.py +2 -2
  170. scipy/interpolate/tests/test_bsplines.py +97 -1
  171. scipy/interpolate/tests/test_fitpack2.py +39 -1
  172. scipy/interpolate/tests/test_interpnd.py +32 -20
  173. scipy/interpolate/tests/test_interpolate.py +48 -4
  174. scipy/interpolate/tests/test_rgi.py +2 -1
  175. scipy/io/_fast_matrix_market/__init__.py +2 -0
  176. scipy/io/_fast_matrix_market/_fmm_core.cpython-312-aarch64-linux-musl.so +0 -0
  177. scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
  178. scipy/io/_harwell_boeing/hb.py +7 -11
  179. scipy/io/_idl.py +5 -7
  180. scipy/io/_netcdf.py +15 -5
  181. scipy/io/_test_fortran.cpython-312-aarch64-linux-musl.so +0 -0
  182. scipy/io/arff/tests/test_arffread.py +3 -3
  183. scipy/io/matlab/__init__.py +5 -3
  184. scipy/io/matlab/_mio.py +4 -1
  185. scipy/io/matlab/_mio5.py +19 -13
  186. scipy/io/matlab/_mio5_utils.cpython-312-aarch64-linux-musl.so +0 -0
  187. scipy/io/matlab/_mio_utils.cpython-312-aarch64-linux-musl.so +0 -0
  188. scipy/io/matlab/_miobase.py +4 -1
  189. scipy/io/matlab/_streams.cpython-312-aarch64-linux-musl.so +0 -0
  190. scipy/io/matlab/tests/test_mio.py +46 -18
  191. scipy/io/matlab/tests/test_mio_funcs.py +1 -1
  192. scipy/io/tests/test_mmio.py +7 -1
  193. scipy/io/tests/test_wavfile.py +41 -0
  194. scipy/io/wavfile.py +57 -10
  195. scipy/linalg/_basic.py +113 -86
  196. scipy/linalg/_cythonized_array_utils.cpython-312-aarch64-linux-musl.so +0 -0
  197. scipy/linalg/_decomp.py +22 -9
  198. scipy/linalg/_decomp_cholesky.py +28 -13
  199. scipy/linalg/_decomp_cossin.py +45 -30
  200. scipy/linalg/_decomp_interpolative.cpython-312-aarch64-linux-musl.so +0 -0
  201. scipy/linalg/_decomp_ldl.py +4 -1
  202. scipy/linalg/_decomp_lu.py +18 -6
  203. scipy/linalg/_decomp_lu_cython.cpython-312-aarch64-linux-musl.so +0 -0
  204. scipy/linalg/_decomp_polar.py +2 -0
  205. scipy/linalg/_decomp_qr.py +6 -2
  206. scipy/linalg/_decomp_qz.py +3 -0
  207. scipy/linalg/_decomp_schur.py +3 -1
  208. scipy/linalg/_decomp_svd.py +13 -2
  209. scipy/linalg/_decomp_update.cpython-312-aarch64-linux-musl.so +0 -0
  210. scipy/linalg/_expm_frechet.py +4 -0
  211. scipy/linalg/_fblas.cpython-312-aarch64-linux-musl.so +0 -0
  212. scipy/linalg/_flapack.cpython-312-aarch64-linux-musl.so +0 -0
  213. scipy/linalg/_linalg_pythran.cpython-312-aarch64-linux-musl.so +0 -0
  214. scipy/linalg/_matfuncs.py +187 -4
  215. scipy/linalg/_matfuncs_expm.cpython-312-aarch64-linux-musl.so +0 -0
  216. scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-aarch64-linux-musl.so +0 -0
  217. scipy/linalg/_matfuncs_sqrtm.py +1 -99
  218. scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-aarch64-linux-musl.so +0 -0
  219. scipy/linalg/_procrustes.py +2 -0
  220. scipy/linalg/_sketches.py +17 -6
  221. scipy/linalg/_solve_toeplitz.cpython-312-aarch64-linux-musl.so +0 -0
  222. scipy/linalg/_solvers.py +7 -2
  223. scipy/linalg/_special_matrices.py +26 -36
  224. scipy/linalg/cython_blas.cpython-312-aarch64-linux-musl.so +0 -0
  225. scipy/linalg/cython_lapack.cpython-312-aarch64-linux-musl.so +0 -0
  226. scipy/linalg/lapack.py +22 -2
  227. scipy/linalg/tests/_cython_examples/meson.build +7 -0
  228. scipy/linalg/tests/test_basic.py +31 -16
  229. scipy/linalg/tests/test_batch.py +588 -0
  230. scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
  231. scipy/linalg/tests/test_decomp.py +40 -3
  232. scipy/linalg/tests/test_decomp_cossin.py +14 -0
  233. scipy/linalg/tests/test_decomp_ldl.py +1 -1
  234. scipy/linalg/tests/test_lapack.py +115 -7
  235. scipy/linalg/tests/test_matfuncs.py +157 -102
  236. scipy/linalg/tests/test_procrustes.py +0 -7
  237. scipy/linalg/tests/test_solve_toeplitz.py +1 -1
  238. scipy/linalg/tests/test_special_matrices.py +1 -5
  239. scipy/ndimage/__init__.py +1 -0
  240. scipy/ndimage/_ctest.cpython-312-aarch64-linux-musl.so +0 -0
  241. scipy/ndimage/_cytest.cpython-312-aarch64-linux-musl.so +0 -0
  242. scipy/ndimage/_delegators.py +8 -2
  243. scipy/ndimage/_filters.py +453 -5
  244. scipy/ndimage/_interpolation.py +36 -6
  245. scipy/ndimage/_measurements.py +4 -2
  246. scipy/ndimage/_morphology.py +5 -0
  247. scipy/ndimage/_nd_image.cpython-312-aarch64-linux-musl.so +0 -0
  248. scipy/ndimage/_ni_docstrings.py +5 -1
  249. scipy/ndimage/_ni_label.cpython-312-aarch64-linux-musl.so +0 -0
  250. scipy/ndimage/_ni_support.py +1 -5
  251. scipy/ndimage/_rank_filter_1d.cpython-312-aarch64-linux-musl.so +0 -0
  252. scipy/ndimage/_support_alternative_backends.py +18 -6
  253. scipy/ndimage/tests/test_filters.py +370 -259
  254. scipy/ndimage/tests/test_fourier.py +7 -9
  255. scipy/ndimage/tests/test_interpolation.py +68 -61
  256. scipy/ndimage/tests/test_measurements.py +18 -35
  257. scipy/ndimage/tests/test_morphology.py +143 -131
  258. scipy/ndimage/tests/test_splines.py +1 -3
  259. scipy/odr/__odrpack.cpython-312-aarch64-linux-musl.so +0 -0
  260. scipy/optimize/_basinhopping.py +13 -7
  261. scipy/optimize/_bglu_dense.cpython-312-aarch64-linux-musl.so +0 -0
  262. scipy/optimize/_bracket.py +17 -24
  263. scipy/optimize/_chandrupatla.py +9 -10
  264. scipy/optimize/_cobyla_py.py +104 -123
  265. scipy/optimize/_constraints.py +14 -10
  266. scipy/optimize/_differentiable_functions.py +371 -230
  267. scipy/optimize/_differentialevolution.py +4 -3
  268. scipy/optimize/_direct.cpython-312-aarch64-linux-musl.so +0 -0
  269. scipy/optimize/_dual_annealing.py +1 -1
  270. scipy/optimize/_elementwise.py +1 -4
  271. scipy/optimize/_group_columns.cpython-312-aarch64-linux-musl.so +0 -0
  272. scipy/optimize/_highspy/_core.cpython-312-aarch64-linux-musl.so +0 -0
  273. scipy/optimize/_highspy/_highs_options.cpython-312-aarch64-linux-musl.so +0 -0
  274. scipy/optimize/_lbfgsb.cpython-312-aarch64-linux-musl.so +0 -0
  275. scipy/optimize/_lbfgsb_py.py +57 -16
  276. scipy/optimize/_linprog_doc.py +2 -2
  277. scipy/optimize/_linprog_highs.py +2 -2
  278. scipy/optimize/_linprog_ip.py +25 -10
  279. scipy/optimize/_linprog_util.py +14 -16
  280. scipy/optimize/_lsap.cpython-312-aarch64-linux-musl.so +0 -0
  281. scipy/optimize/_lsq/common.py +3 -3
  282. scipy/optimize/_lsq/dogbox.py +16 -2
  283. scipy/optimize/_lsq/givens_elimination.cpython-312-aarch64-linux-musl.so +0 -0
  284. scipy/optimize/_lsq/least_squares.py +198 -126
  285. scipy/optimize/_lsq/lsq_linear.py +6 -6
  286. scipy/optimize/_lsq/trf.py +35 -8
  287. scipy/optimize/_milp.py +3 -1
  288. scipy/optimize/_minimize.py +105 -36
  289. scipy/optimize/_minpack.cpython-312-aarch64-linux-musl.so +0 -0
  290. scipy/optimize/_minpack_py.py +21 -14
  291. scipy/optimize/_moduleTNC.cpython-312-aarch64-linux-musl.so +0 -0
  292. scipy/optimize/_nnls.py +20 -21
  293. scipy/optimize/_nonlin.py +34 -3
  294. scipy/optimize/_numdiff.py +288 -110
  295. scipy/optimize/_optimize.py +86 -48
  296. scipy/optimize/_pava_pybind.cpython-312-aarch64-linux-musl.so +0 -0
  297. scipy/optimize/_remove_redundancy.py +5 -5
  298. scipy/optimize/_root_scalar.py +1 -1
  299. scipy/optimize/_shgo.py +6 -0
  300. scipy/optimize/_shgo_lib/_complex.py +1 -1
  301. scipy/optimize/_slsqp_py.py +216 -124
  302. scipy/optimize/_slsqplib.cpython-312-aarch64-linux-musl.so +0 -0
  303. scipy/optimize/_spectral.py +1 -1
  304. scipy/optimize/_tnc.py +8 -1
  305. scipy/optimize/_trlib/_trlib.cpython-312-aarch64-linux-musl.so +0 -0
  306. scipy/optimize/_trustregion.py +20 -6
  307. scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
  308. scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
  309. scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
  310. scipy/optimize/_trustregion_constr/projections.py +12 -8
  311. scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
  312. scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
  313. scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
  314. scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
  315. scipy/optimize/_trustregion_exact.py +0 -1
  316. scipy/optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
  317. scipy/optimize/_zeros_py.py +97 -17
  318. scipy/optimize/cython_optimize/_zeros.cpython-312-aarch64-linux-musl.so +0 -0
  319. scipy/optimize/slsqp.py +0 -1
  320. scipy/optimize/tests/test__basinhopping.py +1 -1
  321. scipy/optimize/tests/test__differential_evolution.py +4 -4
  322. scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
  323. scipy/optimize/tests/test__numdiff.py +66 -22
  324. scipy/optimize/tests/test__remove_redundancy.py +2 -2
  325. scipy/optimize/tests/test__shgo.py +9 -1
  326. scipy/optimize/tests/test_bracket.py +36 -46
  327. scipy/optimize/tests/test_chandrupatla.py +133 -135
  328. scipy/optimize/tests/test_cobyla.py +74 -45
  329. scipy/optimize/tests/test_constraints.py +1 -1
  330. scipy/optimize/tests/test_differentiable_functions.py +226 -6
  331. scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
  332. scipy/optimize/tests/test_least_squares.py +125 -13
  333. scipy/optimize/tests/test_linear_assignment.py +3 -3
  334. scipy/optimize/tests/test_linprog.py +3 -3
  335. scipy/optimize/tests/test_lsq_linear.py +6 -6
  336. scipy/optimize/tests/test_minimize_constrained.py +2 -2
  337. scipy/optimize/tests/test_minpack.py +4 -4
  338. scipy/optimize/tests/test_nnls.py +43 -3
  339. scipy/optimize/tests/test_nonlin.py +36 -0
  340. scipy/optimize/tests/test_optimize.py +95 -17
  341. scipy/optimize/tests/test_slsqp.py +36 -4
  342. scipy/optimize/tests/test_zeros.py +34 -1
  343. scipy/signal/__init__.py +12 -23
  344. scipy/signal/_delegators.py +568 -0
  345. scipy/signal/_filter_design.py +459 -241
  346. scipy/signal/_fir_filter_design.py +262 -90
  347. scipy/signal/_lti_conversion.py +3 -2
  348. scipy/signal/_ltisys.py +118 -91
  349. scipy/signal/_max_len_seq_inner.cpython-312-aarch64-linux-musl.so +0 -0
  350. scipy/signal/_peak_finding_utils.cpython-312-aarch64-linux-musl.so +0 -0
  351. scipy/signal/_polyutils.py +172 -0
  352. scipy/signal/_short_time_fft.py +519 -70
  353. scipy/signal/_signal_api.py +30 -0
  354. scipy/signal/_signaltools.py +719 -399
  355. scipy/signal/_sigtools.cpython-312-aarch64-linux-musl.so +0 -0
  356. scipy/signal/_sosfilt.cpython-312-aarch64-linux-musl.so +0 -0
  357. scipy/signal/_spectral_py.py +230 -50
  358. scipy/signal/_spline.cpython-312-aarch64-linux-musl.so +0 -0
  359. scipy/signal/_spline_filters.py +108 -68
  360. scipy/signal/_support_alternative_backends.py +73 -0
  361. scipy/signal/_upfirdn.py +4 -1
  362. scipy/signal/_upfirdn_apply.cpython-312-aarch64-linux-musl.so +0 -0
  363. scipy/signal/_waveforms.py +2 -11
  364. scipy/signal/_wavelets.py +1 -1
  365. scipy/signal/fir_filter_design.py +1 -0
  366. scipy/signal/spline.py +4 -11
  367. scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
  368. scipy/signal/tests/test_bsplines.py +114 -79
  369. scipy/signal/tests/test_cont2discrete.py +9 -2
  370. scipy/signal/tests/test_filter_design.py +721 -481
  371. scipy/signal/tests/test_fir_filter_design.py +332 -140
  372. scipy/signal/tests/test_savitzky_golay.py +4 -3
  373. scipy/signal/tests/test_short_time_fft.py +221 -3
  374. scipy/signal/tests/test_signaltools.py +2144 -1348
  375. scipy/signal/tests/test_spectral.py +50 -6
  376. scipy/signal/tests/test_splines.py +161 -96
  377. scipy/signal/tests/test_upfirdn.py +84 -50
  378. scipy/signal/tests/test_waveforms.py +20 -0
  379. scipy/signal/tests/test_windows.py +607 -466
  380. scipy/signal/windows/_windows.py +287 -148
  381. scipy/sparse/__init__.py +23 -4
  382. scipy/sparse/_base.py +270 -108
  383. scipy/sparse/_bsr.py +7 -4
  384. scipy/sparse/_compressed.py +59 -231
  385. scipy/sparse/_construct.py +90 -38
  386. scipy/sparse/_coo.py +115 -181
  387. scipy/sparse/_csc.py +4 -4
  388. scipy/sparse/_csparsetools.cpython-312-aarch64-linux-musl.so +0 -0
  389. scipy/sparse/_csr.py +2 -2
  390. scipy/sparse/_data.py +48 -48
  391. scipy/sparse/_dia.py +105 -18
  392. scipy/sparse/_dok.py +0 -23
  393. scipy/sparse/_index.py +4 -4
  394. scipy/sparse/_matrix.py +23 -0
  395. scipy/sparse/_sparsetools.cpython-312-aarch64-linux-musl.so +0 -0
  396. scipy/sparse/_sputils.py +37 -22
  397. scipy/sparse/base.py +0 -9
  398. scipy/sparse/bsr.py +0 -14
  399. scipy/sparse/compressed.py +0 -23
  400. scipy/sparse/construct.py +0 -6
  401. scipy/sparse/coo.py +0 -14
  402. scipy/sparse/csc.py +0 -3
  403. scipy/sparse/csgraph/_flow.cpython-312-aarch64-linux-musl.so +0 -0
  404. scipy/sparse/csgraph/_matching.cpython-312-aarch64-linux-musl.so +0 -0
  405. scipy/sparse/csgraph/_min_spanning_tree.cpython-312-aarch64-linux-musl.so +0 -0
  406. scipy/sparse/csgraph/_reordering.cpython-312-aarch64-linux-musl.so +0 -0
  407. scipy/sparse/csgraph/_shortest_path.cpython-312-aarch64-linux-musl.so +0 -0
  408. scipy/sparse/csgraph/_tools.cpython-312-aarch64-linux-musl.so +0 -0
  409. scipy/sparse/csgraph/_traversal.cpython-312-aarch64-linux-musl.so +0 -0
  410. scipy/sparse/csgraph/tests/test_matching.py +14 -2
  411. scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
  412. scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
  413. scipy/sparse/csr.py +0 -5
  414. scipy/sparse/data.py +1 -6
  415. scipy/sparse/dia.py +0 -7
  416. scipy/sparse/dok.py +0 -10
  417. scipy/sparse/linalg/_dsolve/_superlu.cpython-312-aarch64-linux-musl.so +0 -0
  418. scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
  419. scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
  420. scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-312-aarch64-linux-musl.so +0 -0
  421. scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
  422. scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
  423. scipy/sparse/linalg/_interface.py +17 -18
  424. scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
  425. scipy/sparse/linalg/_isolve/iterative.py +51 -45
  426. scipy/sparse/linalg/_isolve/lgmres.py +6 -6
  427. scipy/sparse/linalg/_isolve/minres.py +5 -5
  428. scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
  429. scipy/sparse/linalg/_isolve/utils.py +2 -8
  430. scipy/sparse/linalg/_matfuncs.py +1 -1
  431. scipy/sparse/linalg/_norm.py +1 -1
  432. scipy/sparse/linalg/_propack/_cpropack.cpython-312-aarch64-linux-musl.so +0 -0
  433. scipy/sparse/linalg/_propack/_dpropack.cpython-312-aarch64-linux-musl.so +0 -0
  434. scipy/sparse/linalg/_propack/_spropack.cpython-312-aarch64-linux-musl.so +0 -0
  435. scipy/sparse/linalg/_propack/_zpropack.cpython-312-aarch64-linux-musl.so +0 -0
  436. scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
  437. scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
  438. scipy/sparse/tests/test_arithmetic1d.py +5 -2
  439. scipy/sparse/tests/test_base.py +214 -42
  440. scipy/sparse/tests/test_common1d.py +7 -7
  441. scipy/sparse/tests/test_construct.py +1 -1
  442. scipy/sparse/tests/test_coo.py +272 -4
  443. scipy/sparse/tests/test_sparsetools.py +5 -0
  444. scipy/sparse/tests/test_sputils.py +36 -7
  445. scipy/spatial/_ckdtree.cpython-312-aarch64-linux-musl.so +0 -0
  446. scipy/spatial/_distance_pybind.cpython-312-aarch64-linux-musl.so +0 -0
  447. scipy/spatial/_distance_wrap.cpython-312-aarch64-linux-musl.so +0 -0
  448. scipy/spatial/_hausdorff.cpython-312-aarch64-linux-musl.so +0 -0
  449. scipy/spatial/_qhull.cpython-312-aarch64-linux-musl.so +0 -0
  450. scipy/spatial/_voronoi.cpython-312-aarch64-linux-musl.so +0 -0
  451. scipy/spatial/distance.py +49 -42
  452. scipy/spatial/tests/test_distance.py +15 -1
  453. scipy/spatial/tests/test_kdtree.py +1 -0
  454. scipy/spatial/tests/test_qhull.py +7 -2
  455. scipy/spatial/transform/__init__.py +5 -3
  456. scipy/spatial/transform/_rigid_transform.cpython-312-aarch64-linux-musl.so +0 -0
  457. scipy/spatial/transform/_rotation.cpython-312-aarch64-linux-musl.so +0 -0
  458. scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
  459. scipy/spatial/transform/tests/test_rotation.py +1213 -832
  460. scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
  461. scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
  462. scipy/special/__init__.py +1 -47
  463. scipy/special/_add_newdocs.py +34 -772
  464. scipy/special/_basic.py +22 -25
  465. scipy/special/_comb.cpython-312-aarch64-linux-musl.so +0 -0
  466. scipy/special/_ellip_harm_2.cpython-312-aarch64-linux-musl.so +0 -0
  467. scipy/special/_gufuncs.cpython-312-aarch64-linux-musl.so +0 -0
  468. scipy/special/_logsumexp.py +67 -58
  469. scipy/special/_orthogonal.pyi +1 -1
  470. scipy/special/_specfun.cpython-312-aarch64-linux-musl.so +0 -0
  471. scipy/special/_special_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
  472. scipy/special/_spherical_bessel.py +4 -4
  473. scipy/special/_support_alternative_backends.py +212 -119
  474. scipy/special/_test_internal.cpython-312-aarch64-linux-musl.so +0 -0
  475. scipy/special/_testutils.py +4 -4
  476. scipy/special/_ufuncs.cpython-312-aarch64-linux-musl.so +0 -0
  477. scipy/special/_ufuncs.pyi +1 -0
  478. scipy/special/_ufuncs.pyx +215 -1400
  479. scipy/special/_ufuncs_cxx.cpython-312-aarch64-linux-musl.so +0 -0
  480. scipy/special/_ufuncs_cxx.pxd +2 -15
  481. scipy/special/_ufuncs_cxx.pyx +5 -44
  482. scipy/special/_ufuncs_cxx_defs.h +2 -16
  483. scipy/special/_ufuncs_defs.h +0 -8
  484. scipy/special/cython_special.cpython-312-aarch64-linux-musl.so +0 -0
  485. scipy/special/cython_special.pxd +1 -1
  486. scipy/special/tests/_cython_examples/meson.build +10 -1
  487. scipy/special/tests/test_basic.py +153 -20
  488. scipy/special/tests/test_boost_ufuncs.py +3 -0
  489. scipy/special/tests/test_cdflib.py +35 -11
  490. scipy/special/tests/test_gammainc.py +16 -0
  491. scipy/special/tests/test_hyp2f1.py +2 -2
  492. scipy/special/tests/test_log1mexp.py +85 -0
  493. scipy/special/tests/test_logsumexp.py +206 -64
  494. scipy/special/tests/test_mpmath.py +1 -0
  495. scipy/special/tests/test_nan_inputs.py +1 -1
  496. scipy/special/tests/test_orthogonal.py +17 -18
  497. scipy/special/tests/test_sf_error.py +3 -2
  498. scipy/special/tests/test_sph_harm.py +6 -7
  499. scipy/special/tests/test_support_alternative_backends.py +211 -76
  500. scipy/stats/__init__.py +4 -1
  501. scipy/stats/_ansari_swilk_statistics.cpython-312-aarch64-linux-musl.so +0 -0
  502. scipy/stats/_axis_nan_policy.py +5 -12
  503. scipy/stats/_biasedurn.cpython-312-aarch64-linux-musl.so +0 -0
  504. scipy/stats/_continued_fraction.py +387 -0
  505. scipy/stats/_continuous_distns.py +277 -310
  506. scipy/stats/_correlation.py +1 -1
  507. scipy/stats/_covariance.py +6 -3
  508. scipy/stats/_discrete_distns.py +39 -32
  509. scipy/stats/_distn_infrastructure.py +39 -12
  510. scipy/stats/_distribution_infrastructure.py +900 -238
  511. scipy/stats/_entropy.py +9 -10
  512. scipy/{_lib → stats}/_finite_differences.py +1 -1
  513. scipy/stats/_hypotests.py +83 -50
  514. scipy/stats/_kde.py +53 -49
  515. scipy/stats/_ksstats.py +1 -1
  516. scipy/stats/_levy_stable/__init__.py +7 -15
  517. scipy/stats/_levy_stable/levyst.cpython-312-aarch64-linux-musl.so +0 -0
  518. scipy/stats/_morestats.py +118 -73
  519. scipy/stats/_mstats_basic.py +13 -17
  520. scipy/stats/_mstats_extras.py +8 -8
  521. scipy/stats/_multivariate.py +89 -113
  522. scipy/stats/_new_distributions.py +97 -20
  523. scipy/stats/_page_trend_test.py +12 -5
  524. scipy/stats/_probability_distribution.py +265 -43
  525. scipy/stats/_qmc.py +14 -9
  526. scipy/stats/_qmc_cy.cpython-312-aarch64-linux-musl.so +0 -0
  527. scipy/stats/_qmvnt.py +16 -95
  528. scipy/stats/_qmvnt_cy.cpython-312-aarch64-linux-musl.so +0 -0
  529. scipy/stats/_quantile.py +335 -0
  530. scipy/stats/_rcont/rcont.cpython-312-aarch64-linux-musl.so +0 -0
  531. scipy/stats/_resampling.py +4 -29
  532. scipy/stats/_sampling.py +1 -1
  533. scipy/stats/_sobol.cpython-312-aarch64-linux-musl.so +0 -0
  534. scipy/stats/_stats.cpython-312-aarch64-linux-musl.so +0 -0
  535. scipy/stats/_stats_mstats_common.py +21 -2
  536. scipy/stats/_stats_py.py +550 -476
  537. scipy/stats/_stats_pythran.cpython-312-aarch64-linux-musl.so +0 -0
  538. scipy/stats/_unuran/unuran_wrapper.cpython-312-aarch64-linux-musl.so +0 -0
  539. scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
  540. scipy/stats/_variation.py +6 -8
  541. scipy/stats/_wilcoxon.py +13 -7
  542. scipy/stats/tests/common_tests.py +6 -4
  543. scipy/stats/tests/test_axis_nan_policy.py +62 -24
  544. scipy/stats/tests/test_continued_fraction.py +173 -0
  545. scipy/stats/tests/test_continuous.py +379 -60
  546. scipy/stats/tests/test_continuous_basic.py +18 -12
  547. scipy/stats/tests/test_discrete_basic.py +14 -8
  548. scipy/stats/tests/test_discrete_distns.py +16 -16
  549. scipy/stats/tests/test_distributions.py +95 -75
  550. scipy/stats/tests/test_entropy.py +40 -48
  551. scipy/stats/tests/test_fit.py +4 -3
  552. scipy/stats/tests/test_hypotests.py +153 -24
  553. scipy/stats/tests/test_kdeoth.py +109 -41
  554. scipy/stats/tests/test_marray.py +289 -0
  555. scipy/stats/tests/test_morestats.py +79 -47
  556. scipy/stats/tests/test_mstats_basic.py +3 -3
  557. scipy/stats/tests/test_multivariate.py +434 -83
  558. scipy/stats/tests/test_qmc.py +13 -10
  559. scipy/stats/tests/test_quantile.py +199 -0
  560. scipy/stats/tests/test_rank.py +119 -112
  561. scipy/stats/tests/test_resampling.py +47 -56
  562. scipy/stats/tests/test_sampling.py +9 -4
  563. scipy/stats/tests/test_stats.py +799 -939
  564. scipy/stats/tests/test_variation.py +8 -6
  565. scipy/version.py +2 -2
  566. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
  567. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
  568. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +1262 -1269
  569. scipy.libs/libgcc_s-69c45f16.so.1 +0 -0
  570. scipy.libs/libgfortran-db0b6589.so.5.0.0 +0 -0
  571. scipy.libs/{libstdc++-1b614e01.so.6.0.32 → libstdc++-1f1a71be.so.6.0.33} +0 -0
  572. scipy/_lib/array_api_extra/_funcs.py +0 -484
  573. scipy/_lib/array_api_extra/_typing.py +0 -8
  574. scipy/interpolate/_bspl.cpython-312-aarch64-linux-musl.so +0 -0
  575. scipy/optimize/_cobyla.cpython-312-aarch64-linux-musl.so +0 -0
  576. scipy/optimize/_cython_nnls.cpython-312-aarch64-linux-musl.so +0 -0
  577. scipy/optimize/_slsqp.cpython-312-aarch64-linux-musl.so +0 -0
  578. scipy/spatial/qhull_src/COPYING.txt +0 -38
  579. scipy/special/libsf_error_state.so +0 -0
  580. scipy/special/tests/test_log_softmax.py +0 -109
  581. scipy/special/tests/test_xsf_cuda.py +0 -114
  582. scipy/special/xsf/binom.h +0 -89
  583. scipy/special/xsf/cdflib.h +0 -100
  584. scipy/special/xsf/cephes/airy.h +0 -307
  585. scipy/special/xsf/cephes/besselpoly.h +0 -51
  586. scipy/special/xsf/cephes/beta.h +0 -257
  587. scipy/special/xsf/cephes/cbrt.h +0 -131
  588. scipy/special/xsf/cephes/chbevl.h +0 -85
  589. scipy/special/xsf/cephes/chdtr.h +0 -193
  590. scipy/special/xsf/cephes/const.h +0 -87
  591. scipy/special/xsf/cephes/ellie.h +0 -293
  592. scipy/special/xsf/cephes/ellik.h +0 -251
  593. scipy/special/xsf/cephes/ellpe.h +0 -107
  594. scipy/special/xsf/cephes/ellpk.h +0 -117
  595. scipy/special/xsf/cephes/expn.h +0 -260
  596. scipy/special/xsf/cephes/gamma.h +0 -398
  597. scipy/special/xsf/cephes/hyp2f1.h +0 -596
  598. scipy/special/xsf/cephes/hyperg.h +0 -361
  599. scipy/special/xsf/cephes/i0.h +0 -149
  600. scipy/special/xsf/cephes/i1.h +0 -158
  601. scipy/special/xsf/cephes/igam.h +0 -421
  602. scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
  603. scipy/special/xsf/cephes/igami.h +0 -313
  604. scipy/special/xsf/cephes/j0.h +0 -225
  605. scipy/special/xsf/cephes/j1.h +0 -198
  606. scipy/special/xsf/cephes/jv.h +0 -715
  607. scipy/special/xsf/cephes/k0.h +0 -164
  608. scipy/special/xsf/cephes/k1.h +0 -163
  609. scipy/special/xsf/cephes/kn.h +0 -243
  610. scipy/special/xsf/cephes/lanczos.h +0 -112
  611. scipy/special/xsf/cephes/ndtr.h +0 -275
  612. scipy/special/xsf/cephes/poch.h +0 -85
  613. scipy/special/xsf/cephes/polevl.h +0 -167
  614. scipy/special/xsf/cephes/psi.h +0 -194
  615. scipy/special/xsf/cephes/rgamma.h +0 -111
  616. scipy/special/xsf/cephes/scipy_iv.h +0 -811
  617. scipy/special/xsf/cephes/shichi.h +0 -248
  618. scipy/special/xsf/cephes/sici.h +0 -224
  619. scipy/special/xsf/cephes/sindg.h +0 -221
  620. scipy/special/xsf/cephes/tandg.h +0 -139
  621. scipy/special/xsf/cephes/trig.h +0 -58
  622. scipy/special/xsf/cephes/unity.h +0 -186
  623. scipy/special/xsf/cephes/zeta.h +0 -172
  624. scipy/special/xsf/config.h +0 -304
  625. scipy/special/xsf/digamma.h +0 -205
  626. scipy/special/xsf/error.h +0 -57
  627. scipy/special/xsf/evalpoly.h +0 -47
  628. scipy/special/xsf/expint.h +0 -266
  629. scipy/special/xsf/hyp2f1.h +0 -694
  630. scipy/special/xsf/iv_ratio.h +0 -173
  631. scipy/special/xsf/lambertw.h +0 -150
  632. scipy/special/xsf/loggamma.h +0 -163
  633. scipy/special/xsf/sici.h +0 -200
  634. scipy/special/xsf/tools.h +0 -427
  635. scipy/special/xsf/trig.h +0 -164
  636. scipy/special/xsf/wright_bessel.h +0 -843
  637. scipy/special/xsf/zlog1.h +0 -35
  638. scipy/stats/_mvn.cpython-312-aarch64-linux-musl.so +0 -0
  639. scipy.libs/libgcc_s-7393e603.so.1 +0 -0
  640. scipy.libs/libgfortran-eb933d8e.so.5.0.0 +0 -0
  641. {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
@@ -1,21 +1,23 @@
1
1
  """Functions for FIR filter design."""
2
2
 
3
- from math import ceil, log
4
- import operator
3
+ from math import ceil, log, log2
5
4
  import warnings
6
5
  from typing import Literal
7
6
 
8
7
  import numpy as np
9
- from numpy.fft import irfft, fft, ifft
10
- from scipy.special import sinc
8
+ from scipy.fft import irfft, fft, ifft
11
9
  from scipy.linalg import (toeplitz, hankel, solve, LinAlgError, LinAlgWarning,
12
10
  lstsq)
13
11
  from scipy.signal._arraytools import _validate_fs
14
12
 
15
13
  from . import _sigtools
16
14
 
15
+ from scipy._lib._array_api import array_namespace, xp_size, xp_default_dtype
16
+ import scipy._lib.array_api_extra as xpx
17
+
18
+
17
19
  __all__ = ['kaiser_beta', 'kaiser_atten', 'kaiserord',
18
- 'firwin', 'firwin2', 'remez', 'firls', 'minimum_phase']
20
+ 'firwin', 'firwin2', 'firwin_2d', 'remez', 'firls', 'minimum_phase']
19
21
 
20
22
 
21
23
  # Some notes on function parameters:
@@ -323,6 +325,7 @@ def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True,
323
325
  See Also
324
326
  --------
325
327
  firwin2
328
+ firwin_2d
326
329
  firls
327
330
  minimum_phase
328
331
  remez
@@ -370,6 +373,9 @@ def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True,
370
373
  array([ 0.04890915, 0.91284326, 0.04890915])
371
374
 
372
375
  """
376
+ # NB: scipy's version of array_namespace returns `np_compat` for int or floats
377
+ xp = array_namespace(cutoff)
378
+
373
379
  # The major enhancements to this function added in November 2010 were
374
380
  # developed by Tom Krauss (see ticket #902).
375
381
  fs = _validate_fs(fs, allow_none=True)
@@ -377,18 +383,19 @@ def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True,
377
383
 
378
384
  nyq = 0.5 * fs
379
385
 
380
- cutoff = np.atleast_1d(cutoff) / float(nyq)
386
+ cutoff = xp.asarray(cutoff, dtype=xp_default_dtype(xp))
387
+ cutoff = xpx.atleast_nd(cutoff, ndim=1, xp=xp) / float(nyq)
381
388
 
382
389
  # Check for invalid input.
383
390
  if cutoff.ndim > 1:
384
391
  raise ValueError("The cutoff argument must be at most "
385
392
  "one-dimensional.")
386
- if cutoff.size == 0:
393
+ if xp_size(cutoff) == 0:
387
394
  raise ValueError("At least one cutoff frequency must be given.")
388
- if cutoff.min() <= 0 or cutoff.max() >= 1:
395
+ if xp.min(cutoff) <= 0 or xp.max(cutoff) >= 1:
389
396
  raise ValueError("Invalid cutoff frequency: frequencies must be "
390
397
  "greater than 0 and less than fs/2.")
391
- if np.any(np.diff(cutoff) <= 0):
398
+ if xp.any(cutoff[1:] - cutoff[:-1] <= 0):
392
399
  raise ValueError("Invalid cutoff frequencies: the frequencies "
393
400
  "must be strictly increasing.")
394
401
 
@@ -399,69 +406,68 @@ def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True,
399
406
  beta = kaiser_beta(atten)
400
407
  window = ('kaiser', beta)
401
408
 
402
- if isinstance(pass_zero, str):
403
- if pass_zero in ('bandstop', 'lowpass'):
404
- if pass_zero == 'lowpass':
405
- if cutoff.size != 1:
406
- raise ValueError('cutoff must have one element if '
407
- f'pass_zero=="lowpass", got {cutoff.shape}')
408
- elif cutoff.size <= 1:
409
- raise ValueError('cutoff must have at least two elements if '
410
- f'pass_zero=="bandstop", got {cutoff.shape}')
411
- pass_zero = True
412
- elif pass_zero in ('bandpass', 'highpass'):
413
- if pass_zero == 'highpass':
414
- if cutoff.size != 1:
415
- raise ValueError('cutoff must have one element if '
416
- f'pass_zero=="highpass", got {cutoff.shape}')
417
- elif cutoff.size <= 1:
418
- raise ValueError('cutoff must have at least two elements if '
419
- f'pass_zero=="bandpass", got {cutoff.shape}')
420
- pass_zero = False
421
- else:
422
- raise ValueError('pass_zero must be True, False, "bandpass", '
423
- '"lowpass", "highpass", or "bandstop", got '
424
- f'{pass_zero}')
425
- pass_zero = bool(operator.index(pass_zero)) # ensure bool-like
426
-
427
- pass_nyquist = bool(cutoff.size & 1) ^ pass_zero
409
+ if pass_zero in ('bandstop', 'lowpass'):
410
+ if pass_zero == 'lowpass':
411
+ if xp_size(cutoff) != 1:
412
+ raise ValueError('cutoff must have one element if '
413
+ f'pass_zero=="lowpass", got {cutoff.shape}')
414
+ elif xp_size(cutoff) <= 1:
415
+ raise ValueError('cutoff must have at least two elements if '
416
+ f'pass_zero=="bandstop", got {cutoff.shape}')
417
+ pass_zero = True
418
+ elif pass_zero in ('bandpass', 'highpass'):
419
+ if pass_zero == 'highpass':
420
+ if xp_size(cutoff) != 1:
421
+ raise ValueError('cutoff must have one element if '
422
+ f'pass_zero=="highpass", got {cutoff.shape}')
423
+ elif xp_size(cutoff) <= 1:
424
+ raise ValueError('cutoff must have at least two elements if '
425
+ f'pass_zero=="bandpass", got {cutoff.shape}')
426
+ pass_zero = False
427
+ elif not (pass_zero is True or pass_zero is False):
428
+ raise ValueError(f"Parameter {pass_zero=} not in (True, False, 'bandpass', " +
429
+ "'lowpass', 'highpass', 'bandstop')")
430
+
431
+ pass_nyquist = (xp_size(cutoff) % 2 == 0) == pass_zero
428
432
  if pass_nyquist and numtaps % 2 == 0:
429
433
  raise ValueError("A filter with an even number of coefficients must "
430
434
  "have zero response at the Nyquist frequency.")
431
435
 
432
436
  # Insert 0 and/or 1 at the ends of cutoff so that the length of cutoff
433
437
  # is even, and each pair in cutoff corresponds to passband.
434
- cutoff = np.hstack(([0.0] * pass_zero, cutoff, [1.0] * pass_nyquist))
438
+ cutoff = xp.concat((xp.zeros(int(pass_zero)), cutoff, xp.ones(int(pass_nyquist))))
439
+
435
440
 
436
441
  # `bands` is a 2-D array; each row gives the left and right edges of
437
442
  # a passband.
438
- bands = cutoff.reshape(-1, 2)
443
+ bands = xp.reshape(cutoff, (-1, 2))
439
444
 
440
445
  # Build up the coefficients.
441
446
  alpha = 0.5 * (numtaps - 1)
442
- m = np.arange(0, numtaps) - alpha
447
+ m = xp.arange(0, numtaps, dtype=cutoff.dtype) - alpha
443
448
  h = 0
444
- for left, right in bands:
445
- h += right * sinc(right * m)
446
- h -= left * sinc(left * m)
449
+ for j in range(bands.shape[0]):
450
+ left, right = bands[j, 0], bands[j, 1]
451
+ h += right * xpx.sinc(right * m, xp=xp)
452
+ h -= left * xpx.sinc(left * m, xp=xp)
447
453
 
448
454
  # Get and apply the window function.
449
455
  from .windows import get_window
450
- win = get_window(window, numtaps, fftbins=False)
456
+ win = get_window(window, numtaps, fftbins=False, xp=xp)
451
457
  h *= win
452
458
 
453
459
  # Now handle scaling if desired.
454
460
  if scale:
455
461
  # Get the first passband.
456
- left, right = bands[0]
462
+ left, right = bands[0, ...]
457
463
  if left == 0:
458
464
  scale_frequency = 0.0
459
465
  elif right == 1:
460
466
  scale_frequency = 1.0
461
467
  else:
462
468
  scale_frequency = 0.5 * (left + right)
463
- c = np.cos(np.pi * m * scale_frequency)
464
- s = np.sum(h * c)
469
+ c = xp.cos(xp.pi * m * scale_frequency)
470
+ s = xp.sum(h * c)
465
471
  h /= s
466
472
 
467
473
  return h
@@ -571,25 +577,29 @@ def firwin2(numtaps, freq, gain, *, nfreqs=None, window='hamming',
571
577
  [-0.02286961 -0.06362756 0.57310236 0.57310236 -0.06362756 -0.02286961]
572
578
 
573
579
  """
580
+ xp = array_namespace(freq, gain)
581
+ freq, gain = xp.asarray(freq), xp.asarray(gain)
582
+
574
583
  fs = _validate_fs(fs, allow_none=True)
575
584
  fs = 2 if fs is None else fs
576
585
  nyq = 0.5 * fs
577
586
 
578
- if len(freq) != len(gain):
587
+ if freq.shape[0] != gain.shape[0]:
579
588
  raise ValueError('freq and gain must be of same length.')
580
589
 
581
590
  if nfreqs is not None and numtaps >= nfreqs:
582
- raise ValueError(('ntaps must be less than nfreqs, but firwin2 was '
583
- 'called with ntaps=%d and nfreqs=%s') %
584
- (numtaps, nfreqs))
591
+ raise ValueError(
592
+ f'ntaps must be less than nfreqs, but firwin2 was called with '
593
+ f'ntaps={numtaps} and nfreqs={nfreqs}'
594
+ )
585
595
 
586
596
  if freq[0] != 0 or freq[-1] != nyq:
587
597
  raise ValueError('freq must start with 0 and end with fs/2.')
588
- d = np.diff(freq)
589
- if (d < 0).any():
598
+ d = freq[1:] - freq[:-1]
599
+ if xp.any(d < 0):
590
600
  raise ValueError('The values in freq must be nondecreasing.')
591
601
  d2 = d[:-1] + d[1:]
592
- if (d2 == 0).any():
602
+ if xp.any(d2 == 0):
593
603
  raise ValueError('A value in freq must not occur more than twice.')
594
604
  if freq[1] == 0:
595
605
  raise ValueError('Value 0 must not be repeated in freq')
@@ -620,28 +630,30 @@ def firwin2(numtaps, freq, gain, *, nfreqs=None, window='hamming',
620
630
  if nfreqs is None:
621
631
  nfreqs = 1 + 2 ** int(ceil(log(numtaps, 2)))
622
632
 
623
- if (d == 0).any():
633
+ if xp.any(d == 0):
624
634
  # Tweak any repeated values in freq so that interp works.
625
- freq = np.array(freq, copy=True)
626
- eps = np.finfo(float).eps * nyq
627
- for k in range(len(freq) - 1):
635
+ freq = xp.asarray(freq, copy=True)
636
+ eps = xp.finfo(xp_default_dtype(xp)).eps * nyq
637
+ for k in range(freq.shape[0] - 1):
628
638
  if freq[k] == freq[k + 1]:
629
639
  freq[k] = freq[k] - eps
630
640
  freq[k + 1] = freq[k + 1] + eps
631
641
  # Check if freq is strictly increasing after tweak
632
- d = np.diff(freq)
633
- if (d <= 0).any():
642
+ d = freq[1:] - freq[:-1]
643
+ if xp.any(d <= 0):
634
644
  raise ValueError("freq cannot contain numbers that are too close "
635
645
  "(within eps * (fs/2): "
636
646
  f"{eps}) to a repeated value")
637
647
 
638
648
  # Linearly interpolate the desired response on a uniform mesh `x`.
639
649
  x = np.linspace(0.0, nyq, nfreqs)
640
- fx = np.interp(x, freq, gain)
650
+ fx = np.interp(x, np.asarray(freq), np.asarray(gain)) # XXX array-api-extra#193
651
+ x = xp.asarray(x)
652
+ fx = xp.asarray(fx)
641
653
 
642
654
  # Adjust the phases of the coefficients so that the first `ntaps` of the
643
655
  # inverse FFT are the desired filter coefficients.
644
- shift = np.exp(-(numtaps - 1) / 2. * 1.j * np.pi * x / nyq)
656
+ shift = xp.exp(-(numtaps - 1) / 2. * 1j * xp.pi * x / nyq)
645
657
  if ftype > 2:
646
658
  shift *= 1j
647
659
 
@@ -653,7 +665,7 @@ def firwin2(numtaps, freq, gain, *, nfreqs=None, window='hamming',
653
665
  if window is not None:
654
666
  # Create the window to apply to the filter coefficients.
655
667
  from .windows import get_window
656
- wind = get_window(window, numtaps, fftbins=False)
668
+ wind = get_window(window, numtaps, fftbins=False, xp=xp)
657
669
  else:
658
670
  wind = 1
659
671
 
@@ -662,7 +674,7 @@ def firwin2(numtaps, freq, gain, *, nfreqs=None, window='hamming',
662
674
  out = out_full[:numtaps] * wind
663
675
 
664
676
  if ftype == 3:
665
- out[out.size // 2] = 0.0
677
+ out[xp_size(out) // 2] = 0.0
666
678
 
667
679
  return out
668
680
 
@@ -819,6 +831,12 @@ def remez(numtaps, bands, desired, *, weight=None, type='bandpass',
819
831
  >>> plt.show()
820
832
 
821
833
  """
834
+ xp = array_namespace(bands, desired, weight)
835
+ bands = np.asarray(bands)
836
+ desired = np.asarray(desired)
837
+ if weight:
838
+ weight = np.asarray(weight)
839
+
822
840
  fs = _validate_fs(fs, allow_none=True)
823
841
  fs = 1.0 if fs is None else fs
824
842
 
@@ -834,8 +852,9 @@ def remez(numtaps, bands, desired, *, weight=None, type='bandpass',
834
852
  weight = [1] * len(desired)
835
853
 
836
854
  bands = np.asarray(bands).copy()
837
- return _sigtools._remez(numtaps, bands, desired, weight, tnum, fs,
838
- maxiter, grid_density)
855
+ result = _sigtools._remez(numtaps, bands, desired, weight, tnum, fs,
856
+ maxiter, grid_density)
857
+ return xp.asarray(result)
839
858
 
840
859
 
841
860
  def firls(numtaps, bands, desired, *, weight=None, fs=None):
@@ -948,6 +967,10 @@ def firls(numtaps, bands, desired, *, weight=None, fs=None):
948
967
  >>> plt.show()
949
968
 
950
969
  """
970
+ xp = array_namespace(bands, desired)
971
+ bands = np.asarray(bands)
972
+ desired = np.asarray(desired)
973
+
951
974
  fs = _validate_fs(fs, allow_none=True)
952
975
  fs = 2 if fs is None else fs
953
976
  nyq = 0.5 * fs
@@ -1051,10 +1074,10 @@ def firls(numtaps, bands, desired, *, weight=None, fs=None):
1051
1074
 
1052
1075
  # make coefficients symmetric (linear phase)
1053
1076
  coeffs = np.hstack((a[:0:-1], 2 * a[0], a[1:]))
1054
- return coeffs
1077
+ return xp.asarray(coeffs)
1055
1078
 
1056
1079
 
1057
- def _dhtm(mag):
1080
+ def _dhtm(mag, xp):
1058
1081
  """Compute the modified 1-D discrete Hilbert transform
1059
1082
 
1060
1083
  Parameters
@@ -1065,20 +1088,20 @@ def _dhtm(mag):
1065
1088
  """
1066
1089
  # Adapted based on code by Niranjan Damera-Venkata,
1067
1090
  # Brian L. Evans and Shawn R. McCaslin (see refs for `minimum_phase`)
1068
- sig = np.zeros(len(mag))
1091
+ sig = xp.zeros(mag.shape[0])
1069
1092
  # Leave Nyquist and DC at 0, knowing np.abs(fftfreq(N)[midpt]) == 0.5
1070
- midpt = len(mag) // 2
1093
+ midpt = mag.shape[0] // 2
1071
1094
  sig[1:midpt] = 1
1072
1095
  sig[midpt+1:] = -1
1073
1096
  # eventually if we want to support complex filters, we will need a
1074
1097
  # np.abs() on the mag inside the log, and should remove the .real
1075
- recon = ifft(mag * np.exp(fft(sig * ifft(np.log(mag))))).real
1098
+ recon = xp.real(ifft(mag * xp.exp(fft(sig * ifft(xp.log(mag))))))
1076
1099
  return recon
1077
1100
 
1078
1101
 
1079
- def minimum_phase(h: np.ndarray,
1102
+ def minimum_phase(h,
1080
1103
  method: Literal['homomorphic', 'hilbert'] = 'homomorphic',
1081
- n_fft: int | None = None, *, half: bool = True) -> np.ndarray:
1104
+ n_fft: int | None = None, *, half: bool = True):
1082
1105
  """Convert a linear-phase FIR filter to minimum phase
1083
1106
 
1084
1107
  Parameters
@@ -1229,13 +1252,16 @@ def minimum_phase(h: np.ndarray,
1229
1252
  linear filter `h` whereas the other minimum phase filters have only half the order
1230
1253
  and the square root of the magnitude response.
1231
1254
  """
1232
- h = np.asarray(h)
1233
- if np.iscomplexobj(h):
1255
+ xp = array_namespace(h)
1256
+
1257
+ h = xp.asarray(h)
1258
+ if xp.isdtype(h.dtype, "complex floating"):
1234
1259
  raise ValueError('Complex filters not supported')
1235
- if h.ndim != 1 or h.size <= 2:
1260
+ if h.ndim != 1 or h.shape[0] <= 2:
1236
1261
  raise ValueError('h must be 1-D and at least 2 samples long')
1237
- n_half = len(h) // 2
1238
- if not np.allclose(h[-n_half:][::-1], h[:n_half]):
1262
+ n_half = h.shape[0] // 2
1263
+
1264
+ if not xp.any(xp.flip(h[-n_half:]) - h[:n_half] <= 1e-8 + 1e-6*abs(h[:n_half])):
1239
1265
  warnings.warn('h does not appear to by symmetric, conversion may fail',
1240
1266
  RuntimeWarning, stacklevel=2)
1241
1267
  if not isinstance(method, str) or method not in \
@@ -1244,43 +1270,189 @@ def minimum_phase(h: np.ndarray,
1244
1270
  if method == "hilbert" and not half:
1245
1271
  raise ValueError("`half=False` is only supported when `method='homomorphic'`")
1246
1272
  if n_fft is None:
1247
- n_fft = 2 ** int(np.ceil(np.log2(2 * (len(h) - 1) / 0.01)))
1273
+ n_fft = 2 ** int(ceil(log2(2 * (h.shape[0] - 1) / 0.01)))
1248
1274
  n_fft = int(n_fft)
1249
- if n_fft < len(h):
1275
+ if n_fft < h.shape[0]:
1250
1276
  raise ValueError(f'n_fft must be at least len(h)=={len(h)}')
1277
+
1251
1278
  if method == 'hilbert':
1252
- w = np.arange(n_fft) * (2 * np.pi / n_fft * n_half)
1253
- H = np.real(fft(h, n_fft) * np.exp(1j * w))
1279
+ w = xp.arange(n_fft, dtype=xp.float64) * (2 * xp.pi / n_fft * n_half)
1280
+ H = xp.real(fft(h, n_fft) * xp.exp(1j * w))
1254
1281
  dp = max(H) - 1
1255
1282
  ds = 0 - min(H)
1256
- S = 4. / (np.sqrt(1+dp+ds) + np.sqrt(1-dp+ds)) ** 2
1283
+ S = 4. / (xp.sqrt(1+dp+ds) + xp.sqrt(1-dp+ds)) ** 2
1257
1284
  H += ds
1258
1285
  H *= S
1259
- H = np.sqrt(H, out=H)
1286
+ H = xp.sqrt(H)
1260
1287
  H += 1e-10 # ensure that the log does not explode
1261
- h_minimum = _dhtm(H)
1288
+ h_minimum = _dhtm(H, xp)
1262
1289
  else: # method == 'homomorphic'
1263
1290
  # zero-pad; calculate the DFT
1264
- h_temp = np.abs(fft(h, n_fft))
1291
+ h_temp = xp.abs(fft(h, n_fft))
1265
1292
  # take 0.25*log(|H|**2) = 0.5*log(|H|)
1266
- h_temp += 1e-7 * h_temp[h_temp > 0].min() # don't let log blow up
1267
- np.log(h_temp, out=h_temp)
1293
+ h_temp += 1e-7 * xp.min(h_temp[h_temp > 0]) # don't let log blow up
1294
+ h_temp = xp.log(h_temp)
1268
1295
  if half: # halving of magnitude spectrum optional
1269
1296
  h_temp *= 0.5
1270
1297
  # IDFT
1271
- h_temp = ifft(h_temp).real
1298
+ h_temp = xp.real(ifft(h_temp))
1272
1299
  # multiply pointwise by the homomorphic filter
1273
1300
  # lmin[n] = 2u[n] - d[n]
1274
1301
  # i.e., double the positive frequencies and zero out the negative ones;
1275
1302
  # Oppenheim+Shafer 3rd ed p991 eq13.42b and p1004 fig13.7
1276
- win = np.zeros(n_fft)
1303
+ win = xp.zeros(n_fft)
1277
1304
  win[0] = 1
1278
1305
  stop = n_fft // 2
1279
1306
  win[1:stop] = 2
1280
1307
  if n_fft % 2:
1281
1308
  win[stop] = 1
1282
1309
  h_temp *= win
1283
- h_temp = ifft(np.exp(fft(h_temp)))
1310
+ h_temp = ifft(xp.exp(fft(h_temp)))
1284
1311
  h_minimum = h_temp.real
1285
- n_out = (n_half + len(h) % 2) if half else len(h)
1312
+ n_out = (n_half + h.shape[0] % 2) if half else h.shape[0]
1286
1313
  return h_minimum[:n_out]
1314
+
1315
+
1316
+ def firwin_2d(hsize, window, *, fc=None, fs=2, circular=False,
1317
+ pass_zero=True, scale=True):
1318
+ """
1319
+ 2D FIR filter design using the window method.
1320
+
1321
+ This function computes the coefficients of a 2D finite impulse response
1322
+ filter. The filter is separable with linear phase; it will be designed
1323
+ as a product of two 1D filters with dimensions defined by `hsize`.
1324
+ Additionally, it can create approximately circularly symmetric 2-D windows.
1325
+
1326
+ Parameters
1327
+ ----------
1328
+ hsize : tuple or list of length 2
1329
+ Lengths of the filter in each dimension. `hsize[0]` specifies the
1330
+ number of coefficients in the row direction and `hsize[1]` specifies
1331
+ the number of coefficients in the column direction.
1332
+ window : tuple or list of length 2 or string
1333
+ Desired window to use for each 1D filter or a single window type
1334
+ for creating circularly symmetric 2-D windows. Each element should be
1335
+ a string or tuple of string and parameter values. See
1336
+ `~scipy.signal.get_window` for a list of windows and required
1337
+ parameters.
1338
+ fc : float or 1-D array_like, optional
1339
+ Cutoff frequency of the filter in the same units as `fs`. This defines
1340
+ the frequency at which the filter's gain drops to approximately -6 dB
1341
+ (half power) in a low-pass or high-pass filter. For multi-band filters,
1342
+ `fc` can be an array of cutoff frequencies (i.e., band edges) in the
1343
+ range [0, fs/2], with each band specified in pairs. Required if
1344
+ `circular` is False.
1345
+ fs : float, optional
1346
+ The sampling frequency of the signal. Default is 2.
1347
+ circular : bool, optional
1348
+ Whether to create a circularly symmetric 2-D window. Default is ``False``.
1349
+ pass_zero : {True, False, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional
1350
+ This parameter is directly passed to `firwin` for each scalar frequency axis.
1351
+ Hence, if ``True``, the DC gain, i.e., the gain at frequency (0, 0), is 1.
1352
+ If ``False``, the DC gain is 0 at frequency (0, 0) if `circular` is ``True``.
1353
+ If `circular` is ``False`` the frequencies (0, f1) and (f0, 0) will
1354
+ have gain 0.
1355
+ It can also be a string argument for the desired filter type
1356
+ (equivalent to ``btype`` in IIR design functions).
1357
+ scale : bool, optional
1358
+ This parameter is directly passed to `firwin` for each scalar frequency axis.
1359
+ Set to ``True`` to scale the coefficients so that the frequency
1360
+ response is exactly unity at a certain frequency on one frequency axis.
1361
+ That frequency is either:
1362
+
1363
+ - 0 (DC) if the first passband starts at 0 (i.e. pass_zero is ``True``)
1364
+ - `fs`/2 (the Nyquist frequency) if the first passband ends at `fs`/2
1365
+ (i.e., the filter is a single band highpass filter);
1366
+ center of first passband otherwise
1367
+
1368
+ Returns
1369
+ -------
1370
+ filter_2d : (hsize[0], hsize[1]) ndarray
1371
+ Coefficients of 2D FIR filter.
1372
+
1373
+ Raises
1374
+ ------
1375
+ ValueError
1376
+ - If `hsize` and `window` are not 2-element tuples or lists.
1377
+ - If `cutoff` is None when `circular` is True.
1378
+ - If `cutoff` is outside the range [0, `fs`/2] and `circular` is ``False``.
1379
+ - If any of the elements in `window` are not recognized.
1380
+ RuntimeError
1381
+ If `firwin` fails to converge when designing the filter.
1382
+
1383
+ See Also
1384
+ --------
1385
+ firwin: FIR filter design using the window method for 1d arrays.
1386
+ get_window: Return a window of a given length and type.
1387
+
1388
+ Examples
1389
+ --------
1390
+ Generate a 5x5 low-pass filter with cutoff frequency 0.1:
1391
+
1392
+ >>> import numpy as np
1393
+ >>> from scipy.signal import get_window
1394
+ >>> from scipy.signal import firwin_2d
1395
+ >>> hsize = (5, 5)
1396
+ >>> window = (("kaiser", 5.0), ("kaiser", 5.0))
1397
+ >>> fc = 0.1
1398
+ >>> filter_2d = firwin_2d(hsize, window, fc=fc)
1399
+ >>> filter_2d
1400
+ array([[0.00025366, 0.00401662, 0.00738617, 0.00401662, 0.00025366],
1401
+ [0.00401662, 0.06360159, 0.11695714, 0.06360159, 0.00401662],
1402
+ [0.00738617, 0.11695714, 0.21507283, 0.11695714, 0.00738617],
1403
+ [0.00401662, 0.06360159, 0.11695714, 0.06360159, 0.00401662],
1404
+ [0.00025366, 0.00401662, 0.00738617, 0.00401662, 0.00025366]])
1405
+
1406
+ Generate a circularly symmetric 5x5 low-pass filter with Hamming window:
1407
+
1408
+ >>> filter_2d = firwin_2d((5, 5), 'hamming', fc=fc, circular=True)
1409
+ >>> filter_2d
1410
+ array([[-0.00020354, -0.00020354, -0.00020354, -0.00020354, -0.00020354],
1411
+ [-0.00020354, 0.01506844, 0.09907658, 0.01506844, -0.00020354],
1412
+ [-0.00020354, 0.09907658, -0.00020354, 0.09907658, -0.00020354],
1413
+ [-0.00020354, 0.01506844, 0.09907658, 0.01506844, -0.00020354],
1414
+ [-0.00020354, -0.00020354, -0.00020354, -0.00020354, -0.00020354]])
1415
+
1416
+ Generate Plots comparing the product of two 1d filters with a circular
1417
+ symmetric filter:
1418
+
1419
+ >>> import matplotlib.pyplot as plt
1420
+ >>> hsize, fc = (50, 50), 0.05
1421
+ >>> window = (("kaiser", 5.0), ("kaiser", 5.0))
1422
+ >>> filter0_2d = firwin_2d(hsize, window, fc=fc)
1423
+ >>> filter1_2d = firwin_2d((50, 50), 'hamming', fc=fc, circular=True)
1424
+ ...
1425
+ >>> fg, (ax0, ax1) = plt.subplots(1, 2, tight_layout=True, figsize=(6.5, 3.5))
1426
+ >>> ax0.set_title("Product of 2 Windows")
1427
+ >>> im0 = ax0.imshow(filter0_2d, cmap='viridis', origin='lower', aspect='equal')
1428
+ >>> fg.colorbar(im0, ax=ax0, shrink=0.7)
1429
+ >>> ax1.set_title("Circular Window")
1430
+ >>> im1 = ax1.imshow(filter1_2d, cmap='plasma', origin='lower', aspect='equal')
1431
+ >>> fg.colorbar(im1, ax=ax1, shrink=0.7)
1432
+ >>> plt.show()
1433
+ """
1434
+ if len(hsize) != 2:
1435
+ raise ValueError("hsize must be a 2-element tuple or list")
1436
+
1437
+ if circular:
1438
+ if fc is None:
1439
+ raise ValueError("Cutoff frequency `fc` must be "
1440
+ "provided when `circular` is True")
1441
+
1442
+ n_r = max(hsize[0], hsize[1]) * 8 # oversample 1d window by factor 8
1443
+
1444
+ win_r = firwin(n_r, cutoff=fc, window=window, fs=fs)
1445
+
1446
+ f1, f2 = np.meshgrid(np.linspace(-1, 1, hsize[0]), np.linspace(-1, 1, hsize[1]))
1447
+ r = np.sqrt(f1**2 + f2**2)
1448
+
1449
+ win_2d = np.interp(r, np.linspace(0, 1, n_r), win_r)
1450
+ return win_2d
1451
+
1452
+ if len(window) != 2:
1453
+ raise ValueError("window must be a 2-element tuple or list")
1454
+
1455
+ row_filter = firwin(hsize[0], cutoff=fc, window=window[0], fs=fs)
1456
+ col_filter = firwin(hsize[1], cutoff=fc, window=window[1], fs=fs)
1457
+
1458
+ return np.outer(row_filter, col_filter)
@@ -431,8 +431,9 @@ def cont2discrete(system, dt, method="zoh", alpha=None):
431
431
  >>> plt.show()
432
432
 
433
433
  """
434
- if len(system) == 1:
435
- return system.to_discrete()
434
+ if hasattr(system, 'to_discrete') and callable(system.to_discrete):
435
+ return system.to_discrete(dt=dt, method=method, alpha=alpha)
436
+
436
437
  if len(system) == 2:
437
438
  sysd = cont2discrete(tf2ss(system[0], system[1]), dt, method=method,
438
439
  alpha=alpha)