scipy 1.15.3__cp313-cp313-musllinux_1_2_aarch64.whl → 1.16.0rc2__cp313-cp313-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.
- scipy/__config__.py +10 -10
- scipy/__init__.py +3 -6
- scipy/_cyutility.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_array_api.py +486 -161
- scipy/_lib/_array_api_compat_vendor.py +9 -0
- scipy/_lib/_bunch.py +4 -0
- scipy/_lib/_ccallback_c.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_docscrape.py +1 -1
- scipy/_lib/_elementwise_iterative_method.py +15 -26
- scipy/_lib/_fpumode.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_ccallback.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_call.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_test_deprecation_def.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_testutils.py +6 -2
- scipy/_lib/_uarray/_uarray.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/_util.py +222 -125
- scipy/_lib/array_api_compat/__init__.py +4 -4
- scipy/_lib/array_api_compat/_internal.py +19 -6
- scipy/_lib/array_api_compat/common/__init__.py +1 -1
- scipy/_lib/array_api_compat/common/_aliases.py +365 -193
- scipy/_lib/array_api_compat/common/_fft.py +94 -64
- scipy/_lib/array_api_compat/common/_helpers.py +413 -180
- scipy/_lib/array_api_compat/common/_linalg.py +116 -40
- scipy/_lib/array_api_compat/common/_typing.py +179 -10
- scipy/_lib/array_api_compat/cupy/__init__.py +1 -4
- scipy/_lib/array_api_compat/cupy/_aliases.py +61 -41
- scipy/_lib/array_api_compat/cupy/_info.py +16 -6
- scipy/_lib/array_api_compat/cupy/_typing.py +24 -39
- scipy/_lib/array_api_compat/dask/array/__init__.py +6 -3
- scipy/_lib/array_api_compat/dask/array/_aliases.py +267 -108
- scipy/_lib/array_api_compat/dask/array/_info.py +105 -34
- scipy/_lib/array_api_compat/dask/array/fft.py +5 -8
- scipy/_lib/array_api_compat/dask/array/linalg.py +21 -22
- scipy/_lib/array_api_compat/numpy/__init__.py +13 -15
- scipy/_lib/array_api_compat/numpy/_aliases.py +98 -49
- scipy/_lib/array_api_compat/numpy/_info.py +36 -16
- scipy/_lib/array_api_compat/numpy/_typing.py +27 -43
- scipy/_lib/array_api_compat/numpy/fft.py +11 -5
- scipy/_lib/array_api_compat/numpy/linalg.py +75 -22
- scipy/_lib/array_api_compat/torch/__init__.py +3 -5
- scipy/_lib/array_api_compat/torch/_aliases.py +262 -159
- scipy/_lib/array_api_compat/torch/_info.py +27 -16
- scipy/_lib/array_api_compat/torch/_typing.py +3 -0
- scipy/_lib/array_api_compat/torch/fft.py +17 -18
- scipy/_lib/array_api_compat/torch/linalg.py +16 -16
- scipy/_lib/array_api_extra/__init__.py +26 -3
- scipy/_lib/array_api_extra/_delegation.py +171 -0
- scipy/_lib/array_api_extra/_lib/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_at.py +463 -0
- scipy/_lib/array_api_extra/_lib/_backends.py +46 -0
- scipy/_lib/array_api_extra/_lib/_funcs.py +937 -0
- scipy/_lib/array_api_extra/_lib/_lazy.py +357 -0
- scipy/_lib/array_api_extra/_lib/_testing.py +278 -0
- scipy/_lib/array_api_extra/_lib/_utils/__init__.py +1 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.py +74 -0
- scipy/_lib/array_api_extra/_lib/_utils/_compat.pyi +45 -0
- scipy/_lib/array_api_extra/_lib/_utils/_helpers.py +559 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.py +10 -0
- scipy/_lib/array_api_extra/_lib/_utils/_typing.pyi +105 -0
- scipy/_lib/array_api_extra/testing.py +359 -0
- scipy/_lib/decorator.py +2 -2
- scipy/_lib/doccer.py +1 -7
- scipy/_lib/messagestream.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/_lib/pyprima/__init__.py +212 -0
- scipy/_lib/pyprima/cobyla/__init__.py +0 -0
- scipy/_lib/pyprima/cobyla/cobyla.py +559 -0
- scipy/_lib/pyprima/cobyla/cobylb.py +714 -0
- scipy/_lib/pyprima/cobyla/geometry.py +226 -0
- scipy/_lib/pyprima/cobyla/initialize.py +215 -0
- scipy/_lib/pyprima/cobyla/trustregion.py +492 -0
- scipy/_lib/pyprima/cobyla/update.py +289 -0
- scipy/_lib/pyprima/common/__init__.py +0 -0
- scipy/_lib/pyprima/common/_bounds.py +34 -0
- scipy/_lib/pyprima/common/_linear_constraints.py +46 -0
- scipy/_lib/pyprima/common/_nonlinear_constraints.py +54 -0
- scipy/_lib/pyprima/common/_project.py +173 -0
- scipy/_lib/pyprima/common/checkbreak.py +93 -0
- scipy/_lib/pyprima/common/consts.py +47 -0
- scipy/_lib/pyprima/common/evaluate.py +99 -0
- scipy/_lib/pyprima/common/history.py +38 -0
- scipy/_lib/pyprima/common/infos.py +30 -0
- scipy/_lib/pyprima/common/linalg.py +435 -0
- scipy/_lib/pyprima/common/message.py +290 -0
- scipy/_lib/pyprima/common/powalg.py +131 -0
- scipy/_lib/pyprima/common/preproc.py +277 -0
- scipy/_lib/pyprima/common/present.py +5 -0
- scipy/_lib/pyprima/common/ratio.py +54 -0
- scipy/_lib/pyprima/common/redrho.py +47 -0
- scipy/_lib/pyprima/common/selectx.py +296 -0
- scipy/_lib/tests/test__util.py +105 -121
- scipy/_lib/tests/test_array_api.py +166 -35
- scipy/_lib/tests/test_bunch.py +7 -0
- scipy/_lib/tests/test_ccallback.py +2 -10
- scipy/_lib/tests/test_public_api.py +13 -0
- scipy/cluster/_hierarchy.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/cluster/_optimal_leaf_ordering.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/cluster/_vq.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/cluster/hierarchy.py +393 -223
- scipy/cluster/tests/test_hierarchy.py +273 -335
- scipy/cluster/tests/test_vq.py +45 -61
- scipy/cluster/vq.py +39 -35
- scipy/conftest.py +263 -157
- scipy/constants/_constants.py +4 -1
- scipy/constants/tests/test_codata.py +2 -2
- scipy/constants/tests/test_constants.py +11 -18
- scipy/datasets/_download_all.py +15 -1
- scipy/datasets/_fetchers.py +7 -1
- scipy/datasets/_utils.py +1 -1
- scipy/differentiate/_differentiate.py +25 -25
- scipy/differentiate/tests/test_differentiate.py +24 -25
- scipy/fft/_basic.py +20 -0
- scipy/fft/_helper.py +3 -34
- scipy/fft/_pocketfft/helper.py +29 -1
- scipy/fft/_pocketfft/pypocketfft.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/fft/_pocketfft/tests/test_basic.py +2 -4
- scipy/fft/_pocketfft/tests/test_real_transforms.py +4 -4
- scipy/fft/_realtransforms.py +13 -0
- scipy/fft/tests/test_basic.py +27 -25
- scipy/fft/tests/test_fftlog.py +16 -7
- scipy/fft/tests/test_helper.py +18 -34
- scipy/fft/tests/test_real_transforms.py +8 -10
- scipy/fftpack/convolve.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/fftpack/tests/test_basic.py +2 -4
- scipy/fftpack/tests/test_real_transforms.py +8 -9
- scipy/integrate/_bvp.py +9 -3
- scipy/integrate/_cubature.py +3 -2
- scipy/integrate/_dop.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/_lsoda.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/_ode.py +9 -2
- scipy/integrate/_odepack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quad_vec.py +21 -29
- scipy/integrate/_quadpack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/_quadpack_py.py +11 -7
- scipy/integrate/_quadrature.py +3 -3
- scipy/integrate/_rules/_base.py +2 -2
- scipy/integrate/_tanhsinh.py +48 -47
- scipy/integrate/_test_multivariate.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/_test_odeint_banded.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/_vode.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/integrate/tests/test__quad_vec.py +0 -6
- scipy/integrate/tests/test_banded_ode_solvers.py +85 -0
- scipy/integrate/tests/test_cubature.py +21 -35
- scipy/integrate/tests/test_quadrature.py +6 -8
- scipy/integrate/tests/test_tanhsinh.py +56 -48
- scipy/interpolate/__init__.py +70 -58
- scipy/interpolate/_bary_rational.py +22 -22
- scipy/interpolate/_bsplines.py +119 -66
- scipy/interpolate/_cubic.py +65 -50
- scipy/interpolate/_dfitpack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_dierckx.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_fitpack2.py +9 -6
- scipy/interpolate/_fitpack_impl.py +32 -26
- scipy/interpolate/_fitpack_repro.py +23 -19
- scipy/interpolate/_interpnd.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_interpolate.py +30 -12
- scipy/interpolate/_ndbspline.py +13 -18
- scipy/interpolate/_ndgriddata.py +5 -8
- scipy/interpolate/_polyint.py +95 -31
- scipy/interpolate/_ppoly.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rbf.py +2 -2
- scipy/interpolate/_rbfinterp.py +1 -1
- scipy/interpolate/_rbfinterp_pythran.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/_rgi.py +31 -26
- scipy/interpolate/_rgi_cython.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/interpolate/dfitpack.py +0 -20
- scipy/interpolate/interpnd.py +1 -2
- scipy/interpolate/tests/test_bary_rational.py +2 -2
- scipy/interpolate/tests/test_bsplines.py +97 -1
- scipy/interpolate/tests/test_fitpack2.py +39 -1
- scipy/interpolate/tests/test_interpnd.py +32 -20
- scipy/interpolate/tests/test_interpolate.py +48 -4
- scipy/interpolate/tests/test_rgi.py +2 -1
- scipy/io/_fast_matrix_market/__init__.py +2 -0
- scipy/io/_fast_matrix_market/_fmm_core.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/io/_harwell_boeing/_fortran_format_parser.py +19 -16
- scipy/io/_harwell_boeing/hb.py +7 -11
- scipy/io/_idl.py +5 -7
- scipy/io/_netcdf.py +15 -5
- scipy/io/_test_fortran.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/io/arff/tests/test_arffread.py +3 -3
- scipy/io/matlab/__init__.py +5 -3
- scipy/io/matlab/_mio.py +4 -1
- scipy/io/matlab/_mio5.py +19 -13
- scipy/io/matlab/_mio5_utils.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_mio_utils.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/_miobase.py +4 -1
- scipy/io/matlab/_streams.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/io/matlab/tests/test_mio.py +46 -18
- scipy/io/matlab/tests/test_mio_funcs.py +1 -1
- scipy/io/tests/test_mmio.py +7 -1
- scipy/io/tests/test_wavfile.py +41 -0
- scipy/io/wavfile.py +57 -10
- scipy/linalg/_basic.py +113 -86
- scipy/linalg/_cythonized_array_utils.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp.py +22 -9
- scipy/linalg/_decomp_cholesky.py +28 -13
- scipy/linalg/_decomp_cossin.py +45 -30
- scipy/linalg/_decomp_interpolative.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_ldl.py +4 -1
- scipy/linalg/_decomp_lu.py +18 -6
- scipy/linalg/_decomp_lu_cython.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_decomp_polar.py +2 -0
- scipy/linalg/_decomp_qr.py +6 -2
- scipy/linalg/_decomp_qz.py +3 -0
- scipy/linalg/_decomp_schur.py +3 -1
- scipy/linalg/_decomp_svd.py +13 -2
- scipy/linalg/_decomp_update.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_expm_frechet.py +4 -0
- scipy/linalg/_fblas.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_flapack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_linalg_pythran.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs.py +187 -4
- scipy/linalg/_matfuncs_expm.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +1 -99
- scipy/linalg/_matfuncs_sqrtm_triu.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_procrustes.py +2 -0
- scipy/linalg/_sketches.py +17 -6
- scipy/linalg/_solve_toeplitz.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/_solvers.py +7 -2
- scipy/linalg/_special_matrices.py +26 -36
- scipy/linalg/cython_blas.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/cython_lapack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/linalg/lapack.py +22 -2
- scipy/linalg/tests/_cython_examples/meson.build +7 -0
- scipy/linalg/tests/test_basic.py +31 -16
- scipy/linalg/tests/test_batch.py +588 -0
- scipy/linalg/tests/test_cythonized_array_utils.py +0 -2
- scipy/linalg/tests/test_decomp.py +40 -3
- scipy/linalg/tests/test_decomp_cossin.py +14 -0
- scipy/linalg/tests/test_decomp_ldl.py +1 -1
- scipy/linalg/tests/test_lapack.py +115 -7
- scipy/linalg/tests/test_matfuncs.py +157 -102
- scipy/linalg/tests/test_procrustes.py +0 -7
- scipy/linalg/tests/test_solve_toeplitz.py +1 -1
- scipy/linalg/tests/test_special_matrices.py +1 -5
- scipy/ndimage/__init__.py +1 -0
- scipy/ndimage/_ctest.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_cytest.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_delegators.py +8 -2
- scipy/ndimage/_filters.py +453 -5
- scipy/ndimage/_interpolation.py +36 -6
- scipy/ndimage/_measurements.py +4 -2
- scipy/ndimage/_morphology.py +5 -0
- scipy/ndimage/_nd_image.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_docstrings.py +5 -1
- scipy/ndimage/_ni_label.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_ni_support.py +1 -5
- scipy/ndimage/_rank_filter_1d.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/ndimage/_support_alternative_backends.py +18 -6
- scipy/ndimage/tests/test_filters.py +370 -259
- scipy/ndimage/tests/test_fourier.py +7 -9
- scipy/ndimage/tests/test_interpolation.py +68 -61
- scipy/ndimage/tests/test_measurements.py +18 -35
- scipy/ndimage/tests/test_morphology.py +143 -131
- scipy/ndimage/tests/test_splines.py +1 -3
- scipy/odr/__odrpack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_basinhopping.py +13 -7
- scipy/optimize/_bglu_dense.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_bracket.py +17 -24
- scipy/optimize/_chandrupatla.py +9 -10
- scipy/optimize/_cobyla_py.py +104 -123
- scipy/optimize/_constraints.py +14 -10
- scipy/optimize/_differentiable_functions.py +371 -230
- scipy/optimize/_differentialevolution.py +4 -3
- scipy/optimize/_direct.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_dual_annealing.py +1 -1
- scipy/optimize/_elementwise.py +1 -4
- scipy/optimize/_group_columns.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_core.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_highspy/_highs_options.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lbfgsb_py.py +57 -16
- scipy/optimize/_linprog_doc.py +2 -2
- scipy/optimize/_linprog_highs.py +2 -2
- scipy/optimize/_linprog_ip.py +25 -10
- scipy/optimize/_linprog_util.py +14 -16
- scipy/optimize/_lsap.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/common.py +3 -3
- scipy/optimize/_lsq/dogbox.py +16 -2
- scipy/optimize/_lsq/givens_elimination.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_lsq/least_squares.py +198 -126
- scipy/optimize/_lsq/lsq_linear.py +6 -6
- scipy/optimize/_lsq/trf.py +35 -8
- scipy/optimize/_milp.py +3 -1
- scipy/optimize/_minimize.py +105 -36
- scipy/optimize/_minpack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_minpack_py.py +21 -14
- scipy/optimize/_moduleTNC.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_nnls.py +20 -21
- scipy/optimize/_nonlin.py +34 -3
- scipy/optimize/_numdiff.py +288 -110
- scipy/optimize/_optimize.py +86 -48
- scipy/optimize/_pava_pybind.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_remove_redundancy.py +5 -5
- scipy/optimize/_root_scalar.py +1 -1
- scipy/optimize/_shgo.py +6 -0
- scipy/optimize/_shgo_lib/_complex.py +1 -1
- scipy/optimize/_slsqp_py.py +216 -124
- scipy/optimize/_slsqplib.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_spectral.py +1 -1
- scipy/optimize/_tnc.py +8 -1
- scipy/optimize/_trlib/_trlib.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_trustregion.py +20 -6
- scipy/optimize/_trustregion_constr/canonical_constraint.py +7 -7
- scipy/optimize/_trustregion_constr/equality_constrained_sqp.py +1 -1
- scipy/optimize/_trustregion_constr/minimize_trustregion_constr.py +11 -3
- scipy/optimize/_trustregion_constr/projections.py +12 -8
- scipy/optimize/_trustregion_constr/qp_subproblem.py +9 -9
- scipy/optimize/_trustregion_constr/tests/test_projections.py +7 -7
- scipy/optimize/_trustregion_constr/tests/test_qp_subproblem.py +77 -77
- scipy/optimize/_trustregion_constr/tr_interior_point.py +5 -5
- scipy/optimize/_trustregion_exact.py +0 -1
- scipy/optimize/_zeros.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_zeros_py.py +97 -17
- scipy/optimize/cython_optimize/_zeros.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/slsqp.py +0 -1
- scipy/optimize/tests/test__basinhopping.py +1 -1
- scipy/optimize/tests/test__differential_evolution.py +4 -4
- scipy/optimize/tests/test__linprog_clean_inputs.py +5 -3
- scipy/optimize/tests/test__numdiff.py +66 -22
- scipy/optimize/tests/test__remove_redundancy.py +2 -2
- scipy/optimize/tests/test__shgo.py +9 -1
- scipy/optimize/tests/test_bracket.py +36 -46
- scipy/optimize/tests/test_chandrupatla.py +133 -135
- scipy/optimize/tests/test_cobyla.py +74 -45
- scipy/optimize/tests/test_constraints.py +1 -1
- scipy/optimize/tests/test_differentiable_functions.py +226 -6
- scipy/optimize/tests/test_lbfgsb_hessinv.py +22 -0
- scipy/optimize/tests/test_least_squares.py +125 -13
- scipy/optimize/tests/test_linear_assignment.py +3 -3
- scipy/optimize/tests/test_linprog.py +3 -3
- scipy/optimize/tests/test_lsq_linear.py +6 -6
- scipy/optimize/tests/test_minimize_constrained.py +2 -2
- scipy/optimize/tests/test_minpack.py +4 -4
- scipy/optimize/tests/test_nnls.py +43 -3
- scipy/optimize/tests/test_nonlin.py +36 -0
- scipy/optimize/tests/test_optimize.py +95 -17
- scipy/optimize/tests/test_slsqp.py +36 -4
- scipy/optimize/tests/test_zeros.py +34 -1
- scipy/signal/__init__.py +12 -23
- scipy/signal/_delegators.py +568 -0
- scipy/signal/_filter_design.py +459 -241
- scipy/signal/_fir_filter_design.py +262 -90
- scipy/signal/_lti_conversion.py +3 -2
- scipy/signal/_ltisys.py +118 -91
- scipy/signal/_max_len_seq_inner.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/signal/_peak_finding_utils.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/signal/_polyutils.py +172 -0
- scipy/signal/_short_time_fft.py +519 -70
- scipy/signal/_signal_api.py +30 -0
- scipy/signal/_signaltools.py +719 -399
- scipy/signal/_sigtools.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/signal/_sosfilt.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/signal/_spectral_py.py +230 -50
- scipy/signal/_spline.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/signal/_spline_filters.py +108 -68
- scipy/signal/_support_alternative_backends.py +73 -0
- scipy/signal/_upfirdn.py +4 -1
- scipy/signal/_upfirdn_apply.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/signal/_waveforms.py +2 -11
- scipy/signal/_wavelets.py +1 -1
- scipy/signal/fir_filter_design.py +1 -0
- scipy/signal/spline.py +4 -11
- scipy/signal/tests/_scipy_spectral_test_shim.py +2 -171
- scipy/signal/tests/test_bsplines.py +114 -79
- scipy/signal/tests/test_cont2discrete.py +9 -2
- scipy/signal/tests/test_filter_design.py +721 -481
- scipy/signal/tests/test_fir_filter_design.py +332 -140
- scipy/signal/tests/test_savitzky_golay.py +4 -3
- scipy/signal/tests/test_short_time_fft.py +221 -3
- scipy/signal/tests/test_signaltools.py +2144 -1348
- scipy/signal/tests/test_spectral.py +50 -6
- scipy/signal/tests/test_splines.py +161 -96
- scipy/signal/tests/test_upfirdn.py +84 -50
- scipy/signal/tests/test_waveforms.py +20 -0
- scipy/signal/tests/test_windows.py +607 -466
- scipy/signal/windows/_windows.py +287 -148
- scipy/sparse/__init__.py +23 -4
- scipy/sparse/_base.py +270 -108
- scipy/sparse/_bsr.py +7 -4
- scipy/sparse/_compressed.py +59 -231
- scipy/sparse/_construct.py +90 -38
- scipy/sparse/_coo.py +115 -181
- scipy/sparse/_csc.py +4 -4
- scipy/sparse/_csparsetools.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/_csr.py +2 -2
- scipy/sparse/_data.py +48 -48
- scipy/sparse/_dia.py +105 -18
- scipy/sparse/_dok.py +0 -23
- scipy/sparse/_index.py +4 -4
- scipy/sparse/_matrix.py +23 -0
- scipy/sparse/_sparsetools.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/_sputils.py +37 -22
- scipy/sparse/base.py +0 -9
- scipy/sparse/bsr.py +0 -14
- scipy/sparse/compressed.py +0 -23
- scipy/sparse/construct.py +0 -6
- scipy/sparse/coo.py +0 -14
- scipy/sparse/csc.py +0 -3
- scipy/sparse/csgraph/_flow.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_matching.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_reordering.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_shortest_path.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_tools.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/_traversal.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/csgraph/tests/test_matching.py +14 -2
- scipy/sparse/csgraph/tests/test_pydata_sparse.py +4 -1
- scipy/sparse/csgraph/tests/test_shortest_path.py +83 -27
- scipy/sparse/csr.py +0 -5
- scipy/sparse/data.py +1 -6
- scipy/sparse/dia.py +0 -7
- scipy/sparse/dok.py +0 -10
- scipy/sparse/linalg/_dsolve/_superlu.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_dsolve/linsolve.py +9 -0
- scipy/sparse/linalg/_dsolve/tests/test_linsolve.py +35 -28
- scipy/sparse/linalg/_eigen/arpack/_arpack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_eigen/arpack/arpack.py +23 -17
- scipy/sparse/linalg/_eigen/lobpcg/lobpcg.py +6 -6
- scipy/sparse/linalg/_interface.py +17 -18
- scipy/sparse/linalg/_isolve/_gcrotmk.py +4 -4
- scipy/sparse/linalg/_isolve/iterative.py +51 -45
- scipy/sparse/linalg/_isolve/lgmres.py +6 -6
- scipy/sparse/linalg/_isolve/minres.py +5 -5
- scipy/sparse/linalg/_isolve/tfqmr.py +7 -7
- scipy/sparse/linalg/_isolve/utils.py +2 -8
- scipy/sparse/linalg/_matfuncs.py +1 -1
- scipy/sparse/linalg/_norm.py +1 -1
- scipy/sparse/linalg/_propack/_cpropack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_spropack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/sparse/linalg/_special_sparse_arrays.py +39 -38
- scipy/sparse/linalg/tests/test_pydata_sparse.py +14 -0
- scipy/sparse/tests/test_arithmetic1d.py +5 -2
- scipy/sparse/tests/test_base.py +214 -42
- scipy/sparse/tests/test_common1d.py +7 -7
- scipy/sparse/tests/test_construct.py +1 -1
- scipy/sparse/tests/test_coo.py +272 -4
- scipy/sparse/tests/test_sparsetools.py +5 -0
- scipy/sparse/tests/test_sputils.py +36 -7
- scipy/spatial/_ckdtree.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_pybind.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/_distance_wrap.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/_hausdorff.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/_qhull.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/_voronoi.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/distance.py +49 -42
- scipy/spatial/tests/test_distance.py +15 -1
- scipy/spatial/tests/test_kdtree.py +1 -0
- scipy/spatial/tests/test_qhull.py +7 -2
- scipy/spatial/transform/__init__.py +5 -3
- scipy/spatial/transform/_rigid_transform.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/_rotation.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/transform/tests/test_rigid_transform.py +1221 -0
- scipy/spatial/transform/tests/test_rotation.py +1213 -832
- scipy/spatial/transform/tests/test_rotation_groups.py +3 -3
- scipy/spatial/transform/tests/test_rotation_spline.py +29 -8
- scipy/special/__init__.py +1 -47
- scipy/special/_add_newdocs.py +34 -772
- scipy/special/_basic.py +22 -25
- scipy/special/_comb.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_ellip_harm_2.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_gufuncs.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_logsumexp.py +67 -58
- scipy/special/_orthogonal.pyi +1 -1
- scipy/special/_specfun.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_special_ufuncs.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_spherical_bessel.py +4 -4
- scipy/special/_support_alternative_backends.py +212 -119
- scipy/special/_test_internal.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_testutils.py +4 -4
- scipy/special/_ufuncs.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs.pyi +1 -0
- scipy/special/_ufuncs.pyx +215 -1400
- scipy/special/_ufuncs_cxx.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/_ufuncs_cxx.pxd +2 -15
- scipy/special/_ufuncs_cxx.pyx +5 -44
- scipy/special/_ufuncs_cxx_defs.h +2 -16
- scipy/special/_ufuncs_defs.h +0 -8
- scipy/special/cython_special.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/special/cython_special.pxd +1 -1
- scipy/special/tests/_cython_examples/meson.build +10 -1
- scipy/special/tests/test_basic.py +153 -20
- scipy/special/tests/test_boost_ufuncs.py +3 -0
- scipy/special/tests/test_cdflib.py +35 -11
- scipy/special/tests/test_gammainc.py +16 -0
- scipy/special/tests/test_hyp2f1.py +2 -2
- scipy/special/tests/test_log1mexp.py +85 -0
- scipy/special/tests/test_logsumexp.py +206 -64
- scipy/special/tests/test_mpmath.py +1 -0
- scipy/special/tests/test_nan_inputs.py +1 -1
- scipy/special/tests/test_orthogonal.py +17 -18
- scipy/special/tests/test_sf_error.py +3 -2
- scipy/special/tests/test_sph_harm.py +6 -7
- scipy/special/tests/test_support_alternative_backends.py +211 -76
- scipy/stats/__init__.py +4 -1
- scipy/stats/_ansari_swilk_statistics.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_axis_nan_policy.py +5 -12
- scipy/stats/_biasedurn.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_continued_fraction.py +387 -0
- scipy/stats/_continuous_distns.py +277 -310
- scipy/stats/_correlation.py +1 -1
- scipy/stats/_covariance.py +6 -3
- scipy/stats/_discrete_distns.py +39 -32
- scipy/stats/_distn_infrastructure.py +39 -12
- scipy/stats/_distribution_infrastructure.py +900 -238
- scipy/stats/_entropy.py +9 -10
- scipy/{_lib → stats}/_finite_differences.py +1 -1
- scipy/stats/_hypotests.py +83 -50
- scipy/stats/_kde.py +53 -49
- scipy/stats/_ksstats.py +1 -1
- scipy/stats/_levy_stable/__init__.py +7 -15
- scipy/stats/_levy_stable/levyst.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_morestats.py +118 -73
- scipy/stats/_mstats_basic.py +13 -17
- scipy/stats/_mstats_extras.py +8 -8
- scipy/stats/_multivariate.py +89 -113
- scipy/stats/_new_distributions.py +97 -20
- scipy/stats/_page_trend_test.py +12 -5
- scipy/stats/_probability_distribution.py +265 -43
- scipy/stats/_qmc.py +14 -9
- scipy/stats/_qmc_cy.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_qmvnt.py +16 -95
- scipy/stats/_qmvnt_cy.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/rcont.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_resampling.py +4 -29
- scipy/stats/_sampling.py +1 -1
- scipy/stats/_sobol.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_stats_mstats_common.py +21 -2
- scipy/stats/_stats_py.py +550 -476
- scipy/stats/_stats_pythran.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.pyi +2 -1
- scipy/stats/_variation.py +6 -8
- scipy/stats/_wilcoxon.py +13 -7
- scipy/stats/tests/common_tests.py +6 -4
- scipy/stats/tests/test_axis_nan_policy.py +62 -24
- scipy/stats/tests/test_continued_fraction.py +173 -0
- scipy/stats/tests/test_continuous.py +379 -60
- scipy/stats/tests/test_continuous_basic.py +18 -12
- scipy/stats/tests/test_discrete_basic.py +14 -8
- scipy/stats/tests/test_discrete_distns.py +16 -16
- scipy/stats/tests/test_distributions.py +95 -75
- scipy/stats/tests/test_entropy.py +40 -48
- scipy/stats/tests/test_fit.py +4 -3
- scipy/stats/tests/test_hypotests.py +153 -24
- scipy/stats/tests/test_kdeoth.py +109 -41
- scipy/stats/tests/test_marray.py +289 -0
- scipy/stats/tests/test_morestats.py +79 -47
- scipy/stats/tests/test_mstats_basic.py +3 -3
- scipy/stats/tests/test_multivariate.py +434 -83
- scipy/stats/tests/test_qmc.py +13 -10
- scipy/stats/tests/test_quantile.py +199 -0
- scipy/stats/tests/test_rank.py +119 -112
- scipy/stats/tests/test_resampling.py +47 -56
- scipy/stats/tests/test_sampling.py +9 -4
- scipy/stats/tests/test_stats.py +799 -939
- scipy/stats/tests/test_variation.py +8 -6
- scipy/version.py +2 -2
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/LICENSE.txt +4 -4
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/METADATA +11 -11
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/RECORD +1262 -1269
- scipy.libs/libgcc_s-69c45f16.so.1 +0 -0
- scipy.libs/libgfortran-db0b6589.so.5.0.0 +0 -0
- scipy.libs/{libstdc++-1b614e01.so.6.0.32 → libstdc++-1f1a71be.so.6.0.33} +0 -0
- scipy/_lib/array_api_extra/_funcs.py +0 -484
- scipy/_lib/array_api_extra/_typing.py +0 -8
- scipy/interpolate/_bspl.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cobyla.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_cython_nnls.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/optimize/_slsqp.cpython-313-aarch64-linux-musl.so +0 -0
- scipy/spatial/qhull_src/COPYING.txt +0 -38
- scipy/special/libsf_error_state.so +0 -0
- scipy/special/tests/test_log_softmax.py +0 -109
- scipy/special/tests/test_xsf_cuda.py +0 -114
- scipy/special/xsf/binom.h +0 -89
- scipy/special/xsf/cdflib.h +0 -100
- scipy/special/xsf/cephes/airy.h +0 -307
- scipy/special/xsf/cephes/besselpoly.h +0 -51
- scipy/special/xsf/cephes/beta.h +0 -257
- scipy/special/xsf/cephes/cbrt.h +0 -131
- scipy/special/xsf/cephes/chbevl.h +0 -85
- scipy/special/xsf/cephes/chdtr.h +0 -193
- scipy/special/xsf/cephes/const.h +0 -87
- scipy/special/xsf/cephes/ellie.h +0 -293
- scipy/special/xsf/cephes/ellik.h +0 -251
- scipy/special/xsf/cephes/ellpe.h +0 -107
- scipy/special/xsf/cephes/ellpk.h +0 -117
- scipy/special/xsf/cephes/expn.h +0 -260
- scipy/special/xsf/cephes/gamma.h +0 -398
- scipy/special/xsf/cephes/hyp2f1.h +0 -596
- scipy/special/xsf/cephes/hyperg.h +0 -361
- scipy/special/xsf/cephes/i0.h +0 -149
- scipy/special/xsf/cephes/i1.h +0 -158
- scipy/special/xsf/cephes/igam.h +0 -421
- scipy/special/xsf/cephes/igam_asymp_coeff.h +0 -195
- scipy/special/xsf/cephes/igami.h +0 -313
- scipy/special/xsf/cephes/j0.h +0 -225
- scipy/special/xsf/cephes/j1.h +0 -198
- scipy/special/xsf/cephes/jv.h +0 -715
- scipy/special/xsf/cephes/k0.h +0 -164
- scipy/special/xsf/cephes/k1.h +0 -163
- scipy/special/xsf/cephes/kn.h +0 -243
- scipy/special/xsf/cephes/lanczos.h +0 -112
- scipy/special/xsf/cephes/ndtr.h +0 -275
- scipy/special/xsf/cephes/poch.h +0 -85
- scipy/special/xsf/cephes/polevl.h +0 -167
- scipy/special/xsf/cephes/psi.h +0 -194
- scipy/special/xsf/cephes/rgamma.h +0 -111
- scipy/special/xsf/cephes/scipy_iv.h +0 -811
- scipy/special/xsf/cephes/shichi.h +0 -248
- scipy/special/xsf/cephes/sici.h +0 -224
- scipy/special/xsf/cephes/sindg.h +0 -221
- scipy/special/xsf/cephes/tandg.h +0 -139
- scipy/special/xsf/cephes/trig.h +0 -58
- scipy/special/xsf/cephes/unity.h +0 -186
- scipy/special/xsf/cephes/zeta.h +0 -172
- scipy/special/xsf/config.h +0 -304
- scipy/special/xsf/digamma.h +0 -205
- scipy/special/xsf/error.h +0 -57
- scipy/special/xsf/evalpoly.h +0 -47
- scipy/special/xsf/expint.h +0 -266
- scipy/special/xsf/hyp2f1.h +0 -694
- scipy/special/xsf/iv_ratio.h +0 -173
- scipy/special/xsf/lambertw.h +0 -150
- scipy/special/xsf/loggamma.h +0 -163
- scipy/special/xsf/sici.h +0 -200
- scipy/special/xsf/tools.h +0 -427
- scipy/special/xsf/trig.h +0 -164
- scipy/special/xsf/wright_bessel.h +0 -843
- scipy/special/xsf/zlog1.h +0 -35
- scipy/stats/_mvn.cpython-313-aarch64-linux-musl.so +0 -0
- scipy.libs/libgcc_s-7393e603.so.1 +0 -0
- scipy.libs/libgfortran-eb933d8e.so.5.0.0 +0 -0
- {scipy-1.15.3.dist-info → scipy-1.16.0rc2.dist-info}/WHEEL +0 -0
scipy/signal/_short_time_fft.py
CHANGED
@@ -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
|
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`
|
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
|
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
|
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
|
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
|
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
|
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:
|
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
|
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
|
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
|
-
"""
|
807
|
-
segment beforehand.
|
1164
|
+
"""Calculate short-time Fourier transform with a trend being subtracted from
|
1165
|
+
each segment beforehand.
|
808
1166
|
|
809
|
-
|
810
|
-
|
811
|
-
|
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
|
-
|
816
|
-
|
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]``
|
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.
|
870
|
-
|
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
|
-
|
876
|
-
|
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
|
-
"""
|
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.
|
955
|
-
|
956
|
-
|
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
|
-
|
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:
|
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
|
-
|
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`
|
1507
|
-
If `left` is set
|
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
|
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,
|