scipy 1.15.3__cp312-cp312-macosx_14_0_arm64.whl → 1.16.0rc2__cp312-cp312-macosx_14_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scipy/__config__.py +4 -4
- scipy/__init__.py +3 -6
- scipy/_cyutility.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/_lib/_docscrape.py +1 -1
- scipy/_lib/_elementwise_iterative_method.py +15 -26
- scipy/_lib/_sparse.py +41 -0
- scipy/_lib/_test_deprecation_call.cpython-312-darwin.so +0 -0
- scipy/_lib/_test_deprecation_def.cpython-312-darwin.so +0 -0
- scipy/_lib/_testutils.py +6 -2
- 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-312-darwin.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-312-darwin.so +0 -0
- scipy/cluster/_optimal_leaf_ordering.cpython-312-darwin.so +0 -0
- scipy/cluster/_vq.cpython-312-darwin.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/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-312-darwin.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-312-darwin.so +0 -0
- scipy/integrate/_lsoda.cpython-312-darwin.so +0 -0
- scipy/integrate/_ode.py +9 -2
- scipy/integrate/_odepack.cpython-312-darwin.so +0 -0
- scipy/integrate/_quad_vec.py +21 -29
- scipy/integrate/_quadpack.cpython-312-darwin.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_odeint_banded.cpython-312-darwin.so +0 -0
- scipy/integrate/_vode.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/interpolate/_dierckx.cpython-312-darwin.so +0 -0
- scipy/interpolate/_fitpack.cpython-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/interpolate/_rbf.py +2 -2
- scipy/interpolate/_rbfinterp.py +1 -1
- scipy/interpolate/_rbfinterp_pythran.cpython-312-darwin.so +0 -0
- scipy/interpolate/_rgi.py +31 -26
- scipy/interpolate/_rgi_cython.cpython-312-darwin.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/_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-312-darwin.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-312-darwin.so +0 -0
- scipy/io/matlab/_mio_utils.cpython-312-darwin.so +0 -0
- scipy/io/matlab/_miobase.py +4 -1
- scipy/io/matlab/_streams.cpython-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/linalg/_decomp_ldl.py +4 -1
- scipy/linalg/_decomp_lu.py +18 -6
- scipy/linalg/_decomp_lu_cython.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/linalg/_expm_frechet.py +4 -0
- scipy/linalg/_fblas.cpython-312-darwin.so +0 -0
- scipy/linalg/_flapack.cpython-312-darwin.so +0 -0
- scipy/linalg/_linalg_pythran.cpython-312-darwin.so +0 -0
- scipy/linalg/_matfuncs.py +187 -4
- scipy/linalg/_matfuncs_expm.cpython-312-darwin.so +0 -0
- scipy/linalg/_matfuncs_schur_sqrtm.cpython-312-darwin.so +0 -0
- scipy/linalg/_matfuncs_sqrtm.py +1 -99
- scipy/linalg/_matfuncs_sqrtm_triu.cpython-312-darwin.so +0 -0
- scipy/linalg/_procrustes.py +2 -0
- scipy/linalg/_sketches.py +17 -6
- scipy/linalg/_solve_toeplitz.cpython-312-darwin.so +0 -0
- scipy/linalg/_solvers.py +7 -2
- scipy/linalg/_special_matrices.py +26 -36
- scipy/linalg/cython_blas.cpython-312-darwin.so +0 -0
- scipy/linalg/cython_lapack.cpython-312-darwin.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/_cytest.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/ndimage/_ni_docstrings.py +5 -1
- scipy/ndimage/_ni_label.cpython-312-darwin.so +0 -0
- scipy/ndimage/_ni_support.py +1 -5
- scipy/ndimage/_rank_filter_1d.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/optimize/_basinhopping.py +13 -7
- scipy/optimize/_bglu_dense.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/optimize/_dual_annealing.py +1 -1
- scipy/optimize/_elementwise.py +1 -4
- scipy/optimize/_group_columns.cpython-312-darwin.so +0 -0
- scipy/optimize/_lbfgsb.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/optimize/_lsq/common.py +3 -3
- scipy/optimize/_lsq/dogbox.py +16 -2
- scipy/optimize/_lsq/givens_elimination.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/optimize/_minpack_py.py +21 -14
- scipy/optimize/_moduleTNC.cpython-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/optimize/_spectral.py +1 -1
- scipy/optimize/_tnc.py +8 -1
- scipy/optimize/_trlib/_trlib.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/optimize/_zeros_py.py +97 -17
- scipy/optimize/cython_optimize/_zeros.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/signal/_peak_finding_utils.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/signal/_sosfilt.cpython-312-darwin.so +0 -0
- scipy/signal/_spectral_py.py +230 -50
- scipy/signal/_spline.cpython-312-darwin.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-312-darwin.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-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/sparse/csgraph/_matching.cpython-312-darwin.so +0 -0
- scipy/sparse/csgraph/_min_spanning_tree.cpython-312-darwin.so +0 -0
- scipy/sparse/csgraph/_reordering.cpython-312-darwin.so +0 -0
- scipy/sparse/csgraph/_shortest_path.cpython-312-darwin.so +0 -0
- scipy/sparse/csgraph/_tools.cpython-312-darwin.so +0 -0
- scipy/sparse/csgraph/_traversal.cpython-312-darwin.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-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/sparse/linalg/_propack/_dpropack.cpython-312-darwin.so +0 -0
- scipy/sparse/linalg/_propack/_spropack.cpython-312-darwin.so +0 -0
- scipy/sparse/linalg/_propack/_zpropack.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/spatial/_distance_pybind.cpython-312-darwin.so +0 -0
- scipy/spatial/_distance_wrap.cpython-312-darwin.so +0 -0
- scipy/spatial/_hausdorff.cpython-312-darwin.so +0 -0
- scipy/spatial/_qhull.cpython-312-darwin.so +0 -0
- scipy/spatial/_voronoi.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/spatial/transform/_rotation.cpython-312-darwin.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-312-darwin.so +0 -0
- scipy/special/_ellip_harm_2.cpython-312-darwin.so +0 -0
- scipy/special/_gufuncs.cpython-312-darwin.so +0 -0
- scipy/special/_logsumexp.py +67 -58
- scipy/special/_orthogonal.pyi +1 -1
- scipy/special/_specfun.cpython-312-darwin.so +0 -0
- scipy/special/_special_ufuncs.cpython-312-darwin.so +0 -0
- scipy/special/_spherical_bessel.py +4 -4
- scipy/special/_support_alternative_backends.py +212 -119
- scipy/special/_test_internal.cpython-312-darwin.so +0 -0
- scipy/special/_testutils.py +4 -4
- scipy/special/_ufuncs.cpython-312-darwin.so +0 -0
- scipy/special/_ufuncs.pyi +1 -0
- scipy/special/_ufuncs.pyx +215 -1400
- scipy/special/_ufuncs_cxx.cpython-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/stats/_axis_nan_policy.py +5 -12
- scipy/stats/_biasedurn.cpython-312-darwin.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-312-darwin.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-312-darwin.so +0 -0
- scipy/stats/_qmvnt.py +16 -95
- scipy/stats/_qmvnt_cy.cpython-312-darwin.so +0 -0
- scipy/stats/_quantile.py +335 -0
- scipy/stats/_rcont/rcont.cpython-312-darwin.so +0 -0
- scipy/stats/_resampling.py +4 -29
- scipy/stats/_sampling.py +1 -1
- scipy/stats/_sobol.cpython-312-darwin.so +0 -0
- scipy/stats/_stats.cpython-312-darwin.so +0 -0
- scipy/stats/_stats_mstats_common.py +21 -2
- scipy/stats/_stats_py.py +550 -476
- scipy/stats/_stats_pythran.cpython-312-darwin.so +0 -0
- scipy/stats/_unuran/unuran_wrapper.cpython-312-darwin.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 +560 -567
- scipy-1.16.0rc2.dist-info/WHEEL +6 -0
- scipy/_lib/array_api_extra/_funcs.py +0 -484
- scipy/_lib/array_api_extra/_typing.py +0 -8
- scipy/interpolate/_bspl.cpython-312-darwin.so +0 -0
- scipy/optimize/_cobyla.cpython-312-darwin.so +0 -0
- scipy/optimize/_cython_nnls.cpython-312-darwin.so +0 -0
- scipy/optimize/_slsqp.cpython-312-darwin.so +0 -0
- scipy/spatial/qhull_src/COPYING.txt +0 -38
- scipy/special/libsf_error_state.dylib +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-312-darwin.so +0 -0
- scipy-1.15.3.dist-info/WHEEL +0 -4
scipy/signal/_filter_design.py
CHANGED
@@ -4,18 +4,23 @@ import operator
|
|
4
4
|
import warnings
|
5
5
|
|
6
6
|
import numpy as np
|
7
|
-
from numpy import (atleast_1d,
|
8
|
-
|
7
|
+
from numpy import (atleast_1d, asarray,
|
8
|
+
pi, absolute, sqrt, tan, log10,
|
9
9
|
arcsinh, sin, exp, cosh, arccosh, ceil, conjugate,
|
10
|
-
|
11
|
-
mintypecode)
|
10
|
+
sinh, concatenate, prod, array)
|
12
11
|
from numpy.polynomial.polynomial import polyval as npp_polyval
|
13
|
-
from numpy.polynomial.polynomial import polyvalfromroots
|
14
12
|
|
15
13
|
from scipy import special, optimize, fft as sp_fft
|
16
14
|
from scipy.special import comb
|
17
15
|
from scipy._lib._util import float_factorial
|
18
16
|
from scipy.signal._arraytools import _validate_fs
|
17
|
+
from scipy.signal import _polyutils as _pu
|
18
|
+
|
19
|
+
import scipy._lib.array_api_extra as xpx
|
20
|
+
from scipy._lib._array_api import (
|
21
|
+
array_namespace, xp_promote, xp_size, xp_default_dtype, is_jax, xp_float_to_complex,
|
22
|
+
)
|
23
|
+
from scipy._lib.array_api_compat import numpy as np_compat
|
19
24
|
|
20
25
|
|
21
26
|
__all__ = ['findfreqs', 'freqs', 'freqz', 'tf2zpk', 'zpk2tf', 'normalize',
|
@@ -55,6 +60,31 @@ def _is_int_type(x):
|
|
55
60
|
return True
|
56
61
|
|
57
62
|
|
63
|
+
# https://github.com/numpy/numpy/blob/v2.2.0/numpy/_core/function_base.py#L195-L302
|
64
|
+
def _logspace(start, stop, num=50, endpoint=True, base=10.0, dtype=None, *, xp):
|
65
|
+
if not isinstance(base, float | int) and xp.asarray(base).ndim > 0:
|
66
|
+
# If base is non-scalar, broadcast it with the others, since it
|
67
|
+
# may influence how axis is interpreted.
|
68
|
+
start, stop, base = map(xp.asarray, (start, stop, base))
|
69
|
+
ndmax = xp.broadcast_arrays(start, stop, base).ndim
|
70
|
+
start, stop, base = (
|
71
|
+
xpx.atleast_nd(a, ndim=ndmax)
|
72
|
+
for a in (start, stop, base)
|
73
|
+
)
|
74
|
+
base = xp.expand_dims(base)
|
75
|
+
try:
|
76
|
+
result_dt = xp.result_type(start, stop, base)
|
77
|
+
except ValueError:
|
78
|
+
# all of start, stop and base are python scalars
|
79
|
+
result_dt = xp_default_dtype(xp)
|
80
|
+
y = xp.linspace(start, stop, num=num, endpoint=endpoint, dtype=result_dt)
|
81
|
+
|
82
|
+
yp = xp.pow(base, y)
|
83
|
+
if dtype is None:
|
84
|
+
return yp
|
85
|
+
return xp.astype(yp, dtype, copy=False)
|
86
|
+
|
87
|
+
|
58
88
|
def findfreqs(num, den, N, kind='ba'):
|
59
89
|
"""
|
60
90
|
Find array of frequencies for computing the response of an analog filter.
|
@@ -90,27 +120,42 @@ def findfreqs(num, den, N, kind='ba'):
|
|
90
120
|
3.16227766e-01, 1.00000000e+00, 3.16227766e+00,
|
91
121
|
1.00000000e+01, 3.16227766e+01, 1.00000000e+02])
|
92
122
|
"""
|
123
|
+
xp = array_namespace(num, den)
|
124
|
+
num, den = map(xp.asarray, (num, den))
|
125
|
+
|
93
126
|
if kind == 'ba':
|
94
|
-
ep =
|
95
|
-
tz =
|
127
|
+
ep = xpx.atleast_nd(_pu.polyroots(den, xp=xp), ndim=1, xp=xp)
|
128
|
+
tz = xpx.atleast_nd(_pu.polyroots(num, xp=xp), ndim=1, xp=xp)
|
96
129
|
elif kind == 'zp':
|
97
|
-
ep =
|
98
|
-
tz =
|
130
|
+
ep = xpx.atleast_nd(den, ndim=1, xp=xp)
|
131
|
+
tz = xpx.atleast_nd(num, ndim=1, xp=xp)
|
99
132
|
else:
|
100
133
|
raise ValueError("input must be one of {'ba', 'zp'}")
|
101
134
|
|
102
|
-
|
103
|
-
|
135
|
+
ep = xp_float_to_complex(ep, xp=xp)
|
136
|
+
tz = xp_float_to_complex(tz, xp=xp)
|
104
137
|
|
105
|
-
|
138
|
+
if ep.shape[0] == 0:
|
139
|
+
ep = xp.asarray([-1000], dtype=ep.dtype)
|
106
140
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
2 * ez.imag)) - 0.5)
|
141
|
+
ez = xp.concat((
|
142
|
+
ep[xp.imag(ep) >= 0],
|
143
|
+
tz[(xp.abs(tz) < 1e5) & (xp.imag(tz) >= 0)]
|
144
|
+
))
|
112
145
|
|
113
|
-
|
146
|
+
integ = xp.astype(xp.abs(ez) < 1e-10, ez.dtype) # XXX True->1, False->0
|
147
|
+
hfreq = xp.round(
|
148
|
+
xp.log10(xp.max(3*xp.abs(xp.real(ez) + integ) + 1.5*xp.imag(ez))) + 0.5
|
149
|
+
)
|
150
|
+
|
151
|
+
# the fudge factor is for backwards compatibility: round(-1.5) can be -1 or -2
|
152
|
+
# depending on the the floating-point jitter in -1.5
|
153
|
+
fudge = 1e-14 if is_jax(xp) else 0
|
154
|
+
lfreq = xp.round(
|
155
|
+
xp.log10(0.1*xp.min(xp.abs(xp.real(ez + integ)) + 2*xp.imag(ez))) - 0.5 - fudge
|
156
|
+
)
|
157
|
+
|
158
|
+
w = _logspace(lfreq, hfreq, N, xp=xp)
|
114
159
|
return w
|
115
160
|
|
116
161
|
|
@@ -175,16 +220,18 @@ def freqs(b, a, worN=200, plot=None):
|
|
175
220
|
>>> plt.show()
|
176
221
|
|
177
222
|
"""
|
223
|
+
xp = array_namespace(b, a, worN)
|
224
|
+
|
178
225
|
if worN is None:
|
179
226
|
# For backwards compatibility
|
180
227
|
w = findfreqs(b, a, 200)
|
181
228
|
elif _is_int_type(worN):
|
182
229
|
w = findfreqs(b, a, worN)
|
183
230
|
else:
|
184
|
-
w =
|
231
|
+
w = xpx.atleast_nd(xp.asarray(worN), ndim=1, xp=xp)
|
185
232
|
|
186
233
|
s = 1j * w
|
187
|
-
h = polyval(b, s) / polyval(a, s)
|
234
|
+
h = _pu.polyval(b, s, xp=xp) / _pu.polyval(a, s, xp=xp)
|
188
235
|
if plot is not None:
|
189
236
|
plot(w, h)
|
190
237
|
|
@@ -251,8 +298,13 @@ def freqs_zpk(z, p, k, worN=200):
|
|
251
298
|
>>> plt.show()
|
252
299
|
|
253
300
|
"""
|
254
|
-
|
255
|
-
|
301
|
+
xp = array_namespace(z, p)
|
302
|
+
z, p = map(xp.asarray, (z, p))
|
303
|
+
|
304
|
+
# NB: k is documented to be a scalar; for backwards compat we keep allowing it
|
305
|
+
# to be a size-1 array, but it does not influence the namespace calculation.
|
306
|
+
k = xp.asarray(k, dtype=xp_default_dtype(xp))
|
307
|
+
if xp_size(k) > 1:
|
256
308
|
raise ValueError('k must be a single scalar gain')
|
257
309
|
|
258
310
|
if worN is None:
|
@@ -263,10 +315,10 @@ def freqs_zpk(z, p, k, worN=200):
|
|
263
315
|
else:
|
264
316
|
w = worN
|
265
317
|
|
266
|
-
w =
|
318
|
+
w = xpx.atleast_nd(xp.asarray(w), ndim=1, xp=xp)
|
267
319
|
s = 1j * w
|
268
|
-
num =
|
269
|
-
den =
|
320
|
+
num = _pu.npp_polyvalfromroots(s, z, xp=xp)
|
321
|
+
den = _pu.npp_polyvalfromroots(s, p, xp=xp)
|
270
322
|
h = k * num/den
|
271
323
|
return w, h
|
272
324
|
|
@@ -430,8 +482,15 @@ def freqz(b, a=1, worN=512, whole=False, plot=None, fs=2*pi,
|
|
430
482
|
(2, 1024)
|
431
483
|
|
432
484
|
"""
|
433
|
-
|
434
|
-
|
485
|
+
xp = array_namespace(b, a)
|
486
|
+
|
487
|
+
b, a = map(xp.asarray, (b, a))
|
488
|
+
if xp.isdtype(a.dtype, 'integral'):
|
489
|
+
a = xp.astype(a, xp_default_dtype(xp))
|
490
|
+
res_dtype = xp.result_type(b, a)
|
491
|
+
|
492
|
+
b = xpx.atleast_nd(b, ndim=1, xp=xp)
|
493
|
+
a = xpx.atleast_nd(a, ndim=1, xp=xp)
|
435
494
|
|
436
495
|
fs = _validate_fs(fs, allow_none=False)
|
437
496
|
|
@@ -449,40 +508,51 @@ def freqz(b, a=1, worN=512, whole=False, plot=None, fs=2*pi,
|
|
449
508
|
lastpoint = 2 * pi if whole else pi
|
450
509
|
# if include_nyquist is true and whole is false, w should
|
451
510
|
# include end point
|
452
|
-
w =
|
453
|
-
endpoint=include_nyquist and not whole)
|
511
|
+
w = xp.linspace(0, lastpoint, N,
|
512
|
+
endpoint=include_nyquist and not whole, dtype=res_dtype)
|
454
513
|
n_fft = N if whole else 2 * (N - 1) if include_nyquist else 2 * N
|
455
|
-
if (a
|
514
|
+
if (xp_size(a) == 1 and (b.ndim == 1 or (b.shape[-1] == 1))
|
456
515
|
and n_fft >= b.shape[0]
|
457
516
|
and n_fft > 0): # TODO: review threshold acc. to benchmark?
|
458
|
-
|
517
|
+
|
518
|
+
if (xp.isdtype(b.dtype, "real floating") and
|
519
|
+
xp.isdtype(a.dtype, "real floating")
|
520
|
+
):
|
459
521
|
fft_func = sp_fft.rfft
|
460
522
|
else:
|
461
523
|
fft_func = sp_fft.fft
|
462
|
-
|
524
|
+
|
525
|
+
h = fft_func(b, n=n_fft, axis=0)
|
526
|
+
h = h[:min(N, h.shape[0]), ...]
|
463
527
|
h /= a
|
528
|
+
|
464
529
|
if fft_func is sp_fft.rfft and whole:
|
465
530
|
# exclude DC and maybe Nyquist (no need to use axis_reverse
|
466
531
|
# here because we can build reversal with the truncation)
|
467
|
-
stop =
|
468
|
-
|
469
|
-
h =
|
532
|
+
stop = None if n_fft % 2 == 1 else -1
|
533
|
+
h_flipped = xp.flip(h[1:stop, ...], axis=0)
|
534
|
+
h = xp.concat((h, xp.conj(h_flipped)))
|
470
535
|
if b.ndim > 1:
|
471
536
|
# Last axis of h has length 1, so drop it.
|
472
537
|
h = h[..., 0]
|
473
538
|
# Move the first axis of h to the end.
|
474
|
-
h =
|
539
|
+
h = xp.moveaxis(h, 0, -1)
|
475
540
|
else:
|
476
|
-
|
541
|
+
if isinstance(worN, complex):
|
542
|
+
# backwards compat
|
543
|
+
worN = worN.real
|
544
|
+
w = xpx.atleast_nd(xp.asarray(worN, dtype=res_dtype), ndim=1, xp=xp)
|
545
|
+
if xp.isdtype(w.dtype, 'integral'):
|
546
|
+
w = xp.astype(w, xp_default_dtype(xp))
|
477
547
|
del worN
|
478
|
-
w = 2*pi*w/fs
|
548
|
+
w = 2 * pi * w / fs
|
479
549
|
|
480
550
|
if h is None: # still need to compute using freqs w
|
481
|
-
zm1 = exp(-1j * w)
|
482
|
-
h = (npp_polyval(zm1, b, tensor=False) /
|
483
|
-
npp_polyval(zm1, a, tensor=False))
|
551
|
+
zm1 = xp.exp(-1j * w)
|
552
|
+
h = (_pu.npp_polyval(zm1, b, tensor=False, xp=xp) /
|
553
|
+
_pu.npp_polyval(zm1, a, tensor=False, xp=xp))
|
484
554
|
|
485
|
-
w = w*(fs/(2*pi))
|
555
|
+
w = w * (fs / (2 * pi))
|
486
556
|
|
487
557
|
if plot is not None:
|
488
558
|
plot(w, h)
|
@@ -573,7 +643,13 @@ def freqz_zpk(z, p, k, worN=512, whole=False, fs=2*pi):
|
|
573
643
|
>>> plt.show()
|
574
644
|
|
575
645
|
"""
|
576
|
-
|
646
|
+
xp = array_namespace(z, p)
|
647
|
+
z, p = map(xp.asarray, (z, p))
|
648
|
+
|
649
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
650
|
+
p = xpx.atleast_nd(p, ndim=1, xp=xp)
|
651
|
+
res_dtype = xp.result_type(z, p)
|
652
|
+
res_dtype = xp.float64 if res_dtype in (xp.float64, xp.complex128) else xp.float32
|
577
653
|
|
578
654
|
fs = _validate_fs(fs, allow_none=False)
|
579
655
|
|
@@ -584,15 +660,19 @@ def freqz_zpk(z, p, k, worN=512, whole=False, fs=2*pi):
|
|
584
660
|
|
585
661
|
if worN is None:
|
586
662
|
# For backwards compatibility
|
587
|
-
w =
|
663
|
+
w = xp.linspace(0, lastpoint, 512, endpoint=False, dtype=res_dtype)
|
588
664
|
elif _is_int_type(worN):
|
589
|
-
w =
|
665
|
+
w = xp.linspace(0, lastpoint, worN, endpoint=False, dtype=res_dtype)
|
590
666
|
else:
|
591
|
-
w =
|
592
|
-
w
|
667
|
+
w = xp.asarray(worN)
|
668
|
+
if xp.isdtype(w.dtype, 'integral'):
|
669
|
+
w = xp.astype(w, xp_default_dtype(xp))
|
670
|
+
w = xpx.atleast_nd(w, ndim=1, xp=xp)
|
671
|
+
w = 2 * pi * w / fs
|
593
672
|
|
594
|
-
zm1 = exp(1j * w)
|
595
|
-
|
673
|
+
zm1 = xp.exp(1j * w)
|
674
|
+
func = _pu.npp_polyvalfromroots
|
675
|
+
h = xp.asarray(k, dtype=res_dtype) * func(zm1, z, xp=xp) / func(zm1, p, xp=xp)
|
596
676
|
|
597
677
|
w = w*(fs/(2*pi))
|
598
678
|
|
@@ -725,15 +805,19 @@ def group_delay(system, w=512, whole=False, fs=2*pi):
|
|
725
805
|
return w, gd
|
726
806
|
|
727
807
|
|
728
|
-
def _validate_sos(sos):
|
808
|
+
def _validate_sos(sos, xp=None):
|
729
809
|
"""Helper to validate a SOS input"""
|
730
|
-
|
810
|
+
if xp is None:
|
811
|
+
xp = np # backcompat, cf sosfilt, sosfiltfilt
|
812
|
+
|
813
|
+
sos = xp.asarray(sos)
|
814
|
+
sos = xpx.atleast_nd(sos, ndim=2, xp=xp)
|
731
815
|
if sos.ndim != 2:
|
732
816
|
raise ValueError('sos array must be 2D')
|
733
817
|
n_sections, m = sos.shape
|
734
818
|
if m != 6:
|
735
819
|
raise ValueError('sos array must be shape (n_sections, 6)')
|
736
|
-
if not (sos[:, 3] == 1)
|
820
|
+
if not xp.all(sos[:, 3] == 1):
|
737
821
|
raise ValueError('sos[:, 3] should be all ones')
|
738
822
|
return sos, n_sections
|
739
823
|
|
@@ -787,12 +871,14 @@ def freqz_sos(sos, worN=512, whole=False, fs=2*pi):
|
|
787
871
|
|
788
872
|
See Also
|
789
873
|
--------
|
790
|
-
freqz, sosfilt
|
874
|
+
freqz, sosfilt, sosfreqz
|
791
875
|
|
792
876
|
Notes
|
793
877
|
-----
|
794
|
-
|
795
|
-
|
878
|
+
This function used to be called ``sosfreqz`` in older versions (≥ 0.19.0)
|
879
|
+
|
880
|
+
.. versionadded:: 1.15.0
|
881
|
+
|
796
882
|
Examples
|
797
883
|
--------
|
798
884
|
Design a 15th-order bandpass filter in SOS format.
|
@@ -850,13 +936,16 @@ def freqz_sos(sos, worN=512, whole=False, fs=2*pi):
|
|
850
936
|
>>> plt.show()
|
851
937
|
|
852
938
|
"""
|
939
|
+
xp = array_namespace(sos)
|
940
|
+
|
853
941
|
fs = _validate_fs(fs, allow_none=False)
|
854
942
|
|
855
|
-
sos, n_sections = _validate_sos(sos)
|
943
|
+
sos, n_sections = _validate_sos(sos, xp)
|
856
944
|
if n_sections == 0:
|
857
945
|
raise ValueError('Cannot compute frequencies with no sections')
|
858
946
|
h = 1.
|
859
|
-
for
|
947
|
+
for j in range(sos.shape[0]):
|
948
|
+
row = sos[j, :]
|
860
949
|
w, rowh = freqz(row[:3], row[3:], worN=worN, whole=whole, fs=fs)
|
861
950
|
h *= rowh
|
862
951
|
return w, h
|
@@ -864,11 +953,14 @@ def freqz_sos(sos, worN=512, whole=False, fs=2*pi):
|
|
864
953
|
|
865
954
|
def sosfreqz(*args, **kwargs):
|
866
955
|
"""
|
867
|
-
Compute the frequency response of a digital filter in SOS format.
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
956
|
+
Compute the frequency response of a digital filter in SOS format (legacy).
|
957
|
+
|
958
|
+
.. legacy:: function
|
959
|
+
|
960
|
+
This function is an alias, provided for backward compatibility.
|
961
|
+
New code should use the function :func:`scipy.signal.freqz_sos`.
|
962
|
+
This function became obsolete from version 1.15.0.
|
963
|
+
|
872
964
|
"""
|
873
965
|
return freqz_sos(*args, **kwargs)
|
874
966
|
|
@@ -1122,13 +1214,15 @@ def tf2zpk(b, a):
|
|
1122
1214
|
array([ -2.5+2.59807621j , -2.5-2.59807621j]),
|
1123
1215
|
3.0)
|
1124
1216
|
"""
|
1217
|
+
xp = array_namespace(b, a)
|
1125
1218
|
b, a = normalize(b, a)
|
1126
|
-
|
1127
|
-
a = (a
|
1219
|
+
|
1220
|
+
a, b = xp_promote(a, b, xp=xp, force_floating=True)
|
1221
|
+
|
1128
1222
|
k = b[0]
|
1129
|
-
b
|
1130
|
-
z =
|
1131
|
-
p =
|
1223
|
+
b = b / b[0]
|
1224
|
+
z = _pu.polyroots(b, xp=xp)
|
1225
|
+
p = _pu.polyroots(a, xp=xp)
|
1132
1226
|
return z, p, k
|
1133
1227
|
|
1134
1228
|
|
@@ -1170,38 +1264,26 @@ def zpk2tf(z, p, k):
|
|
1170
1264
|
>>> zpk2tf(z, p, k)
|
1171
1265
|
( array([ 5., -40., 60.]), array([ 1., -9., 8.]))
|
1172
1266
|
"""
|
1173
|
-
|
1174
|
-
k =
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1267
|
+
xp = array_namespace(z, p)
|
1268
|
+
z, p, k = map(xp.asarray, (z, p, k))
|
1269
|
+
|
1270
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
1271
|
+
k = xpx.atleast_nd(k, ndim=1, xp=xp)
|
1272
|
+
if xp.isdtype(k.dtype, 'integral'):
|
1273
|
+
k = xp.astype(k, xp_default_dtype(xp))
|
1274
|
+
|
1275
|
+
if z.ndim > 1:
|
1276
|
+
temp = _pu.poly(z[0], xp=xp)
|
1277
|
+
b = xp.empty((z.shape[0], z.shape[1] + 1), dtype=temp.dtype)
|
1278
|
+
if k.shape[0] == 1:
|
1179
1279
|
k = [k[0]] * z.shape[0]
|
1180
1280
|
for i in range(z.shape[0]):
|
1181
|
-
b[i] = k[i] * poly(z[i])
|
1281
|
+
b[i] = k[i] * _pu.poly(z[i], xp=xp)
|
1182
1282
|
else:
|
1183
|
-
b = k * poly(z)
|
1184
|
-
|
1185
|
-
|
1186
|
-
|
1187
|
-
# we can't depend on a specific version of numpy.
|
1188
|
-
if issubclass(b.dtype.type, np.complexfloating):
|
1189
|
-
# if complex roots are all complex conjugates, the roots are real.
|
1190
|
-
roots = np.asarray(z, complex)
|
1191
|
-
pos_roots = np.compress(roots.imag > 0, roots)
|
1192
|
-
neg_roots = np.conjugate(np.compress(roots.imag < 0, roots))
|
1193
|
-
if len(pos_roots) == len(neg_roots):
|
1194
|
-
if np.all(np.sort_complex(neg_roots) == np.sort_complex(pos_roots)):
|
1195
|
-
b = b.real.copy()
|
1196
|
-
|
1197
|
-
if issubclass(a.dtype.type, np.complexfloating):
|
1198
|
-
# if complex roots are all complex conjugates, the roots are real.
|
1199
|
-
roots = np.asarray(p, complex)
|
1200
|
-
pos_roots = np.compress(roots.imag > 0, roots)
|
1201
|
-
neg_roots = np.conjugate(np.compress(roots.imag < 0, roots))
|
1202
|
-
if len(pos_roots) == len(neg_roots):
|
1203
|
-
if np.all(np.sort_complex(neg_roots) == np.sort_complex(pos_roots)):
|
1204
|
-
a = a.real.copy()
|
1283
|
+
b = k * _pu.poly(z, xp=xp)
|
1284
|
+
|
1285
|
+
a = _pu.poly(p, xp=xp)
|
1286
|
+
a = xpx.atleast_nd(xp.asarray(a), ndim=1, xp=xp)
|
1205
1287
|
|
1206
1288
|
return b, a
|
1207
1289
|
|
@@ -1297,17 +1379,20 @@ def sos2tf(sos):
|
|
1297
1379
|
( array([0.91256522, 0.91256522, 0. ]),
|
1298
1380
|
array([1. , 0.82513043, 0. ]))
|
1299
1381
|
"""
|
1300
|
-
|
1382
|
+
xp = array_namespace(sos)
|
1383
|
+
sos = xp.asarray(sos)
|
1384
|
+
|
1301
1385
|
result_type = sos.dtype
|
1302
|
-
if result_type
|
1303
|
-
result_type =
|
1386
|
+
if xp.isdtype(result_type, 'integral'):
|
1387
|
+
result_type = xp_default_dtype(xp)
|
1388
|
+
|
1389
|
+
b = xp.asarray([1], dtype=result_type)
|
1390
|
+
a = xp.asarray([1], dtype=result_type)
|
1304
1391
|
|
1305
|
-
b = np.array([1], dtype=result_type)
|
1306
|
-
a = np.array([1], dtype=result_type)
|
1307
1392
|
n_sections = sos.shape[0]
|
1308
1393
|
for section in range(n_sections):
|
1309
|
-
b =
|
1310
|
-
a =
|
1394
|
+
b = _pu.polymul(b, sos[section, :3], xp=xp)
|
1395
|
+
a = _pu.polymul(a, sos[section, 3:], xp=xp)
|
1311
1396
|
return b, a
|
1312
1397
|
|
1313
1398
|
|
@@ -1338,15 +1423,17 @@ def sos2zpk(sos):
|
|
1338
1423
|
|
1339
1424
|
.. versionadded:: 0.16.0
|
1340
1425
|
"""
|
1341
|
-
|
1426
|
+
xp = array_namespace(sos)
|
1427
|
+
sos = xp.asarray(sos)
|
1428
|
+
|
1342
1429
|
n_sections = sos.shape[0]
|
1343
|
-
z =
|
1344
|
-
p =
|
1430
|
+
z = xp.zeros(n_sections*2, dtype=xp.complex128)
|
1431
|
+
p = xp.zeros(n_sections*2, dtype=xp.complex128)
|
1345
1432
|
k = 1.
|
1346
1433
|
for section in range(n_sections):
|
1347
1434
|
zpk = tf2zpk(sos[section, :3], sos[section, 3:])
|
1348
|
-
z
|
1349
|
-
p
|
1435
|
+
z = xpx.at(z, slice(2*section, 2*section + zpk[0].shape[0])).set(zpk[0])
|
1436
|
+
p = xpx.at(p, slice(2*section, 2*section + zpk[1].shape[0])).set(zpk[1])
|
1350
1437
|
k *= zpk[2]
|
1351
1438
|
return z, p, k
|
1352
1439
|
|
@@ -1561,6 +1648,12 @@ def zpk2sos(z, p, k, pairing=None, *, analog=False):
|
|
1561
1648
|
# 4. Further optimizations of the section ordering / pole-zero pairing.
|
1562
1649
|
# See the wiki for other potential issues.
|
1563
1650
|
|
1651
|
+
xp = array_namespace(z, p)
|
1652
|
+
|
1653
|
+
# convert to numpy, convert back on exit XXX
|
1654
|
+
z, p = map(np.asarray, (z, p))
|
1655
|
+
k = np.asarray(k)
|
1656
|
+
|
1564
1657
|
if pairing is None:
|
1565
1658
|
pairing = 'minimal' if analog else 'nearest'
|
1566
1659
|
|
@@ -1574,9 +1667,9 @@ def zpk2sos(z, p, k, pairing=None, *, analog=False):
|
|
1574
1667
|
|
1575
1668
|
if len(z) == len(p) == 0:
|
1576
1669
|
if not analog:
|
1577
|
-
return np.
|
1670
|
+
return xp.asarray(np.asarray([[k, 0., 0., 1., 0., 0.]]))
|
1578
1671
|
else:
|
1579
|
-
return np.
|
1672
|
+
return xp.asarray(np.asarray([[0., 0., k, 0., 0., 1.]]))
|
1580
1673
|
|
1581
1674
|
if pairing != 'minimal':
|
1582
1675
|
# ensure we have the same number of poles and zeros, and make copies
|
@@ -1687,10 +1780,10 @@ def zpk2sos(z, p, k, pairing=None, *, analog=False):
|
|
1687
1780
|
|
1688
1781
|
# put gain in first sos
|
1689
1782
|
sos[0][:3] *= k
|
1690
|
-
return sos
|
1783
|
+
return xp.asarray(sos)
|
1691
1784
|
|
1692
1785
|
|
1693
|
-
def _align_nums(nums):
|
1786
|
+
def _align_nums(nums, xp):
|
1694
1787
|
"""Aligns the shapes of multiple numerators.
|
1695
1788
|
|
1696
1789
|
Given an array of numerator coefficient arrays [[a_1, a_2,...,
|
@@ -1715,19 +1808,19 @@ def _align_nums(nums):
|
|
1715
1808
|
# The statement can throw a ValueError if one
|
1716
1809
|
# of the numerators is a single digit and another
|
1717
1810
|
# is array-like e.g. if nums = [5, [1, 2, 3]]
|
1718
|
-
nums = asarray(nums)
|
1811
|
+
nums = xp.asarray(nums)
|
1719
1812
|
|
1720
|
-
if not
|
1813
|
+
if not xp.isdtype(nums.dtype, "numeric"):
|
1721
1814
|
raise ValueError("dtype of numerator is non-numeric")
|
1722
1815
|
|
1723
1816
|
return nums
|
1724
1817
|
|
1725
1818
|
except ValueError:
|
1726
|
-
nums = [
|
1727
|
-
max_width = max(num
|
1819
|
+
nums = [xpx.atleast_nd(xp.asarray(num), ndim=1) for num in nums]
|
1820
|
+
max_width = max(xp_size(num) for num in nums)
|
1728
1821
|
|
1729
1822
|
# pre-allocate
|
1730
|
-
aligned_nums =
|
1823
|
+
aligned_nums = xp.zeros((len(nums), max_width))
|
1731
1824
|
|
1732
1825
|
# Create numerators with padded zeros
|
1733
1826
|
for index, num in enumerate(nums):
|
@@ -1792,29 +1885,37 @@ def normalize(b, a):
|
|
1792
1885
|
Badly conditioned filter coefficients (numerator): the results may be meaningless
|
1793
1886
|
|
1794
1887
|
"""
|
1795
|
-
|
1888
|
+
try:
|
1889
|
+
xp = array_namespace(b, a)
|
1890
|
+
except TypeError:
|
1891
|
+
# object arrays, test_ltisys.py::TestSS2TF::test_simo_round_trip
|
1892
|
+
xp = np_compat
|
1893
|
+
|
1894
|
+
den = xp.asarray(a)
|
1895
|
+
den = xpx.atleast_nd(den, ndim=1, xp=xp)
|
1796
1896
|
|
1797
|
-
|
1798
|
-
num =
|
1897
|
+
num = xp.asarray(b)
|
1898
|
+
num = xpx.atleast_nd(_align_nums(num, xp), ndim=2, xp=xp)
|
1799
1899
|
|
1800
1900
|
if den.ndim != 1:
|
1801
1901
|
raise ValueError("Denominator polynomial must be rank-1 array.")
|
1802
1902
|
if num.ndim > 2:
|
1803
1903
|
raise ValueError("Numerator polynomial must be rank-1 or"
|
1804
1904
|
" rank-2 array.")
|
1805
|
-
if
|
1905
|
+
if xp.all(den == 0):
|
1806
1906
|
raise ValueError("Denominator must have at least on nonzero element.")
|
1807
1907
|
|
1808
1908
|
# Trim leading zeros in denominator, leave at least one.
|
1809
|
-
den =
|
1909
|
+
den = _pu._trim_zeros(den, 'f')
|
1810
1910
|
|
1811
1911
|
# Normalize transfer function
|
1812
1912
|
num, den = num / den[0], den / den[0]
|
1813
1913
|
|
1814
1914
|
# Count numerator columns that are all zero
|
1815
1915
|
leading_zeros = 0
|
1816
|
-
for
|
1817
|
-
|
1916
|
+
for j in range(num.shape[-1]):
|
1917
|
+
col = num[:, j]
|
1918
|
+
if xp.all(xp.abs(col) <= 1e-14):
|
1818
1919
|
leading_zeros += 1
|
1819
1920
|
else:
|
1820
1921
|
break
|
@@ -1892,15 +1993,20 @@ def lp2lp(b, a, wo=1.0):
|
|
1892
1993
|
>>> plt.legend()
|
1893
1994
|
|
1894
1995
|
"""
|
1895
|
-
|
1996
|
+
xp = array_namespace(a, b)
|
1997
|
+
a, b = map(xp.asarray, (a, b))
|
1998
|
+
a, b = xp_promote(a, b, force_floating=True, xp=xp)
|
1999
|
+
a = xpx.atleast_nd(a, ndim=1, xp=xp)
|
2000
|
+
b = xpx.atleast_nd(b, ndim=1, xp=xp)
|
2001
|
+
|
1896
2002
|
try:
|
1897
2003
|
wo = float(wo)
|
1898
2004
|
except TypeError:
|
1899
2005
|
wo = float(wo[0])
|
1900
|
-
d =
|
1901
|
-
n =
|
2006
|
+
d = a.shape[0]
|
2007
|
+
n = b.shape[0]
|
1902
2008
|
M = max((d, n))
|
1903
|
-
pwo =
|
2009
|
+
pwo = wo ** xp.arange(M - 1, -1, -1, dtype=xp.float64)
|
1904
2010
|
start1 = max((n - d, 0))
|
1905
2011
|
start2 = max((d - n, 0))
|
1906
2012
|
b = b * pwo[start1] / pwo[start2:]
|
@@ -1908,6 +2014,28 @@ def lp2lp(b, a, wo=1.0):
|
|
1908
2014
|
return normalize(b, a)
|
1909
2015
|
|
1910
2016
|
|
2017
|
+
def _resize(a, new_shape, xp):
|
2018
|
+
# https://github.com/numpy/numpy/blob/v2.2.4/numpy/_core/fromnumeric.py#L1535
|
2019
|
+
a = xp.reshape(a, (-1,))
|
2020
|
+
|
2021
|
+
new_size = 1
|
2022
|
+
for dim_length in new_shape:
|
2023
|
+
new_size *= dim_length
|
2024
|
+
if dim_length < 0:
|
2025
|
+
raise ValueError(
|
2026
|
+
'all elements of `new_shape` must be non-negative'
|
2027
|
+
)
|
2028
|
+
|
2029
|
+
if xp_size(a) == 0 or new_size == 0:
|
2030
|
+
# First case must zero fill. The second would have repeats == 0.
|
2031
|
+
return xp.zeros_like(a, shape=new_shape)
|
2032
|
+
|
2033
|
+
repeats = -(-new_size // xp_size(a)) # ceil division
|
2034
|
+
a = xp.concat((a,) * repeats)[:new_size]
|
2035
|
+
|
2036
|
+
return xp.reshape(a, new_shape)
|
2037
|
+
|
2038
|
+
|
1911
2039
|
def lp2hp(b, a, wo=1.0):
|
1912
2040
|
r"""
|
1913
2041
|
Transform a lowpass filter prototype to a highpass filter.
|
@@ -1966,27 +2094,33 @@ def lp2hp(b, a, wo=1.0):
|
|
1966
2094
|
>>> plt.legend()
|
1967
2095
|
|
1968
2096
|
"""
|
1969
|
-
|
2097
|
+
xp = array_namespace(a, b)
|
2098
|
+
|
2099
|
+
a, b = map(xp.asarray, (a, b))
|
2100
|
+
a, b = xp_promote(a, b, force_floating=True, xp=xp)
|
2101
|
+
a = xpx.atleast_nd(a, ndim=1, xp=xp)
|
2102
|
+
b = xpx.atleast_nd(b, ndim=1, xp=xp)
|
2103
|
+
|
1970
2104
|
try:
|
1971
2105
|
wo = float(wo)
|
1972
2106
|
except TypeError:
|
1973
2107
|
wo = float(wo[0])
|
1974
|
-
d =
|
1975
|
-
n =
|
2108
|
+
d = a.shape[0]
|
2109
|
+
n = b.shape[0]
|
1976
2110
|
if wo != 1:
|
1977
|
-
pwo =
|
2111
|
+
pwo = wo ** xp.arange(max((d, n)), dtype=b.dtype)
|
1978
2112
|
else:
|
1979
|
-
pwo =
|
2113
|
+
pwo = xp.ones(max((d, n)), dtype=b.dtype)
|
1980
2114
|
if d >= n:
|
1981
|
-
outa = a
|
1982
|
-
outb =
|
2115
|
+
outa = xp.flip(a) * pwo
|
2116
|
+
outb = _resize(b, (d,), xp=xp)
|
1983
2117
|
outb[n:] = 0.0
|
1984
|
-
outb[:n] = b
|
2118
|
+
outb[:n] = xp.flip(b) * pwo[:n]
|
1985
2119
|
else:
|
1986
|
-
outb = b
|
1987
|
-
outa =
|
2120
|
+
outb = xp.flip(b) * pwo
|
2121
|
+
outa = _resize(a, (n,), xp=xp)
|
1988
2122
|
outa[d:] = 0.0
|
1989
|
-
outa[:d] = a
|
2123
|
+
outa[:d] = xp.flip(a) * pwo[:d]
|
1990
2124
|
|
1991
2125
|
return normalize(outb, outa)
|
1992
2126
|
|
@@ -2051,16 +2185,20 @@ def lp2bp(b, a, wo=1.0, bw=1.0):
|
|
2051
2185
|
>>> plt.ylabel('Amplitude [dB]')
|
2052
2186
|
>>> plt.legend()
|
2053
2187
|
"""
|
2188
|
+
xp = array_namespace(a, b)
|
2054
2189
|
|
2055
|
-
a, b = map(
|
2056
|
-
|
2057
|
-
|
2058
|
-
|
2190
|
+
a, b = map(xp.asarray, (a, b))
|
2191
|
+
a, b = xp_promote(a, b, force_floating=True, xp=xp)
|
2192
|
+
a = xpx.atleast_nd(a, ndim=1, xp=xp)
|
2193
|
+
b = xpx.atleast_nd(b, ndim=1, xp=xp)
|
2194
|
+
|
2195
|
+
D = a.shape[0] - 1
|
2196
|
+
N = b.shape[0] - 1
|
2059
2197
|
ma = max([N, D])
|
2060
2198
|
Np = N + ma
|
2061
2199
|
Dp = D + ma
|
2062
|
-
bprime =
|
2063
|
-
aprime =
|
2200
|
+
bprime = xp.empty(Np + 1, dtype=b.dtype)
|
2201
|
+
aprime = xp.empty(Dp + 1, dtype=a.dtype)
|
2064
2202
|
wosq = wo * wo
|
2065
2203
|
for j in range(Np + 1):
|
2066
2204
|
val = 0.0
|
@@ -2139,15 +2277,20 @@ def lp2bs(b, a, wo=1.0, bw=1.0):
|
|
2139
2277
|
>>> plt.ylabel('Amplitude [dB]')
|
2140
2278
|
>>> plt.legend()
|
2141
2279
|
"""
|
2142
|
-
|
2143
|
-
|
2144
|
-
|
2145
|
-
|
2280
|
+
xp = array_namespace(a, b)
|
2281
|
+
|
2282
|
+
a, b = map(xp.asarray, (a, b))
|
2283
|
+
a, b = xp_promote(a, b, force_floating=True, xp=xp)
|
2284
|
+
a = xpx.atleast_nd(a, ndim=1, xp=xp)
|
2285
|
+
b = xpx.atleast_nd(b, ndim=1, xp=xp)
|
2286
|
+
|
2287
|
+
D = a.shape[0] - 1
|
2288
|
+
N = b.shape[0] - 1
|
2146
2289
|
M = max([N, D])
|
2147
2290
|
Np = M + M
|
2148
2291
|
Dp = M + M
|
2149
|
-
bprime =
|
2150
|
-
aprime =
|
2292
|
+
bprime = xp.empty(Np + 1, dtype=b.dtype)
|
2293
|
+
aprime = xp.empty(Dp + 1, dtype=a.dtype)
|
2151
2294
|
wosq = wo * wo
|
2152
2295
|
for j in range(Np + 1):
|
2153
2296
|
val = 0.0
|
@@ -2170,88 +2313,141 @@ def lp2bs(b, a, wo=1.0, bw=1.0):
|
|
2170
2313
|
|
2171
2314
|
|
2172
2315
|
def bilinear(b, a, fs=1.0):
|
2173
|
-
r"""
|
2174
|
-
|
2175
|
-
|
2176
|
-
Transform a set of poles and zeros from the analog s-plane to the digital
|
2177
|
-
z-plane using Tustin's method, which substitutes ``2*fs*(z-1) / (z+1)`` for
|
2178
|
-
``s``, maintaining the shape of the frequency response.
|
2316
|
+
r"""Calculate a digital IIR filter from an analog transfer function by utilizing
|
2317
|
+
the bilinear transform.
|
2179
2318
|
|
2180
2319
|
Parameters
|
2181
2320
|
----------
|
2182
2321
|
b : array_like
|
2183
|
-
|
2322
|
+
Coefficients of the numerator polynomial of the analog transfer function in
|
2323
|
+
form of a complex- or real-valued 1d array.
|
2184
2324
|
a : array_like
|
2185
|
-
|
2325
|
+
Coefficients of the denominator polynomial of the analog transfer function in
|
2326
|
+
form of a complex- or real-valued 1d array.
|
2186
2327
|
fs : float
|
2187
|
-
Sample rate, as ordinary frequency (e.g., hertz). No
|
2328
|
+
Sample rate, as ordinary frequency (e.g., hertz). No pre-warping is
|
2188
2329
|
done in this function.
|
2189
2330
|
|
2190
2331
|
Returns
|
2191
2332
|
-------
|
2192
|
-
|
2193
|
-
|
2194
|
-
|
2195
|
-
|
2333
|
+
beta : ndarray
|
2334
|
+
Coefficients of the numerator polynomial of the digital transfer function in
|
2335
|
+
form of a complex- or real-valued 1d array.
|
2336
|
+
alpha : ndarray
|
2337
|
+
Coefficients of the denominator polynomial of the digital transfer function in
|
2338
|
+
form of a complex- or real-valued 1d array.
|
2339
|
+
|
2340
|
+
Notes
|
2341
|
+
-----
|
2342
|
+
The parameters :math:`b = [b_0, \ldots, b_Q]` and :math:`a = [a_0, \ldots, a_P]`
|
2343
|
+
are 1d arrays of length :math:`Q+1` and :math:`P+1`. They define the analog
|
2344
|
+
transfer function
|
2345
|
+
|
2346
|
+
.. math::
|
2347
|
+
|
2348
|
+
H_a(s) = \frac{b_0 s^Q + b_1 s^{Q-1} + \cdots + b_Q}{
|
2349
|
+
a_0 s^P + a_1 s^{P-1} + \cdots + a_P}\ .
|
2350
|
+
|
2351
|
+
The bilinear transform [1]_ is applied by substituting
|
2352
|
+
|
2353
|
+
.. math::
|
2354
|
+
|
2355
|
+
s = \kappa \frac{z-1}{z+1}\ , \qquad \kappa := 2 f_s\ ,
|
2356
|
+
|
2357
|
+
into :math:`H_a(s)`, with :math:`f_s` being the sampling rate.
|
2358
|
+
This results in the digital transfer function in the :math:`z`-domain
|
2359
|
+
|
2360
|
+
.. math::
|
2361
|
+
|
2362
|
+
H_d(z) = \frac{b_0 \left(\kappa \frac{z-1}{z+1}\right)^Q +
|
2363
|
+
b_1 \left(\kappa \frac{z-1}{z+1}\right)^{Q-1} +
|
2364
|
+
\cdots + b_Q}{
|
2365
|
+
a_0 \left(\kappa \frac{z-1}{z+1}\right)^P +
|
2366
|
+
a_1 \left(\kappa \frac{z-1}{z+1}\right)^{P-1} +
|
2367
|
+
\cdots + a_P}\ .
|
2368
|
+
|
2369
|
+
This expression can be simplified by multiplying numerator and denominator by
|
2370
|
+
:math:`(z+1)^N`, with :math:`N=\max(P, Q)`. This allows :math:`H_d(z)` to be
|
2371
|
+
reformulated as
|
2372
|
+
|
2373
|
+
.. math::
|
2374
|
+
|
2375
|
+
& & \frac{b_0 \big(\kappa (z-1)\big)^Q (z+1)^{N-Q} +
|
2376
|
+
b_1 \big(\kappa (z-1)\big)^{Q-1} (z+1)^{N-Q+1} +
|
2377
|
+
\cdots + b_Q(z+1)^N}{
|
2378
|
+
a_0 \big(\kappa (z-1)\big)^P (z+1)^{N-P} +
|
2379
|
+
a_1 \big(\kappa (z-1)\big)^{P-1} (z+1)^{N-P+1} +
|
2380
|
+
\cdots + a_P(z+1)^N}\\
|
2381
|
+
&=:& \frac{\beta_0 + \beta_1 z^{-1} + \cdots + \beta_N z^{-N}}{
|
2382
|
+
\alpha_0 + \alpha_1 z^{-1} + \cdots + \alpha_N z^{-N}}\ .
|
2383
|
+
|
2384
|
+
|
2385
|
+
This is the equation implemented to perform the bilinear transform. Note that for
|
2386
|
+
large :math:`f_s`, :math:`\kappa^Q` or :math:`\kappa^P` can cause a numeric
|
2387
|
+
overflow for sufficiently large :math:`P` or :math:`Q`.
|
2388
|
+
|
2389
|
+
References
|
2390
|
+
----------
|
2391
|
+
.. [1] "Bilinear Transform", Wikipedia,
|
2392
|
+
https://en.wikipedia.org/wiki/Bilinear_transform
|
2196
2393
|
|
2197
2394
|
See Also
|
2198
2395
|
--------
|
2199
|
-
lp2lp, lp2hp, lp2bp, lp2bs
|
2200
|
-
bilinear_zpk
|
2396
|
+
lp2lp, lp2hp, lp2bp, lp2bs, bilinear_zpk
|
2201
2397
|
|
2202
2398
|
Examples
|
2203
2399
|
--------
|
2400
|
+
The following example shows the frequency response of an analog bandpass filter and
|
2401
|
+
the corresponding digital filter derived by utilitzing the bilinear transform:
|
2402
|
+
|
2204
2403
|
>>> from scipy import signal
|
2205
2404
|
>>> import matplotlib.pyplot as plt
|
2206
2405
|
>>> import numpy as np
|
2406
|
+
...
|
2407
|
+
>>> fs = 100 # sampling frequency
|
2408
|
+
>>> om_c = 2 * np.pi * np.array([7, 13]) # corner frequencies
|
2409
|
+
>>> bb_s, aa_s = signal.butter(4, om_c, btype='bandpass', analog=True, output='ba')
|
2410
|
+
>>> bb_z, aa_z = signal.bilinear(bb_s, aa_s, fs)
|
2411
|
+
...
|
2412
|
+
>>> w_z, H_z = signal.freqz(bb_z, aa_z) # frequency response of digitial filter
|
2413
|
+
>>> w_s, H_s = signal.freqs(bb_s, aa_s, worN=w_z*fs) # analog filter response
|
2414
|
+
...
|
2415
|
+
>>> f_z, f_s = w_z * fs / (2*np.pi), w_s / (2*np.pi)
|
2416
|
+
>>> Hz_dB, Hs_dB = (20*np.log10(np.abs(H_).clip(1e-10)) for H_ in (H_z, H_s))
|
2417
|
+
>>> fg0, ax0 = plt.subplots()
|
2418
|
+
>>> ax0.set_title("Frequency Response of 4-th order Bandpass Filter")
|
2419
|
+
>>> ax0.set(xlabel='Frequency $f$ in Hertz', ylabel='Magnitude in dB',
|
2420
|
+
... xlim=[f_z[1], fs/2], ylim=[-200, 2])
|
2421
|
+
>>> ax0.semilogx(f_z, Hz_dB, alpha=.5, label=r'$|H_z(e^{j 2 \pi f})|$')
|
2422
|
+
>>> ax0.semilogx(f_s, Hs_dB, alpha=.5, label=r'$|H_s(j 2 \pi f)|$')
|
2423
|
+
>>> ax0.legend()
|
2424
|
+
>>> ax0.grid(which='both', axis='x')
|
2425
|
+
>>> ax0.grid(which='major', axis='y')
|
2426
|
+
>>> plt.show()
|
2207
2427
|
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
... analog=True))
|
2212
|
-
>>> filtz = signal.lti(*signal.bilinear(filts.num, filts.den, fs))
|
2213
|
-
>>> wz, hz = signal.freqz(filtz.num, filtz.den)
|
2214
|
-
>>> ws, hs = signal.freqs(filts.num, filts.den, worN=fs*wz)
|
2215
|
-
|
2216
|
-
>>> plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hz).clip(1e-15)),
|
2217
|
-
... label=r'$|H_z(e^{j \omega})|$')
|
2218
|
-
>>> plt.semilogx(wz*fs/(2*np.pi), 20*np.log10(np.abs(hs).clip(1e-15)),
|
2219
|
-
... label=r'$|H(j \omega)|$')
|
2220
|
-
>>> plt.legend()
|
2221
|
-
>>> plt.xlabel('Frequency [Hz]')
|
2222
|
-
>>> plt.ylabel('Amplitude [dB]')
|
2223
|
-
>>> plt.grid(True)
|
2428
|
+
The difference in the higher frequencies shown in the plot is caused by an effect
|
2429
|
+
called "frequency warping". [1]_ describes a method called "pre-warping" to
|
2430
|
+
reduce those deviations.
|
2224
2431
|
"""
|
2432
|
+
b, a = np.atleast_1d(b), np.atleast_1d(a) # convert scalars, if needed
|
2433
|
+
if not a.ndim == 1:
|
2434
|
+
raise ValueError(f"Parameter a is not a 1d array since {a.shape=}")
|
2435
|
+
if not b.ndim == 1:
|
2436
|
+
raise ValueError(f"Parameter b is not a 1d array since {b.shape=}")
|
2437
|
+
b, a = np.trim_zeros(b, 'f'), np.trim_zeros(a, 'f') # remove leading zeros
|
2225
2438
|
fs = _validate_fs(fs, allow_none=False)
|
2226
|
-
a, b = map(atleast_1d, (a, b))
|
2227
|
-
D = len(a) - 1
|
2228
|
-
N = len(b) - 1
|
2229
|
-
artype = float
|
2230
|
-
M = max([N, D])
|
2231
|
-
Np = M
|
2232
|
-
Dp = M
|
2233
|
-
bprime = np.empty(Np + 1, artype)
|
2234
|
-
aprime = np.empty(Dp + 1, artype)
|
2235
|
-
for j in range(Np + 1):
|
2236
|
-
val = 0.0
|
2237
|
-
for i in range(N + 1):
|
2238
|
-
for k in range(i + 1):
|
2239
|
-
for l in range(M - i + 1):
|
2240
|
-
if k + l == j:
|
2241
|
-
val += (comb(i, k) * comb(M - i, l) * b[N - i] *
|
2242
|
-
pow(2 * fs, i) * (-1) ** k)
|
2243
|
-
bprime[j] = real(val)
|
2244
|
-
for j in range(Dp + 1):
|
2245
|
-
val = 0.0
|
2246
|
-
for i in range(D + 1):
|
2247
|
-
for k in range(i + 1):
|
2248
|
-
for l in range(M - i + 1):
|
2249
|
-
if k + l == j:
|
2250
|
-
val += (comb(i, k) * comb(M - i, l) * a[D - i] *
|
2251
|
-
pow(2 * fs, i) * (-1) ** k)
|
2252
|
-
aprime[j] = real(val)
|
2253
2439
|
|
2254
|
-
|
2440
|
+
# Splitting the factor fs*2 between numerator and denominator reduces the chance of
|
2441
|
+
# numeric overflow for large fs and large N:
|
2442
|
+
fac = np.sqrt(fs*2)
|
2443
|
+
zp1 = np.polynomial.Polynomial((+1, 1)) / fac # Polynomial (z + 1) / fac
|
2444
|
+
zm1 = np.polynomial.Polynomial((-1, 1)) * fac # Polynomial (z - 1) * fac
|
2445
|
+
# Note that NumPy's Polynomial coefficient order is backward compared to a and b.
|
2446
|
+
|
2447
|
+
N = max(len(a), len(b)) - 1
|
2448
|
+
numerator = sum(b_ * zp1**(N-q) * zm1**q for q, b_ in enumerate(b[::-1]))
|
2449
|
+
denominator = sum(a_ * zp1**(N-p) * zm1**p for p, a_ in enumerate(a[::-1]))
|
2450
|
+
return normalize(numerator.coef[::-1], denominator.coef[::-1])
|
2255
2451
|
|
2256
2452
|
|
2257
2453
|
def _validate_gpass_gstop(gpass, gstop):
|
@@ -2685,7 +2881,7 @@ def _relative_degree(z, p):
|
|
2685
2881
|
"""
|
2686
2882
|
Return relative degree of transfer function from zeros and poles
|
2687
2883
|
"""
|
2688
|
-
degree =
|
2884
|
+
degree = p.shape[0] - z.shape[0]
|
2689
2885
|
if degree < 0:
|
2690
2886
|
raise ValueError("Improper transfer function. "
|
2691
2887
|
"Must have at least as many poles as zeros.")
|
@@ -2755,8 +2951,11 @@ def bilinear_zpk(z, p, k, fs):
|
|
2755
2951
|
>>> plt.ylabel('Amplitude [dB]')
|
2756
2952
|
>>> plt.grid(True)
|
2757
2953
|
"""
|
2758
|
-
|
2759
|
-
|
2954
|
+
xp = array_namespace(z, p)
|
2955
|
+
|
2956
|
+
z, p = map(xp.asarray, (z, p))
|
2957
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
2958
|
+
p = xpx.atleast_nd(p, ndim=1, xp=xp)
|
2760
2959
|
|
2761
2960
|
fs = _validate_fs(fs, allow_none=False)
|
2762
2961
|
|
@@ -2769,10 +2968,10 @@ def bilinear_zpk(z, p, k, fs):
|
|
2769
2968
|
p_z = (fs2 + p) / (fs2 - p)
|
2770
2969
|
|
2771
2970
|
# Any zeros that were at infinity get moved to the Nyquist frequency
|
2772
|
-
z_z =
|
2971
|
+
z_z = xp.concat((z_z, -xp.ones(degree)))
|
2773
2972
|
|
2774
2973
|
# Compensate for gain change
|
2775
|
-
k_z = k * real(prod(fs2 - z) / prod(fs2 - p))
|
2974
|
+
k_z = k * xp.real(xp.prod(fs2 - z) / xp.prod(fs2 - p))
|
2776
2975
|
|
2777
2976
|
return z_z, p_z, k_z
|
2778
2977
|
|
@@ -2832,8 +3031,12 @@ def lp2lp_zpk(z, p, k, wo=1.0):
|
|
2832
3031
|
>>> lp2lp_zpk(z, p, k, wo)
|
2833
3032
|
( array([2.8, 0.8]), array([2. , 5.2]), 0.8)
|
2834
3033
|
"""
|
2835
|
-
|
2836
|
-
|
3034
|
+
xp = array_namespace(z, p)
|
3035
|
+
|
3036
|
+
z, p = map(xp.asarray, (z, p))
|
3037
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
3038
|
+
p = xpx.atleast_nd(p, ndim=1, xp=xp)
|
3039
|
+
|
2837
3040
|
wo = float(wo) # Avoid int wraparound
|
2838
3041
|
|
2839
3042
|
degree = _relative_degree(z, p)
|
@@ -2909,8 +3112,13 @@ def lp2hp_zpk(z, p, k, wo=1.0):
|
|
2909
3112
|
array([-0.6 , -0.15]),
|
2910
3113
|
8.5)
|
2911
3114
|
"""
|
2912
|
-
|
2913
|
-
|
3115
|
+
xp = array_namespace(z, p)
|
3116
|
+
|
3117
|
+
z, p = map(xp.asarray, (z, p))
|
3118
|
+
# XXX: no xp_promote here since that breaks TestButter
|
3119
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
3120
|
+
p = xpx.atleast_nd(p, ndim=1, xp=xp)
|
3121
|
+
|
2914
3122
|
wo = float(wo)
|
2915
3123
|
|
2916
3124
|
degree = _relative_degree(z, p)
|
@@ -2921,10 +3129,10 @@ def lp2hp_zpk(z, p, k, wo=1.0):
|
|
2921
3129
|
p_hp = wo / p
|
2922
3130
|
|
2923
3131
|
# If lowpass had zeros at infinity, inverting moves them to origin.
|
2924
|
-
z_hp =
|
3132
|
+
z_hp = xp.concat((z_hp, xp.zeros(degree)))
|
2925
3133
|
|
2926
3134
|
# Cancel out gain change caused by inversion
|
2927
|
-
k_hp = k * real(prod(-z) / prod(-p))
|
3135
|
+
k_hp = k * xp.real(xp.prod(-z) / xp.prod(-p))
|
2928
3136
|
|
2929
3137
|
return z_hp, p_hp, k_hp
|
2930
3138
|
|
@@ -2995,8 +3203,13 @@ def lp2bp_zpk(z, p, k, wo=1.0, bw=1.0):
|
|
2995
3203
|
array([1.04996339e+02+0.j, -1.60167736e-03+0.j, 3.66108003e-03+0.j,
|
2996
3204
|
-2.39998398e+02+0.j]), 0.8)
|
2997
3205
|
"""
|
2998
|
-
|
2999
|
-
|
3206
|
+
xp = array_namespace(z, p)
|
3207
|
+
|
3208
|
+
z, p = map(xp.asarray, (z, p))
|
3209
|
+
z, p = xp_promote(z, p, force_floating=True, xp=xp)
|
3210
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
3211
|
+
p = xpx.atleast_nd(p, ndim=1, xp=xp)
|
3212
|
+
|
3000
3213
|
wo = float(wo)
|
3001
3214
|
bw = float(bw)
|
3002
3215
|
|
@@ -3007,17 +3220,17 @@ def lp2bp_zpk(z, p, k, wo=1.0, bw=1.0):
|
|
3007
3220
|
p_lp = p * bw/2
|
3008
3221
|
|
3009
3222
|
# Square root needs to produce complex result, not NaN
|
3010
|
-
z_lp =
|
3011
|
-
p_lp =
|
3223
|
+
z_lp = xp.astype(z_lp, xp.complex128)
|
3224
|
+
p_lp = xp.astype(p_lp, xp.complex128)
|
3012
3225
|
|
3013
3226
|
# Duplicate poles and zeros and shift from baseband to +wo and -wo
|
3014
|
-
z_bp =
|
3015
|
-
|
3016
|
-
p_bp =
|
3017
|
-
|
3227
|
+
z_bp = xp.concat((z_lp + xp.sqrt(z_lp**2 - wo**2),
|
3228
|
+
z_lp - xp.sqrt(z_lp**2 - wo**2)))
|
3229
|
+
p_bp = xp.concat((p_lp + xp.sqrt(p_lp**2 - wo**2),
|
3230
|
+
p_lp - xp.sqrt(p_lp**2 - wo**2)))
|
3018
3231
|
|
3019
3232
|
# Move degree zeros to origin, leaving degree zeros at infinity for BPF
|
3020
|
-
z_bp =
|
3233
|
+
z_bp = xp.concat((z_bp, xp.zeros(degree)))
|
3021
3234
|
|
3022
3235
|
# Cancel out gain change from frequency scaling
|
3023
3236
|
k_bp = k * bw**degree
|
@@ -3090,8 +3303,13 @@ def lp2bs_zpk(z, p, k, wo=1.0, bw=1.0):
|
|
3090
3303
|
array([14.2681928 +0.j, -0.02506281+0.j, 0.01752149+0.j, -9.97493719+0.j]),
|
3091
3304
|
-12.857142857142858)
|
3092
3305
|
"""
|
3093
|
-
|
3094
|
-
|
3306
|
+
xp = array_namespace(z, p)
|
3307
|
+
|
3308
|
+
z, p = map(xp.asarray, (z, p))
|
3309
|
+
z, p = xp_promote(z, p, force_floating=True, xp=xp)
|
3310
|
+
z = xpx.atleast_nd(z, ndim=1, xp=xp)
|
3311
|
+
p = xpx.atleast_nd(p, ndim=1, xp=xp)
|
3312
|
+
|
3095
3313
|
wo = float(wo)
|
3096
3314
|
bw = float(bw)
|
3097
3315
|
|
@@ -3102,21 +3320,21 @@ def lp2bs_zpk(z, p, k, wo=1.0, bw=1.0):
|
|
3102
3320
|
p_hp = (bw/2) / p
|
3103
3321
|
|
3104
3322
|
# Square root needs to produce complex result, not NaN
|
3105
|
-
z_hp =
|
3106
|
-
p_hp =
|
3323
|
+
z_hp = xp.astype(z_hp, xp.complex128)
|
3324
|
+
p_hp = xp.astype(p_hp, xp.complex128)
|
3107
3325
|
|
3108
3326
|
# Duplicate poles and zeros and shift from baseband to +wo and -wo
|
3109
|
-
z_bs =
|
3110
|
-
|
3111
|
-
p_bs =
|
3112
|
-
|
3327
|
+
z_bs = xp.concat((z_hp + xp.sqrt(z_hp**2 - wo**2),
|
3328
|
+
z_hp - xp.sqrt(z_hp**2 - wo**2)))
|
3329
|
+
p_bs = xp.concat((p_hp + xp.sqrt(p_hp**2 - wo**2),
|
3330
|
+
p_hp - xp.sqrt(p_hp**2 - wo**2)))
|
3113
3331
|
|
3114
3332
|
# Move any zeros that were at infinity to the center of the stopband
|
3115
|
-
z_bs =
|
3116
|
-
z_bs =
|
3333
|
+
z_bs = xp.concat((z_bs, xp.full(degree, +1j*wo)))
|
3334
|
+
z_bs = xp.concat((z_bs, xp.full(degree, -1j*wo)))
|
3117
3335
|
|
3118
3336
|
# Cancel out gain change caused by inversion
|
3119
|
-
k_bs = k * real(prod(-z) / prod(-p))
|
3337
|
+
k_bs = k * xp.real(xp.prod(-z) / xp.prod(-p))
|
3120
3338
|
|
3121
3339
|
return z_bs, p_bs, k_bs
|
3122
3340
|
|