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
@@ -15,7 +15,7 @@
15
15
  # was added to the "See Also" section of each method/property. These links
16
16
  # can be removed, when SciPy updates ``pydata-sphinx-theme`` to >= 0.13.3
17
17
  # (currently 0.9). Consult Issue 18512 and PR 16660 for further details.
18
- #
18
+
19
19
 
20
20
  # Provides typing union operator ``|`` in Python 3.9:
21
21
  # Linter does not allow to import ``Generator`` from ``typing`` module:
@@ -26,10 +26,10 @@ from typing import get_args, Literal
26
26
  import numpy as np
27
27
 
28
28
  import scipy.fft as fft_lib
29
- from scipy.signal import detrend
29
+ from scipy.signal._signaltools import detrend
30
30
  from scipy.signal.windows import get_window
31
31
 
32
- __all__ = ['ShortTimeFFT']
32
+ __all__ = ['closest_STFT_dual_window', 'ShortTimeFFT']
33
33
 
34
34
 
35
35
  #: Allowed values for parameter `padding` of method `ShortTimeFFT.stft()`:
@@ -43,7 +43,7 @@ def _calc_dual_canonical_window(win: np.ndarray, hop: int) -> np.ndarray:
43
43
  """Calculate canonical dual window for 1d window `win` and a time step
44
44
  of `hop` samples.
45
45
 
46
- A ``ValueError`` is raised, if the inversion fails.
46
+ A ``ValueError`` is raised if the inversion fails.
47
47
 
48
48
  This is a separate function not a method, since it is also used in the
49
49
  class method ``ShortTimeFFT.from_dual()``.
@@ -73,6 +73,158 @@ def _calc_dual_canonical_window(win: np.ndarray, hop: int) -> np.ndarray:
73
73
  return win / DD
74
74
 
75
75
 
76
+ def closest_STFT_dual_window(win: np.ndarray, hop: int,
77
+ desired_dual: np.ndarray | None = None, *,
78
+ scaled: bool = True) \
79
+ -> tuple[np.ndarray, float | complex]:
80
+ r"""Calculate the STFT dual window of a given window closest to a desired dual
81
+ window.
82
+
83
+ For a given short-time Fourier transform window `win` incremented by `hop`
84
+ samples, the dual window is calculated, which minimizes
85
+ ``abs(dual_win - desired_dual)**2`` when `scaled` is ``False``. For `scaled`
86
+ set to ``True``, ``abs(alpha*dual_win - desired_dual)**2`` is minimized with
87
+ `alpha` being the optimal scaling factor.
88
+ A ``ValueError`` is raised if no valid dual window can be determined.
89
+
90
+
91
+ Parameters
92
+ ----------
93
+ win : np.ndarray
94
+ The window must be a real- or complex-valued 1d array.
95
+ hop : int
96
+ The increment in samples by which the window is shifted in each step.
97
+ desired_dual: np.ndarray | None
98
+ The desired dual window must be a 1d array of the same length as `win`.
99
+ If set to ``None`` (default), then `desired_dual` is assumed to be the
100
+ rectangular window, i.e., ``np.ones_like(win)``.
101
+ scaled : bool
102
+ If set (default), the closest scaled version instead of closest dual window
103
+ is calculated.
104
+
105
+ Returns
106
+ -------
107
+ dual_win : np.ndarray
108
+ A dual window of ``alpha*win`` (with hop interval `hop`), which is closest
109
+ to `desired_dual`. Note that the dual window of `win` is `dual_win/alpha`
110
+ and that the dual window of `dual_win` is `alpha*win`.
111
+ `dual_win` has the same shape as `win` and `desired_win`.
112
+ alpha : float | complex
113
+ Scale factor for `win`. It is always one if `scaled` is set to ``False``.
114
+
115
+ Notes
116
+ -----
117
+ For a given window and `hop` interval, all possible dual windows are expressed
118
+ by the `hop` linear conditions of Eq. :math:numref:`eq_STFT_AllDualWinsCond` in
119
+ the :ref:`tutorial_stft` section of the :ref:`user_guide`. Hence, decreasing
120
+ `hop`, increases the number of degrees of freedom of the set of all possible
121
+ dual windows, improving the ability to better approximate a `desired_dual`.
122
+
123
+ This function can also be used to determine windows which fulfill the
124
+ so-called "Constant OverLap Add" (COLA) condition [1]_. It states that summing
125
+ all touching window values at any given sample position results in the same
126
+ constant :math:`\alpha`. Eq. :math:numref:`eq_STFT_AllDualWinsCond` shows that
127
+ this is equal to having a rectangular dual window, i.e., the dual being
128
+ ``alpha*np.ones(m)``.
129
+
130
+ Some examples of windows that satisfy COLA (taken from [2]_):
131
+
132
+ - Rectangular window at overlap of 0, 1/2, 2/3, 3/4, ...
133
+ - Bartlett window at overlap of 1/2, 3/4, 5/6, ...
134
+ - Hann window at 1/2, 2/3, 3/4, ...
135
+ - Any Blackman family window at 2/3 overlap
136
+ - Any window with ``hop=1``
137
+
138
+ References
139
+ ----------
140
+ .. [1] Julius O. Smith III, "Spectral Audio Signal Processing",
141
+ online book, 2011, https://www.dsprelated.com/freebooks/sasp/
142
+ .. [2] G. Heinzel, A. Ruediger and R. Schilling, "Spectrum and spectral density
143
+ estimation by the Discrete Fourier transform (DFT), including a
144
+ comprehensive list of window functions and some new at-top windows",
145
+ 2002, http://hdl.handle.net/11858/00-001M-0000-0013-557A-5
146
+
147
+ Examples
148
+ --------
149
+ Let's show that a Bartlett window with 75% overlap fulfills the COLA condition:
150
+
151
+ >>> import matplotlib.pyplot as plt
152
+ >>> import numpy as np
153
+ >>> from scipy.signal import closest_STFT_dual_window, windows
154
+ ...
155
+ >>> m = 24
156
+ >>> win, w_rect = windows.bartlett(m, sym=False), np.ones(m)
157
+ >>> d_win, alpha = closest_STFT_dual_window(win, m//4, w_rect, scaled=True)
158
+ >>> print(f"COLA: {np.allclose(d_win, w_rect*alpha)}, {alpha = :g}")
159
+ COLA: True, alpha = 0.5
160
+
161
+ We can also determine for which hop intervals the COLA condition is fulfilled:
162
+
163
+ >>> hops, deviations, alphas = np.arange(1, 16, dtype=int), [], []
164
+ >>> for h_ in hops:
165
+ ... w_cola, alpha = closest_STFT_dual_window(w_rect, h_, win, scaled=True)
166
+ ... deviations.append(np.linalg.norm(w_cola - win*alpha))
167
+ ... alphas.append(alpha)
168
+ ...
169
+ >>> fg0, (ax0, ax1) = plt.subplots(2, 1, sharex='all', tight_layout=True)
170
+ >>> ax0.set_title(f"COLA Window closest to a {m}-Sample Bartlett Window")
171
+ >>> ax0.set(ylabel=r"$||w_\text{cola}-\alpha w||$", xlim=(0, hops[-1]-.5))
172
+ >>> ax1.set(xlabel="Hop Interval", ylabel=r"Scaling factor $\alpha$",
173
+ ... ylim=(0, 1.25))
174
+ >>> ax0.plot(hops, deviations, 'C0.-')
175
+ >>> ax1.plot(hops, alphas, 'C1.-')
176
+ >>> for ax_ in (ax0, ax1):
177
+ ... ax_.grid()
178
+ >>> plt.show()
179
+
180
+ The lower plot shows the calculated scaling factor :math:`\alpha` for different
181
+ `hops` whereas the upper displays the :math:`L^2`-norm of the difference
182
+ between the scaled Bartlett window and the calculated window. Since for `hops`
183
+ 1 to 4 as well as for 6 and 12 the :math:`L^2`-norm of the difference is
184
+ practically zero, the COLA condition is fulfilled for those.
185
+
186
+ See Also
187
+ --------
188
+ ShortTimeFFT: Short-time Fourier transform which is able to utilize a dual
189
+ window for calculating the inverse.
190
+ ShortTimeFFT.from_win_equals_dual: Create instance where the window and its
191
+ dual are equal.
192
+
193
+ """
194
+ if desired_dual is None: # default is rectangular window
195
+ desired_dual = np.ones_like(win)
196
+ if not (win.ndim == 1 and win.shape == desired_dual.shape):
197
+ raise ValueError("Parameters `win` and `desired_dual` are not 1d arrays of " +
198
+ f"equal length ({win.shape=}, {desired_dual.shape=})!")
199
+ if not all(np.isfinite(win)):
200
+ raise ValueError("Parameter win must have finite entries!")
201
+ if not all(np.isfinite(desired_dual)):
202
+ raise ValueError("Parameter desired_dual must have finite entries!")
203
+ if not (1 <= hop <= len(win) and isinstance(hop, int | np.integer)):
204
+ raise ValueError(f"Parameter {hop=} is not an integer between 1 and " +
205
+ f"{len(win)=}!")
206
+
207
+ w_d = _calc_dual_canonical_window(win, hop)
208
+ wdd = win.conjugate() * desired_dual
209
+ q_d = wdd.copy()
210
+ for k_ in range(hop, len(win), hop):
211
+ q_d[k_:] += wdd[:-k_]
212
+ q_d[:-k_] += wdd[k_:]
213
+ q_d = w_d * q_d
214
+
215
+ if not scaled:
216
+ return w_d + desired_dual - q_d, 1.
217
+
218
+ numerator = q_d.conjugate().T @ w_d
219
+ denominator = q_d.T.real @ q_d.real + q_d.T.imag @ q_d.imag # always >= 0
220
+ if not (abs(numerator) > 0 and denominator > np.finfo(w_d.dtype).resolution):
221
+ raise ValueError(
222
+ "Unable to calculate scaled closest dual window due to numerically " +
223
+ "unstable scaling factor! Try setting parameter `scaled` to False.")
224
+ alpha = numerator / denominator
225
+ return w_d + alpha * (desired_dual - q_d), alpha
226
+
227
+
76
228
  # noinspection PyShadowingNames
77
229
  class ShortTimeFFT:
78
230
  r"""Provide a parametrized discrete Short-time Fourier transform (stft)
@@ -98,14 +250,19 @@ class ShortTimeFFT:
98
250
  the signal. If only the dual window is known and the STFT is invertible,
99
251
  `from_dual` can be used to instantiate this class.
100
252
 
253
+ By default, the so-called canonical dual window is used. It is the window with
254
+ minimal energy among all possible dual windows. `from_win_equals_dual` and
255
+ `~scipy.signal.closest_STFT_dual_window` provide means for utilizing alterantive
256
+ dual windows. Note that `win` is also always a dual window of `dual_win`.
257
+
101
258
  Due to the convention of time t = 0 being at the first sample of the input
102
259
  signal, the STFT values typically have negative time slots. Hence,
103
260
  negative indexes like `p_min` or `k_min` do not indicate counting
104
261
  backwards from an array's end like in standard Python indexing but being
105
262
  left of t = 0.
106
263
 
107
- More detailed information can be found in the :ref:`tutorial_stft` section
108
- of the :ref:`user_guide`.
264
+ More detailed information can be found in the :ref:`tutorial_stft`
265
+ section of the :ref:`user_guide`.
109
266
 
110
267
  Note that all parameters of the initializer, except `scale_to` (which uses
111
268
  `scaling`) have identical named attributes.
@@ -130,15 +287,33 @@ class ShortTimeFFT:
130
287
  needed.
131
288
  scale_to : 'magnitude', 'psd' | None
132
289
  If not ``None`` (default) the window function is scaled, so each STFT
133
- column represents either a 'magnitude' or a power spectral density
290
+ column represents either a 'magnitude' or a power spectral density
134
291
  ('psd') spectrum. This parameter sets the property `scaling` to the
135
292
  same value. See method `scale_to` for details.
136
293
  phase_shift : int | None
137
294
  If set, add a linear phase `phase_shift` / `mfft` * `f` to each
138
- frequency `f`. The default value 0 ensures that there is no phase shift
295
+ frequency `f`. The default value of 0 ensures that there is no phase shift
139
296
  on the zeroth slice (in which t=0 is centered). See property
140
297
  `phase_shift` for more details.
141
298
 
299
+ Notes
300
+ -----
301
+ A typical STFT application is the creation of various types of time-frequency
302
+ plots, often subsumed under the term "spectrogram". Note that this term is also
303
+ used to explecitly refer to the absolute square of a STFT [11]_, as done in
304
+ :meth:`spectrogram`.
305
+
306
+ The STFT can also be used for filtering and filter banks as discussed in [12]_.
307
+
308
+
309
+ References
310
+ ----------
311
+ .. [11] Karlheinz Gröchenig: "Foundations of Time-Frequency Analysis",
312
+ Birkhäuser Boston 2001, `10.1007/978-1-4612-0003-1`
313
+ .. [12] Julius O. Smith III, "Spectral Audio Signal Processing", online book, 2011,
314
+ https://www.dsprelated.com/freebooks/sasp/
315
+
316
+
142
317
  Examples
143
318
  --------
144
319
  The following example shows the magnitude of the STFT of a sine with
@@ -166,7 +341,7 @@ class ShortTimeFFT:
166
341
  In the plot, the time extent of the signal `x` is marked by vertical dashed
167
342
  lines. Note that the SFT produces values outside the time range of `x`. The
168
343
  shaded areas on the left and the right indicate border effects caused
169
- by the window slices in that area not fully being inside time range of
344
+ by the window slices in that area not fully being inside time range of
170
345
  `x`:
171
346
 
172
347
  >>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit
@@ -196,7 +371,7 @@ class ShortTimeFFT:
196
371
 
197
372
  Reconstructing the signal with the `~ShortTimeFFT.istft` is
198
373
  straightforward, but note that the length of `x1` should be specified,
199
- since the SFT length increases in `hop` steps:
374
+ since the STFT length increases in `hop` steps:
200
375
 
201
376
  >>> SFT.invertible # check if invertible
202
377
  True
@@ -204,7 +379,7 @@ class ShortTimeFFT:
204
379
  >>> np.allclose(x, x1)
205
380
  True
206
381
 
207
- It is possible to calculate the SFT of signal parts:
382
+ It is possible to calculate the STFT of signal parts:
208
383
 
209
384
  >>> N2 = SFT.nearest_k_p(N // 2)
210
385
  >>> Sx0 = SFT.stft(x[:N2])
@@ -237,7 +412,7 @@ class ShortTimeFFT:
237
412
  _fs: float # sampling frequency of input signal and window
238
413
  _fft_mode: FFT_MODE_TYPE = 'onesided' # Mode of FFT to use
239
414
  _mfft: int # length of FFT used - defaults to len(win)
240
- _scaling: Literal['magnitude', 'psd'] | None = None # Scaling of _win
415
+ _scaling: Literal['magnitude', 'psd', 'unitary'] | None = None # Scaling of _win
241
416
  _phase_shift: int | None # amount to shift phase of FFT in samples
242
417
 
243
418
  # attributes for caching calculated values:
@@ -255,10 +430,11 @@ class ShortTimeFFT:
255
430
  raise ValueError(f"Parameter win must be 1d, but {win.shape=}!")
256
431
  if not all(np.isfinite(win)):
257
432
  raise ValueError("Parameter win must have finite entries!")
258
- if not (hop >= 1 and isinstance(hop, int)):
433
+ if not (hop >= 1 and isinstance(hop, int | np.integer)):
259
434
  raise ValueError(f"Parameter {hop=} is not an integer >= 1!")
260
- self._win, self._hop, self.fs = win, hop, fs
261
435
 
436
+ self._win, self._hop, self.fs = win, hop, fs
437
+ self.win.setflags(write=False)
262
438
  self.mfft = len(win) if mfft is None else mfft
263
439
 
264
440
  if dual_win is not None:
@@ -266,6 +442,7 @@ class ShortTimeFFT:
266
442
  raise ValueError(f"{dual_win.shape=} must equal {win.shape=}!")
267
443
  if not all(np.isfinite(dual_win)):
268
444
  raise ValueError("Parameter dual_win must be a finite array!")
445
+ dual_win.setflags(write=False)
269
446
  self._dual_win = dual_win # needs to be set before scaling
270
447
 
271
448
  if scale_to is not None: # needs to be set before fft_mode
@@ -311,7 +488,7 @@ class ShortTimeFFT:
311
488
  >>> fg1, ax1 = plt.subplots()
312
489
  >>> ax1.set_title(r"Dual Window: Gaussian with $\sigma_t=1$")
313
490
  >>> ax1.set(xlabel=f"Time $t$ in seconds ({N} samples, $T={T}$ s)",
314
- ... xlim=(t[0], t[-1]), ylim=(0, 1.1*max(d_win)))
491
+ ... xlim=(t[0], t[-1]), ylim=(0, 1.1*np.max(d_win)))
315
492
  >>> ax1.plot(t, d_win, 'C0-')
316
493
 
317
494
  The following plot with the overlap of 41, 11 and 2 samples show how
@@ -430,20 +607,191 @@ class ShortTimeFFT:
430
607
  return cls(win, hop=nperseg-noverlap, fs=fs, fft_mode=fft_mode,
431
608
  mfft=mfft, scale_to=scale_to, phase_shift=phase_shift)
432
609
 
610
+ @classmethod
611
+ def from_win_equals_dual(
612
+ cls, desired_win: np.ndarray, hop: int, fs: float, *,
613
+ fft_mode: FFT_MODE_TYPE = 'onesided', mfft: int | None = None,
614
+ scale_to: Literal['magnitude', 'psd', 'unitary'] | None = None,
615
+ phase_shift: int | None = 0):
616
+ r"""Create instance where the window and its dual are equal up to a
617
+ scaling factor.
618
+
619
+ An instance is created were window and dual window are equal as well as being
620
+ closest to the parameter `desired_win` in the least-squares sense, i.e.,
621
+ minimizing ``abs(win-desired_win)**2``. Hence, `win` has the same length as
622
+ `desired_win`. Then a scaling factor is applied accoring to the `scale_to`
623
+ parameter.
624
+
625
+ All other parameters have the identical meaning as in the initializer.
626
+
627
+ To be able to calculate a valid window, `desired_win` needs to have a valid
628
+ dual STFT window for the given `hop` interval.
629
+ If this is not the case, a ``ValueError`` is raised.
630
+
631
+ Parameters
632
+ ----------
633
+ desired_win : np.ndarray
634
+ A real-valued or complex-valued 1d array containing the sample of the
635
+ desired window.
636
+ hop : int
637
+ The increment in samples, by which the window is shifted in each step.
638
+ fs : float
639
+ Sampling frequency of input signal and window. Its relation to the
640
+ sampling interval `T` is ``T = 1 / fs``.
641
+ fft_mode : 'twosided', 'centered', 'onesided', 'onesided2X'
642
+ Mode of FFT to be used (default 'onesided').
643
+ See property `fft_mode` for details.
644
+ mfft: int | None
645
+ Length of the FFT used, if a zero padded FFT is desired.
646
+ If ``None`` (default), the length of the window `win` is used.
647
+ scale_to : 'magnitude' | 'psd' | 'unitary' | None
648
+ If not ``None`` (default) the window function is scaled, so each STFT
649
+ column represents either a 'magnitude' or a power spectral density ('psd')
650
+ spectrum, Alternatively, the STFT can be scaled to a`unitary` mapping,
651
+ i.e., dividing the window by ``np.sqrt(mfft)`` and multiplying the dual
652
+ window by the same amount.
653
+ phase_shift : int | None
654
+ If set, add a linear phase `phase_shift` / `mfft` * `f` to each
655
+ frequency `f`. The default value of 0 ensures that there is no phase shift
656
+ on the zeroth slice (in which t=0 is centered). See property
657
+ `phase_shift` for more details.
658
+
659
+
660
+ Notes
661
+ -----
662
+ The set of all possible windows with identical dual is defined by the set of
663
+ linear constraints of Eq. :math:numref:`eq_STFT_AllDualWinsCond` in the
664
+ :ref:`tutorial_stft` section of the :ref:`user_guide`. There it is also
665
+ derived that ``ShortTimeFFT.dual_win == ShortTimeFFT.m_pts * ShortTimeFFT.win``
666
+ needs to hold for an STFT to be a unitary mapping.
667
+
668
+ A unitary mapping preserves the value of the scalar product, i.e.,
669
+
670
+ .. math::
671
+
672
+ \langle x, y\rangle = \sum_k x[k]\, \overline{y[k]}
673
+ \stackrel{\stackrel{\text{unitary}}{\downarrow}}{=}
674
+ \sum_{q,p} S_x[q,p]\, \overline{S_y[q,p]}
675
+ = \langle S_x[q,p], S_y[q,p]\rangle\ ,
676
+
677
+ with :math:`S_{x,y}` being the STFT of :math:`x,y`. Hence, the energy
678
+ :math:`E_x=T\sum_k |x[k]|^2` of a signal is also preserved. This is also
679
+ illustrated in the example below.
680
+
681
+ Thie reason of distinguishing between no scaling (i.e., parameter `scale_to` is
682
+ ``None``) and unitary scaling (i.e., ``scale_to = 'unitary'``) is due to the
683
+ utilized FFT function not being unitary (i.e., using the default value
684
+ ``'backward'`` for the `~scipy.fft.fft` parameter `norm`).
685
+
686
+
687
+ See Also
688
+ --------
689
+ closest_STFT_dual_window: Calculate the STFT dual window of a given window
690
+ closest to a desired dual window.
691
+ ShortTimeFFT.spectrogram: Calculate squared STFTs
692
+ ShortTimeFFT: Class this property belongs to.
693
+
694
+ Examples
695
+ --------
696
+ The following example shows that an STFT can be indeed unitary:
697
+
698
+ >>> import matplotlib.pyplot as plt
699
+ >>> import numpy as np
700
+ >>> from scipy.signal import ShortTimeFFT, windows
701
+ ...
702
+ >>> m, hop, std = 36, 8, 5
703
+ >>> desired_win = windows.gaussian(m, std, sym=True)
704
+ >>> SFT = ShortTimeFFT.from_win_equals_dual(desired_win, hop, fs=1/m,
705
+ ... fft_mode='twosided',
706
+ ... scale_to='unitary')
707
+ >>> np.allclose(SFT.dual_win, SFT.win * SFT.m_num) # check if STFT is unitary
708
+ True
709
+ >>> x1, x2 = np.tile([-1, -1, 1, 1], 5), np.tile([1, -1, -1, 1], 5)
710
+ >>> np.sum(x1*x2) # scalar product is zero -> orthogonal signals
711
+ 0
712
+ >>> np.sum(x1**2) # scalar product of x1 with itself
713
+ 20
714
+ >>> Sx11, Sx12 = SFT.spectrogram(x1), SFT.spectrogram(x1, x2)
715
+ >>> np.sum(Sx12) # STFT scalar product is also zero
716
+ -4.163336342344337e-16+0j # may vary
717
+ >>> np.sum(Sx11) # == np.sum(x1**2)
718
+ 19.999999999999996 # may vary
719
+ ...
720
+ ... # Do the plotting:
721
+ >>> fg1, (ax11, ax12) = plt.subplots(1, 2, tight_layout=True, figsize=(8, 4))
722
+ >>> s_fac = np.sqrt(SFT.mfft)
723
+ >>> _ = fg1.suptitle(f"Scaled Unitary Window of {m} Sample Gaussian with " +
724
+ ... rf"{hop=}, $\sigma={std}$, Scale factor: {s_fac}")
725
+ >>> ax11.set(ylabel="Amplitude", xlabel="Samples", xlim=(0, m))
726
+ >>> ax12.set(xlabel="Frequency Bins", ylabel="Magnitude Spectrum",
727
+ ... xlim=(0, 15), ylim=(1e-5, 1.5))
728
+ >>> u_win_str = rf"Unitary $\times{s_fac:g}$"
729
+ >>> for x_, n_ in zip((desired_win, SFT.win*s_fac), ('Desired', u_win_str)):
730
+ ... ax11.plot(x_, '.-', alpha=0.5, label=n_)
731
+ ... X_ = np.fft.rfft(x_) / np.sum(abs(x_))
732
+ ... ax12.semilogy(abs(X_), '.-', alpha=0.5, label=n_)
733
+ >>> for ax_ in (ax11, ax12):
734
+ ... ax_.grid(True)
735
+ ... ax_.legend()
736
+ >>> plt.show()
737
+
738
+ Note that ``fftmode='twosided'`` is used, since we need sum over the complete
739
+ time frequency plane. Due to passing ``scale_to='unitary'`` the window
740
+ ``SFT.win`` is scaled by ``1/np.sqrt(SFT.mfft)``. Hence, ``SFT.win`` needs to
741
+ be scaled by `s_fac` in the plot above.
742
+ """
743
+ if not (desired_win.ndim == 1 and desired_win.size > 0):
744
+ raise ValueError(f"Parameter desired_win is not 1d, but "
745
+ f"{desired_win.shape=}!")
746
+ if issubclass(desired_win.dtype.type, np.integer):
747
+ raise ValueError("Parameter desired_win cannot be of integer type, " +
748
+ f"but {desired_win.dtype=} => cast to float | complex ")
749
+ if not all(np.isfinite(desired_win)):
750
+ raise ValueError("Parameter desired_win must have finite entries!")
751
+ if not (1 <= hop <= len(desired_win) and isinstance(hop, int | np.integer)):
752
+ raise ValueError(f"Parameter {hop=} is not an integer between 1 and " +
753
+ f"{len(desired_win)=}!")
754
+ if scale_to not in ['magnitude', 'psd', 'unitary', None]:
755
+ raise ValueError(f"Parameter {scale_to=} not in " +
756
+ "['magnitude', 'psd', 'unitary', None]!")
757
+
758
+ mfft = len(desired_win) if mfft is None else mfft
759
+ s_fac = np.sqrt(mfft) if scale_to == 'unitary' else 1
760
+
761
+ win = desired_win.copy() # we do not want to modify input parameters
762
+ relative_resolution = np.finfo(win.dtype).resolution * max(win)
763
+ for m in range(hop):
764
+ a = np.linalg.norm(desired_win[m::hop])
765
+ if not (a > relative_resolution):
766
+ raise ValueError("Parameter desired_win does not have valid STFT dual "
767
+ f"window for {hop=}!")
768
+ win[m::hop] /= a
769
+
770
+ SFT = cls(win=win/s_fac, hop=hop, fs=fs, fft_mode=fft_mode, mfft=mfft,
771
+ dual_win=win*s_fac, phase_shift=phase_shift,
772
+ scale_to=None if scale_to=='unitary' else scale_to)
773
+
774
+ if scale_to == 'unitary':
775
+ SFT._scaling = scale_to
776
+ return SFT
777
+
778
+
433
779
  @property
434
780
  def win(self) -> np.ndarray:
435
781
  """Window function as real- or complex-valued 1d array.
436
782
 
437
- This attribute is read only, since `dual_win` depends on it.
783
+ This attribute is read-only, since `dual_win` depends on it.
784
+ To make this array immutable, its WRITEABLE flag is set to ``FALSE``.
438
785
 
439
786
  See Also
440
787
  --------
441
- dual_win: Canonical dual window.
788
+ dual_win: Dual window.
442
789
  m_num: Number of samples in window `win`.
443
790
  m_num_mid: Center index of window `win`.
444
791
  mfft: Length of input for the FFT used - may be larger than `m_num`.
445
792
  hop: ime increment in signal samples for sliding window.
446
793
  win: Window function as real- or complex-valued 1d array.
794
+ numpy.ndarray.setflags: Modify array flags.
447
795
  ShortTimeFFT: Class this property belongs to.
448
796
  """
449
797
  return self._win
@@ -607,15 +955,19 @@ class ShortTimeFFT:
607
955
  self._mfft = n_
608
956
 
609
957
  @property
610
- def scaling(self) -> Literal['magnitude', 'psd'] | None:
958
+ def scaling(self) -> Literal['magnitude', 'psd', 'unitary'] | None:
611
959
  """Normalization applied to the window function
612
- ('magnitude', 'psd' or ``None``).
960
+ ('magnitude', 'psd', 'unitary', or ``None``).
613
961
 
614
- If not ``None``, the FFTs can be either interpreted as a magnitude or
615
- a power spectral density spectrum.
962
+ If not ``None``, the FFT slices may be either interpreted as a `magnitude` or
963
+ a power spectral density spectrum (`psd`). If set to `unitary`, the STFT may be
964
+ interpreted as a unitary mapping, i.e., preserving the value of the scalar
965
+ product.
616
966
 
617
967
  The window function can be scaled by calling the `scale_to` method,
618
- or it is set by the initializer parameter ``scale_to``.
968
+ or it is set by the initializer parameter ``scale_to``. Note that a
969
+ window cannot to be scaled to be `unitary`. Use `from_win_equals_dual`
970
+ to create a unitary `ShortTimeFFT` instance.
619
971
 
620
972
  See Also
621
973
  --------
@@ -623,6 +975,7 @@ class ShortTimeFFT:
623
975
  fac_psd: Scaling factor for to a power spectral density spectrum.
624
976
  fft_mode: Mode of utilized FFT
625
977
  scale_to: Scale window to obtain 'magnitude' or 'psd' scaling.
978
+ from_win_equals_dual: Class-method for creating a unitary instance.
626
979
  ShortTimeFFT: Class this property belongs to.
627
980
  """
628
981
  return self._scaling
@@ -643,6 +996,9 @@ class ShortTimeFFT:
643
996
  `fac_magnitude` and `fac_psd` show the scaling factors required to
644
997
  scale the STFT values to a magnitude or a psd spectrum.
645
998
 
999
+ Note that a window cannot to be scaled to be `unitary`. Use
1000
+ `from_win_equals_dual` to create a unitary `ShortTimeFFT` instance.
1001
+
646
1002
  This method is called, if the initializer parameter `scale_to` is set.
647
1003
 
648
1004
  See Also
@@ -660,8 +1016,10 @@ class ShortTimeFFT:
660
1016
 
661
1017
  s_fac = self.fac_psd if scaling == 'psd' else self.fac_magnitude
662
1018
  self._win = self._win * s_fac
1019
+ self.win.setflags(write=False)
663
1020
  if self._dual_win is not None:
664
1021
  self._dual_win = self._dual_win / s_fac
1022
+ self.dual_win.setflags(write=False)
665
1023
  self._fac_mag, self._fac_psd = None, None # reset scaling factors
666
1024
  self._scaling = scaling
667
1025
 
@@ -701,7 +1059,7 @@ class ShortTimeFFT:
701
1059
  if v is None:
702
1060
  self._phase_shift = v
703
1061
  return
704
- if not isinstance(v, int):
1062
+ if not isinstance(v, int | np.integer):
705
1063
  raise ValueError(f"phase_shift={v} has the unit samples. Hence " +
706
1064
  "it needs to be an int or it may be None!")
707
1065
  if not (-self.mfft < v < self.mfft):
@@ -750,33 +1108,33 @@ class ShortTimeFFT:
750
1108
 
751
1109
  Parameters
752
1110
  ----------
753
- x
1111
+ x : np.ndarray
754
1112
  The input signal as real or complex valued array. For complex values, the
755
1113
  property `fft_mode` must be set to 'twosided' or 'centered'.
756
- p0
1114
+ p0 : int | None
757
1115
  The first element of the range of slices to calculate. If ``None``
758
1116
  then it is set to :attr:`p_min`, which is the smallest possible
759
1117
  slice.
760
- p1
1118
+ p1 : int | None
761
1119
  The end of the array. If ``None`` then `p_max(n)` is used.
762
- k_offset
1120
+ k_offset : int
763
1121
  Index of first sample (t = 0) in `x`.
764
- padding
1122
+ padding : 'zeros' | 'edge' | 'even' | 'odd'
765
1123
  Kind of values which are added, when the sliding window sticks out
766
1124
  on either the lower or upper end of the input `x`. Zeros are added
767
1125
  if the default 'zeros' is set. For 'edge' either the first or the
768
1126
  last value of `x` is used. 'even' pads by reflecting the
769
1127
  signal on the first or last sample and 'odd' additionally
770
1128
  multiplies it with -1.
771
- axis
1129
+ axis : int
772
1130
  The axis of `x` over which to compute the STFT.
773
1131
  If not given, the last axis is used.
774
1132
 
775
1133
  Returns
776
1134
  -------
777
- S
1135
+ S : np.ndarray
778
1136
  A complex array is returned with the dimension always being larger
779
- by one than of `x`. The last axis always represent the time slices
1137
+ by one than of `x`. The last axis always represents the time slices
780
1138
  of the STFT. `axis` defines the frequency axis (default second to
781
1139
  last). E.g., for a one-dimensional `x`, a complex 2d array is
782
1140
  returned, with axis 0 representing frequency and axis 1 the time
@@ -803,17 +1161,51 @@ class ShortTimeFFT:
803
1161
  k_offset: int = 0, padding: PAD_TYPE = 'zeros',
804
1162
  axis: int = -1) \
805
1163
  -> np.ndarray:
806
- """Short-time Fourier transform with a trend being subtracted from each
807
- segment beforehand.
1164
+ """Calculate short-time Fourier transform with a trend being subtracted from
1165
+ each segment beforehand.
808
1166
 
809
- If `detr` is set to 'constant', the mean is subtracted, if set to
810
- "linear", the linear trend is removed. This is achieved by calling
811
- :func:`scipy.signal.detrend`. If `detr` is a function, `detr` is
812
- applied to each segment.
813
- All other parameters have the same meaning as in `~ShortTimeFFT.stft`.
1167
+ When the parameter `detr` is ``None``, this method's behavior is identical to
1168
+ the `~ShortTimeFFT.stft` method. Note that due to the detrending, the original
1169
+ signal cannot be reconstructed by the `~ShortTimeFFT.istft`.
814
1170
 
815
- Note that due to the detrending, the original signal cannot be
816
- reconstructed by the `~ShortTimeFFT.istft`.
1171
+ Parameters
1172
+ ----------
1173
+ x : np.ndarray
1174
+ The input signal as real or complex valued array. For complex values, the
1175
+ property `fft_mode` must be set to 'twosided' or 'centered'.
1176
+ detr : 'linear' | 'constant' | Callable[[np.ndarray], np.ndarray] | None
1177
+ If 'constant', the mean is subtracted, if set to "linear", the linear
1178
+ trend is removed from each segment. This is achieved by calling
1179
+ `~scipy.signal.detrend`. If `detr` is a function with one parameter, `detr`
1180
+ is applied to each segment.
1181
+ p0 : int | None
1182
+ The first element of the range of slices to calculate. If ``None``
1183
+ then it is set to :attr:`p_min`, which is the smallest possible
1184
+ slice.
1185
+ p1 : int | None
1186
+ The end of the array. If ``None`` then `p_max(n)` is used.
1187
+ k_offset : int
1188
+ Index of first sample (t = 0) in `x`.
1189
+ padding : 'zeros' | 'edge' | 'even' | 'odd'
1190
+ Kind of values which are added, when the sliding window sticks out
1191
+ on either the lower or upper end of the input `x`. Zeros are added
1192
+ if the default 'zeros' is set. For 'edge' either the first or the
1193
+ last value of `x` is used. 'even' pads by reflecting the
1194
+ signal on the first or last sample and 'odd' additionally
1195
+ multiplies it with -1.
1196
+ axis: int
1197
+ The axis of `x` over which to compute the STFT.
1198
+ If not given, the last axis is used.
1199
+
1200
+ Returns
1201
+ -------
1202
+ S : np.ndarray
1203
+ A complex array is returned with the dimension always being larger
1204
+ by one than of `x`. The last axis always represents the time slices
1205
+ of the STFT. `axis` defines the frequency axis (default second to
1206
+ last). E.g., for a one-dimensional `x`, a complex 2d array is
1207
+ returned, with axis 0 representing frequency and axis 1 the time
1208
+ slices.
817
1209
 
818
1210
  See Also
819
1211
  --------
@@ -861,19 +1253,80 @@ class ShortTimeFFT:
861
1253
  r"""Calculate spectrogram or cross-spectrogram.
862
1254
 
863
1255
  The spectrogram is the absolute square of the STFT, i.e., it is
864
- ``abs(S[q,p])**2`` for given ``S[q,p]`` and thus is always
1256
+ ``abs(S[q,p])**2`` for given ``S[q,p]`` and thus is always
865
1257
  non-negative.
866
1258
  For two STFTs ``Sx[q,p], Sy[q,p]``, the cross-spectrogram is defined
867
1259
  as ``Sx[q,p] * np.conj(Sy[q,p])`` and is complex-valued.
868
1260
  This is a convenience function for calling `~ShortTimeFFT.stft` /
869
- `stft_detrend`, hence all parameters are discussed there. If `y` is not
870
- ``None`` it needs to have the same shape as `x`.
1261
+ `stft_detrend`, hence all parameters are discussed there.
1262
+
1263
+ Parameters
1264
+ ----------
1265
+ x : np.ndarray
1266
+ The input signal as real or complex valued array. For complex values, the
1267
+ property `fft_mode` must be set to 'twosided' or 'centered'.
1268
+ y : np.ndarray
1269
+ The second input signal of the same shape as `x`. If ``None``, it is
1270
+ assumed to be `x`. For complex values, the property `fft_mode` must be
1271
+ set to 'twosided' or 'centered'.
1272
+ detr : 'linear' | 'constant' | Callable[[np.ndarray], np.ndarray] | None
1273
+ If 'constant', the mean is subtracted, if set to "linear", the linear
1274
+ trend is removed from each segment. This is achieved by calling
1275
+ `~scipy.signal.detrend`. If `detr` is a function with one parameter, `detr`
1276
+ is applied to each segment. For ``None`` (default), no trends are removed.
1277
+ p0 : int | None
1278
+ The first element of the range of slices to calculate. If ``None``
1279
+ then it is set to :attr:`p_min`, which is the smallest possible
1280
+ slice.
1281
+ p1 : int | None
1282
+ The end of the array. If ``None`` then `p_max(n)` is used.
1283
+ k_offset : int
1284
+ Index of first sample (t = 0) in `x`.
1285
+ padding : 'zeros' | 'edge' | 'even' | 'odd'
1286
+ Kind of values which are added, when the sliding window sticks out
1287
+ on either the lower or upper end of the input `x`. Zeros are added
1288
+ if the default 'zeros' is set. For 'edge' either the first or the
1289
+ last value of `x` is used. 'even' pads by reflecting the
1290
+ signal on the first or last sample and 'odd' additionally
1291
+ multiplies it with -1.
1292
+ axis : int
1293
+ The axis of `x` over which to compute the STFT.
1294
+ If not given, the last axis is used.
1295
+
1296
+ Returns
1297
+ -------
1298
+ S_xy : np.ndarray
1299
+ A real-valued array with non-negative values is returned, if ``x is y`` or
1300
+ `y` is ``None``. The dimension is always by one larger than of `x`. The
1301
+ last axis always represents the time slices of the spectrogram. `axis`
1302
+ defines the frequency axis (default second to last). E.g., for a
1303
+ one-dimensional `x`, a complex 2d array is returned, with axis 0
1304
+ representing frequency and axis 1 the time slices.
1305
+
1306
+ Notes
1307
+ -----
1308
+ The cross-spectrogram may be interpreted as the time-frequency analogon of the
1309
+ cross-spectral density (consult `csd`). The absolute square `|Sxy|²` of a
1310
+ cross-spectrogram `Sxy` divided by the spectrograms `Sxx` and `Syy` can be
1311
+ interpreted as a coherence spectrogram ``Cxy := abs(Sxy)**2 / (Sxx*Syy)``,
1312
+ which is the time-frequency analogon to `~coherence`.
1313
+
1314
+ If the STFT is parametrized to be a unitary transform, i.e., utilitzing
1315
+ `~from_win_equals_dual`, then the value of the scalar product, hence also the
1316
+ energy, is preserved.
871
1317
 
872
1318
  Examples
873
1319
  --------
874
- The following example shows the spectrogram of a square wave with
875
- varying frequency :math:`f_i(t)` (marked by a green dashed line in the
876
- plot) sampled with 20 Hz:
1320
+ The following example shows the spectrogram of a square wave with varying
1321
+ frequency :math:`f_i(t)` (marked by a green dashed line in the plot) sampled
1322
+ with 20 Hz. The utilized Gaussian window is 50 samples or 2.5 s long. For the
1323
+ `ShortTimeFFT`, the parameter ``mfft=800`` (oversampling factor 16) and the
1324
+ `hop` interval of 2 in was chosen to produce a sufficient number of points.
1325
+
1326
+ The plot's colormap is logarithmically scaled as the power spectral
1327
+ density is in dB. The time extent of the signal `x` is marked by
1328
+ vertical dashed lines, and the shaded areas mark the presence of border
1329
+ effects.
877
1330
 
878
1331
  >>> import matplotlib.pyplot as plt
879
1332
  >>> import numpy as np
@@ -884,22 +1337,12 @@ class ShortTimeFFT:
884
1337
  >>> t_x = np.arange(N) * T_x # time indexes for signal
885
1338
  >>> f_i = 5e-3*(t_x - t_x[N // 3])**2 + 1 # varying frequency
886
1339
  >>> x = square(2*np.pi*np.cumsum(f_i)*T_x) # the signal
887
-
888
- The utilized Gaussian window is 50 samples or 2.5 s long. The
889
- parameter ``mfft=800`` (oversampling factor 16) and the `hop` interval
890
- of 2 in `ShortTimeFFT` was chosen to produce a sufficient number of
891
- points:
892
-
1340
+ ...
893
1341
  >>> g_std = 12 # standard deviation for Gaussian window in samples
894
1342
  >>> win = gaussian(50, std=g_std, sym=True) # symmetric Gaussian wind.
895
1343
  >>> SFT = ShortTimeFFT(win, hop=2, fs=1/T_x, mfft=800, scale_to='psd')
896
1344
  >>> Sx2 = SFT.spectrogram(x) # calculate absolute square of STFT
897
-
898
- The plot's colormap is logarithmically scaled as the power spectral
899
- density is in dB. The time extent of the signal `x` is marked by
900
- vertical dashed lines and the shaded areas mark the presence of border
901
- effects:
902
-
1345
+ ...
903
1346
  >>> fig1, ax1 = plt.subplots(figsize=(6., 4.)) # enlarge plot a bit
904
1347
  >>> t_lo, t_hi = SFT.extent(N)[:2] # time range of plot
905
1348
  >>> ax1.set_title(rf"Spectrogram ({SFT.m_num*SFT.T:g}$\,s$ Gaussian " +
@@ -930,7 +1373,6 @@ class ShortTimeFFT:
930
1373
  which are reflected at the Nyquist frequency of 10 Hz. This aliasing
931
1374
  is also the main source of the noise artifacts in the plot.
932
1375
 
933
-
934
1376
  See Also
935
1377
  --------
936
1378
  :meth:`~ShortTimeFFT.stft`: Perform the short-time Fourier transform.
@@ -948,27 +1390,34 @@ class ShortTimeFFT:
948
1390
 
949
1391
  @property
950
1392
  def dual_win(self) -> np.ndarray:
951
- """Canonical dual window.
1393
+ """Dual window (canonical dual window by default).
952
1394
 
953
1395
  A STFT can be interpreted as the input signal being expressed as a
954
- weighted sum of modulated and time-shifted dual windows. Note that for
955
- a given window there exist many dual windows. The canonical window is
956
- the one with the minimal energy (i.e., :math:`L_2` norm).
1396
+ weighted sum of modulated and time-shifted dual windows. If no dual window is
1397
+ given on instantiation, the canonical dual window, i.e., the window with the
1398
+ minimal energy (i.e., minimal L²-norm) is calculated. Alternative means for
1399
+ determining dual windows are provided by `closest_STFT_dual_window` and the
1400
+ `from_win_equals_dual` class-method. Note that `win` is also always a
1401
+ dual window of `dual_win`.
957
1402
 
958
1403
  `dual_win` has same length as `win`, namely `m_num` samples.
959
1404
 
960
1405
  If the dual window cannot be calculated a ``ValueError`` is raised.
961
1406
  This attribute is read only and calculated lazily.
1407
+ To make this array immutable, its WRITEABLE flag is set to ``FALSE``.
962
1408
 
963
1409
  See Also
964
1410
  --------
965
- dual_win: Canonical dual window.
966
- m_num: Number of samples in window `win`.
1411
+ m_num: Number of samples in window `win` and `dual_win`.
967
1412
  win: Window function as real- or complex-valued 1d array.
1413
+ from_win_equals_dual: Create instance where `win` and `dual_win` are equal.
1414
+ closest_STFT_dual_window: Calculate dual window closest to a desired window.
1415
+ numpy.ndarray.setflags: Modify array flags.
968
1416
  ShortTimeFFT: Class this property belongs to.
969
1417
  """
970
1418
  if self._dual_win is None:
971
1419
  self._dual_win = _calc_dual_canonical_window(self.win, self.hop)
1420
+ self.dual_win.setflags(write=False)
972
1421
  return self._dual_win
973
1422
 
974
1423
  @property
@@ -981,7 +1430,7 @@ class ShortTimeFFT:
981
1430
  --------
982
1431
  :meth:`~ShortTimeFFT.istft`: Inverse short-time Fourier transform.
983
1432
  m_num: Number of samples in window `win` and `dual_win`.
984
- dual_win: Canonical dual window.
1433
+ dual_win: Dual window.
985
1434
  win: Window for STFT.
986
1435
  ShortTimeFFT: Class this property belongs to.
987
1436
  """
@@ -1472,7 +1921,7 @@ class ShortTimeFFT:
1472
1921
  the same parametrization. Note that the slices are
1473
1922
  ``delta_t = hop * T`` time units apart.
1474
1923
 
1475
- Parameters
1924
+ Parameters
1476
1925
  ----------
1477
1926
  n
1478
1927
  Number of sample of the input signal.
@@ -1503,8 +1952,8 @@ class ShortTimeFFT:
1503
1952
 
1504
1953
  The nearest next smaller time sample p (where t[p] is the center
1505
1954
  position of the window of the p-th slice) is p_k = k // `hop`.
1506
- If `hop` is a divisor of `k` than `k` is returned.
1507
- If `left` is set than p_k * `hop` is returned else (p_k+1) * `hop`.
1955
+ If `hop` is a divisor of `k` then `k` is returned.
1956
+ If `left` is set then p_k * `hop` is returned else (p_k+1) * `hop`.
1508
1957
 
1509
1958
  This method can be used to slice an input signal into chunks for
1510
1959
  calculating the STFT and iSTFT incrementally.
@@ -1669,7 +2118,7 @@ class ShortTimeFFT:
1669
2118
  n : int
1670
2119
  Number of samples in input signal.
1671
2120
  axes_seq : {'tf', 'ft'}
1672
- Return time extent first and then frequency extent or vice-versa.
2121
+ Return time extent first and then frequency extent or vice versa.
1673
2122
  center_bins: bool
1674
2123
  If set (default ``False``), the values of the time slots and
1675
2124
  frequency bins are moved from the side the middle. This is useful,