anonymous-CAP commited on
Commit
d2f6bdd
·
verified ·
1 Parent(s): eb0a62b

Upload folder using huggingface_hub (part 18)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +78 -0
  2. venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/INSTALLER +1 -0
  3. venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/LICENSE.txt +937 -0
  4. venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/METADATA +1091 -0
  5. venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/RECORD +0 -0
  6. venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/WHEEL +6 -0
  7. venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/sboms/auditwheel.cdx.json +1 -0
  8. venv/lib/python3.12/site-packages/scipy.libs/libgfortran-040039e1-0352e75f.so.5.0.0 +3 -0
  9. venv/lib/python3.12/site-packages/scipy.libs/libgfortran-83c28eba.so.5.0.0 +3 -0
  10. venv/lib/python3.12/site-packages/scipy.libs/libquadmath-2284e583.so.0.0.0 +3 -0
  11. venv/lib/python3.12/site-packages/scipy.libs/libquadmath-96973f99-934c22de.so.0.0.0 +3 -0
  12. venv/lib/python3.12/site-packages/scipy.libs/libscipy_openblas-5f890258.so +3 -0
  13. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_filter_design.cpython-312.pyc +3 -0
  14. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_fir_filter_design.cpython-312.pyc +0 -0
  15. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_lti_conversion.cpython-312.pyc +0 -0
  16. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_ltisys.cpython-312.pyc +3 -0
  17. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_max_len_seq.cpython-312.pyc +0 -0
  18. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_peak_finding.cpython-312.pyc +0 -0
  19. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_polyutils.cpython-312.pyc +0 -0
  20. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_savitzky_golay.cpython-312.pyc +0 -0
  21. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_short_time_fft.cpython-312.pyc +3 -0
  22. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_signal_api.cpython-312.pyc +0 -0
  23. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_signaltools.cpython-312.pyc +3 -0
  24. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_spectral_py.cpython-312.pyc +3 -0
  25. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_spline_filters.cpython-312.pyc +0 -0
  26. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_support_alternative_backends.cpython-312.pyc +0 -0
  27. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_upfirdn.cpython-312.pyc +0 -0
  28. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_waveforms.cpython-312.pyc +0 -0
  29. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_wavelets.cpython-312.pyc +0 -0
  30. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_whittaker.cpython-312.pyc +0 -0
  31. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/bsplines.cpython-312.pyc +0 -0
  32. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/filter_design.cpython-312.pyc +0 -0
  33. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/fir_filter_design.cpython-312.pyc +0 -0
  34. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/lti_conversion.cpython-312.pyc +0 -0
  35. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/ltisys.cpython-312.pyc +0 -0
  36. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/signaltools.cpython-312.pyc +0 -0
  37. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/spectral.cpython-312.pyc +0 -0
  38. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/spline.cpython-312.pyc +0 -0
  39. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/waveforms.cpython-312.pyc +0 -0
  40. venv/lib/python3.12/site-packages/scipy/signal/__pycache__/wavelets.cpython-312.pyc +0 -0
  41. venv/lib/python3.12/site-packages/scipy/signal/_arraytools.py +264 -0
  42. venv/lib/python3.12/site-packages/scipy/signal/_czt.py +580 -0
  43. venv/lib/python3.12/site-packages/scipy/signal/_delegators.py +573 -0
  44. venv/lib/python3.12/site-packages/scipy/signal/_filter_design.py +0 -0
  45. venv/lib/python3.12/site-packages/scipy/signal/_fir_filter_design.py +1553 -0
  46. venv/lib/python3.12/site-packages/scipy/signal/_lti_conversion.py +583 -0
  47. venv/lib/python3.12/site-packages/scipy/signal/_ltisys.py +0 -0
  48. venv/lib/python3.12/site-packages/scipy/signal/_max_len_seq.py +139 -0
  49. venv/lib/python3.12/site-packages/scipy/signal/_max_len_seq_inner.cpython-312-x86_64-linux-gnu.so +0 -0
  50. venv/lib/python3.12/site-packages/scipy/signal/_peak_finding.py +1307 -0
.gitattributes CHANGED
@@ -302,3 +302,81 @@ venv/lib/python3.12/site-packages/scipy/optimize/_trlib/_trlib.cpython-312-x86_6
302
  venv/lib/python3.12/site-packages/scipy/optimize/cython_optimize/_zeros.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
303
  venv/lib/python3.12/site-packages/scipy/optimize/tests/__pycache__/test_linprog.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
304
  venv/lib/python3.12/site-packages/scipy/optimize/tests/__pycache__/test_optimize.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
302
  venv/lib/python3.12/site-packages/scipy/optimize/cython_optimize/_zeros.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
303
  venv/lib/python3.12/site-packages/scipy/optimize/tests/__pycache__/test_linprog.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
304
  venv/lib/python3.12/site-packages/scipy/optimize/tests/__pycache__/test_optimize.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
305
+ venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_filter_design.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
306
+ venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_ltisys.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
307
+ venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_short_time_fft.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
308
+ venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_signaltools.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
309
+ venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_spectral_py.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
310
+ venv/lib/python3.12/site-packages/scipy/signal/_peak_finding_utils.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
311
+ venv/lib/python3.12/site-packages/scipy/signal/_sigtools.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
312
+ venv/lib/python3.12/site-packages/scipy/signal/_sosfilt.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
313
+ venv/lib/python3.12/site-packages/scipy/signal/_upfirdn_apply.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
314
+ venv/lib/python3.12/site-packages/scipy/signal/tests/__pycache__/test_filter_design.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
315
+ venv/lib/python3.12/site-packages/scipy/signal/tests/__pycache__/test_signaltools.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
316
+ venv/lib/python3.12/site-packages/scipy/signal/tests/__pycache__/test_spectral.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
317
+ venv/lib/python3.12/site-packages/scipy/signal/windows/__pycache__/_windows.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
318
+ venv/lib/python3.12/site-packages/scipy/sparse/_csparsetools.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
319
+ venv/lib/python3.12/site-packages/scipy/sparse/_sparsetools.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
320
+ venv/lib/python3.12/site-packages/scipy/sparse/csgraph/_flow.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
321
+ venv/lib/python3.12/site-packages/scipy/sparse/csgraph/_matching.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
322
+ venv/lib/python3.12/site-packages/scipy/sparse/csgraph/_reordering.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
323
+ venv/lib/python3.12/site-packages/scipy/sparse/csgraph/_shortest_path.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
324
+ venv/lib/python3.12/site-packages/scipy/sparse/csgraph/_tools.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
325
+ venv/lib/python3.12/site-packages/scipy/sparse/csgraph/_traversal.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
326
+ venv/lib/python3.12/site-packages/scipy/sparse/linalg/_dsolve/_superlu.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
327
+ venv/lib/python3.12/site-packages/scipy/sparse/linalg/_eigen/arpack/_arpacklib.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
328
+ venv/lib/python3.12/site-packages/scipy/sparse/linalg/_propack.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
329
+ venv/lib/python3.12/site-packages/scipy/sparse/tests/__pycache__/test_base.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
330
+ venv/lib/python3.12/site-packages/scipy/spatial/__pycache__/distance.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
331
+ venv/lib/python3.12/site-packages/scipy/spatial/_ckdtree.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
332
+ venv/lib/python3.12/site-packages/scipy/spatial/_distance_pybind.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
333
+ venv/lib/python3.12/site-packages/scipy/spatial/_distance_wrap.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
334
+ venv/lib/python3.12/site-packages/scipy/spatial/_qhull.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
335
+ venv/lib/python3.12/site-packages/scipy/spatial/tests/__pycache__/test_distance.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
336
+ venv/lib/python3.12/site-packages/scipy/spatial/tests/__pycache__/test_kdtree.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
337
+ venv/lib/python3.12/site-packages/scipy/spatial/transform/__pycache__/_rotation.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
338
+ venv/lib/python3.12/site-packages/scipy/spatial/transform/_rigid_transform_cy.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
339
+ venv/lib/python3.12/site-packages/scipy/spatial/transform/_rotation_cy.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
340
+ venv/lib/python3.12/site-packages/scipy/spatial/transform/tests/__pycache__/test_rotation.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
341
+ venv/lib/python3.12/site-packages/scipy/special/__pycache__/_add_newdocs.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
342
+ venv/lib/python3.12/site-packages/scipy/special/__pycache__/_basic.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
343
+ venv/lib/python3.12/site-packages/scipy/special/_ellip_harm_2.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
344
+ venv/lib/python3.12/site-packages/scipy/special/_gufuncs.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
345
+ venv/lib/python3.12/site-packages/scipy/special/_specfun.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
346
+ venv/lib/python3.12/site-packages/scipy/special/_special_ufuncs.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
347
+ venv/lib/python3.12/site-packages/scipy/special/_ufuncs.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
348
+ venv/lib/python3.12/site-packages/scipy/special/_ufuncs_cxx.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
349
+ venv/lib/python3.12/site-packages/scipy/special/cython_special.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
350
+ venv/lib/python3.12/site-packages/scipy/special/tests/__pycache__/test_basic.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
351
+ venv/lib/python3.12/site-packages/scipy/special/tests/__pycache__/test_mpmath.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
352
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_continuous_distns.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
353
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_distn_infrastructure.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
354
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_distribution_infrastructure.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
355
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_morestats.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
356
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_mstats_basic.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
357
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_multivariate.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
358
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_qmc.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
359
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_resampling.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
360
+ venv/lib/python3.12/site-packages/scipy/stats/__pycache__/_stats_py.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
361
+ venv/lib/python3.12/site-packages/scipy/stats/_ansari_swilk_statistics.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
362
+ venv/lib/python3.12/site-packages/scipy/stats/_biasedurn.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
363
+ venv/lib/python3.12/site-packages/scipy/stats/_qmc_cy.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
364
+ venv/lib/python3.12/site-packages/scipy/stats/_qmvnt_cy.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
365
+ venv/lib/python3.12/site-packages/scipy/stats/_rcont/rcont.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
366
+ venv/lib/python3.12/site-packages/scipy/stats/_sobol.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
367
+ venv/lib/python3.12/site-packages/scipy/stats/_stats.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
368
+ venv/lib/python3.12/site-packages/scipy/stats/_stats_pythran.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
369
+ venv/lib/python3.12/site-packages/scipy/stats/_unuran/unuran_wrapper.cpython-312-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text
370
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_continuous.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
371
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_distributions.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
372
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_hypotests.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
373
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_morestats.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
374
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_mstats_basic.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
375
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_multivariate.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
376
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_resampling.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
377
+ venv/lib/python3.12/site-packages/scipy/stats/tests/__pycache__/test_stats.cpython-312.pyc filter=lfs diff=lfs merge=lfs -text
378
+ venv/lib/python3.12/site-packages/scipy.libs/libgfortran-040039e1-0352e75f.so.5.0.0 filter=lfs diff=lfs merge=lfs -text
379
+ venv/lib/python3.12/site-packages/scipy.libs/libgfortran-83c28eba.so.5.0.0 filter=lfs diff=lfs merge=lfs -text
380
+ venv/lib/python3.12/site-packages/scipy.libs/libquadmath-2284e583.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
381
+ venv/lib/python3.12/site-packages/scipy.libs/libquadmath-96973f99-934c22de.so.0.0.0 filter=lfs diff=lfs merge=lfs -text
382
+ venv/lib/python3.12/site-packages/scipy.libs/libscipy_openblas-5f890258.so filter=lfs diff=lfs merge=lfs -text
venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/INSTALLER ADDED
@@ -0,0 +1 @@
 
 
1
+ pip
venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/LICENSE.txt ADDED
@@ -0,0 +1,937 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Copyright (c) 2001-2002 Enthought, Inc. 2003, SciPy Developers.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above
12
+ copyright notice, this list of conditions and the following
13
+ disclaimer in the documentation and/or other materials provided
14
+ with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived
18
+ from this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
+
32
+ ----
33
+
34
+
35
+ ----
36
+
37
+ This binary distribution of SciPy can also bundle the following software
38
+ (depending on the build):
39
+
40
+
41
+ Name: OpenBLAS
42
+ Files: scipy.libs/libscipy_openblas*.so
43
+ Description: bundled as a dynamically linked library
44
+ Availability: https://github.com/OpenMathLib/OpenBLAS/
45
+ License: BSD-3-Clause
46
+ Copyright (c) 2011-2014, The OpenBLAS Project
47
+ All rights reserved.
48
+
49
+ Redistribution and use in source and binary forms, with or without
50
+ modification, are permitted provided that the following conditions are
51
+ met:
52
+
53
+ 1. Redistributions of source code must retain the above copyright
54
+ notice, this list of conditions and the following disclaimer.
55
+
56
+ 2. Redistributions in binary form must reproduce the above copyright
57
+ notice, this list of conditions and the following disclaimer in
58
+ the documentation and/or other materials provided with the
59
+ distribution.
60
+ 3. Neither the name of the OpenBLAS project nor the names of
61
+ its contributors may be used to endorse or promote products
62
+ derived from this software without specific prior written
63
+ permission.
64
+
65
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
66
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
69
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
70
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
71
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
72
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
73
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
74
+ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
75
+
76
+
77
+ Name: LAPACK
78
+ Files: scipy.libs/libscipy_openblas*.so
79
+ Description: bundled in OpenBLAS
80
+ Availability: https://github.com/OpenMathLib/OpenBLAS/
81
+ License: BSD-3-Clause-Open-MPI
82
+ Copyright (c) 1992-2013 The University of Tennessee and The University
83
+ of Tennessee Research Foundation. All rights
84
+ reserved.
85
+ Copyright (c) 2000-2013 The University of California Berkeley. All
86
+ rights reserved.
87
+ Copyright (c) 2006-2013 The University of Colorado Denver. All rights
88
+ reserved.
89
+
90
+ $COPYRIGHT$
91
+
92
+ Additional copyrights may follow
93
+
94
+ $HEADER$
95
+
96
+ Redistribution and use in source and binary forms, with or without
97
+ modification, are permitted provided that the following conditions are
98
+ met:
99
+
100
+ - Redistributions of source code must retain the above copyright
101
+ notice, this list of conditions and the following disclaimer.
102
+
103
+ - Redistributions in binary form must reproduce the above copyright
104
+ notice, this list of conditions and the following disclaimer listed
105
+ in this license in the documentation and/or other materials
106
+ provided with the distribution.
107
+
108
+ - Neither the name of the copyright holders nor the names of its
109
+ contributors may be used to endorse or promote products derived from
110
+ this software without specific prior written permission.
111
+
112
+ The copyright holders provide no reassurances that the source code
113
+ provided does not infringe any patent, copyright, or any other
114
+ intellectual property rights of third parties. The copyright holders
115
+ disclaim any liability to any recipient for claims brought against
116
+ recipient by any third party for infringement of that parties
117
+ intellectual property rights.
118
+
119
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
120
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
121
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
122
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
123
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
124
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
125
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
126
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
127
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
128
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
129
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
130
+
131
+
132
+ Name: GCC runtime library
133
+ Files: scipy.libs/libgfortran*.so
134
+ Description: dynamically linked to files compiled with gcc
135
+ Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libgfortran
136
+ License: GPL-3.0-or-later WITH GCC-exception-3.1
137
+ Copyright (C) 2002-2017 Free Software Foundation, Inc.
138
+
139
+ Libgfortran is free software; you can redistribute it and/or modify
140
+ it under the terms of the GNU General Public License as published by
141
+ the Free Software Foundation; either version 3, or (at your option)
142
+ any later version.
143
+
144
+ Libgfortran is distributed in the hope that it will be useful,
145
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
146
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
147
+ GNU General Public License for more details.
148
+
149
+ Under Section 7 of GPL version 3, you are granted additional
150
+ permissions described in the GCC Runtime Library Exception, version
151
+ 3.1, as published by the Free Software Foundation.
152
+
153
+ You should have received a copy of the GNU General Public License and
154
+ a copy of the GCC Runtime Library Exception along with this program;
155
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
156
+ <http://www.gnu.org/licenses/>.
157
+
158
+ ----
159
+
160
+ Full text of license texts referred to above follows (that they are
161
+ listed below does not necessarily imply the conditions apply to the
162
+ present binary release):
163
+
164
+ ----
165
+
166
+ GCC RUNTIME LIBRARY EXCEPTION
167
+
168
+ Version 3.1, 31 March 2009
169
+
170
+ Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/>
171
+
172
+ Everyone is permitted to copy and distribute verbatim copies of this
173
+ license document, but changing it is not allowed.
174
+
175
+ This GCC Runtime Library Exception ("Exception") is an additional
176
+ permission under section 7 of the GNU General Public License, version
177
+ 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that
178
+ bears a notice placed by the copyright holder of the file stating that
179
+ the file is governed by GPLv3 along with this Exception.
180
+
181
+ When you use GCC to compile a program, GCC may combine portions of
182
+ certain GCC header files and runtime libraries with the compiled
183
+ program. The purpose of this Exception is to allow compilation of
184
+ non-GPL (including proprietary) programs to use, in this way, the
185
+ header files and runtime libraries covered by this Exception.
186
+
187
+ 0. Definitions.
188
+
189
+ A file is an "Independent Module" if it either requires the Runtime
190
+ Library for execution after a Compilation Process, or makes use of an
191
+ interface provided by the Runtime Library, but is not otherwise based
192
+ on the Runtime Library.
193
+
194
+ "GCC" means a version of the GNU Compiler Collection, with or without
195
+ modifications, governed by version 3 (or a specified later version) of
196
+ the GNU General Public License (GPL) with the option of using any
197
+ subsequent versions published by the FSF.
198
+
199
+ "GPL-compatible Software" is software whose conditions of propagation,
200
+ modification and use would permit combination with GCC in accord with
201
+ the license of GCC.
202
+
203
+ "Target Code" refers to output from any compiler for a real or virtual
204
+ target processor architecture, in executable form or suitable for
205
+ input to an assembler, loader, linker and/or execution
206
+ phase. Notwithstanding that, Target Code does not include data in any
207
+ format that is used as a compiler intermediate representation, or used
208
+ for producing a compiler intermediate representation.
209
+
210
+ The "Compilation Process" transforms code entirely represented in
211
+ non-intermediate languages designed for human-written code, and/or in
212
+ Java Virtual Machine byte code, into Target Code. Thus, for example,
213
+ use of source code generators and preprocessors need not be considered
214
+ part of the Compilation Process, since the Compilation Process can be
215
+ understood as starting with the output of the generators or
216
+ preprocessors.
217
+
218
+ A Compilation Process is "Eligible" if it is done using GCC, alone or
219
+ with other GPL-compatible software, or if it is done without using any
220
+ work based on GCC. For example, using non-GPL-compatible Software to
221
+ optimize any GCC intermediate representations would not qualify as an
222
+ Eligible Compilation Process.
223
+
224
+ 1. Grant of Additional Permission.
225
+
226
+ You have permission to propagate a work of Target Code formed by
227
+ combining the Runtime Library with Independent Modules, even if such
228
+ propagation would otherwise violate the terms of GPLv3, provided that
229
+ all Target Code was generated by Eligible Compilation Processes. You
230
+ may then convey such a combination under terms of your choice,
231
+ consistent with the licensing of the Independent Modules.
232
+
233
+ 2. No Weakening of GCC Copyleft.
234
+
235
+ The availability of this Exception does not imply any general
236
+ presumption that third-party software is unaffected by the copyleft
237
+ requirements of the license of GCC.
238
+
239
+ ----
240
+
241
+ GNU GENERAL PUBLIC LICENSE
242
+ Version 3, 29 June 2007
243
+
244
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
245
+ Everyone is permitted to copy and distribute verbatim copies
246
+ of this license document, but changing it is not allowed.
247
+
248
+ Preamble
249
+
250
+ The GNU General Public License is a free, copyleft license for
251
+ software and other kinds of works.
252
+
253
+ The licenses for most software and other practical works are designed
254
+ to take away your freedom to share and change the works. By contrast,
255
+ the GNU General Public License is intended to guarantee your freedom to
256
+ share and change all versions of a program--to make sure it remains free
257
+ software for all its users. We, the Free Software Foundation, use the
258
+ GNU General Public License for most of our software; it applies also to
259
+ any other work released this way by its authors. You can apply it to
260
+ your programs, too.
261
+
262
+ When we speak of free software, we are referring to freedom, not
263
+ price. Our General Public Licenses are designed to make sure that you
264
+ have the freedom to distribute copies of free software (and charge for
265
+ them if you wish), that you receive source code or can get it if you
266
+ want it, that you can change the software or use pieces of it in new
267
+ free programs, and that you know you can do these things.
268
+
269
+ To protect your rights, we need to prevent others from denying you
270
+ these rights or asking you to surrender the rights. Therefore, you have
271
+ certain responsibilities if you distribute copies of the software, or if
272
+ you modify it: responsibilities to respect the freedom of others.
273
+
274
+ For example, if you distribute copies of such a program, whether
275
+ gratis or for a fee, you must pass on to the recipients the same
276
+ freedoms that you received. You must make sure that they, too, receive
277
+ or can get the source code. And you must show them these terms so they
278
+ know their rights.
279
+
280
+ Developers that use the GNU GPL protect your rights with two steps:
281
+ (1) assert copyright on the software, and (2) offer you this License
282
+ giving you legal permission to copy, distribute and/or modify it.
283
+
284
+ For the developers' and authors' protection, the GPL clearly explains
285
+ that there is no warranty for this free software. For both users' and
286
+ authors' sake, the GPL requires that modified versions be marked as
287
+ changed, so that their problems will not be attributed erroneously to
288
+ authors of previous versions.
289
+
290
+ Some devices are designed to deny users access to install or run
291
+ modified versions of the software inside them, although the manufacturer
292
+ can do so. This is fundamentally incompatible with the aim of
293
+ protecting users' freedom to change the software. The systematic
294
+ pattern of such abuse occurs in the area of products for individuals to
295
+ use, which is precisely where it is most unacceptable. Therefore, we
296
+ have designed this version of the GPL to prohibit the practice for those
297
+ products. If such problems arise substantially in other domains, we
298
+ stand ready to extend this provision to those domains in future versions
299
+ of the GPL, as needed to protect the freedom of users.
300
+
301
+ Finally, every program is threatened constantly by software patents.
302
+ States should not allow patents to restrict development and use of
303
+ software on general-purpose computers, but in those that do, we wish to
304
+ avoid the special danger that patents applied to a free program could
305
+ make it effectively proprietary. To prevent this, the GPL assures that
306
+ patents cannot be used to render the program non-free.
307
+
308
+ The precise terms and conditions for copying, distribution and
309
+ modification follow.
310
+
311
+ TERMS AND CONDITIONS
312
+
313
+ 0. Definitions.
314
+
315
+ "This License" refers to version 3 of the GNU General Public License.
316
+
317
+ "Copyright" also means copyright-like laws that apply to other kinds of
318
+ works, such as semiconductor masks.
319
+
320
+ "The Program" refers to any copyrightable work licensed under this
321
+ License. Each licensee is addressed as "you". "Licensees" and
322
+ "recipients" may be individuals or organizations.
323
+
324
+ To "modify" a work means to copy from or adapt all or part of the work
325
+ in a fashion requiring copyright permission, other than the making of an
326
+ exact copy. The resulting work is called a "modified version" of the
327
+ earlier work or a work "based on" the earlier work.
328
+
329
+ A "covered work" means either the unmodified Program or a work based
330
+ on the Program.
331
+
332
+ To "propagate" a work means to do anything with it that, without
333
+ permission, would make you directly or secondarily liable for
334
+ infringement under applicable copyright law, except executing it on a
335
+ computer or modifying a private copy. Propagation includes copying,
336
+ distribution (with or without modification), making available to the
337
+ public, and in some countries other activities as well.
338
+
339
+ To "convey" a work means any kind of propagation that enables other
340
+ parties to make or receive copies. Mere interaction with a user through
341
+ a computer network, with no transfer of a copy, is not conveying.
342
+
343
+ An interactive user interface displays "Appropriate Legal Notices"
344
+ to the extent that it includes a convenient and prominently visible
345
+ feature that (1) displays an appropriate copyright notice, and (2)
346
+ tells the user that there is no warranty for the work (except to the
347
+ extent that warranties are provided), that licensees may convey the
348
+ work under this License, and how to view a copy of this License. If
349
+ the interface presents a list of user commands or options, such as a
350
+ menu, a prominent item in the list meets this criterion.
351
+
352
+ 1. Source Code.
353
+
354
+ The "source code" for a work means the preferred form of the work
355
+ for making modifications to it. "Object code" means any non-source
356
+ form of a work.
357
+
358
+ A "Standard Interface" means an interface that either is an official
359
+ standard defined by a recognized standards body, or, in the case of
360
+ interfaces specified for a particular programming language, one that
361
+ is widely used among developers working in that language.
362
+
363
+ The "System Libraries" of an executable work include anything, other
364
+ than the work as a whole, that (a) is included in the normal form of
365
+ packaging a Major Component, but which is not part of that Major
366
+ Component, and (b) serves only to enable use of the work with that
367
+ Major Component, or to implement a Standard Interface for which an
368
+ implementation is available to the public in source code form. A
369
+ "Major Component", in this context, means a major essential component
370
+ (kernel, window system, and so on) of the specific operating system
371
+ (if any) on which the executable work runs, or a compiler used to
372
+ produce the work, or an object code interpreter used to run it.
373
+
374
+ The "Corresponding Source" for a work in object code form means all
375
+ the source code needed to generate, install, and (for an executable
376
+ work) run the object code and to modify the work, including scripts to
377
+ control those activities. However, it does not include the work's
378
+ System Libraries, or general-purpose tools or generally available free
379
+ programs which are used unmodified in performing those activities but
380
+ which are not part of the work. For example, Corresponding Source
381
+ includes interface definition files associated with source files for
382
+ the work, and the source code for shared libraries and dynamically
383
+ linked subprograms that the work is specifically designed to require,
384
+ such as by intimate data communication or control flow between those
385
+ subprograms and other parts of the work.
386
+
387
+ The Corresponding Source need not include anything that users
388
+ can regenerate automatically from other parts of the Corresponding
389
+ Source.
390
+
391
+ The Corresponding Source for a work in source code form is that
392
+ same work.
393
+
394
+ 2. Basic Permissions.
395
+
396
+ All rights granted under this License are granted for the term of
397
+ copyright on the Program, and are irrevocable provided the stated
398
+ conditions are met. This License explicitly affirms your unlimited
399
+ permission to run the unmodified Program. The output from running a
400
+ covered work is covered by this License only if the output, given its
401
+ content, constitutes a covered work. This License acknowledges your
402
+ rights of fair use or other equivalent, as provided by copyright law.
403
+
404
+ You may make, run and propagate covered works that you do not
405
+ convey, without conditions so long as your license otherwise remains
406
+ in force. You may convey covered works to others for the sole purpose
407
+ of having them make modifications exclusively for you, or provide you
408
+ with facilities for running those works, provided that you comply with
409
+ the terms of this License in conveying all material for which you do
410
+ not control copyright. Those thus making or running the covered works
411
+ for you must do so exclusively on your behalf, under your direction
412
+ and control, on terms that prohibit them from making any copies of
413
+ your copyrighted material outside their relationship with you.
414
+
415
+ Conveying under any other circumstances is permitted solely under
416
+ the conditions stated below. Sublicensing is not allowed; section 10
417
+ makes it unnecessary.
418
+
419
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
420
+
421
+ No covered work shall be deemed part of an effective technological
422
+ measure under any applicable law fulfilling obligations under article
423
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
424
+ similar laws prohibiting or restricting circumvention of such
425
+ measures.
426
+
427
+ When you convey a covered work, you waive any legal power to forbid
428
+ circumvention of technological measures to the extent such circumvention
429
+ is effected by exercising rights under this License with respect to
430
+ the covered work, and you disclaim any intention to limit operation or
431
+ modification of the work as a means of enforcing, against the work's
432
+ users, your or third parties' legal rights to forbid circumvention of
433
+ technological measures.
434
+
435
+ 4. Conveying Verbatim Copies.
436
+
437
+ You may convey verbatim copies of the Program's source code as you
438
+ receive it, in any medium, provided that you conspicuously and
439
+ appropriately publish on each copy an appropriate copyright notice;
440
+ keep intact all notices stating that this License and any
441
+ non-permissive terms added in accord with section 7 apply to the code;
442
+ keep intact all notices of the absence of any warranty; and give all
443
+ recipients a copy of this License along with the Program.
444
+
445
+ You may charge any price or no price for each copy that you convey,
446
+ and you may offer support or warranty protection for a fee.
447
+
448
+ 5. Conveying Modified Source Versions.
449
+
450
+ You may convey a work based on the Program, or the modifications to
451
+ produce it from the Program, in the form of source code under the
452
+ terms of section 4, provided that you also meet all of these conditions:
453
+
454
+ a) The work must carry prominent notices stating that you modified
455
+ it, and giving a relevant date.
456
+
457
+ b) The work must carry prominent notices stating that it is
458
+ released under this License and any conditions added under section
459
+ 7. This requirement modifies the requirement in section 4 to
460
+ "keep intact all notices".
461
+
462
+ c) You must license the entire work, as a whole, under this
463
+ License to anyone who comes into possession of a copy. This
464
+ License will therefore apply, along with any applicable section 7
465
+ additional terms, to the whole of the work, and all its parts,
466
+ regardless of how they are packaged. This License gives no
467
+ permission to license the work in any other way, but it does not
468
+ invalidate such permission if you have separately received it.
469
+
470
+ d) If the work has interactive user interfaces, each must display
471
+ Appropriate Legal Notices; however, if the Program has interactive
472
+ interfaces that do not display Appropriate Legal Notices, your
473
+ work need not make them do so.
474
+
475
+ A compilation of a covered work with other separate and independent
476
+ works, which are not by their nature extensions of the covered work,
477
+ and which are not combined with it such as to form a larger program,
478
+ in or on a volume of a storage or distribution medium, is called an
479
+ "aggregate" if the compilation and its resulting copyright are not
480
+ used to limit the access or legal rights of the compilation's users
481
+ beyond what the individual works permit. Inclusion of a covered work
482
+ in an aggregate does not cause this License to apply to the other
483
+ parts of the aggregate.
484
+
485
+ 6. Conveying Non-Source Forms.
486
+
487
+ You may convey a covered work in object code form under the terms
488
+ of sections 4 and 5, provided that you also convey the
489
+ machine-readable Corresponding Source under the terms of this License,
490
+ in one of these ways:
491
+
492
+ a) Convey the object code in, or embodied in, a physical product
493
+ (including a physical distribution medium), accompanied by the
494
+ Corresponding Source fixed on a durable physical medium
495
+ customarily used for software interchange.
496
+
497
+ b) Convey the object code in, or embodied in, a physical product
498
+ (including a physical distribution medium), accompanied by a
499
+ written offer, valid for at least three years and valid for as
500
+ long as you offer spare parts or customer support for that product
501
+ model, to give anyone who possesses the object code either (1) a
502
+ copy of the Corresponding Source for all the software in the
503
+ product that is covered by this License, on a durable physical
504
+ medium customarily used for software interchange, for a price no
505
+ more than your reasonable cost of physically performing this
506
+ conveying of source, or (2) access to copy the
507
+ Corresponding Source from a network server at no charge.
508
+
509
+ c) Convey individual copies of the object code with a copy of the
510
+ written offer to provide the Corresponding Source. This
511
+ alternative is allowed only occasionally and noncommercially, and
512
+ only if you received the object code with such an offer, in accord
513
+ with subsection 6b.
514
+
515
+ d) Convey the object code by offering access from a designated
516
+ place (gratis or for a charge), and offer equivalent access to the
517
+ Corresponding Source in the same way through the same place at no
518
+ further charge. You need not require recipients to copy the
519
+ Corresponding Source along with the object code. If the place to
520
+ copy the object code is a network server, the Corresponding Source
521
+ may be on a different server (operated by you or a third party)
522
+ that supports equivalent copying facilities, provided you maintain
523
+ clear directions next to the object code saying where to find the
524
+ Corresponding Source. Regardless of what server hosts the
525
+ Corresponding Source, you remain obligated to ensure that it is
526
+ available for as long as needed to satisfy these requirements.
527
+
528
+ e) Convey the object code using peer-to-peer transmission, provided
529
+ you inform other peers where the object code and Corresponding
530
+ Source of the work are being offered to the general public at no
531
+ charge under subsection 6d.
532
+
533
+ A separable portion of the object code, whose source code is excluded
534
+ from the Corresponding Source as a System Library, need not be
535
+ included in conveying the object code work.
536
+
537
+ A "User Product" is either (1) a "consumer product", which means any
538
+ tangible personal property which is normally used for personal, family,
539
+ or household purposes, or (2) anything designed or sold for incorporation
540
+ into a dwelling. In determining whether a product is a consumer product,
541
+ doubtful cases shall be resolved in favor of coverage. For a particular
542
+ product received by a particular user, "normally used" refers to a
543
+ typical or common use of that class of product, regardless of the status
544
+ of the particular user or of the way in which the particular user
545
+ actually uses, or expects or is expected to use, the product. A product
546
+ is a consumer product regardless of whether the product has substantial
547
+ commercial, industrial or non-consumer uses, unless such uses represent
548
+ the only significant mode of use of the product.
549
+
550
+ "Installation Information" for a User Product means any methods,
551
+ procedures, authorization keys, or other information required to install
552
+ and execute modified versions of a covered work in that User Product from
553
+ a modified version of its Corresponding Source. The information must
554
+ suffice to ensure that the continued functioning of the modified object
555
+ code is in no case prevented or interfered with solely because
556
+ modification has been made.
557
+
558
+ If you convey an object code work under this section in, or with, or
559
+ specifically for use in, a User Product, and the conveying occurs as
560
+ part of a transaction in which the right of possession and use of the
561
+ User Product is transferred to the recipient in perpetuity or for a
562
+ fixed term (regardless of how the transaction is characterized), the
563
+ Corresponding Source conveyed under this section must be accompanied
564
+ by the Installation Information. But this requirement does not apply
565
+ if neither you nor any third party retains the ability to install
566
+ modified object code on the User Product (for example, the work has
567
+ been installed in ROM).
568
+
569
+ The requirement to provide Installation Information does not include a
570
+ requirement to continue to provide support service, warranty, or updates
571
+ for a work that has been modified or installed by the recipient, or for
572
+ the User Product in which it has been modified or installed. Access to a
573
+ network may be denied when the modification itself materially and
574
+ adversely affects the operation of the network or violates the rules and
575
+ protocols for communication across the network.
576
+
577
+ Corresponding Source conveyed, and Installation Information provided,
578
+ in accord with this section must be in a format that is publicly
579
+ documented (and with an implementation available to the public in
580
+ source code form), and must require no special password or key for
581
+ unpacking, reading or copying.
582
+
583
+ 7. Additional Terms.
584
+
585
+ "Additional permissions" are terms that supplement the terms of this
586
+ License by making exceptions from one or more of its conditions.
587
+ Additional permissions that are applicable to the entire Program shall
588
+ be treated as though they were included in this License, to the extent
589
+ that they are valid under applicable law. If additional permissions
590
+ apply only to part of the Program, that part may be used separately
591
+ under those permissions, but the entire Program remains governed by
592
+ this License without regard to the additional permissions.
593
+
594
+ When you convey a copy of a covered work, you may at your option
595
+ remove any additional permissions from that copy, or from any part of
596
+ it. (Additional permissions may be written to require their own
597
+ removal in certain cases when you modify the work.) You may place
598
+ additional permissions on material, added by you to a covered work,
599
+ for which you have or can give appropriate copyright permission.
600
+
601
+ Notwithstanding any other provision of this License, for material you
602
+ add to a covered work, you may (if authorized by the copyright holders of
603
+ that material) supplement the terms of this License with terms:
604
+
605
+ a) Disclaiming warranty or limiting liability differently from the
606
+ terms of sections 15 and 16 of this License; or
607
+
608
+ b) Requiring preservation of specified reasonable legal notices or
609
+ author attributions in that material or in the Appropriate Legal
610
+ Notices displayed by works containing it; or
611
+
612
+ c) Prohibiting misrepresentation of the origin of that material, or
613
+ requiring that modified versions of such material be marked in
614
+ reasonable ways as different from the original version; or
615
+
616
+ d) Limiting the use for publicity purposes of names of licensors or
617
+ authors of the material; or
618
+
619
+ e) Declining to grant rights under trademark law for use of some
620
+ trade names, trademarks, or service marks; or
621
+
622
+ f) Requiring indemnification of licensors and authors of that
623
+ material by anyone who conveys the material (or modified versions of
624
+ it) with contractual assumptions of liability to the recipient, for
625
+ any liability that these contractual assumptions directly impose on
626
+ those licensors and authors.
627
+
628
+ All other non-permissive additional terms are considered "further
629
+ restrictions" within the meaning of section 10. If the Program as you
630
+ received it, or any part of it, contains a notice stating that it is
631
+ governed by this License along with a term that is a further
632
+ restriction, you may remove that term. If a license document contains
633
+ a further restriction but permits relicensing or conveying under this
634
+ License, you may add to a covered work material governed by the terms
635
+ of that license document, provided that the further restriction does
636
+ not survive such relicensing or conveying.
637
+
638
+ If you add terms to a covered work in accord with this section, you
639
+ must place, in the relevant source files, a statement of the
640
+ additional terms that apply to those files, or a notice indicating
641
+ where to find the applicable terms.
642
+
643
+ Additional terms, permissive or non-permissive, may be stated in the
644
+ form of a separately written license, or stated as exceptions;
645
+ the above requirements apply either way.
646
+
647
+ 8. Termination.
648
+
649
+ You may not propagate or modify a covered work except as expressly
650
+ provided under this License. Any attempt otherwise to propagate or
651
+ modify it is void, and will automatically terminate your rights under
652
+ this License (including any patent licenses granted under the third
653
+ paragraph of section 11).
654
+
655
+ However, if you cease all violation of this License, then your
656
+ license from a particular copyright holder is reinstated (a)
657
+ provisionally, unless and until the copyright holder explicitly and
658
+ finally terminates your license, and (b) permanently, if the copyright
659
+ holder fails to notify you of the violation by some reasonable means
660
+ prior to 60 days after the cessation.
661
+
662
+ Moreover, your license from a particular copyright holder is
663
+ reinstated permanently if the copyright holder notifies you of the
664
+ violation by some reasonable means, this is the first time you have
665
+ received notice of violation of this License (for any work) from that
666
+ copyright holder, and you cure the violation prior to 30 days after
667
+ your receipt of the notice.
668
+
669
+ Termination of your rights under this section does not terminate the
670
+ licenses of parties who have received copies or rights from you under
671
+ this License. If your rights have been terminated and not permanently
672
+ reinstated, you do not qualify to receive new licenses for the same
673
+ material under section 10.
674
+
675
+ 9. Acceptance Not Required for Having Copies.
676
+
677
+ You are not required to accept this License in order to receive or
678
+ run a copy of the Program. Ancillary propagation of a covered work
679
+ occurring solely as a consequence of using peer-to-peer transmission
680
+ to receive a copy likewise does not require acceptance. However,
681
+ nothing other than this License grants you permission to propagate or
682
+ modify any covered work. These actions infringe copyright if you do
683
+ not accept this License. Therefore, by modifying or propagating a
684
+ covered work, you indicate your acceptance of this License to do so.
685
+
686
+ 10. Automatic Licensing of Downstream Recipients.
687
+
688
+ Each time you convey a covered work, the recipient automatically
689
+ receives a license from the original licensors, to run, modify and
690
+ propagate that work, subject to this License. You are not responsible
691
+ for enforcing compliance by third parties with this License.
692
+
693
+ An "entity transaction" is a transaction transferring control of an
694
+ organization, or substantially all assets of one, or subdividing an
695
+ organization, or merging organizations. If propagation of a covered
696
+ work results from an entity transaction, each party to that
697
+ transaction who receives a copy of the work also receives whatever
698
+ licenses to the work the party's predecessor in interest had or could
699
+ give under the previous paragraph, plus a right to possession of the
700
+ Corresponding Source of the work from the predecessor in interest, if
701
+ the predecessor has it or can get it with reasonable efforts.
702
+
703
+ You may not impose any further restrictions on the exercise of the
704
+ rights granted or affirmed under this License. For example, you may
705
+ not impose a license fee, royalty, or other charge for exercise of
706
+ rights granted under this License, and you may not initiate litigation
707
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
708
+ any patent claim is infringed by making, using, selling, offering for
709
+ sale, or importing the Program or any portion of it.
710
+
711
+ 11. Patents.
712
+
713
+ A "contributor" is a copyright holder who authorizes use under this
714
+ License of the Program or a work on which the Program is based. The
715
+ work thus licensed is called the contributor's "contributor version".
716
+
717
+ A contributor's "essential patent claims" are all patent claims
718
+ owned or controlled by the contributor, whether already acquired or
719
+ hereafter acquired, that would be infringed by some manner, permitted
720
+ by this License, of making, using, or selling its contributor version,
721
+ but do not include claims that would be infringed only as a
722
+ consequence of further modification of the contributor version. For
723
+ purposes of this definition, "control" includes the right to grant
724
+ patent sublicenses in a manner consistent with the requirements of
725
+ this License.
726
+
727
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
728
+ patent license under the contributor's essential patent claims, to
729
+ make, use, sell, offer for sale, import and otherwise run, modify and
730
+ propagate the contents of its contributor version.
731
+
732
+ In the following three paragraphs, a "patent license" is any express
733
+ agreement or commitment, however denominated, not to enforce a patent
734
+ (such as an express permission to practice a patent or covenant not to
735
+ sue for patent infringement). To "grant" such a patent license to a
736
+ party means to make such an agreement or commitment not to enforce a
737
+ patent against the party.
738
+
739
+ If you convey a covered work, knowingly relying on a patent license,
740
+ and the Corresponding Source of the work is not available for anyone
741
+ to copy, free of charge and under the terms of this License, through a
742
+ publicly available network server or other readily accessible means,
743
+ then you must either (1) cause the Corresponding Source to be so
744
+ available, or (2) arrange to deprive yourself of the benefit of the
745
+ patent license for this particular work, or (3) arrange, in a manner
746
+ consistent with the requirements of this License, to extend the patent
747
+ license to downstream recipients. "Knowingly relying" means you have
748
+ actual knowledge that, but for the patent license, your conveying the
749
+ covered work in a country, or your recipient's use of the covered work
750
+ in a country, would infringe one or more identifiable patents in that
751
+ country that you have reason to believe are valid.
752
+
753
+ If, pursuant to or in connection with a single transaction or
754
+ arrangement, you convey, or propagate by procuring conveyance of, a
755
+ covered work, and grant a patent license to some of the parties
756
+ receiving the covered work authorizing them to use, propagate, modify
757
+ or convey a specific copy of the covered work, then the patent license
758
+ you grant is automatically extended to all recipients of the covered
759
+ work and works based on it.
760
+
761
+ A patent license is "discriminatory" if it does not include within
762
+ the scope of its coverage, prohibits the exercise of, or is
763
+ conditioned on the non-exercise of one or more of the rights that are
764
+ specifically granted under this License. You may not convey a covered
765
+ work if you are a party to an arrangement with a third party that is
766
+ in the business of distributing software, under which you make payment
767
+ to the third party based on the extent of your activity of conveying
768
+ the work, and under which the third party grants, to any of the
769
+ parties who would receive the covered work from you, a discriminatory
770
+ patent license (a) in connection with copies of the covered work
771
+ conveyed by you (or copies made from those copies), or (b) primarily
772
+ for and in connection with specific products or compilations that
773
+ contain the covered work, unless you entered into that arrangement,
774
+ or that patent license was granted, prior to 28 March 2007.
775
+
776
+ Nothing in this License shall be construed as excluding or limiting
777
+ any implied license or other defenses to infringement that may
778
+ otherwise be available to you under applicable patent law.
779
+
780
+ 12. No Surrender of Others' Freedom.
781
+
782
+ If conditions are imposed on you (whether by court order, agreement or
783
+ otherwise) that contradict the conditions of this License, they do not
784
+ excuse you from the conditions of this License. If you cannot convey a
785
+ covered work so as to satisfy simultaneously your obligations under this
786
+ License and any other pertinent obligations, then as a consequence you may
787
+ not convey it at all. For example, if you agree to terms that obligate you
788
+ to collect a royalty for further conveying from those to whom you convey
789
+ the Program, the only way you could satisfy both those terms and this
790
+ License would be to refrain entirely from conveying the Program.
791
+
792
+ 13. Use with the GNU Affero General Public License.
793
+
794
+ Notwithstanding any other provision of this License, you have
795
+ permission to link or combine any covered work with a work licensed
796
+ under version 3 of the GNU Affero General Public License into a single
797
+ combined work, and to convey the resulting work. The terms of this
798
+ License will continue to apply to the part which is the covered work,
799
+ but the special requirements of the GNU Affero General Public License,
800
+ section 13, concerning interaction through a network will apply to the
801
+ combination as such.
802
+
803
+ 14. Revised Versions of this License.
804
+
805
+ The Free Software Foundation may publish revised and/or new versions of
806
+ the GNU General Public License from time to time. Such new versions will
807
+ be similar in spirit to the present version, but may differ in detail to
808
+ address new problems or concerns.
809
+
810
+ Each version is given a distinguishing version number. If the
811
+ Program specifies that a certain numbered version of the GNU General
812
+ Public License "or any later version" applies to it, you have the
813
+ option of following the terms and conditions either of that numbered
814
+ version or of any later version published by the Free Software
815
+ Foundation. If the Program does not specify a version number of the
816
+ GNU General Public License, you may choose any version ever published
817
+ by the Free Software Foundation.
818
+
819
+ If the Program specifies that a proxy can decide which future
820
+ versions of the GNU General Public License can be used, that proxy's
821
+ public statement of acceptance of a version permanently authorizes you
822
+ to choose that version for the Program.
823
+
824
+ Later license versions may give you additional or different
825
+ permissions. However, no additional obligations are imposed on any
826
+ author or copyright holder as a result of your choosing to follow a
827
+ later version.
828
+
829
+ 15. Disclaimer of Warranty.
830
+
831
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
832
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
833
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
834
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
835
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
836
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
837
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
838
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
839
+
840
+ 16. Limitation of Liability.
841
+
842
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
843
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
844
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
845
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
846
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
847
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
848
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
849
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
850
+ SUCH DAMAGES.
851
+
852
+ 17. Interpretation of Sections 15 and 16.
853
+
854
+ If the disclaimer of warranty and limitation of liability provided
855
+ above cannot be given local legal effect according to their terms,
856
+ reviewing courts shall apply local law that most closely approximates
857
+ an absolute waiver of all civil liability in connection with the
858
+ Program, unless a warranty or assumption of liability accompanies a
859
+ copy of the Program in return for a fee.
860
+
861
+ END OF TERMS AND CONDITIONS
862
+
863
+ How to Apply These Terms to Your New Programs
864
+
865
+ If you develop a new program, and you want it to be of the greatest
866
+ possible use to the public, the best way to achieve this is to make it
867
+ free software which everyone can redistribute and change under these terms.
868
+
869
+ To do so, attach the following notices to the program. It is safest
870
+ to attach them to the start of each source file to most effectively
871
+ state the exclusion of warranty; and each file should have at least
872
+ the "copyright" line and a pointer to where the full notice is found.
873
+
874
+ <one line to give the program's name and a brief idea of what it does.>
875
+ Copyright (C) <year> <name of author>
876
+
877
+ This program is free software: you can redistribute it and/or modify
878
+ it under the terms of the GNU General Public License as published by
879
+ the Free Software Foundation, either version 3 of the License, or
880
+ (at your option) any later version.
881
+
882
+ This program is distributed in the hope that it will be useful,
883
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
884
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
885
+ GNU General Public License for more details.
886
+
887
+ You should have received a copy of the GNU General Public License
888
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
889
+
890
+ Also add information on how to contact you by electronic and paper mail.
891
+
892
+ If the program does terminal interaction, make it output a short
893
+ notice like this when it starts in an interactive mode:
894
+
895
+ <program> Copyright (C) <year> <name of author>
896
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
897
+ This is free software, and you are welcome to redistribute it
898
+ under certain conditions; type `show c' for details.
899
+
900
+ The hypothetical commands `show w' and `show c' should show the appropriate
901
+ parts of the General Public License. Of course, your program's commands
902
+ might be different; for a GUI interface, you would use an "about box".
903
+
904
+ You should also get your employer (if you work as a programmer) or school,
905
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
906
+ For more information on this, and how to apply and follow the GNU GPL, see
907
+ <http://www.gnu.org/licenses/>.
908
+
909
+ The GNU General Public License does not permit incorporating your program
910
+ into proprietary programs. If your program is a subroutine library, you
911
+ may consider it more useful to permit linking proprietary applications with
912
+ the library. If this is what you want to do, use the GNU Lesser General
913
+ Public License instead of this License. But first, please read
914
+ <http://www.gnu.org/philosophy/why-not-lgpl.html>.
915
+
916
+
917
+ Name: libquadmath
918
+ Files: scipy.libs/libquadmath*.so
919
+ Description: dynamically linked to files compiled with gcc
920
+ Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libquadmath
921
+ License: LGPL-2.1-or-later
922
+
923
+ GCC Quad-Precision Math Library
924
+ Copyright (C) 2010-2019 Free Software Foundation, Inc.
925
+ Written by Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
926
+
927
+ This file is part of the libquadmath library.
928
+ Libquadmath is free software; you can redistribute it and/or
929
+ modify it under the terms of the GNU Library General Public
930
+ License as published by the Free Software Foundation; either
931
+ version 2.1 of the License, or (at your option) any later version.
932
+
933
+ Libquadmath is distributed in the hope that it will be useful,
934
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
935
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
936
+ Lesser General Public License for more details.
937
+ https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/METADATA ADDED
@@ -0,0 +1,1091 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.1
2
+ Name: scipy
3
+ Version: 1.18.0
4
+ Summary: Fundamental algorithms for scientific computing in Python
5
+ Maintainer-Email: SciPy Developers <scipy-dev@python.org>
6
+ License: Copyright (c) 2001-2002 Enthought, Inc. 2003, SciPy Developers.
7
+ All rights reserved.
8
+
9
+ Redistribution and use in source and binary forms, with or without
10
+ modification, are permitted provided that the following conditions
11
+ are met:
12
+
13
+ 1. Redistributions of source code must retain the above copyright
14
+ notice, this list of conditions and the following disclaimer.
15
+
16
+ 2. Redistributions in binary form must reproduce the above
17
+ copyright notice, this list of conditions and the following
18
+ disclaimer in the documentation and/or other materials provided
19
+ with the distribution.
20
+
21
+ 3. Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived
23
+ from this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+
37
+ ----
38
+
39
+
40
+ ----
41
+
42
+ This binary distribution of SciPy can also bundle the following software
43
+ (depending on the build):
44
+
45
+
46
+ Name: OpenBLAS
47
+ Files: scipy.libs/libscipy_openblas*.so
48
+ Description: bundled as a dynamically linked library
49
+ Availability: https://github.com/OpenMathLib/OpenBLAS/
50
+ License: BSD-3-Clause
51
+ Copyright (c) 2011-2014, The OpenBLAS Project
52
+ All rights reserved.
53
+
54
+ Redistribution and use in source and binary forms, with or without
55
+ modification, are permitted provided that the following conditions are
56
+ met:
57
+
58
+ 1. Redistributions of source code must retain the above copyright
59
+ notice, this list of conditions and the following disclaimer.
60
+
61
+ 2. Redistributions in binary form must reproduce the above copyright
62
+ notice, this list of conditions and the following disclaimer in
63
+ the documentation and/or other materials provided with the
64
+ distribution.
65
+ 3. Neither the name of the OpenBLAS project nor the names of
66
+ its contributors may be used to endorse or promote products
67
+ derived from this software without specific prior written
68
+ permission.
69
+
70
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
71
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
72
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
73
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
74
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
75
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
76
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
77
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
78
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
79
+ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
80
+
81
+
82
+ Name: LAPACK
83
+ Files: scipy.libs/libscipy_openblas*.so
84
+ Description: bundled in OpenBLAS
85
+ Availability: https://github.com/OpenMathLib/OpenBLAS/
86
+ License: BSD-3-Clause-Open-MPI
87
+ Copyright (c) 1992-2013 The University of Tennessee and The University
88
+ of Tennessee Research Foundation. All rights
89
+ reserved.
90
+ Copyright (c) 2000-2013 The University of California Berkeley. All
91
+ rights reserved.
92
+ Copyright (c) 2006-2013 The University of Colorado Denver. All rights
93
+ reserved.
94
+
95
+ $COPYRIGHT$
96
+
97
+ Additional copyrights may follow
98
+
99
+ $HEADER$
100
+
101
+ Redistribution and use in source and binary forms, with or without
102
+ modification, are permitted provided that the following conditions are
103
+ met:
104
+
105
+ - Redistributions of source code must retain the above copyright
106
+ notice, this list of conditions and the following disclaimer.
107
+
108
+ - Redistributions in binary form must reproduce the above copyright
109
+ notice, this list of conditions and the following disclaimer listed
110
+ in this license in the documentation and/or other materials
111
+ provided with the distribution.
112
+
113
+ - Neither the name of the copyright holders nor the names of its
114
+ contributors may be used to endorse or promote products derived from
115
+ this software without specific prior written permission.
116
+
117
+ The copyright holders provide no reassurances that the source code
118
+ provided does not infringe any patent, copyright, or any other
119
+ intellectual property rights of third parties. The copyright holders
120
+ disclaim any liability to any recipient for claims brought against
121
+ recipient by any third party for infringement of that parties
122
+ intellectual property rights.
123
+
124
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
125
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
126
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
127
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
128
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
129
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
130
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
131
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
132
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
133
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
134
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
135
+
136
+
137
+ Name: GCC runtime library
138
+ Files: scipy.libs/libgfortran*.so
139
+ Description: dynamically linked to files compiled with gcc
140
+ Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libgfortran
141
+ License: GPL-3.0-or-later WITH GCC-exception-3.1
142
+ Copyright (C) 2002-2017 Free Software Foundation, Inc.
143
+
144
+ Libgfortran is free software; you can redistribute it and/or modify
145
+ it under the terms of the GNU General Public License as published by
146
+ the Free Software Foundation; either version 3, or (at your option)
147
+ any later version.
148
+
149
+ Libgfortran is distributed in the hope that it will be useful,
150
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
151
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
152
+ GNU General Public License for more details.
153
+
154
+ Under Section 7 of GPL version 3, you are granted additional
155
+ permissions described in the GCC Runtime Library Exception, version
156
+ 3.1, as published by the Free Software Foundation.
157
+
158
+ You should have received a copy of the GNU General Public License and
159
+ a copy of the GCC Runtime Library Exception along with this program;
160
+ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
161
+ <http://www.gnu.org/licenses/>.
162
+
163
+ ----
164
+
165
+ Full text of license texts referred to above follows (that they are
166
+ listed below does not necessarily imply the conditions apply to the
167
+ present binary release):
168
+
169
+ ----
170
+
171
+ GCC RUNTIME LIBRARY EXCEPTION
172
+
173
+ Version 3.1, 31 March 2009
174
+
175
+ Copyright (C) 2009 Free Software Foundation, Inc. <http://fsf.org/>
176
+
177
+ Everyone is permitted to copy and distribute verbatim copies of this
178
+ license document, but changing it is not allowed.
179
+
180
+ This GCC Runtime Library Exception ("Exception") is an additional
181
+ permission under section 7 of the GNU General Public License, version
182
+ 3 ("GPLv3"). It applies to a given file (the "Runtime Library") that
183
+ bears a notice placed by the copyright holder of the file stating that
184
+ the file is governed by GPLv3 along with this Exception.
185
+
186
+ When you use GCC to compile a program, GCC may combine portions of
187
+ certain GCC header files and runtime libraries with the compiled
188
+ program. The purpose of this Exception is to allow compilation of
189
+ non-GPL (including proprietary) programs to use, in this way, the
190
+ header files and runtime libraries covered by this Exception.
191
+
192
+ 0. Definitions.
193
+
194
+ A file is an "Independent Module" if it either requires the Runtime
195
+ Library for execution after a Compilation Process, or makes use of an
196
+ interface provided by the Runtime Library, but is not otherwise based
197
+ on the Runtime Library.
198
+
199
+ "GCC" means a version of the GNU Compiler Collection, with or without
200
+ modifications, governed by version 3 (or a specified later version) of
201
+ the GNU General Public License (GPL) with the option of using any
202
+ subsequent versions published by the FSF.
203
+
204
+ "GPL-compatible Software" is software whose conditions of propagation,
205
+ modification and use would permit combination with GCC in accord with
206
+ the license of GCC.
207
+
208
+ "Target Code" refers to output from any compiler for a real or virtual
209
+ target processor architecture, in executable form or suitable for
210
+ input to an assembler, loader, linker and/or execution
211
+ phase. Notwithstanding that, Target Code does not include data in any
212
+ format that is used as a compiler intermediate representation, or used
213
+ for producing a compiler intermediate representation.
214
+
215
+ The "Compilation Process" transforms code entirely represented in
216
+ non-intermediate languages designed for human-written code, and/or in
217
+ Java Virtual Machine byte code, into Target Code. Thus, for example,
218
+ use of source code generators and preprocessors need not be considered
219
+ part of the Compilation Process, since the Compilation Process can be
220
+ understood as starting with the output of the generators or
221
+ preprocessors.
222
+
223
+ A Compilation Process is "Eligible" if it is done using GCC, alone or
224
+ with other GPL-compatible software, or if it is done without using any
225
+ work based on GCC. For example, using non-GPL-compatible Software to
226
+ optimize any GCC intermediate representations would not qualify as an
227
+ Eligible Compilation Process.
228
+
229
+ 1. Grant of Additional Permission.
230
+
231
+ You have permission to propagate a work of Target Code formed by
232
+ combining the Runtime Library with Independent Modules, even if such
233
+ propagation would otherwise violate the terms of GPLv3, provided that
234
+ all Target Code was generated by Eligible Compilation Processes. You
235
+ may then convey such a combination under terms of your choice,
236
+ consistent with the licensing of the Independent Modules.
237
+
238
+ 2. No Weakening of GCC Copyleft.
239
+
240
+ The availability of this Exception does not imply any general
241
+ presumption that third-party software is unaffected by the copyleft
242
+ requirements of the license of GCC.
243
+
244
+ ----
245
+
246
+ GNU GENERAL PUBLIC LICENSE
247
+ Version 3, 29 June 2007
248
+
249
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
250
+ Everyone is permitted to copy and distribute verbatim copies
251
+ of this license document, but changing it is not allowed.
252
+
253
+ Preamble
254
+
255
+ The GNU General Public License is a free, copyleft license for
256
+ software and other kinds of works.
257
+
258
+ The licenses for most software and other practical works are designed
259
+ to take away your freedom to share and change the works. By contrast,
260
+ the GNU General Public License is intended to guarantee your freedom to
261
+ share and change all versions of a program--to make sure it remains free
262
+ software for all its users. We, the Free Software Foundation, use the
263
+ GNU General Public License for most of our software; it applies also to
264
+ any other work released this way by its authors. You can apply it to
265
+ your programs, too.
266
+
267
+ When we speak of free software, we are referring to freedom, not
268
+ price. Our General Public Licenses are designed to make sure that you
269
+ have the freedom to distribute copies of free software (and charge for
270
+ them if you wish), that you receive source code or can get it if you
271
+ want it, that you can change the software or use pieces of it in new
272
+ free programs, and that you know you can do these things.
273
+
274
+ To protect your rights, we need to prevent others from denying you
275
+ these rights or asking you to surrender the rights. Therefore, you have
276
+ certain responsibilities if you distribute copies of the software, or if
277
+ you modify it: responsibilities to respect the freedom of others.
278
+
279
+ For example, if you distribute copies of such a program, whether
280
+ gratis or for a fee, you must pass on to the recipients the same
281
+ freedoms that you received. You must make sure that they, too, receive
282
+ or can get the source code. And you must show them these terms so they
283
+ know their rights.
284
+
285
+ Developers that use the GNU GPL protect your rights with two steps:
286
+ (1) assert copyright on the software, and (2) offer you this License
287
+ giving you legal permission to copy, distribute and/or modify it.
288
+
289
+ For the developers' and authors' protection, the GPL clearly explains
290
+ that there is no warranty for this free software. For both users' and
291
+ authors' sake, the GPL requires that modified versions be marked as
292
+ changed, so that their problems will not be attributed erroneously to
293
+ authors of previous versions.
294
+
295
+ Some devices are designed to deny users access to install or run
296
+ modified versions of the software inside them, although the manufacturer
297
+ can do so. This is fundamentally incompatible with the aim of
298
+ protecting users' freedom to change the software. The systematic
299
+ pattern of such abuse occurs in the area of products for individuals to
300
+ use, which is precisely where it is most unacceptable. Therefore, we
301
+ have designed this version of the GPL to prohibit the practice for those
302
+ products. If such problems arise substantially in other domains, we
303
+ stand ready to extend this provision to those domains in future versions
304
+ of the GPL, as needed to protect the freedom of users.
305
+
306
+ Finally, every program is threatened constantly by software patents.
307
+ States should not allow patents to restrict development and use of
308
+ software on general-purpose computers, but in those that do, we wish to
309
+ avoid the special danger that patents applied to a free program could
310
+ make it effectively proprietary. To prevent this, the GPL assures that
311
+ patents cannot be used to render the program non-free.
312
+
313
+ The precise terms and conditions for copying, distribution and
314
+ modification follow.
315
+
316
+ TERMS AND CONDITIONS
317
+
318
+ 0. Definitions.
319
+
320
+ "This License" refers to version 3 of the GNU General Public License.
321
+
322
+ "Copyright" also means copyright-like laws that apply to other kinds of
323
+ works, such as semiconductor masks.
324
+
325
+ "The Program" refers to any copyrightable work licensed under this
326
+ License. Each licensee is addressed as "you". "Licensees" and
327
+ "recipients" may be individuals or organizations.
328
+
329
+ To "modify" a work means to copy from or adapt all or part of the work
330
+ in a fashion requiring copyright permission, other than the making of an
331
+ exact copy. The resulting work is called a "modified version" of the
332
+ earlier work or a work "based on" the earlier work.
333
+
334
+ A "covered work" means either the unmodified Program or a work based
335
+ on the Program.
336
+
337
+ To "propagate" a work means to do anything with it that, without
338
+ permission, would make you directly or secondarily liable for
339
+ infringement under applicable copyright law, except executing it on a
340
+ computer or modifying a private copy. Propagation includes copying,
341
+ distribution (with or without modification), making available to the
342
+ public, and in some countries other activities as well.
343
+
344
+ To "convey" a work means any kind of propagation that enables other
345
+ parties to make or receive copies. Mere interaction with a user through
346
+ a computer network, with no transfer of a copy, is not conveying.
347
+
348
+ An interactive user interface displays "Appropriate Legal Notices"
349
+ to the extent that it includes a convenient and prominently visible
350
+ feature that (1) displays an appropriate copyright notice, and (2)
351
+ tells the user that there is no warranty for the work (except to the
352
+ extent that warranties are provided), that licensees may convey the
353
+ work under this License, and how to view a copy of this License. If
354
+ the interface presents a list of user commands or options, such as a
355
+ menu, a prominent item in the list meets this criterion.
356
+
357
+ 1. Source Code.
358
+
359
+ The "source code" for a work means the preferred form of the work
360
+ for making modifications to it. "Object code" means any non-source
361
+ form of a work.
362
+
363
+ A "Standard Interface" means an interface that either is an official
364
+ standard defined by a recognized standards body, or, in the case of
365
+ interfaces specified for a particular programming language, one that
366
+ is widely used among developers working in that language.
367
+
368
+ The "System Libraries" of an executable work include anything, other
369
+ than the work as a whole, that (a) is included in the normal form of
370
+ packaging a Major Component, but which is not part of that Major
371
+ Component, and (b) serves only to enable use of the work with that
372
+ Major Component, or to implement a Standard Interface for which an
373
+ implementation is available to the public in source code form. A
374
+ "Major Component", in this context, means a major essential component
375
+ (kernel, window system, and so on) of the specific operating system
376
+ (if any) on which the executable work runs, or a compiler used to
377
+ produce the work, or an object code interpreter used to run it.
378
+
379
+ The "Corresponding Source" for a work in object code form means all
380
+ the source code needed to generate, install, and (for an executable
381
+ work) run the object code and to modify the work, including scripts to
382
+ control those activities. However, it does not include the work's
383
+ System Libraries, or general-purpose tools or generally available free
384
+ programs which are used unmodified in performing those activities but
385
+ which are not part of the work. For example, Corresponding Source
386
+ includes interface definition files associated with source files for
387
+ the work, and the source code for shared libraries and dynamically
388
+ linked subprograms that the work is specifically designed to require,
389
+ such as by intimate data communication or control flow between those
390
+ subprograms and other parts of the work.
391
+
392
+ The Corresponding Source need not include anything that users
393
+ can regenerate automatically from other parts of the Corresponding
394
+ Source.
395
+
396
+ The Corresponding Source for a work in source code form is that
397
+ same work.
398
+
399
+ 2. Basic Permissions.
400
+
401
+ All rights granted under this License are granted for the term of
402
+ copyright on the Program, and are irrevocable provided the stated
403
+ conditions are met. This License explicitly affirms your unlimited
404
+ permission to run the unmodified Program. The output from running a
405
+ covered work is covered by this License only if the output, given its
406
+ content, constitutes a covered work. This License acknowledges your
407
+ rights of fair use or other equivalent, as provided by copyright law.
408
+
409
+ You may make, run and propagate covered works that you do not
410
+ convey, without conditions so long as your license otherwise remains
411
+ in force. You may convey covered works to others for the sole purpose
412
+ of having them make modifications exclusively for you, or provide you
413
+ with facilities for running those works, provided that you comply with
414
+ the terms of this License in conveying all material for which you do
415
+ not control copyright. Those thus making or running the covered works
416
+ for you must do so exclusively on your behalf, under your direction
417
+ and control, on terms that prohibit them from making any copies of
418
+ your copyrighted material outside their relationship with you.
419
+
420
+ Conveying under any other circumstances is permitted solely under
421
+ the conditions stated below. Sublicensing is not allowed; section 10
422
+ makes it unnecessary.
423
+
424
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
425
+
426
+ No covered work shall be deemed part of an effective technological
427
+ measure under any applicable law fulfilling obligations under article
428
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
429
+ similar laws prohibiting or restricting circumvention of such
430
+ measures.
431
+
432
+ When you convey a covered work, you waive any legal power to forbid
433
+ circumvention of technological measures to the extent such circumvention
434
+ is effected by exercising rights under this License with respect to
435
+ the covered work, and you disclaim any intention to limit operation or
436
+ modification of the work as a means of enforcing, against the work's
437
+ users, your or third parties' legal rights to forbid circumvention of
438
+ technological measures.
439
+
440
+ 4. Conveying Verbatim Copies.
441
+
442
+ You may convey verbatim copies of the Program's source code as you
443
+ receive it, in any medium, provided that you conspicuously and
444
+ appropriately publish on each copy an appropriate copyright notice;
445
+ keep intact all notices stating that this License and any
446
+ non-permissive terms added in accord with section 7 apply to the code;
447
+ keep intact all notices of the absence of any warranty; and give all
448
+ recipients a copy of this License along with the Program.
449
+
450
+ You may charge any price or no price for each copy that you convey,
451
+ and you may offer support or warranty protection for a fee.
452
+
453
+ 5. Conveying Modified Source Versions.
454
+
455
+ You may convey a work based on the Program, or the modifications to
456
+ produce it from the Program, in the form of source code under the
457
+ terms of section 4, provided that you also meet all of these conditions:
458
+
459
+ a) The work must carry prominent notices stating that you modified
460
+ it, and giving a relevant date.
461
+
462
+ b) The work must carry prominent notices stating that it is
463
+ released under this License and any conditions added under section
464
+ 7. This requirement modifies the requirement in section 4 to
465
+ "keep intact all notices".
466
+
467
+ c) You must license the entire work, as a whole, under this
468
+ License to anyone who comes into possession of a copy. This
469
+ License will therefore apply, along with any applicable section 7
470
+ additional terms, to the whole of the work, and all its parts,
471
+ regardless of how they are packaged. This License gives no
472
+ permission to license the work in any other way, but it does not
473
+ invalidate such permission if you have separately received it.
474
+
475
+ d) If the work has interactive user interfaces, each must display
476
+ Appropriate Legal Notices; however, if the Program has interactive
477
+ interfaces that do not display Appropriate Legal Notices, your
478
+ work need not make them do so.
479
+
480
+ A compilation of a covered work with other separate and independent
481
+ works, which are not by their nature extensions of the covered work,
482
+ and which are not combined with it such as to form a larger program,
483
+ in or on a volume of a storage or distribution medium, is called an
484
+ "aggregate" if the compilation and its resulting copyright are not
485
+ used to limit the access or legal rights of the compilation's users
486
+ beyond what the individual works permit. Inclusion of a covered work
487
+ in an aggregate does not cause this License to apply to the other
488
+ parts of the aggregate.
489
+
490
+ 6. Conveying Non-Source Forms.
491
+
492
+ You may convey a covered work in object code form under the terms
493
+ of sections 4 and 5, provided that you also convey the
494
+ machine-readable Corresponding Source under the terms of this License,
495
+ in one of these ways:
496
+
497
+ a) Convey the object code in, or embodied in, a physical product
498
+ (including a physical distribution medium), accompanied by the
499
+ Corresponding Source fixed on a durable physical medium
500
+ customarily used for software interchange.
501
+
502
+ b) Convey the object code in, or embodied in, a physical product
503
+ (including a physical distribution medium), accompanied by a
504
+ written offer, valid for at least three years and valid for as
505
+ long as you offer spare parts or customer support for that product
506
+ model, to give anyone who possesses the object code either (1) a
507
+ copy of the Corresponding Source for all the software in the
508
+ product that is covered by this License, on a durable physical
509
+ medium customarily used for software interchange, for a price no
510
+ more than your reasonable cost of physically performing this
511
+ conveying of source, or (2) access to copy the
512
+ Corresponding Source from a network server at no charge.
513
+
514
+ c) Convey individual copies of the object code with a copy of the
515
+ written offer to provide the Corresponding Source. This
516
+ alternative is allowed only occasionally and noncommercially, and
517
+ only if you received the object code with such an offer, in accord
518
+ with subsection 6b.
519
+
520
+ d) Convey the object code by offering access from a designated
521
+ place (gratis or for a charge), and offer equivalent access to the
522
+ Corresponding Source in the same way through the same place at no
523
+ further charge. You need not require recipients to copy the
524
+ Corresponding Source along with the object code. If the place to
525
+ copy the object code is a network server, the Corresponding Source
526
+ may be on a different server (operated by you or a third party)
527
+ that supports equivalent copying facilities, provided you maintain
528
+ clear directions next to the object code saying where to find the
529
+ Corresponding Source. Regardless of what server hosts the
530
+ Corresponding Source, you remain obligated to ensure that it is
531
+ available for as long as needed to satisfy these requirements.
532
+
533
+ e) Convey the object code using peer-to-peer transmission, provided
534
+ you inform other peers where the object code and Corresponding
535
+ Source of the work are being offered to the general public at no
536
+ charge under subsection 6d.
537
+
538
+ A separable portion of the object code, whose source code is excluded
539
+ from the Corresponding Source as a System Library, need not be
540
+ included in conveying the object code work.
541
+
542
+ A "User Product" is either (1) a "consumer product", which means any
543
+ tangible personal property which is normally used for personal, family,
544
+ or household purposes, or (2) anything designed or sold for incorporation
545
+ into a dwelling. In determining whether a product is a consumer product,
546
+ doubtful cases shall be resolved in favor of coverage. For a particular
547
+ product received by a particular user, "normally used" refers to a
548
+ typical or common use of that class of product, regardless of the status
549
+ of the particular user or of the way in which the particular user
550
+ actually uses, or expects or is expected to use, the product. A product
551
+ is a consumer product regardless of whether the product has substantial
552
+ commercial, industrial or non-consumer uses, unless such uses represent
553
+ the only significant mode of use of the product.
554
+
555
+ "Installation Information" for a User Product means any methods,
556
+ procedures, authorization keys, or other information required to install
557
+ and execute modified versions of a covered work in that User Product from
558
+ a modified version of its Corresponding Source. The information must
559
+ suffice to ensure that the continued functioning of the modified object
560
+ code is in no case prevented or interfered with solely because
561
+ modification has been made.
562
+
563
+ If you convey an object code work under this section in, or with, or
564
+ specifically for use in, a User Product, and the conveying occurs as
565
+ part of a transaction in which the right of possession and use of the
566
+ User Product is transferred to the recipient in perpetuity or for a
567
+ fixed term (regardless of how the transaction is characterized), the
568
+ Corresponding Source conveyed under this section must be accompanied
569
+ by the Installation Information. But this requirement does not apply
570
+ if neither you nor any third party retains the ability to install
571
+ modified object code on the User Product (for example, the work has
572
+ been installed in ROM).
573
+
574
+ The requirement to provide Installation Information does not include a
575
+ requirement to continue to provide support service, warranty, or updates
576
+ for a work that has been modified or installed by the recipient, or for
577
+ the User Product in which it has been modified or installed. Access to a
578
+ network may be denied when the modification itself materially and
579
+ adversely affects the operation of the network or violates the rules and
580
+ protocols for communication across the network.
581
+
582
+ Corresponding Source conveyed, and Installation Information provided,
583
+ in accord with this section must be in a format that is publicly
584
+ documented (and with an implementation available to the public in
585
+ source code form), and must require no special password or key for
586
+ unpacking, reading or copying.
587
+
588
+ 7. Additional Terms.
589
+
590
+ "Additional permissions" are terms that supplement the terms of this
591
+ License by making exceptions from one or more of its conditions.
592
+ Additional permissions that are applicable to the entire Program shall
593
+ be treated as though they were included in this License, to the extent
594
+ that they are valid under applicable law. If additional permissions
595
+ apply only to part of the Program, that part may be used separately
596
+ under those permissions, but the entire Program remains governed by
597
+ this License without regard to the additional permissions.
598
+
599
+ When you convey a copy of a covered work, you may at your option
600
+ remove any additional permissions from that copy, or from any part of
601
+ it. (Additional permissions may be written to require their own
602
+ removal in certain cases when you modify the work.) You may place
603
+ additional permissions on material, added by you to a covered work,
604
+ for which you have or can give appropriate copyright permission.
605
+
606
+ Notwithstanding any other provision of this License, for material you
607
+ add to a covered work, you may (if authorized by the copyright holders of
608
+ that material) supplement the terms of this License with terms:
609
+
610
+ a) Disclaiming warranty or limiting liability differently from the
611
+ terms of sections 15 and 16 of this License; or
612
+
613
+ b) Requiring preservation of specified reasonable legal notices or
614
+ author attributions in that material or in the Appropriate Legal
615
+ Notices displayed by works containing it; or
616
+
617
+ c) Prohibiting misrepresentation of the origin of that material, or
618
+ requiring that modified versions of such material be marked in
619
+ reasonable ways as different from the original version; or
620
+
621
+ d) Limiting the use for publicity purposes of names of licensors or
622
+ authors of the material; or
623
+
624
+ e) Declining to grant rights under trademark law for use of some
625
+ trade names, trademarks, or service marks; or
626
+
627
+ f) Requiring indemnification of licensors and authors of that
628
+ material by anyone who conveys the material (or modified versions of
629
+ it) with contractual assumptions of liability to the recipient, for
630
+ any liability that these contractual assumptions directly impose on
631
+ those licensors and authors.
632
+
633
+ All other non-permissive additional terms are considered "further
634
+ restrictions" within the meaning of section 10. If the Program as you
635
+ received it, or any part of it, contains a notice stating that it is
636
+ governed by this License along with a term that is a further
637
+ restriction, you may remove that term. If a license document contains
638
+ a further restriction but permits relicensing or conveying under this
639
+ License, you may add to a covered work material governed by the terms
640
+ of that license document, provided that the further restriction does
641
+ not survive such relicensing or conveying.
642
+
643
+ If you add terms to a covered work in accord with this section, you
644
+ must place, in the relevant source files, a statement of the
645
+ additional terms that apply to those files, or a notice indicating
646
+ where to find the applicable terms.
647
+
648
+ Additional terms, permissive or non-permissive, may be stated in the
649
+ form of a separately written license, or stated as exceptions;
650
+ the above requirements apply either way.
651
+
652
+ 8. Termination.
653
+
654
+ You may not propagate or modify a covered work except as expressly
655
+ provided under this License. Any attempt otherwise to propagate or
656
+ modify it is void, and will automatically terminate your rights under
657
+ this License (including any patent licenses granted under the third
658
+ paragraph of section 11).
659
+
660
+ However, if you cease all violation of this License, then your
661
+ license from a particular copyright holder is reinstated (a)
662
+ provisionally, unless and until the copyright holder explicitly and
663
+ finally terminates your license, and (b) permanently, if the copyright
664
+ holder fails to notify you of the violation by some reasonable means
665
+ prior to 60 days after the cessation.
666
+
667
+ Moreover, your license from a particular copyright holder is
668
+ reinstated permanently if the copyright holder notifies you of the
669
+ violation by some reasonable means, this is the first time you have
670
+ received notice of violation of this License (for any work) from that
671
+ copyright holder, and you cure the violation prior to 30 days after
672
+ your receipt of the notice.
673
+
674
+ Termination of your rights under this section does not terminate the
675
+ licenses of parties who have received copies or rights from you under
676
+ this License. If your rights have been terminated and not permanently
677
+ reinstated, you do not qualify to receive new licenses for the same
678
+ material under section 10.
679
+
680
+ 9. Acceptance Not Required for Having Copies.
681
+
682
+ You are not required to accept this License in order to receive or
683
+ run a copy of the Program. Ancillary propagation of a covered work
684
+ occurring solely as a consequence of using peer-to-peer transmission
685
+ to receive a copy likewise does not require acceptance. However,
686
+ nothing other than this License grants you permission to propagate or
687
+ modify any covered work. These actions infringe copyright if you do
688
+ not accept this License. Therefore, by modifying or propagating a
689
+ covered work, you indicate your acceptance of this License to do so.
690
+
691
+ 10. Automatic Licensing of Downstream Recipients.
692
+
693
+ Each time you convey a covered work, the recipient automatically
694
+ receives a license from the original licensors, to run, modify and
695
+ propagate that work, subject to this License. You are not responsible
696
+ for enforcing compliance by third parties with this License.
697
+
698
+ An "entity transaction" is a transaction transferring control of an
699
+ organization, or substantially all assets of one, or subdividing an
700
+ organization, or merging organizations. If propagation of a covered
701
+ work results from an entity transaction, each party to that
702
+ transaction who receives a copy of the work also receives whatever
703
+ licenses to the work the party's predecessor in interest had or could
704
+ give under the previous paragraph, plus a right to possession of the
705
+ Corresponding Source of the work from the predecessor in interest, if
706
+ the predecessor has it or can get it with reasonable efforts.
707
+
708
+ You may not impose any further restrictions on the exercise of the
709
+ rights granted or affirmed under this License. For example, you may
710
+ not impose a license fee, royalty, or other charge for exercise of
711
+ rights granted under this License, and you may not initiate litigation
712
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
713
+ any patent claim is infringed by making, using, selling, offering for
714
+ sale, or importing the Program or any portion of it.
715
+
716
+ 11. Patents.
717
+
718
+ A "contributor" is a copyright holder who authorizes use under this
719
+ License of the Program or a work on which the Program is based. The
720
+ work thus licensed is called the contributor's "contributor version".
721
+
722
+ A contributor's "essential patent claims" are all patent claims
723
+ owned or controlled by the contributor, whether already acquired or
724
+ hereafter acquired, that would be infringed by some manner, permitted
725
+ by this License, of making, using, or selling its contributor version,
726
+ but do not include claims that would be infringed only as a
727
+ consequence of further modification of the contributor version. For
728
+ purposes of this definition, "control" includes the right to grant
729
+ patent sublicenses in a manner consistent with the requirements of
730
+ this License.
731
+
732
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
733
+ patent license under the contributor's essential patent claims, to
734
+ make, use, sell, offer for sale, import and otherwise run, modify and
735
+ propagate the contents of its contributor version.
736
+
737
+ In the following three paragraphs, a "patent license" is any express
738
+ agreement or commitment, however denominated, not to enforce a patent
739
+ (such as an express permission to practice a patent or covenant not to
740
+ sue for patent infringement). To "grant" such a patent license to a
741
+ party means to make such an agreement or commitment not to enforce a
742
+ patent against the party.
743
+
744
+ If you convey a covered work, knowingly relying on a patent license,
745
+ and the Corresponding Source of the work is not available for anyone
746
+ to copy, free of charge and under the terms of this License, through a
747
+ publicly available network server or other readily accessible means,
748
+ then you must either (1) cause the Corresponding Source to be so
749
+ available, or (2) arrange to deprive yourself of the benefit of the
750
+ patent license for this particular work, or (3) arrange, in a manner
751
+ consistent with the requirements of this License, to extend the patent
752
+ license to downstream recipients. "Knowingly relying" means you have
753
+ actual knowledge that, but for the patent license, your conveying the
754
+ covered work in a country, or your recipient's use of the covered work
755
+ in a country, would infringe one or more identifiable patents in that
756
+ country that you have reason to believe are valid.
757
+
758
+ If, pursuant to or in connection with a single transaction or
759
+ arrangement, you convey, or propagate by procuring conveyance of, a
760
+ covered work, and grant a patent license to some of the parties
761
+ receiving the covered work authorizing them to use, propagate, modify
762
+ or convey a specific copy of the covered work, then the patent license
763
+ you grant is automatically extended to all recipients of the covered
764
+ work and works based on it.
765
+
766
+ A patent license is "discriminatory" if it does not include within
767
+ the scope of its coverage, prohibits the exercise of, or is
768
+ conditioned on the non-exercise of one or more of the rights that are
769
+ specifically granted under this License. You may not convey a covered
770
+ work if you are a party to an arrangement with a third party that is
771
+ in the business of distributing software, under which you make payment
772
+ to the third party based on the extent of your activity of conveying
773
+ the work, and under which the third party grants, to any of the
774
+ parties who would receive the covered work from you, a discriminatory
775
+ patent license (a) in connection with copies of the covered work
776
+ conveyed by you (or copies made from those copies), or (b) primarily
777
+ for and in connection with specific products or compilations that
778
+ contain the covered work, unless you entered into that arrangement,
779
+ or that patent license was granted, prior to 28 March 2007.
780
+
781
+ Nothing in this License shall be construed as excluding or limiting
782
+ any implied license or other defenses to infringement that may
783
+ otherwise be available to you under applicable patent law.
784
+
785
+ 12. No Surrender of Others' Freedom.
786
+
787
+ If conditions are imposed on you (whether by court order, agreement or
788
+ otherwise) that contradict the conditions of this License, they do not
789
+ excuse you from the conditions of this License. If you cannot convey a
790
+ covered work so as to satisfy simultaneously your obligations under this
791
+ License and any other pertinent obligations, then as a consequence you may
792
+ not convey it at all. For example, if you agree to terms that obligate you
793
+ to collect a royalty for further conveying from those to whom you convey
794
+ the Program, the only way you could satisfy both those terms and this
795
+ License would be to refrain entirely from conveying the Program.
796
+
797
+ 13. Use with the GNU Affero General Public License.
798
+
799
+ Notwithstanding any other provision of this License, you have
800
+ permission to link or combine any covered work with a work licensed
801
+ under version 3 of the GNU Affero General Public License into a single
802
+ combined work, and to convey the resulting work. The terms of this
803
+ License will continue to apply to the part which is the covered work,
804
+ but the special requirements of the GNU Affero General Public License,
805
+ section 13, concerning interaction through a network will apply to the
806
+ combination as such.
807
+
808
+ 14. Revised Versions of this License.
809
+
810
+ The Free Software Foundation may publish revised and/or new versions of
811
+ the GNU General Public License from time to time. Such new versions will
812
+ be similar in spirit to the present version, but may differ in detail to
813
+ address new problems or concerns.
814
+
815
+ Each version is given a distinguishing version number. If the
816
+ Program specifies that a certain numbered version of the GNU General
817
+ Public License "or any later version" applies to it, you have the
818
+ option of following the terms and conditions either of that numbered
819
+ version or of any later version published by the Free Software
820
+ Foundation. If the Program does not specify a version number of the
821
+ GNU General Public License, you may choose any version ever published
822
+ by the Free Software Foundation.
823
+
824
+ If the Program specifies that a proxy can decide which future
825
+ versions of the GNU General Public License can be used, that proxy's
826
+ public statement of acceptance of a version permanently authorizes you
827
+ to choose that version for the Program.
828
+
829
+ Later license versions may give you additional or different
830
+ permissions. However, no additional obligations are imposed on any
831
+ author or copyright holder as a result of your choosing to follow a
832
+ later version.
833
+
834
+ 15. Disclaimer of Warranty.
835
+
836
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
837
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
838
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
839
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
840
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
841
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
842
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
843
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
844
+
845
+ 16. Limitation of Liability.
846
+
847
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
848
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
849
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
850
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
851
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
852
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
853
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
854
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
855
+ SUCH DAMAGES.
856
+
857
+ 17. Interpretation of Sections 15 and 16.
858
+
859
+ If the disclaimer of warranty and limitation of liability provided
860
+ above cannot be given local legal effect according to their terms,
861
+ reviewing courts shall apply local law that most closely approximates
862
+ an absolute waiver of all civil liability in connection with the
863
+ Program, unless a warranty or assumption of liability accompanies a
864
+ copy of the Program in return for a fee.
865
+
866
+ END OF TERMS AND CONDITIONS
867
+
868
+ How to Apply These Terms to Your New Programs
869
+
870
+ If you develop a new program, and you want it to be of the greatest
871
+ possible use to the public, the best way to achieve this is to make it
872
+ free software which everyone can redistribute and change under these terms.
873
+
874
+ To do so, attach the following notices to the program. It is safest
875
+ to attach them to the start of each source file to most effectively
876
+ state the exclusion of warranty; and each file should have at least
877
+ the "copyright" line and a pointer to where the full notice is found.
878
+
879
+ <one line to give the program's name and a brief idea of what it does.>
880
+ Copyright (C) <year> <name of author>
881
+
882
+ This program is free software: you can redistribute it and/or modify
883
+ it under the terms of the GNU General Public License as published by
884
+ the Free Software Foundation, either version 3 of the License, or
885
+ (at your option) any later version.
886
+
887
+ This program is distributed in the hope that it will be useful,
888
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
889
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
890
+ GNU General Public License for more details.
891
+
892
+ You should have received a copy of the GNU General Public License
893
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
894
+
895
+ Also add information on how to contact you by electronic and paper mail.
896
+
897
+ If the program does terminal interaction, make it output a short
898
+ notice like this when it starts in an interactive mode:
899
+
900
+ <program> Copyright (C) <year> <name of author>
901
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
902
+ This is free software, and you are welcome to redistribute it
903
+ under certain conditions; type `show c' for details.
904
+
905
+ The hypothetical commands `show w' and `show c' should show the appropriate
906
+ parts of the General Public License. Of course, your program's commands
907
+ might be different; for a GUI interface, you would use an "about box".
908
+
909
+ You should also get your employer (if you work as a programmer) or school,
910
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
911
+ For more information on this, and how to apply and follow the GNU GPL, see
912
+ <http://www.gnu.org/licenses/>.
913
+
914
+ The GNU General Public License does not permit incorporating your program
915
+ into proprietary programs. If your program is a subroutine library, you
916
+ may consider it more useful to permit linking proprietary applications with
917
+ the library. If this is what you want to do, use the GNU Lesser General
918
+ Public License instead of this License. But first, please read
919
+ <http://www.gnu.org/philosophy/why-not-lgpl.html>.
920
+
921
+
922
+ Name: libquadmath
923
+ Files: scipy.libs/libquadmath*.so
924
+ Description: dynamically linked to files compiled with gcc
925
+ Availability: https://gcc.gnu.org/git/?p=gcc.git;a=tree;f=libquadmath
926
+ License: LGPL-2.1-or-later
927
+
928
+ GCC Quad-Precision Math Library
929
+ Copyright (C) 2010-2019 Free Software Foundation, Inc.
930
+ Written by Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
931
+
932
+ This file is part of the libquadmath library.
933
+ Libquadmath is free software; you can redistribute it and/or
934
+ modify it under the terms of the GNU Library General Public
935
+ License as published by the Free Software Foundation; either
936
+ version 2.1 of the License, or (at your option) any later version.
937
+
938
+ Libquadmath is distributed in the hope that it will be useful,
939
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
940
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
941
+ Lesser General Public License for more details.
942
+ https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
943
+
944
+ Classifier: Development Status :: 5 - Production/Stable
945
+ Classifier: Intended Audience :: Science/Research
946
+ Classifier: Intended Audience :: Developers
947
+ Classifier: License :: OSI Approved :: BSD License
948
+ Classifier: Programming Language :: C
949
+ Classifier: Programming Language :: Python
950
+ Classifier: Programming Language :: Python :: 3
951
+ Classifier: Programming Language :: Python :: 3.12
952
+ Classifier: Programming Language :: Python :: 3.13
953
+ Classifier: Programming Language :: Python :: 3.14
954
+ Classifier: Topic :: Software Development :: Libraries
955
+ Classifier: Topic :: Scientific/Engineering
956
+ Classifier: Operating System :: Microsoft :: Windows
957
+ Classifier: Operating System :: POSIX :: Linux
958
+ Classifier: Operating System :: POSIX
959
+ Classifier: Operating System :: Unix
960
+ Classifier: Operating System :: MacOS
961
+ Project-URL: homepage, https://scipy.org/
962
+ Project-URL: documentation, https://docs.scipy.org/doc/scipy/
963
+ Project-URL: source, https://github.com/scipy/scipy
964
+ Project-URL: download, https://github.com/scipy/scipy/releases
965
+ Project-URL: tracker, https://github.com/scipy/scipy/issues
966
+ Requires-Python: >=3.12
967
+ Requires-Dist: numpy<2.8,>=2.0.0
968
+ Provides-Extra: test
969
+ Requires-Dist: pytest>=8.0.0; extra == "test"
970
+ Requires-Dist: pytest-cov; extra == "test"
971
+ Requires-Dist: pytest-timeout; extra == "test"
972
+ Requires-Dist: pytest-xdist; extra == "test"
973
+ Requires-Dist: asv; extra == "test"
974
+ Requires-Dist: mpmath; extra == "test"
975
+ Requires-Dist: gmpy2; extra == "test"
976
+ Requires-Dist: threadpoolctl; extra == "test"
977
+ Requires-Dist: scikit-umfpack; extra == "test"
978
+ Requires-Dist: pooch; extra == "test"
979
+ Requires-Dist: hypothesis>=6.30; extra == "test"
980
+ Requires-Dist: array-api-strict>=2.3.1; extra == "test"
981
+ Requires-Dist: Cython; extra == "test"
982
+ Requires-Dist: meson; extra == "test"
983
+ Requires-Dist: ninja; sys_platform != "emscripten" and extra == "test"
984
+ Requires-Dist: scipy-doctest>=2.0.0; extra == "test"
985
+ Provides-Extra: doc
986
+ Requires-Dist: sphinx<8.2.0,>=5.0.0; extra == "doc"
987
+ Requires-Dist: intersphinx_registry; extra == "doc"
988
+ Requires-Dist: pydata-sphinx-theme>=0.15.2; extra == "doc"
989
+ Requires-Dist: sphinx-copybutton; extra == "doc"
990
+ Requires-Dist: sphinx-design>=0.4.0; extra == "doc"
991
+ Requires-Dist: matplotlib>=3.5; extra == "doc"
992
+ Requires-Dist: numpydoc; extra == "doc"
993
+ Requires-Dist: jupytext; extra == "doc"
994
+ Requires-Dist: myst-nb>=1.2.0; extra == "doc"
995
+ Requires-Dist: pooch; extra == "doc"
996
+ Requires-Dist: jupyterlite-sphinx>=0.19.1; extra == "doc"
997
+ Requires-Dist: jupyterlite-pyodide-kernel; extra == "doc"
998
+ Requires-Dist: linkify-it-py; extra == "doc"
999
+ Requires-Dist: tabulate; extra == "doc"
1000
+ Provides-Extra: dev
1001
+ Requires-Dist: click<8.3.0; extra == "dev"
1002
+ Requires-Dist: spin; extra == "dev"
1003
+ Requires-Dist: mypy==1.19.1; extra == "dev"
1004
+ Requires-Dist: pyrefly==0.63.0; extra == "dev"
1005
+ Requires-Dist: typing_extensions; extra == "dev"
1006
+ Requires-Dist: types-psutil; extra == "dev"
1007
+ Requires-Dist: pycodestyle; extra == "dev"
1008
+ Requires-Dist: ruff>=0.12.0; extra == "dev"
1009
+ Requires-Dist: cython-lint>=0.12.2; extra == "dev"
1010
+ Description-Content-Type: text/x-rst
1011
+
1012
+ .. image:: https://raw.githubusercontent.com/scipy/scipy/main/doc/source/_static/logo.svg
1013
+ :target: https://scipy.org
1014
+ :width: 110
1015
+ :height: 110
1016
+ :align: left
1017
+
1018
+ .. image:: https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A
1019
+ :target: https://numfocus.org
1020
+
1021
+ .. image:: https://img.shields.io/pypi/dm/scipy.svg?label=Pypi%20downloads
1022
+ :target: https://pypi.org/project/scipy/
1023
+
1024
+ .. image:: https://img.shields.io/conda/dn/conda-forge/scipy.svg?label=Conda%20downloads
1025
+ :target: https://anaconda.org/conda-forge/scipy
1026
+
1027
+ .. image:: https://img.shields.io/badge/stackoverflow-Ask%20questions-blue.svg
1028
+ :target: https://stackoverflow.com/questions/tagged/scipy
1029
+
1030
+ .. image:: https://img.shields.io/badge/DOI-10.1038%2Fs41592--019--0686--2-blue.svg
1031
+ :target: https://www.nature.com/articles/s41592-019-0686-2
1032
+
1033
+ .. image:: https://insights.linuxfoundation.org/api/badge/health-score?project=scipy
1034
+ :target: https://insights.linuxfoundation.org/project/scipy
1035
+
1036
+ SciPy (pronounced "Sigh Pie") is an open-source software for mathematics,
1037
+ science, and engineering. It includes modules for statistics, optimization,
1038
+ integration, linear algebra, Fourier transforms, signal and image processing,
1039
+ ODE solvers, and more.
1040
+
1041
+ - **Website:** https://scipy.org
1042
+ - **Documentation:** https://docs.scipy.org/doc/scipy/
1043
+ - **Development version of the documentation:** https://scipy.github.io/devdocs
1044
+ - **SciPy development forum:** https://discuss.scientific-python.org/c/contributor/scipy
1045
+ - **Stack Overflow:** https://stackoverflow.com/questions/tagged/scipy
1046
+ - **Source code:** https://github.com/scipy/scipy
1047
+ - **Contributing:** https://scipy.github.io/devdocs/dev/index.html
1048
+ - **Bug reports:** https://github.com/scipy/scipy/issues
1049
+ - **Code of Conduct:** https://docs.scipy.org/doc/scipy/dev/conduct/code_of_conduct.html
1050
+ - **Report a security vulnerability:** via Tidelift, as explained in
1051
+ `our docs on Security <https://scipy.github.io/devdocs/tutorial/security.html>`__
1052
+ - **Citing in your work:** https://www.scipy.org/citing-scipy/
1053
+
1054
+ SciPy is built to work with
1055
+ NumPy arrays, and provides many user-friendly and efficient numerical routines,
1056
+ such as routines for numerical integration and optimization. Together, they
1057
+ run on all popular operating systems, are quick to install, and are free of
1058
+ charge. NumPy and SciPy are easy to use, but powerful enough to be depended
1059
+ upon by some of the world's leading scientists and engineers. If you need to
1060
+ manipulate numbers on a computer and display or publish the results, give
1061
+ SciPy a try!
1062
+
1063
+ For the installation instructions, see `our install
1064
+ guide <https://scipy.org/install/>`__.
1065
+
1066
+
1067
+ Call for Contributions
1068
+ ----------------------
1069
+
1070
+ We appreciate and welcome contributions. Small improvements or fixes are always appreciated; issues labeled as "good
1071
+ first issue" may be a good starting point. Have a look at `our contributing
1072
+ guide <https://scipy.github.io/devdocs/dev/index.html>`__.
1073
+
1074
+ Writing code isn’t the only way to contribute to SciPy. You can also:
1075
+
1076
+ - review pull requests
1077
+ - triage issues
1078
+ - develop tutorials, presentations, and other educational materials
1079
+ - maintain and improve `our website <https://github.com/scipy/scipy.org>`__
1080
+ - develop graphic design for our brand assets and promotional materials
1081
+ - help with outreach and onboard new contributors
1082
+ - write grant proposals and help with other fundraising efforts
1083
+
1084
+ If you’re unsure where to start or how your skills fit in, reach out! You can
1085
+ ask on the `forum <https://discuss.scientific-python.org/c/contributor/scipy>`__
1086
+ or here, on GitHub, by leaving a comment on a relevant issue that is already
1087
+ open.
1088
+
1089
+ If you are new to contributing to open source, `this
1090
+ guide <https://opensource.guide/how-to-contribute/>`__ helps explain why, what,
1091
+ and how to get involved.
venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/RECORD ADDED
The diff for this file is too large to render. See raw diff
 
venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/WHEEL ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ Wheel-Version: 1.0
2
+ Generator: meson
3
+ Root-Is-Purelib: false
4
+ Tag: cp312-cp312-manylinux_2_27_x86_64
5
+ Tag: cp312-cp312-manylinux_2_28_x86_64
6
+
venv/lib/python3.12/site-packages/scipy-1.18.0.dist-info/sboms/auditwheel.cdx.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"bomFormat": "CycloneDX", "specVersion": "1.4", "version": 1, "metadata": {"component": {"type": "library", "bom-ref": "pkg:pypi/scipy@1.18.0?file_name=scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", "name": "scipy", "version": "1.18.0", "purl": "pkg:pypi/scipy@1.18.0?file_name=scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl"}, "tools": [{"name": "auditwheel", "version": "6.6.0"}]}, "components": [{"type": "library", "bom-ref": "pkg:pypi/scipy@1.18.0?file_name=scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", "name": "scipy", "version": "1.18.0", "purl": "pkg:pypi/scipy@1.18.0?file_name=scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl"}, {"type": "library", "bom-ref": "pkg:rpm/almalinux/libgfortran@8.5.0-28.el8_10.alma.1#3362720ce454cc402813e3cfe54f0eb4efaedcd690fa35129ddc4b3de40e0650", "name": "libgfortran", "version": "8.5.0-28.el8_10.alma.1", "purl": "pkg:rpm/almalinux/libgfortran@8.5.0-28.el8_10.alma.1"}, {"type": "library", "bom-ref": "pkg:rpm/almalinux/libquadmath@8.5.0-28.el8_10.alma.1#97b91febe2a1c7927c6482d6bd6567b83be3467f33c9d2764df1283f7ac9a5fa", "name": "libquadmath", "version": "8.5.0-28.el8_10.alma.1", "purl": "pkg:rpm/almalinux/libquadmath@8.5.0-28.el8_10.alma.1"}], "dependencies": [{"ref": "pkg:pypi/scipy@1.18.0?file_name=scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", "dependsOn": ["pkg:rpm/almalinux/libgfortran@8.5.0-28.el8_10.alma.1#3362720ce454cc402813e3cfe54f0eb4efaedcd690fa35129ddc4b3de40e0650", "pkg:rpm/almalinux/libquadmath@8.5.0-28.el8_10.alma.1#97b91febe2a1c7927c6482d6bd6567b83be3467f33c9d2764df1283f7ac9a5fa"]}, {"ref": "pkg:rpm/almalinux/libgfortran@8.5.0-28.el8_10.alma.1#3362720ce454cc402813e3cfe54f0eb4efaedcd690fa35129ddc4b3de40e0650"}, {"ref": "pkg:rpm/almalinux/libquadmath@8.5.0-28.el8_10.alma.1#97b91febe2a1c7927c6482d6bd6567b83be3467f33c9d2764df1283f7ac9a5fa"}]}
venv/lib/python3.12/site-packages/scipy.libs/libgfortran-040039e1-0352e75f.so.5.0.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c6090048eccc763522c12ef016f81da6b627cb3a044f55cf0479a839c41c0980
3
+ size 2833617
venv/lib/python3.12/site-packages/scipy.libs/libgfortran-83c28eba.so.5.0.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e554879fee7eda80055b6ca1407a54d00050874f830cc86b2bf3f50df5c7bb2b
3
+ size 2714697
venv/lib/python3.12/site-packages/scipy.libs/libquadmath-2284e583.so.0.0.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a9307bbaa353623fa014e8b682d50c4629f5e5c7d61daa9a8652f7d165cd421a
3
+ size 272193
venv/lib/python3.12/site-packages/scipy.libs/libquadmath-96973f99-934c22de.so.0.0.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ed5137f412781ad7863439fb543613f620b43c32b63292a0029246162f5bbc6
3
+ size 250985
venv/lib/python3.12/site-packages/scipy.libs/libscipy_openblas-5f890258.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2a5b4c1a6311873c59132b2ceaaf3c26258a17350b7cd27ea8782a47e3322981
3
+ size 25094609
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_filter_design.cpython-312.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:55b921e2782ba8dd39a88c30f0018319dd4f72999e6b0fad987bcd6443c1da37
3
+ size 233862
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_fir_filter_design.cpython-312.pyc ADDED
Binary file (67 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_lti_conversion.cpython-312.pyc ADDED
Binary file (23.1 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_ltisys.cpython-312.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e3d43b2ac7e4040326b82b41dbbfb5a2581ef321e5e83d544f18dd4ebd289e2d
3
+ size 145690
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_max_len_seq.cpython-312.pyc ADDED
Binary file (6.18 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_peak_finding.cpython-312.pyc ADDED
Binary file (50.2 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_polyutils.cpython-312.pyc ADDED
Binary file (10.9 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_savitzky_golay.cpython-312.pyc ADDED
Binary file (14.8 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_short_time_fft.cpython-312.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:954defa35a6f080d3bf7ff3f9f31555eb33020f918c684404d8b2f9c9d3e1e81
3
+ size 115538
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_signal_api.cpython-312.pyc ADDED
Binary file (1.29 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_signaltools.cpython-312.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b989c729845c9a21a61a26113ca3ebd61813efbed4b8269a7c58bcb4ece0e6c9
3
+ size 216301
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_spectral_py.cpython-312.pyc ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:461e16d5df558b5e59dee9847ba64640edbd082489ea5732f7bac9f29ed244df
3
+ size 100138
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_spline_filters.cpython-312.pyc ADDED
Binary file (32.3 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_support_alternative_backends.cpython-312.pyc ADDED
Binary file (11.9 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_upfirdn.cpython-312.pyc ADDED
Binary file (8.09 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_waveforms.cpython-312.pyc ADDED
Binary file (24.7 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_wavelets.cpython-312.pyc ADDED
Binary file (1.88 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/_whittaker.cpython-312.pyc ADDED
Binary file (17.3 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/bsplines.cpython-312.pyc ADDED
Binary file (768 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/filter_design.cpython-312.pyc ADDED
Binary file (1.13 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/fir_filter_design.cpython-312.pyc ADDED
Binary file (781 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/lti_conversion.cpython-312.pyc ADDED
Binary file (765 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/ltisys.cpython-312.pyc ADDED
Binary file (924 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/signaltools.cpython-312.pyc ADDED
Binary file (1.08 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/spectral.cpython-312.pyc ADDED
Binary file (772 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/spline.cpython-312.pyc ADDED
Binary file (659 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/waveforms.cpython-312.pyc ADDED
Binary file (723 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/__pycache__/wavelets.cpython-312.pyc ADDED
Binary file (709 Bytes). View file
 
venv/lib/python3.12/site-packages/scipy/signal/_arraytools.py ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Functions for acting on a axis of an array.
3
+ """
4
+ import numpy as np
5
+
6
+
7
+ def axis_slice(a, start=None, stop=None, step=None, axis=-1):
8
+ """Take a slice along axis 'axis' from 'a'.
9
+
10
+ Parameters
11
+ ----------
12
+ a : numpy.ndarray
13
+ The array to be sliced.
14
+ start, stop, step : int or None
15
+ The slice parameters.
16
+ axis : int, optional
17
+ The axis of `a` to be sliced.
18
+
19
+ Examples
20
+ --------
21
+ >>> import numpy as np
22
+ >>> from scipy.signal._arraytools import axis_slice
23
+ >>> a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
24
+ >>> axis_slice(a, start=0, stop=1, axis=1)
25
+ array([[1],
26
+ [4],
27
+ [7]])
28
+ >>> axis_slice(a, start=1, axis=0)
29
+ array([[4, 5, 6],
30
+ [7, 8, 9]])
31
+
32
+ Notes
33
+ -----
34
+ The keyword arguments start, stop and step are used by calling
35
+ slice(start, stop, step). This implies axis_slice() does not
36
+ handle its arguments the exactly the same as indexing. To select
37
+ a single index k, for example, use
38
+ axis_slice(a, start=k, stop=k+1)
39
+ In this case, the length of the axis 'axis' in the result will
40
+ be 1; the trivial dimension is not removed. (Use numpy.squeeze()
41
+ to remove trivial axes.)
42
+ """
43
+ a_slice = [slice(None)] * a.ndim
44
+ a_slice[axis] = slice(start, stop, step)
45
+ b = a[tuple(a_slice)]
46
+ return b
47
+
48
+
49
+ def axis_reverse(a, axis=-1):
50
+ """Reverse the 1-D slices of `a` along axis `axis`.
51
+
52
+ Returns axis_slice(a, step=-1, axis=axis).
53
+ """
54
+ return axis_slice(a, step=-1, axis=axis)
55
+
56
+
57
+ def odd_ext(x, n, axis=-1):
58
+ """
59
+ Odd extension at the boundaries of an array
60
+
61
+ Generate a new ndarray by making an odd extension of `x` along an axis.
62
+
63
+ Parameters
64
+ ----------
65
+ x : ndarray
66
+ The array to be extended.
67
+ n : int
68
+ The number of elements by which to extend `x` at each end of the axis.
69
+ axis : int, optional
70
+ The axis along which to extend `x`. Default is -1.
71
+
72
+ Examples
73
+ --------
74
+ >>> import numpy as np
75
+ >>> from scipy.signal._arraytools import odd_ext
76
+ >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]])
77
+ >>> odd_ext(a, 2)
78
+ array([[-1, 0, 1, 2, 3, 4, 5, 6, 7],
79
+ [-4, -1, 0, 1, 4, 9, 16, 23, 28]])
80
+
81
+ Odd extension is a "180 degree rotation" at the endpoints of the original
82
+ array:
83
+
84
+ >>> t = np.linspace(0, 1.5, 100)
85
+ >>> a = 0.9 * np.sin(2 * np.pi * t**2)
86
+ >>> b = odd_ext(a, 40)
87
+ >>> import matplotlib.pyplot as plt
88
+ >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='odd extension')
89
+ >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original')
90
+ >>> plt.legend(loc='best')
91
+ >>> plt.show()
92
+ """
93
+ if n < 1:
94
+ return x
95
+ if n > x.shape[axis] - 1:
96
+ raise ValueError(("The extension length n (%d) is too big. " +
97
+ "It must not exceed x.shape[axis]-1, which is %d.")
98
+ % (n, x.shape[axis] - 1))
99
+ left_end = axis_slice(x, start=0, stop=1, axis=axis)
100
+ left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis)
101
+ right_end = axis_slice(x, start=-1, axis=axis)
102
+ right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis)
103
+ ext = np.concatenate((2 * left_end - left_ext,
104
+ x,
105
+ 2 * right_end - right_ext),
106
+ axis=axis)
107
+ return ext
108
+
109
+
110
+ def even_ext(x, n, axis=-1):
111
+ """
112
+ Even extension at the boundaries of an array
113
+
114
+ Generate a new ndarray by making an even extension of `x` along an axis.
115
+
116
+ Parameters
117
+ ----------
118
+ x : ndarray
119
+ The array to be extended.
120
+ n : int
121
+ The number of elements by which to extend `x` at each end of the axis.
122
+ axis : int, optional
123
+ The axis along which to extend `x`. Default is -1.
124
+
125
+ Examples
126
+ --------
127
+ >>> import numpy as np
128
+ >>> from scipy.signal._arraytools import even_ext
129
+ >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]])
130
+ >>> even_ext(a, 2)
131
+ array([[ 3, 2, 1, 2, 3, 4, 5, 4, 3],
132
+ [ 4, 1, 0, 1, 4, 9, 16, 9, 4]])
133
+
134
+ Even extension is a "mirror image" at the boundaries of the original array:
135
+
136
+ >>> t = np.linspace(0, 1.5, 100)
137
+ >>> a = 0.9 * np.sin(2 * np.pi * t**2)
138
+ >>> b = even_ext(a, 40)
139
+ >>> import matplotlib.pyplot as plt
140
+ >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='even extension')
141
+ >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original')
142
+ >>> plt.legend(loc='best')
143
+ >>> plt.show()
144
+ """
145
+ if n < 1:
146
+ return x
147
+ if n > x.shape[axis] - 1:
148
+ raise ValueError(("The extension length n (%d) is too big. " +
149
+ "It must not exceed x.shape[axis]-1, which is %d.")
150
+ % (n, x.shape[axis] - 1))
151
+ left_ext = axis_slice(x, start=n, stop=0, step=-1, axis=axis)
152
+ right_ext = axis_slice(x, start=-2, stop=-(n + 2), step=-1, axis=axis)
153
+ ext = np.concatenate((left_ext,
154
+ x,
155
+ right_ext),
156
+ axis=axis)
157
+ return ext
158
+
159
+
160
+ def const_ext(x, n, axis=-1):
161
+ """
162
+ Constant extension at the boundaries of an array
163
+
164
+ Generate a new ndarray that is a constant extension of `x` along an axis.
165
+
166
+ The extension repeats the values at the first and last element of
167
+ the axis.
168
+
169
+ Parameters
170
+ ----------
171
+ x : ndarray
172
+ The array to be extended.
173
+ n : int
174
+ The number of elements by which to extend `x` at each end of the axis.
175
+ axis : int, optional
176
+ The axis along which to extend `x`. Default is -1.
177
+
178
+ Examples
179
+ --------
180
+ >>> import numpy as np
181
+ >>> from scipy.signal._arraytools import const_ext
182
+ >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]])
183
+ >>> const_ext(a, 2)
184
+ array([[ 1, 1, 1, 2, 3, 4, 5, 5, 5],
185
+ [ 0, 0, 0, 1, 4, 9, 16, 16, 16]])
186
+
187
+ Constant extension continues with the same values as the endpoints of the
188
+ array:
189
+
190
+ >>> t = np.linspace(0, 1.5, 100)
191
+ >>> a = 0.9 * np.sin(2 * np.pi * t**2)
192
+ >>> b = const_ext(a, 40)
193
+ >>> import matplotlib.pyplot as plt
194
+ >>> plt.plot(np.arange(-40, 140), b, 'b', lw=1, label='constant extension')
195
+ >>> plt.plot(np.arange(100), a, 'r', lw=2, label='original')
196
+ >>> plt.legend(loc='best')
197
+ >>> plt.show()
198
+ """
199
+ if n < 1:
200
+ return x
201
+ left_end = axis_slice(x, start=0, stop=1, axis=axis)
202
+ ones_shape = [1] * x.ndim
203
+ ones_shape[axis] = n
204
+ ones = np.ones(ones_shape, dtype=x.dtype)
205
+ left_ext = ones * left_end
206
+ right_end = axis_slice(x, start=-1, axis=axis)
207
+ right_ext = ones * right_end
208
+ ext = np.concatenate((left_ext,
209
+ x,
210
+ right_ext),
211
+ axis=axis)
212
+ return ext
213
+
214
+
215
+ def zero_ext(x, n, axis=-1):
216
+ """
217
+ Zero padding at the boundaries of an array
218
+
219
+ Generate a new ndarray that is a zero-padded extension of `x` along
220
+ an axis.
221
+
222
+ Parameters
223
+ ----------
224
+ x : ndarray
225
+ The array to be extended.
226
+ n : int
227
+ The number of elements by which to extend `x` at each end of the
228
+ axis.
229
+ axis : int, optional
230
+ The axis along which to extend `x`. Default is -1.
231
+
232
+ Examples
233
+ --------
234
+ >>> import numpy as np
235
+ >>> from scipy.signal._arraytools import zero_ext
236
+ >>> a = np.array([[1, 2, 3, 4, 5], [0, 1, 4, 9, 16]])
237
+ >>> zero_ext(a, 2)
238
+ array([[ 0, 0, 1, 2, 3, 4, 5, 0, 0],
239
+ [ 0, 0, 0, 1, 4, 9, 16, 0, 0]])
240
+ """
241
+ if n < 1:
242
+ return x
243
+ zeros_shape = list(x.shape)
244
+ zeros_shape[axis] = n
245
+ zeros = np.zeros(zeros_shape, dtype=x.dtype)
246
+ ext = np.concatenate((zeros, x, zeros), axis=axis)
247
+ return ext
248
+
249
+
250
+ def _validate_fs(fs, allow_none=True):
251
+ """
252
+ Check if the given sampling frequency is a scalar and raises an exception
253
+ otherwise. If allow_none is False, also raises an exception for none
254
+ sampling rates. Returns the sampling frequency as float or none if the
255
+ input is none.
256
+ """
257
+ if fs is None:
258
+ if not allow_none:
259
+ raise ValueError("Sampling frequency can not be none.")
260
+ else: # should be float
261
+ if not np.isscalar(fs):
262
+ raise ValueError("Sampling frequency fs must be a single scalar.")
263
+ fs = float(fs)
264
+ return fs
venv/lib/python3.12/site-packages/scipy/signal/_czt.py ADDED
@@ -0,0 +1,580 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This program is public domain
2
+ # Authors: Paul Kienzle, Nadav Horesh
3
+ """
4
+ Chirp z-transform.
5
+
6
+ We provide two interfaces to the chirp z-transform: an object interface
7
+ which precalculates part of the transform and can be applied efficiently
8
+ to many different data sets, and a functional interface which is applied
9
+ only to the given data set.
10
+
11
+ Transforms
12
+ ----------
13
+
14
+ CZT : callable (x, axis=-1) -> array
15
+ Define a chirp z-transform that can be applied to different signals.
16
+ ZoomFFT : callable (x, axis=-1) -> array
17
+ Define a Fourier transform on a range of frequencies.
18
+
19
+ Functions
20
+ ---------
21
+
22
+ czt : array
23
+ Compute the chirp z-transform for a signal.
24
+ zoom_fft : array
25
+ Compute the Fourier transform on a range of frequencies.
26
+ """
27
+
28
+ import cmath
29
+ import numbers
30
+ import numpy as np
31
+ from numpy import pi, arange
32
+ from scipy.fft import fft, ifft, next_fast_len
33
+
34
+ __all__ = ['czt', 'zoom_fft', 'CZT', 'ZoomFFT', 'czt_points']
35
+
36
+
37
+ def _validate_sizes(n, m):
38
+ if n < 1 or not isinstance(n, numbers.Integral):
39
+ raise ValueError('Invalid number of CZT data '
40
+ f'points ({n}) specified. '
41
+ 'n must be positive and integer type.')
42
+
43
+ if m is None:
44
+ m = n
45
+ elif m < 1 or not isinstance(m, numbers.Integral):
46
+ raise ValueError('Invalid number of CZT output '
47
+ f'points ({m}) specified. '
48
+ 'm must be positive and integer type.')
49
+
50
+ return m
51
+
52
+
53
+ def czt_points(m, w=None, a=1+0j):
54
+ """
55
+ Return the points at which the chirp z-transform is computed.
56
+
57
+ Parameters
58
+ ----------
59
+ m : int
60
+ The number of points desired.
61
+ w : complex, optional
62
+ The ratio between points in each step.
63
+ Defaults to equally spaced points around the entire unit circle.
64
+ a : complex, optional
65
+ The starting point in the complex plane. Default is 1+0j.
66
+
67
+ Returns
68
+ -------
69
+ out : ndarray
70
+ The points in the Z plane at which `CZT` samples the z-transform,
71
+ when called with arguments `m`, `w`, and `a`, as complex numbers.
72
+
73
+ See Also
74
+ --------
75
+ CZT : Class that creates a callable chirp z-transform function.
76
+ czt : Convenience function for quickly calculating CZT.
77
+
78
+ Examples
79
+ --------
80
+ Plot the points of a 16-point FFT:
81
+
82
+ >>> import numpy as np
83
+ >>> from scipy.signal import czt_points
84
+ >>> points = czt_points(16)
85
+ >>> import matplotlib.pyplot as plt
86
+ >>> plt.plot(points.real, points.imag, 'o')
87
+ >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3))
88
+ >>> plt.axis('equal')
89
+ >>> plt.show()
90
+
91
+ and a 91-point logarithmic spiral that crosses the unit circle:
92
+
93
+ >>> m, w, a = 91, 0.995*np.exp(-1j*np.pi*.05), 0.8*np.exp(1j*np.pi/6)
94
+ >>> points = czt_points(m, w, a)
95
+ >>> plt.plot(points.real, points.imag, 'o')
96
+ >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3))
97
+ >>> plt.axis('equal')
98
+ >>> plt.show()
99
+ """
100
+ m = _validate_sizes(1, m)
101
+
102
+ k = arange(m)
103
+
104
+ a = 1.0 * a # at least float
105
+
106
+ if w is None:
107
+ # Nothing specified, default to FFT
108
+ return a * np.exp(2j * pi * k / m)
109
+ else:
110
+ # w specified
111
+ w = 1.0 * w # at least float
112
+ return a * w**-k
113
+
114
+
115
+ class CZT:
116
+ """
117
+ Create a callable chirp z-transform function.
118
+
119
+ Transform to compute the frequency response around a spiral.
120
+ Objects of this class are callables which can compute the
121
+ chirp z-transform on their inputs. This object precalculates the constant
122
+ chirps used in the given transform.
123
+
124
+ Parameters
125
+ ----------
126
+ n : int
127
+ The size of the signal.
128
+ m : int, optional
129
+ The number of output points desired. Default is `n`.
130
+ w : complex, optional
131
+ The ratio between points in each step. This must be precise or the
132
+ accumulated error will degrade the tail of the output sequence.
133
+ Defaults to equally spaced points around the entire unit circle.
134
+ a : complex, optional
135
+ The starting point in the complex plane. Default is 1+0j.
136
+
137
+ Returns
138
+ -------
139
+ f : CZT
140
+ Callable object ``f(x, axis=-1)`` for computing the chirp z-transform
141
+ on `x`.
142
+
143
+ See Also
144
+ --------
145
+ czt : Convenience function for quickly calculating CZT.
146
+ ZoomFFT : Class that creates a callable partial FFT function.
147
+
148
+ Notes
149
+ -----
150
+ The defaults are chosen such that ``f(x)`` is equivalent to
151
+ ``fft.fft(x)`` and, if ``m > len(x)``, that ``f(x, m)`` is equivalent to
152
+ ``fft.fft(x, m)``.
153
+
154
+ If `w` does not lie on the unit circle, then the transform will be
155
+ around a spiral with exponentially-increasing radius. Regardless,
156
+ angle will increase linearly.
157
+
158
+ For transforms that do lie on the unit circle, accuracy is better when
159
+ using `ZoomFFT`, since any numerical error in `w` is
160
+ accumulated for long data lengths, drifting away from the unit circle.
161
+
162
+ The chirp z-transform can be faster than an equivalent FFT with
163
+ zero padding. Try it with your own array sizes to see.
164
+
165
+ However, the chirp z-transform is considerably less precise than the
166
+ equivalent zero-padded FFT.
167
+
168
+ As this CZT is implemented using the Bluestein algorithm, it can compute
169
+ large prime-length Fourier transforms in O(N log N) time, rather than the
170
+ O(N**2) time required by the direct DFT calculation. (`scipy.fft` also
171
+ uses Bluestein's algorithm'.)
172
+
173
+ (The name "chirp z-transform" comes from the use of a chirp in the
174
+ Bluestein algorithm. It does not decompose signals into chirps, like
175
+ other transforms with "chirp" in the name.)
176
+
177
+ References
178
+ ----------
179
+ .. [1] Leo I. Bluestein, "A linear filtering approach to the computation
180
+ of the discrete Fourier transform," Northeast Electronics Research
181
+ and Engineering Meeting Record 10, 218-219 (1968).
182
+ .. [2] Rabiner, Schafer, and Rader, "The chirp z-transform algorithm and
183
+ its application," Bell Syst. Tech. J. 48, 1249-1292 (1969).
184
+
185
+ Examples
186
+ --------
187
+ Compute multiple prime-length FFTs:
188
+
189
+ >>> from scipy.signal import CZT
190
+ >>> import numpy as np
191
+ >>> a = np.random.rand(7)
192
+ >>> b = np.random.rand(7)
193
+ >>> c = np.random.rand(7)
194
+ >>> czt_7 = CZT(n=7)
195
+ >>> A = czt_7(a)
196
+ >>> B = czt_7(b)
197
+ >>> C = czt_7(c)
198
+
199
+ Display the points at which the FFT is calculated:
200
+
201
+ >>> czt_7.points()
202
+ array([ 1.00000000+0.j , 0.62348980+0.78183148j,
203
+ -0.22252093+0.97492791j, -0.90096887+0.43388374j,
204
+ -0.90096887-0.43388374j, -0.22252093-0.97492791j,
205
+ 0.62348980-0.78183148j])
206
+ >>> import matplotlib.pyplot as plt
207
+ >>> plt.plot(czt_7.points().real, czt_7.points().imag, 'o')
208
+ >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3))
209
+ >>> plt.axis('equal')
210
+ >>> plt.show()
211
+ """
212
+
213
+ def __init__(self, n, m=None, w=None, a=1+0j):
214
+ m = _validate_sizes(n, m)
215
+
216
+ k = arange(max(m, n), dtype=np.min_scalar_type(-max(m, n)**2))
217
+
218
+ if w is None:
219
+ # Nothing specified, default to FFT-like
220
+ w = cmath.exp(-2j*pi/m)
221
+ wk2 = np.exp(-(1j * pi * ((k**2) % (2*m))) / m)
222
+ else:
223
+ # w specified
224
+ wk2 = w**(k**2/2.)
225
+
226
+ a = 1.0 * a # at least float
227
+
228
+ self.w, self.a = w, a
229
+ self.m, self.n = m, n
230
+
231
+ nfft = next_fast_len(n + m - 1)
232
+ self._Awk2 = a**-k[:n] * wk2[:n]
233
+ self._nfft = nfft
234
+ self._Fwk2 = fft(1/np.hstack((wk2[n-1:0:-1], wk2[:m])), nfft)
235
+ self._wk2 = wk2[:m]
236
+ self._yidx = slice(n-1, n+m-1)
237
+
238
+ def __call__(self, x, *, axis=-1):
239
+ """
240
+ Calculate the chirp z-transform of a signal.
241
+
242
+ Parameters
243
+ ----------
244
+ x : array
245
+ The signal to transform.
246
+ axis : int, optional
247
+ Axis over which to compute the FFT. If not given, the last axis is
248
+ used.
249
+
250
+ Returns
251
+ -------
252
+ out : ndarray
253
+ An array of the same dimensions as `x`, but with the length of the
254
+ transformed axis set to `m`.
255
+ """
256
+ x = np.asarray(x)
257
+ if x.shape[axis] != self.n:
258
+ raise ValueError(f"CZT defined for length {self.n}, not "
259
+ f"{x.shape[axis]}")
260
+ # Calculate transpose coordinates, to allow operation on any given axis
261
+ trnsp = np.arange(x.ndim)
262
+ trnsp[[axis, -1]] = [-1, axis]
263
+ x = x.transpose(*trnsp)
264
+ y = ifft(self._Fwk2 * fft(x*self._Awk2, self._nfft))
265
+ y = y[..., self._yidx] * self._wk2
266
+ return y.transpose(*trnsp)
267
+
268
+ def points(self):
269
+ """
270
+ Return the points at which the chirp z-transform is computed.
271
+
272
+ Returns
273
+ -------
274
+ ndarray
275
+ The points in the Z plane at which `CZT` samples the z-transform.
276
+ """
277
+ return czt_points(self.m, self.w, self.a)
278
+
279
+
280
+ class ZoomFFT(CZT):
281
+ """
282
+ Create a callable zoom FFT transform function.
283
+
284
+ This is a specialization of the chirp z-transform (`CZT`) for a set of
285
+ equally-spaced frequencies around the unit circle, used to calculate a
286
+ section of the FFT more efficiently than calculating the entire FFT and
287
+ truncating.
288
+
289
+ Parameters
290
+ ----------
291
+ n : int
292
+ The size of the signal.
293
+ fn : array_like
294
+ A length-2 sequence [`f1`, `f2`] giving the frequency range, or a
295
+ scalar, for which the range [0, `fn`] is assumed.
296
+ m : int, optional
297
+ The number of points to evaluate. Default is `n`.
298
+ fs : float, optional
299
+ The sampling frequency. If ``fs=10`` represented 10 kHz, for example,
300
+ then `f1` and `f2` would also be given in kHz.
301
+ The default sampling frequency is 2, so `f1` and `f2` should be
302
+ in the range [0, 1] to keep the transform below the Nyquist
303
+ frequency.
304
+ endpoint : bool, optional
305
+ If True, `f2` is the last sample. Otherwise, it is not included.
306
+ Default is False.
307
+
308
+ Returns
309
+ -------
310
+ f : ZoomFFT
311
+ Callable object ``f(x, axis=-1)`` for computing the zoom FFT on `x`.
312
+
313
+ See Also
314
+ --------
315
+ zoom_fft : Convenience function for calculating a zoom FFT.
316
+
317
+ Notes
318
+ -----
319
+ The defaults are chosen such that ``f(x, 2)`` is equivalent to
320
+ ``fft.fft(x)`` and, if ``m > len(x)``, that ``f(x, 2, m)`` is equivalent to
321
+ ``fft.fft(x, m)``.
322
+
323
+ Sampling frequency is 1/dt, the time step between samples in the
324
+ signal `x`. The unit circle corresponds to frequencies from 0 up
325
+ to the sampling frequency. The default sampling frequency of 2
326
+ means that `f1`, `f2` values up to the Nyquist frequency are in the
327
+ range [0, 1). For `f1`, `f2` values expressed in radians, a sampling
328
+ frequency of 2*pi should be used.
329
+
330
+ Remember that a zoom FFT can only interpolate the points of the existing
331
+ FFT. It cannot help to resolve two separate nearby frequencies.
332
+ Frequency resolution can only be increased by increasing acquisition
333
+ time.
334
+
335
+ These functions are implemented using Bluestein's algorithm (as is
336
+ `scipy.fft`). [2]_
337
+
338
+ References
339
+ ----------
340
+ .. [1] Steve Alan Shilling, "A study of the chirp z-transform and its
341
+ applications", pg 29 (1970)
342
+ https://krex.k-state.edu/dspace/bitstream/handle/2097/7844/LD2668R41972S43.pdf
343
+ .. [2] Leo I. Bluestein, "A linear filtering approach to the computation
344
+ of the discrete Fourier transform," Northeast Electronics Research
345
+ and Engineering Meeting Record 10, 218-219 (1968).
346
+
347
+ Examples
348
+ --------
349
+ To plot the transform results use something like the following:
350
+
351
+ >>> import numpy as np
352
+ >>> from scipy.signal import ZoomFFT
353
+ >>> t = np.linspace(0, 1, 1021)
354
+ >>> x = np.cos(2*np.pi*15*t) + np.sin(2*np.pi*17*t)
355
+ >>> f1, f2 = 5, 27
356
+ >>> transform = ZoomFFT(len(x), [f1, f2], len(x), fs=1021)
357
+ >>> X = transform(x)
358
+ >>> f = np.linspace(f1, f2, len(x))
359
+ >>> import matplotlib.pyplot as plt
360
+ >>> plt.plot(f, 20*np.log10(np.abs(X)))
361
+ >>> plt.show()
362
+ """
363
+
364
+ def __init__(self, n, fn, m=None, *, fs=2, endpoint=False):
365
+ m = _validate_sizes(n, m)
366
+
367
+ k = arange(max(m, n), dtype=np.min_scalar_type(-max(m, n)**2))
368
+
369
+ if np.size(fn) == 2:
370
+ f1, f2 = fn
371
+ elif np.size(fn) == 1:
372
+ f1, f2 = 0.0, fn
373
+ else:
374
+ raise ValueError('fn must be a scalar or 2-length sequence')
375
+
376
+ self.f1, self.f2, self.fs = f1, f2, fs
377
+
378
+ if endpoint:
379
+ scale = ((f2 - f1) * m) / (fs * (m - 1))
380
+ else:
381
+ scale = (f2 - f1) / fs
382
+ a = cmath.exp(2j * pi * f1/fs)
383
+ wk2 = np.exp(-(1j * pi * scale * k**2) / m)
384
+
385
+ self.w = cmath.exp(-2j*pi/m * scale)
386
+ self.a = a
387
+ self.m, self.n = m, n
388
+
389
+ ak = np.exp(-2j * pi * f1/fs * k[:n])
390
+ self._Awk2 = ak * wk2[:n]
391
+
392
+ nfft = next_fast_len(n + m - 1)
393
+ self._nfft = nfft
394
+ self._Fwk2 = fft(1/np.hstack((wk2[n-1:0:-1], wk2[:m])), nfft)
395
+ self._wk2 = wk2[:m]
396
+ self._yidx = slice(n-1, n+m-1)
397
+
398
+
399
+ def czt(x, m=None, w=None, a=1+0j, *, axis=-1):
400
+ """
401
+ Compute the frequency response around a spiral in the Z plane.
402
+
403
+ Parameters
404
+ ----------
405
+ x : array
406
+ The signal to transform.
407
+ m : int, optional
408
+ The number of output points desired. Default is the length of the
409
+ input data.
410
+ w : complex, optional
411
+ The ratio between points in each step. This must be precise or the
412
+ accumulated error will degrade the tail of the output sequence.
413
+ Defaults to equally spaced points around the entire unit circle.
414
+ a : complex, optional
415
+ The starting point in the complex plane. Default is 1+0j.
416
+ axis : int, optional
417
+ Axis over which to compute the FFT. If not given, the last axis is
418
+ used.
419
+
420
+ Returns
421
+ -------
422
+ out : ndarray
423
+ An array of the same dimensions as `x`, but with the length of the
424
+ transformed axis set to `m`.
425
+
426
+ See Also
427
+ --------
428
+ CZT : Class that creates a callable chirp z-transform function.
429
+ zoom_fft : Convenience function for partial FFT calculations.
430
+
431
+ Notes
432
+ -----
433
+ The defaults are chosen such that ``signal.czt(x)`` is equivalent to
434
+ ``fft.fft(x)`` and, if ``m > len(x)``, that ``signal.czt(x, m)`` is
435
+ equivalent to ``fft.fft(x, m)``.
436
+
437
+ If the transform needs to be repeated, use `CZT` to construct a
438
+ specialized transform function which can be reused without
439
+ recomputing constants.
440
+
441
+ An example application is in system identification, repeatedly evaluating
442
+ small slices of the z-transform of a system, around where a pole is
443
+ expected to exist, to refine the estimate of the pole's true location. [1]_
444
+
445
+ References
446
+ ----------
447
+ .. [1] Steve Alan Shilling, "A study of the chirp z-transform and its
448
+ applications", pg 20 (1970)
449
+ https://krex.k-state.edu/dspace/bitstream/handle/2097/7844/LD2668R41972S43.pdf
450
+
451
+ Examples
452
+ --------
453
+ Generate a sinusoid:
454
+
455
+ >>> import numpy as np
456
+ >>> f1, f2, fs = 8, 10, 200 # Hz
457
+ >>> t = np.linspace(0, 1, fs, endpoint=False)
458
+ >>> x = np.sin(2*np.pi*t*f2)
459
+ >>> import matplotlib.pyplot as plt
460
+ >>> plt.plot(t, x)
461
+ >>> plt.axis([0, 1, -1.1, 1.1])
462
+ >>> plt.show()
463
+
464
+ Its discrete Fourier transform has all of its energy in a single frequency
465
+ bin:
466
+
467
+ >>> from scipy.fft import rfft, rfftfreq
468
+ >>> from scipy.signal import czt, czt_points
469
+ >>> plt.plot(rfftfreq(fs, 1/fs), abs(rfft(x)))
470
+ >>> plt.margins(0, 0.1)
471
+ >>> plt.show()
472
+
473
+ However, if the sinusoid is logarithmically-decaying:
474
+
475
+ >>> x = np.exp(-t*f1) * np.sin(2*np.pi*t*f2)
476
+ >>> plt.plot(t, x)
477
+ >>> plt.axis([0, 1, -1.1, 1.1])
478
+ >>> plt.show()
479
+
480
+ the DFT will have spectral leakage:
481
+
482
+ >>> plt.plot(rfftfreq(fs, 1/fs), abs(rfft(x)))
483
+ >>> plt.margins(0, 0.1)
484
+ >>> plt.show()
485
+
486
+ While the DFT always samples the z-transform around the unit circle, the
487
+ chirp z-transform allows us to sample the Z-transform along any
488
+ logarithmic spiral, such as a circle with radius smaller than unity:
489
+
490
+ >>> M = fs // 2 # Just positive frequencies, like rfft
491
+ >>> a = np.exp(-f1/fs) # Starting point of the circle, radius < 1
492
+ >>> w = np.exp(-1j*np.pi/M) # "Step size" of circle
493
+ >>> points = czt_points(M + 1, w, a) # M + 1 to include Nyquist
494
+ >>> plt.plot(points.real, points.imag, '.')
495
+ >>> plt.gca().add_patch(plt.Circle((0,0), radius=1, fill=False, alpha=.3))
496
+ >>> plt.axis('equal'); plt.axis([-1.05, 1.05, -0.05, 1.05])
497
+ >>> plt.show()
498
+
499
+ With the correct radius, this transforms the decaying sinusoid (and others
500
+ with the same decay rate) without spectral leakage:
501
+
502
+ >>> z_vals = czt(x, M + 1, w, a) # Include Nyquist for comparison to rfft
503
+ >>> freqs = np.angle(points)*fs/(2*np.pi) # angle = omega, radius = sigma
504
+ >>> plt.plot(freqs, abs(z_vals))
505
+ >>> plt.margins(0, 0.1)
506
+ >>> plt.show()
507
+ """
508
+ x = np.asarray(x)
509
+ transform = CZT(x.shape[axis], m=m, w=w, a=a)
510
+ return transform(x, axis=axis)
511
+
512
+
513
+ def zoom_fft(x, fn, m=None, *, fs=2, endpoint=False, axis=-1):
514
+ """
515
+ Compute the DFT of `x` only for frequencies in range `fn`.
516
+
517
+ Parameters
518
+ ----------
519
+ x : array
520
+ The signal to transform.
521
+ fn : array_like
522
+ A length-2 sequence [`f1`, `f2`] giving the frequency range, or a
523
+ scalar, for which the range [0, `fn`] is assumed.
524
+ m : int, optional
525
+ The number of points to evaluate. The default is the length of `x`.
526
+ fs : float, optional
527
+ The sampling frequency. If ``fs=10`` represented 10 kHz, for example,
528
+ then `f1` and `f2` would also be given in kHz.
529
+ The default sampling frequency is 2, so `f1` and `f2` should be
530
+ in the range [0, 1] to keep the transform below the Nyquist
531
+ frequency.
532
+ endpoint : bool, optional
533
+ If True, `f2` is the last sample. Otherwise, it is not included.
534
+ Default is False.
535
+ axis : int, optional
536
+ Axis over which to compute the FFT. If not given, the last axis is
537
+ used.
538
+
539
+ Returns
540
+ -------
541
+ out : ndarray
542
+ The transformed signal. The Fourier transform will be calculated
543
+ at the points f1, f1+df, f1+2df, ..., f2, where df=(f2-f1)/m.
544
+
545
+ See Also
546
+ --------
547
+ ZoomFFT : Class that creates a callable partial FFT function.
548
+
549
+ Notes
550
+ -----
551
+ The defaults are chosen such that ``signal.zoom_fft(x, 2)`` is equivalent
552
+ to ``fft.fft(x)`` and, if ``m > len(x)``, that ``signal.zoom_fft(x, 2, m)``
553
+ is equivalent to ``fft.fft(x, m)``.
554
+
555
+ To graph the magnitude of the resulting transform, use::
556
+
557
+ plot(linspace(f1, f2, m, endpoint=False), abs(zoom_fft(x, [f1, f2], m)))
558
+
559
+ If the transform needs to be repeated, use `ZoomFFT` to construct
560
+ a specialized transform function which can be reused without
561
+ recomputing constants.
562
+
563
+ Examples
564
+ --------
565
+ To plot the transform results use something like the following:
566
+
567
+ >>> import numpy as np
568
+ >>> from scipy.signal import zoom_fft
569
+ >>> t = np.linspace(0, 1, 1021)
570
+ >>> x = np.cos(2*np.pi*15*t) + np.sin(2*np.pi*17*t)
571
+ >>> f1, f2 = 5, 27
572
+ >>> X = zoom_fft(x, [f1, f2], len(x), fs=1021)
573
+ >>> f = np.linspace(f1, f2, len(x))
574
+ >>> import matplotlib.pyplot as plt
575
+ >>> plt.plot(f, 20*np.log10(np.abs(X)))
576
+ >>> plt.show()
577
+ """
578
+ x = np.asarray(x)
579
+ transform = ZoomFFT(x.shape[axis], fn, m=m, fs=fs, endpoint=endpoint)
580
+ return transform(x, axis=axis)
venv/lib/python3.12/site-packages/scipy/signal/_delegators.py ADDED
@@ -0,0 +1,573 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Delegators for alternative backends in scipy.signal.
2
+
3
+ The signature of `func_signature` must match the signature of signal.func.
4
+ The job of a `func_signature` is to know which arguments of `signal.func`
5
+ are arrays.
6
+
7
+ * signatures are generated by
8
+
9
+ --------------
10
+ import inspect
11
+ from scipy import signal
12
+
13
+ names = [x for x in dir(signal) if not x.startswith('_')]
14
+ objs = [getattr(signal, name) for name in names]
15
+ funcs = [obj for obj in objs if inspect.isroutine(obj)]
16
+
17
+ for func in funcs:
18
+ try:
19
+ sig = inspect.signature(func)
20
+ except ValueError:
21
+ sig = "( FIXME )"
22
+ print(f"def {func.__name__}_signature{sig}:\n\treturn array_namespace(...
23
+ )\n\n")
24
+ ---------------
25
+
26
+ * which arguments to delegate on: manually trawled the documentation for
27
+ array-like and array arguments
28
+
29
+ """
30
+ import numpy as np
31
+ from scipy._lib._array_api import array_namespace, np_compat, is_jax
32
+
33
+
34
+ def _skip_if_lti(arg):
35
+ """Handle `system` arg overloads.
36
+
37
+ ATM, only pass tuples through. Consider updating when cupyx.lti class
38
+ is supported.
39
+ """
40
+ if isinstance(arg, tuple):
41
+ return arg
42
+ else:
43
+ return (None,)
44
+
45
+
46
+ def _skip_if_str_or_tuple(window):
47
+ """Handle `window` being a str or a tuple or an array-like.
48
+ """
49
+ if isinstance(window, str) or isinstance(window, tuple) or callable(window):
50
+ return None
51
+ else:
52
+ return window
53
+
54
+
55
+ def _skip_if_poly1d(arg):
56
+ return None if isinstance(arg, np.poly1d) else arg
57
+
58
+
59
+ ###################
60
+
61
+ def abcd_normalize_signature(A=None, B=None, C=None, D=None):
62
+ return array_namespace(A, B, C, D)
63
+
64
+ def argrelextrema_signature(data, *args, **kwds):
65
+ return array_namespace(data)
66
+
67
+ argrelmax_signature = argrelextrema_signature
68
+ argrelmin_signature = argrelextrema_signature
69
+
70
+
71
+ def band_stop_obj_signature(wp, ind, passb, stopb, gpass, gstop, type):
72
+ return array_namespace(passb, stopb)
73
+
74
+
75
+ def bessel_signature(N, Wn, *args, **kwds):
76
+ return array_namespace(Wn)
77
+
78
+ butter_signature = bessel_signature
79
+
80
+
81
+ def cheby2_signature(N, rs, Wn, *args, **kwds):
82
+ return array_namespace(Wn)
83
+
84
+
85
+ def cheby1_signature(N, rp, Wn, *args, **kwds):
86
+ return array_namespace(Wn)
87
+
88
+
89
+ def ellip_signature(N, rp, rs, Wn, *args, **kwds):
90
+ return array_namespace(Wn)
91
+
92
+
93
+ ########################## XXX: no arrays in, arrays out
94
+ def besselap_signature(N, norm='phase', *, xp=None, device=None):
95
+ return np if xp is None else xp
96
+
97
+
98
+ def buttap_signature(N, *, xp=None, device=None):
99
+ return np if xp is None else xp
100
+
101
+
102
+ def cheb1ap_signature(N, rp, *, xp=None, device=None):
103
+ return np if xp is None else xp
104
+
105
+
106
+ def cheb2ap_signature(N, rs, *, xp=None, device=None):
107
+ return np if xp is None else xp
108
+
109
+ def ellipap_signature(N, rp, rs, *, xp=None, device=None):
110
+ return np if xp is None else xp
111
+
112
+ def correlation_lags_signature(in1_len, in2_len, mode='full'):
113
+ return np
114
+
115
+
116
+ def czt_points_signature(m, w=None, a=(1+0j)):
117
+ return np
118
+
119
+
120
+ def gammatone_signature(
121
+ freq, ftype, order=None, numtaps=None, fs=None, *, xp=None, device=None
122
+ ):
123
+ return np_compat if xp is None else xp
124
+
125
+
126
+ def iircomb_signature(
127
+ w0, Q, ftype='notch', fs=2.0, *, pass_zero=False, xp=None, device=None
128
+ ):
129
+ return np_compat if xp is None else xp
130
+
131
+
132
+ def iirnotch_signature(w0, Q, fs=2.0, *, xp=None, device=None):
133
+ return np if xp is None else xp
134
+
135
+ iirpeak_signature = iirnotch_signature
136
+
137
+
138
+ def savgol_coeffs_signature(
139
+ window_length, polyorder, deriv=0, delta=1.0, pos=None, use='conv',
140
+ *, xp=None, device=None
141
+ ):
142
+ return np if xp is None else xp
143
+
144
+
145
+
146
+ def unit_impulse_signature(shape, idx=None, dtype=float):
147
+ return np
148
+ ############################
149
+
150
+
151
+ def buttord_signature(wp, ws, gpass, gstop, analog=False, fs=None):
152
+ return array_namespace(wp, ws)
153
+
154
+ cheb1ord_signature = buttord_signature
155
+ cheb2ord_signature = buttord_signature
156
+ ellipord_signature = buttord_signature
157
+
158
+
159
+ ########### NB: scalars in, scalars out
160
+ def kaiser_atten_signature(numtaps, width):
161
+ return np
162
+
163
+ def kaiser_beta_signature(a):
164
+ return np
165
+
166
+ def kaiserord_signature(ripple, width):
167
+ return np
168
+
169
+ def get_window_signature(window, Nx, fftbins=True, *, xp=None, device=None):
170
+ return np if xp is None else xp
171
+ #################################
172
+
173
+
174
+ def bode_signature(system, w=None, n=100):
175
+ return array_namespace(*_skip_if_lti(system), w)
176
+
177
+ dbode_signature = bode_signature
178
+
179
+
180
+ def freqresp_signature(system, w=None, n=10000):
181
+ return array_namespace(*_skip_if_lti(system), w)
182
+
183
+ dfreqresp_signature = freqresp_signature
184
+
185
+
186
+ def impulse_signature(system, X0=None, T=None, N=None):
187
+ return array_namespace(*_skip_if_lti(system), X0, T)
188
+
189
+
190
+ def dimpulse_signature(system, x0=None, t=None, n=None):
191
+ return array_namespace(*_skip_if_lti(system), x0, t)
192
+
193
+
194
+ def lsim_signature(system, U, T, X0=None, interp=True):
195
+ return array_namespace(*_skip_if_lti(system), U, T, X0)
196
+
197
+
198
+ def dlsim_signature(system, u, t=None, x0=None):
199
+ return array_namespace(*_skip_if_lti(system), u, t, x0)
200
+
201
+
202
+ def step_signature(system, X0=None, T=None, N=None):
203
+ return array_namespace(*_skip_if_lti(system), X0, T)
204
+
205
+ def dstep_signature(system, x0=None, t=None, n=None):
206
+ return array_namespace(*_skip_if_lti(system), x0, t)
207
+
208
+
209
+ def cont2discrete_signature(system, dt, method='zoh', alpha=None):
210
+ return array_namespace(*_skip_if_lti(system))
211
+
212
+
213
+ def bilinear_signature(b, a, fs=1.0):
214
+ return array_namespace(b, a)
215
+
216
+
217
+ def bilinear_zpk_signature(z, p, k, fs):
218
+ return array_namespace(z, p)
219
+
220
+
221
+ def chirp_signature(t,*args, **kwds):
222
+ return array_namespace(t)
223
+
224
+
225
+ ############## XXX: array-likes in, str out
226
+ def choose_conv_method_signature(in1, in2, *args, **kwds):
227
+ return array_namespace(in1, in2)
228
+ ############################################
229
+
230
+
231
+ def convolve_signature(in1, in2, *args, **kwds):
232
+ return array_namespace(in1, in2)
233
+
234
+ fftconvolve_signature = convolve_signature
235
+ oaconvolve_signature = convolve_signature
236
+ correlate_signature = convolve_signature
237
+ correlate_signature = convolve_signature
238
+ convolve2d_signature = convolve_signature
239
+ correlate2d_signature = convolve_signature
240
+
241
+
242
+ def coherence_signature(x, y, fs=1.0, window='hann_periodic', *args, **kwds):
243
+ return array_namespace(x, y, _skip_if_str_or_tuple(window))
244
+
245
+
246
+ def csd_signature(x, y, fs=1.0, window='hann_periodic', *args, **kwds):
247
+ return array_namespace(x, y, _skip_if_str_or_tuple(window))
248
+
249
+
250
+ def periodogram_signature(x, fs=1.0, window='boxcar', *args, **kwds):
251
+ return array_namespace(x, _skip_if_str_or_tuple(window))
252
+
253
+
254
+ def welch_signature(x, fs=1.0, window='hann_periodic', *args, **kwds):
255
+ return array_namespace(x, _skip_if_str_or_tuple(window))
256
+
257
+
258
+ def spectrogram_signature(x, fs=1.0, window=('tukey_periodic', 0.25), *args, **kwds):
259
+ return array_namespace(x, _skip_if_str_or_tuple(window))
260
+
261
+
262
+ def stft_signature(x, fs=1.0, window='hann_periodic', *args, **kwds):
263
+ return array_namespace(x, _skip_if_str_or_tuple(window))
264
+
265
+
266
+ def istft_signature(Zxx, fs=1.0, window='hann_periodic', *args, **kwds):
267
+ return array_namespace(Zxx, _skip_if_str_or_tuple(window))
268
+
269
+
270
+ def resample_signature(x, num, t=None, axis=0, window=None, domain='time'):
271
+ return array_namespace(x, t, _skip_if_str_or_tuple(window))
272
+
273
+
274
+ def resample_poly_signature(x, up, down, axis=0, window=('kaiser', 5.0), *args, **kwds):
275
+ return array_namespace(x, _skip_if_str_or_tuple(window))
276
+
277
+
278
+ def check_COLA_signature(window, nperseg, noverlap, tol=1e-10):
279
+ return array_namespace(_skip_if_str_or_tuple(window))
280
+
281
+
282
+ def check_NOLA_signature(window, nperseg, noverlap, tol=1e-10):
283
+ return array_namespace(_skip_if_str_or_tuple(window))
284
+
285
+
286
+ def czt_signature(x, *args, **kwds):
287
+ return array_namespace(x)
288
+
289
+ decimate_signature = czt_signature
290
+ gauss_spline_signature = czt_signature
291
+
292
+
293
+ def deconvolve_signature(signal, divisor):
294
+ return array_namespace(signal, divisor)
295
+
296
+
297
+ def detrend_signature(data, axis=1, type='linear', bp=0, *args, **kwds):
298
+ xp = array_namespace(data)
299
+ # JAX doesn't accept JAX arrays for bp, only ints, lists and NumPy
300
+ # arrays.
301
+ return xp if is_jax(xp) else array_namespace(data, bp)
302
+
303
+
304
+ def filtfilt_signature(b, a, x, *args, **kwds):
305
+ return array_namespace(b, a, x)
306
+
307
+
308
+ def lfilter_signature(b, a, x, axis=-1, zi=None):
309
+ return array_namespace(b, a, x, zi)
310
+
311
+
312
+ def envelope_signature(z, *args, **kwds):
313
+ return array_namespace(z)
314
+
315
+
316
+ def find_peaks_signature(
317
+ x, height=None, threshold=None, distance=None, prominence=None, width=None,
318
+ wlen=None, rel_height=0.5, plateau_size=None
319
+ ):
320
+ # TODO: fix me - `prominence` is not necessarily an array.
321
+ # return array_namespace(x, height, threshold, prominence, width, plateau_size)
322
+ # See https://github.com/scipy/scipy/pull/22644#issuecomment-3568443768. For now:
323
+ return np_compat
324
+
325
+ def find_peaks_cwt_signature(
326
+ vector, widths, wavelet=None, max_distances=None, *args, **kwds
327
+ ):
328
+ return array_namespace(vector, widths, max_distances)
329
+
330
+
331
+ def findfreqs_signature(num, den, N, kind='ba'):
332
+ return array_namespace(num, den)
333
+
334
+
335
+ def firls_signature(numtaps, bands, desired, *, weight=None, fs=None):
336
+ return array_namespace(bands, desired, weight)
337
+
338
+
339
+ def firwin_signature(numtaps, cutoff, *args, **kwds):
340
+ if isinstance(cutoff, int | float):
341
+ xp = np_compat
342
+ else:
343
+ xp = array_namespace(cutoff)
344
+ return xp
345
+
346
+
347
+ def firwin2_signature(numtaps, freq, gain, *args, **kwds):
348
+ return array_namespace(freq, gain)
349
+
350
+
351
+ def freqs_zpk_signature(z, p, k, worN=200, *args, **kwds):
352
+ return array_namespace(z, p, worN)
353
+
354
+ freqz_zpk_signature = freqs_zpk_signature
355
+
356
+
357
+ def freqs_signature(b, a, worN=200, *args, **kwds):
358
+ return array_namespace(b, a, worN)
359
+
360
+
361
+ def freqz_signature(b, a=1, worN=512, *args, **kwds):
362
+ # differs from freqs: `a` has a default value
363
+ return array_namespace(b, a, worN)
364
+
365
+
366
+ def freqz_sos_signature(sos, worN=512, *args, **kwds):
367
+ return array_namespace(sos, worN)
368
+
369
+ sosfreqz_signature = freqz_sos_signature
370
+
371
+
372
+ def gausspulse_signature(t, *args, **kwds):
373
+ arr_t = None if isinstance(t, str) else t
374
+ return array_namespace(arr_t)
375
+
376
+
377
+ def group_delay_signature(system, w=512, whole=False, fs=6.283185307179586):
378
+ return array_namespace(*system, w)
379
+
380
+
381
+ def hilbert_signature(x, *args, **kwds):
382
+ return array_namespace(x)
383
+
384
+ hilbert2_signature = hilbert_signature
385
+
386
+
387
+ def iirdesign_signature(wp, ws, *args, **kwds):
388
+ return array_namespace(wp, ws)
389
+
390
+
391
+ def iirfilter_signature(N, Wn, *args, **kwds):
392
+ return array_namespace(Wn)
393
+
394
+
395
+ def invres_signature(r, p, k, tol=0.001, rtype='avg'):
396
+ return array_namespace(r, p, k)
397
+
398
+ invresz_signature = invres_signature
399
+
400
+
401
+ ############################### XXX: excluded, blacklisted on CuPy (mismatched API)
402
+ def lfilter_zi_signature(b, a):
403
+ return array_namespace(b, a)
404
+
405
+ def sosfilt_zi_signature(sos):
406
+ return array_namespace(sos)
407
+
408
+ # needs to be blacklisted on CuPy (is not implemented)
409
+ def remez_signature(numtaps, bands, desired, *, weight=None, **kwds):
410
+ return array_namespace(bands, desired, weight)
411
+ #############################################
412
+
413
+ def lfiltic_signature(b, a, y, x=None):
414
+ return array_namespace(b, a, y, x)
415
+
416
+
417
+ def lombscargle_signature(
418
+ x, y, freqs, precenter=False, normalize=False, *,
419
+ weights=None, floating_mean=False
420
+ ):
421
+ return array_namespace(x, y, freqs, weights)
422
+
423
+
424
+ def lp2bp_signature(b, a, *args, **kwds):
425
+ return array_namespace(b, a)
426
+
427
+ lp2bs_signature = lp2bp_signature
428
+ lp2hp_signature = lp2bp_signature
429
+ lp2lp_signature = lp2bp_signature
430
+
431
+ tf2zpk_signature = lp2bp_signature
432
+ tf2sos_signature = lp2bp_signature
433
+
434
+ normalize_signature = lp2bp_signature
435
+ residue_signature = lp2bp_signature
436
+ residuez_signature = residue_signature
437
+
438
+
439
+ def lp2bp_zpk_signature(z, p, k, *args, **kwds):
440
+ return array_namespace(z, p)
441
+
442
+ lp2bs_zpk_signature = lp2bp_zpk_signature
443
+ lp2hp_zpk_signature = lp2bs_zpk_signature
444
+ lp2lp_zpk_signature = lp2bs_zpk_signature
445
+
446
+
447
+ def zpk2sos_signature(z, p, k, *args, **kwds):
448
+ return array_namespace(z, p)
449
+
450
+ zpk2ss_signature = zpk2sos_signature
451
+ zpk2tf_signature = zpk2sos_signature
452
+
453
+
454
+ def max_len_seq_signature(nbits, state=None, length=None, taps=None):
455
+ return array_namespace(state, taps)
456
+
457
+
458
+ def medfilt_signature(volume, kernel_size=None):
459
+ return array_namespace(volume)
460
+
461
+
462
+ def medfilt2d_signature(input, kernel_size=3):
463
+ return array_namespace(input)
464
+
465
+
466
+ def minimum_phase_signature(h, *args, **kwds):
467
+ return array_namespace(h)
468
+
469
+
470
+ def order_filter_signature(a, domain, rank):
471
+ return array_namespace(a, domain)
472
+
473
+
474
+ def peak_prominences_signature(x, peaks, *args, **kwds):
475
+ return array_namespace(x, peaks)
476
+
477
+
478
+ peak_widths_signature = peak_prominences_signature
479
+
480
+
481
+ def place_poles_signature(A, B, poles, method='YT', rtol=0.001, maxiter=30):
482
+ return array_namespace(A, B, poles)
483
+
484
+
485
+ def savgol_filter_signature(x, *args, **kwds):
486
+ return array_namespace(x)
487
+
488
+
489
+ def sawtooth_signature(t, width=1):
490
+ return array_namespace(t)
491
+
492
+
493
+ def sepfir2d_signature(input, hrow, hcol):
494
+ return array_namespace(input, hrow, hcol)
495
+
496
+
497
+ def sos2tf_signature(sos):
498
+ return array_namespace(sos)
499
+
500
+
501
+ sos2zpk_signature = sos2tf_signature
502
+
503
+
504
+ def sosfilt_signature(sos, x, axis=-1, zi=None):
505
+ return array_namespace(sos, x, zi)
506
+
507
+
508
+ def sosfiltfilt_signature(sos, x, *args, **kwds):
509
+ return array_namespace(sos, x)
510
+
511
+
512
+ def spline_filter_signature(Iin, lmbda=5.0):
513
+ return array_namespace(Iin)
514
+
515
+
516
+ def square_signature(t, duty=0.5):
517
+ return array_namespace(t, duty)
518
+
519
+
520
+ def ss2tf_signature(A, B, C, D, input=0):
521
+ return array_namespace(A, B, C, D)
522
+
523
+ ss2zpk_signature = ss2tf_signature
524
+
525
+
526
+ def sweep_poly_signature(t, poly, phi=0):
527
+ return array_namespace(t, _skip_if_poly1d(poly))
528
+
529
+
530
+ def symiirorder1_signature(signal, c0, z1, precision=-1.0):
531
+ return array_namespace(signal)
532
+
533
+
534
+ def symiirorder2_signature(input, r, omega, precision=-1.0):
535
+ return array_namespace(input, r, omega)
536
+
537
+
538
+ def cspline1d_signature(signal, *args, **kwds):
539
+ return array_namespace(signal)
540
+
541
+ qspline1d_signature = cspline1d_signature
542
+ cspline2d_signature = cspline1d_signature
543
+ qspline2d_signature = qspline1d_signature
544
+
545
+
546
+ def cspline1d_eval_signature(cj, newx, *args, **kwds):
547
+ return array_namespace(cj, newx)
548
+
549
+ qspline1d_eval_signature = cspline1d_eval_signature
550
+
551
+
552
+ def tf2ss_signature(num, den):
553
+ return array_namespace(num, den)
554
+
555
+
556
+ def unique_roots_signature(p, tol=0.001, rtype='min'):
557
+ return array_namespace(p)
558
+
559
+
560
+ def upfirdn_signature(h, x, up=1, down=1, axis=-1, mode='constant', cval=0):
561
+ return array_namespace(h, x)
562
+
563
+
564
+ def vectorstrength_signature(events, period):
565
+ return array_namespace(events, period)
566
+
567
+
568
+ def wiener_signature(im, mysize=None, noise=None):
569
+ return array_namespace(im)
570
+
571
+
572
+ def zoom_fft_signature(x, fn, m=None, *, fs=2, endpoint=False, axis=-1):
573
+ return array_namespace(x, fn)
venv/lib/python3.12/site-packages/scipy/signal/_filter_design.py ADDED
The diff for this file is too large to render. See raw diff
 
venv/lib/python3.12/site-packages/scipy/signal/_fir_filter_design.py ADDED
@@ -0,0 +1,1553 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Functions for FIR filter design."""
2
+
3
+ from math import ceil, log, log2
4
+ import warnings
5
+ from typing import Literal
6
+
7
+ import numpy as np
8
+ from scipy.fft import irfft, fft, ifft
9
+ from scipy.linalg import (toeplitz, hankel, solve, LinAlgError, LinAlgWarning,
10
+ lstsq)
11
+ from scipy.signal._arraytools import _validate_fs
12
+ from .windows import get_window
13
+ from . import _sigtools
14
+
15
+ from scipy._lib._array_api import array_namespace, xp_size, xp_default_dtype
16
+ import scipy._external.array_api_extra as xpx
17
+
18
+
19
+ __all__ = ['kaiser_beta', 'kaiser_atten', 'kaiserord',
20
+ 'firwin', 'firwin2', 'firwin_2d', 'remez', 'firls', 'minimum_phase']
21
+
22
+
23
+ # Some notes on function parameters:
24
+ #
25
+ # `cutoff` and `width` are given as numbers between 0 and 1. These are
26
+ # relative frequencies, expressed as a fraction of the Nyquist frequency.
27
+ # For example, if the Nyquist frequency is 2 KHz, then width=0.15 is a width
28
+ # of 300 Hz.
29
+ #
30
+ # The `order` of a FIR filter is one less than the number of taps.
31
+ # This is a potential source of confusion, so in the following code,
32
+ # we will always use the number of taps as the parameterization of
33
+ # the 'size' of the filter. The "number of taps" means the number
34
+ # of coefficients, which is the same as the length of the impulse
35
+ # response of the filter.
36
+
37
+
38
+ def kaiser_beta(a):
39
+ """Compute the Kaiser parameter `beta`, given the attenuation `a`.
40
+
41
+ Parameters
42
+ ----------
43
+ a : float
44
+ The desired attenuation in the stopband and maximum ripple in
45
+ the passband, in dB. This should be a *positive* number.
46
+
47
+ Returns
48
+ -------
49
+ beta : float
50
+ The `beta` parameter to be used in the formula for a Kaiser window.
51
+
52
+ References
53
+ ----------
54
+ Oppenheim, Schafer, "Discrete-Time Signal Processing", p.475-476.
55
+
56
+ Examples
57
+ --------
58
+ Suppose we want to design a lowpass filter, with 65 dB attenuation
59
+ in the stop band. The Kaiser window parameter to be used in the
60
+ window method is computed by ``kaiser_beta(65)``:
61
+
62
+ >>> from scipy.signal import kaiser_beta
63
+ >>> kaiser_beta(65)
64
+ 6.20426
65
+
66
+ """
67
+ if a > 50:
68
+ beta = 0.1102 * (a - 8.7)
69
+ elif a > 21:
70
+ beta = 0.5842 * (a - 21) ** 0.4 + 0.07886 * (a - 21)
71
+ else:
72
+ beta = 0.0
73
+ return beta
74
+
75
+
76
+ def kaiser_atten(numtaps, width):
77
+ """Compute the attenuation of a Kaiser FIR filter.
78
+
79
+ Given the number of taps `N` and the transition width `width`, compute the
80
+ attenuation `a` in dB, given by Kaiser's formula:
81
+
82
+ a = 2.285 * (N - 1) * pi * width + 7.95
83
+
84
+ Parameters
85
+ ----------
86
+ numtaps : int
87
+ The number of taps in the FIR filter.
88
+ width : float
89
+ The desired width of the transition region between passband and
90
+ stopband (or, in general, at any discontinuity) for the filter,
91
+ expressed as a fraction of the Nyquist frequency.
92
+
93
+ Returns
94
+ -------
95
+ a : float
96
+ The attenuation of the ripple, in dB.
97
+
98
+ See Also
99
+ --------
100
+ kaiserord, kaiser_beta
101
+
102
+ Examples
103
+ --------
104
+ Suppose we want to design a FIR filter using the Kaiser window method
105
+ that will have 211 taps and a transition width of 9 Hz for a signal that
106
+ is sampled at 480 Hz. Expressed as a fraction of the Nyquist frequency,
107
+ the width is 9/(0.5*480) = 0.0375. The approximate attenuation (in dB)
108
+ is computed as follows:
109
+
110
+ >>> from scipy.signal import kaiser_atten
111
+ >>> kaiser_atten(211, 0.0375)
112
+ 64.48099630593983
113
+
114
+ """
115
+ a = 2.285 * (numtaps - 1) * np.pi * width + 7.95
116
+ return a
117
+
118
+
119
+ def kaiserord(ripple, width):
120
+ """
121
+ Determine the filter window parameters for the Kaiser window method.
122
+
123
+ The parameters returned by this function are generally used to create
124
+ a finite impulse response filter using the window method, with either
125
+ `firwin` or `firwin2`.
126
+
127
+ Parameters
128
+ ----------
129
+ ripple : float
130
+ Upper bound for the deviation (in dB) of the magnitude of the
131
+ filter's frequency response from that of the desired filter (not
132
+ including frequencies in any transition intervals). That is, if w
133
+ is the frequency expressed as a fraction of the Nyquist frequency,
134
+ A(w) is the actual frequency response of the filter and D(w) is the
135
+ desired frequency response, the design requirement is that::
136
+
137
+ abs(A(w) - D(w))) < 10**(-ripple/20)
138
+
139
+ for 0 <= w <= 1 and w not in a transition interval.
140
+ width : float
141
+ Width of transition region, normalized so that 1 corresponds to pi
142
+ radians / sample. That is, the frequency is expressed as a fraction
143
+ of the Nyquist frequency.
144
+
145
+ Returns
146
+ -------
147
+ numtaps : int
148
+ The length of the Kaiser window.
149
+ beta : float
150
+ The beta parameter for the Kaiser window.
151
+
152
+ See Also
153
+ --------
154
+ kaiser_beta, kaiser_atten
155
+
156
+ Notes
157
+ -----
158
+ There are several ways to obtain the Kaiser window:
159
+
160
+ - ``signal.windows.kaiser(numtaps, beta, sym=True)``
161
+ - ``signal.get_window(beta, numtaps)``
162
+ - ``signal.get_window(('kaiser', beta), numtaps)``
163
+
164
+ The empirical equations discovered by Kaiser are used.
165
+
166
+ References
167
+ ----------
168
+ Oppenheim, Schafer, "Discrete-Time Signal Processing", pp.475-476.
169
+
170
+ Examples
171
+ --------
172
+ We will use the Kaiser window method to design a lowpass FIR filter
173
+ for a signal that is sampled at 1000 Hz.
174
+
175
+ We want at least 65 dB rejection in the stop band, and in the pass
176
+ band the gain should vary no more than 0.5%.
177
+
178
+ We want a cutoff frequency of 175 Hz, with a transition between the
179
+ pass band and the stop band of 24 Hz. That is, in the band [0, 163],
180
+ the gain varies no more than 0.5%, and in the band [187, 500], the
181
+ signal is attenuated by at least 65 dB.
182
+
183
+ >>> import numpy as np
184
+ >>> from scipy.signal import kaiserord, firwin, freqz
185
+ >>> import matplotlib.pyplot as plt
186
+ >>> fs = 1000.0
187
+ >>> cutoff = 175
188
+ >>> width = 24
189
+
190
+ The Kaiser method accepts just a single parameter to control the pass
191
+ band ripple and the stop band rejection, so we use the more restrictive
192
+ of the two. In this case, the pass band ripple is 0.005, or 46.02 dB,
193
+ so we will use 65 dB as the design parameter.
194
+
195
+ Use `kaiserord` to determine the length of the filter and the
196
+ parameter for the Kaiser window.
197
+
198
+ >>> numtaps, beta = kaiserord(65, width/(0.5*fs))
199
+ >>> numtaps
200
+ 167
201
+ >>> beta
202
+ 6.20426
203
+
204
+ Use `firwin` to create the FIR filter.
205
+
206
+ >>> taps = firwin(numtaps, cutoff, window=('kaiser', beta),
207
+ ... scale=False, fs=fs)
208
+
209
+ Compute the frequency response of the filter. ``w`` is the array of
210
+ frequencies, and ``h`` is the corresponding complex array of frequency
211
+ responses.
212
+
213
+ >>> w, h = freqz(taps, worN=8000)
214
+ >>> w *= 0.5*fs/np.pi # Convert w to Hz.
215
+
216
+ Compute the deviation of the magnitude of the filter's response from
217
+ that of the ideal lowpass filter. Values in the transition region are
218
+ set to ``nan``, so they won't appear in the plot.
219
+
220
+ >>> ideal = w < cutoff # The "ideal" frequency response.
221
+ >>> deviation = np.abs(np.abs(h) - ideal)
222
+ >>> deviation[(w > cutoff - 0.5*width) & (w < cutoff + 0.5*width)] = np.nan
223
+
224
+ Plot the deviation. A close look at the left end of the stop band shows
225
+ that the requirement for 65 dB attenuation is violated in the first lobe
226
+ by about 0.125 dB. This is not unusual for the Kaiser window method.
227
+
228
+ >>> plt.plot(w, 20*np.log10(np.abs(deviation)))
229
+ >>> plt.xlim(0, 0.5*fs)
230
+ >>> plt.ylim(-90, -60)
231
+ >>> plt.grid(alpha=0.25)
232
+ >>> plt.axhline(-65, color='r', ls='--', alpha=0.3)
233
+ >>> plt.xlabel('Frequency (Hz)')
234
+ >>> plt.ylabel('Deviation from ideal (dB)')
235
+ >>> plt.title('Lowpass Filter Frequency Response')
236
+ >>> plt.show()
237
+
238
+ """
239
+ A = abs(ripple) # in case somebody is confused as to what's meant
240
+ if A < 8:
241
+ # Formula for N is not valid in this range.
242
+ raise ValueError("Requested maximum ripple attenuation "
243
+ f"{A:f} is too small for the Kaiser formula.")
244
+ beta = kaiser_beta(A)
245
+
246
+ # Kaiser's formula (as given in Oppenheim and Schafer) is for the filter
247
+ # order, so we have to add 1 to get the number of taps.
248
+ numtaps = (A - 7.95) / 2.285 / (np.pi * width) + 1
249
+
250
+ return int(ceil(numtaps)), beta
251
+
252
+
253
+ def firwin(numtaps, cutoff, *, width=None, window='hamming', pass_zero=True,
254
+ scale=True, fs=None):
255
+ r"""FIR filter design using the window method.
256
+
257
+ This function computes the coefficients of a finite impulse response
258
+ filter. The filter will have linear phase; it will be Type I if
259
+ `numtaps` is odd and Type II if `numtaps` is even.
260
+
261
+ Type II filters always have zero response at the Nyquist frequency, so a
262
+ ValueError exception is raised if firwin is called with `numtaps` even and
263
+ having a passband whose right end is at the Nyquist frequency.
264
+
265
+ Parameters
266
+ ----------
267
+ numtaps : int
268
+ Length of the filter (number of coefficients, i.e., the filter
269
+ order + 1). `numtaps` must be odd if a passband includes the
270
+ Nyquist frequency.
271
+ cutoff : float or 1-D array_like
272
+ Cutoff frequency of filter (expressed in the same units as `fs`)
273
+ or an array of cutoff frequencies (that is, band edges). In the
274
+ former case, as a float, the cutoff frequency should correspond
275
+ with the half-amplitude point, where the attenuation will be -6 dB.
276
+ In the latter case, the frequencies in `cutoff` should be positive
277
+ and monotonically increasing between 0 and `fs/2`. The values 0
278
+ and `fs/2` must not be included in `cutoff`. It should be noted
279
+ that this is different from the behavior of `~scipy.signal.iirdesign`,
280
+ where the cutoff is the half-power point (-3 dB).
281
+ width : float or None, optional
282
+ If not ``None``, then a `~scipy.signal.windows.kaiser` window is calculated
283
+ where `width` specifies the approximate width of the transition region
284
+ (expressed in the same unit as `fs`). This is achieved by utilizing
285
+ `~scipy.signal.kaiser_atten` to calculate an attenuation which is passed to
286
+ `~scipy.signal.kaiser_beta` for determining the β parameter for the kaiser
287
+ window. In this case, the `window` argument is ignored.
288
+ window : str or tuple of str and parameter values, optional
289
+ Desired window to use. Default is ``'hamming'``. The window will be symmetric,
290
+ unless a suffix ``'_periodic'`` is appended to the window name (e.g.,
291
+ ``'hamming_perodic'``) Consult `~scipy.signal.get_window` for a list of windows
292
+ and required parameters.
293
+ pass_zero : {True, False, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional
294
+ Toggles the zero frequency bin (or DC gain) to be in the passband (``True``) or
295
+ in the stopband (``False``). ``'bandstop'``, ``'lowpass'`` are synonyms for
296
+ ``True`` and ``'bandpass'``, ``'highpass'`` are synonyms for ``False``.
297
+ ``'lowpass'``, ``'highpass'`` additionally require `cutoff` to be a scalar
298
+ value or a length-one array. Default: ``True``.
299
+
300
+ .. versionadded:: 1.3.0
301
+ Support for string arguments.
302
+ scale : bool, optional
303
+ Set to ``True`` to scale the coefficients so that the frequency response is
304
+ exactly unity at a certain frequency. That frequency is either:
305
+
306
+ - 0 (DC) if the first passband starts at 0 (i.e., `pass_zero` is ``True``)
307
+ - `fs/2` (the Nyquist frequency) if the first passband ends at `fs/2`
308
+ (i.e., the filter is a single band highpass filter); center of first
309
+ passband otherwise
310
+
311
+ fs : float, optional
312
+ The sampling frequency of the signal. Each frequency in `cutoff`
313
+ must be between 0 and ``fs/2``. Default is 2.
314
+
315
+ Returns
316
+ -------
317
+ h : ndarray
318
+ FIR filter coefficients as 1d array with `numtaps` entries.
319
+
320
+ Raises
321
+ ------
322
+ ValueError
323
+ If any value in `cutoff` is less than or equal to 0 or greater
324
+ than or equal to ``fs/2``, if the values in `cutoff` are not strictly
325
+ monotonically increasing, or if `numtaps` is even but a passband
326
+ includes the Nyquist frequency.
327
+
328
+ See Also
329
+ --------
330
+ firwin2: Window method FIR filter design specifying gain-frequency pairs.
331
+ firwin_2d: 2D FIR filter design using the window method.
332
+ firls: FIR filter design using least-squares error minimization.
333
+ minimum_phase: Convert a FIR filter to minimum phase
334
+ remez: Calculate the minimax optimal filter using the Remez exchange algorithm.
335
+
336
+ Examples
337
+ --------
338
+ The following example calculates frequency responses of a 30 Hz low-pass filter
339
+ with various numbers of taps. The transition region width is 20 Hz. The upper plot
340
+ shows the gain and the lower plot the phase. The vertical dashed line marks the
341
+ corner frequency and the gray background the transition region.
342
+
343
+ >>> import matplotlib.pyplot as plt
344
+ >>> import numpy as np
345
+ >>> import scipy.signal as signal
346
+ ...
347
+ >>> fs = 200 # sampling frequency and number of taps
348
+ >>> f_c, width = 30, 20 # corner frequency (-6 dB gain)
349
+ >>> taps = [20, 40, 60] # number of taps
350
+ ...
351
+ >>> fg, (ax0, ax1) = plt.subplots(2, 1, sharex='all', layout="constrained",
352
+ ... figsize=(5, 4))
353
+ >>> ax0.set_title(rf"Response of ${f_c}\,$Hz low-pass Filter with " +
354
+ ... rf"${width}\,$Hz transition region")
355
+ >>> ax0.set(ylabel="Gain in dB", xlim=(0, fs/2))
356
+ >>> ax1.set(xlabel=rf"Frequency $f\,$ in hertz (sampling frequency $f_S={fs}\,$Hz)",
357
+ ... ylabel="Phase in Degrees")
358
+ ...
359
+ >>> for n in taps: # calculate filter and plot response:
360
+ ... bb = signal.firwin(n, f_c, width=width, fs=fs)
361
+ ... f, H = signal.freqz(bb, fs=fs) # calculate frequency response
362
+ ... H_dB, H_ph = 20 * np.log10(abs(H)), np.rad2deg(np.unwrap(np.angle(H)))
363
+ ... H_ph[H_dB<-150] = np.nan
364
+ ... ax0.plot(f, H_dB, alpha=.5, label=rf"{n} taps")
365
+ ... ax1.plot(f, H_ph, alpha=.5, label=rf"{n} taps")
366
+ >>> for ax_ in (ax0, ax1):
367
+ ... ax_.axvspan(f_c-width/2, f_c+width/2,color='gray', alpha=.25)
368
+ ... ax_.axvline(f_c, color='gray', linestyle='--', alpha=.5)
369
+ ... ax_.grid()
370
+ ... ax_.legend()
371
+ >>> plt.show()
372
+
373
+ The plots show that with increasing number of taps, the suppression in the stopband
374
+ increases and the (negative) slope of the phase steepens, which signifies a longer
375
+ signal delay in the filter. Note that the plots contain numeric artifacts caused by
376
+ the limited frequency resolution: The phase jumps are not real---in reality the
377
+ phase is a straight line with a constant negative slope. Furthermore, the gains
378
+ contain zero values (i.e., -∞ dB), which are also not depicted.
379
+
380
+ The second example determines the frequency responses of a 30 Hz low-pass filter
381
+ with 40 taps. This time the width of the transition region varies:
382
+
383
+ >>> import matplotlib.pyplot as plt
384
+ >>> import numpy as np
385
+ >>> import scipy.signal as signal
386
+ ...
387
+ >>> fs = 200 # sampling frequency and number of taps
388
+ >>> n, f_c = 40, 30 # number of taps and corner frequency (-6 dB gain)
389
+ >>> widths = (2, 10, 25) # width of transition region
390
+ ...
391
+ >>> fg, ax = plt.subplots(1, 1, layout="constrained",)
392
+ >>> ax.set(title=rf"Response of {n}-tap ${f_c}\,$Hz low-pass Filter",
393
+ ... xlabel=rf"Frequency $f\,$ in hertz (sampling frequency $f_S={fs}\,$Hz)",
394
+ ... ylabel="Gain in dB", xlim=(0, fs/2))
395
+ >>> ax.axvline(f_c, color='gray', linestyle='--', alpha=.7) # mark corner frequency
396
+ ...
397
+ >>> for width in widths: # calculate filter and plot response:
398
+ ... bb = signal.firwin(n, f_c, width=width, fs=fs)
399
+ ... f, H = signal.freqz(bb, fs=fs) # calculate frequency response
400
+ ... H_dB= 20 * np.log10(abs(H)) # convert to dB
401
+ ... ax.plot(f, H_dB, alpha=.5, label=rf"width$={width}\,$Hz")
402
+ ...
403
+ >>> ax.grid()
404
+ >>> ax.legend()
405
+ >>> plt.show()
406
+
407
+ It can be seen in the plot above that with increasing width of the transition
408
+ region the suppression in the stopband increases. Since the phase does not vary, it
409
+ is not depicted.
410
+
411
+ Instead of defining a transition region width, a window function can also be
412
+ specified. The plot below depicts the response of an 80 tap band-pass filter having
413
+ a passband ranging form 40 Hz to 60 Hz (denoted by a gray background) with
414
+ different windows:
415
+
416
+ >>> import matplotlib.pyplot as plt
417
+ >>> import numpy as np
418
+ >>> import scipy.signal as signal
419
+ ...
420
+ >>> fs, n = 200, 80 # sampling frequency and number of taps
421
+ >>> cutoff = [40, 60] # corner frequencies (-6 dB gain)
422
+ >>> windows = ('boxcar', 'hamming', 'hann', 'blackman')
423
+ ...
424
+ >>> fg, ax = plt.subplots(1, 1, layout="constrained") # set up plotting
425
+ >>> ax.set(title=rf"Response of {n}-tap Filter with ${cutoff}\,$Hz passband",
426
+ ... xlabel=rf"Frequency $f\,$ in hertz (sampling frequency $f_S={fs}\,$Hz)",
427
+ ... ylabel="Gain in dB", xlim=(0, fs/2))
428
+ >>> ax.axvspan(*cutoff, color='gray', alpha=.25) # mark passband
429
+ ...
430
+ >>> for win in windows: # calculate filter and plot response:
431
+ ... bb = signal.firwin(n, cutoff, window=win, pass_zero=False, fs=fs)
432
+ ... f, H = signal.freqz(bb, fs=fs) # calculate frequency response
433
+ ... H_dB = 20 * np.log10(abs(H)) # convert to dB
434
+ ... ax.plot(f, H_dB, alpha=.5, label=win)
435
+ ...
436
+ >>> ax.grid()
437
+ >>> ax.legend()
438
+ >>> plt.show()
439
+
440
+ The plot shows that the choice of window mainly influences the balance of the
441
+ suppression in the stopband against the width of the transition region. Note that
442
+ utilizing the `~scipy.signal.windows.boxcar` window corresponds to just truncating
443
+ the ideal infinite impulse response to the length of `numtaps` samples.
444
+
445
+ The last example illustrates how to use the `cutoff` and `pass_zero` parameters to
446
+ create a low-pass, a high-pass, a band-stop and a band-pass filter. The desired
447
+ ideal frequency gain is drawn as a gray dashed line whereas the response of the FIR
448
+ filter is depicted as a blue continuous line:
449
+
450
+ >>> from itertools import product
451
+ >>> import matplotlib.pyplot as plt
452
+ >>> import numpy as np
453
+ >>> import scipy.signal as signal
454
+ ...
455
+ >>> cutoffs = [0.5, (.25, .75)] # cutoff parameters
456
+ >>> fg, axx = plt.subplots(4, 1, sharex='all', layout="constrained",
457
+ ... figsize=(5, 4))
458
+ >>> for ax, (cutoff, pass_zero) in zip(axx, product(cutoffs, (True, False))):
459
+ ... ax.set(title=f"firwin(41, {cutoff=}, {pass_zero=}, fs=2)", ylabel="Gain")
460
+ ... ax.set_yticks([0.5], minor=True) # mark gain of 0.5 (= -6 dB)
461
+ ... ax.grid(which='minor', axis='y')
462
+ ...
463
+ ... bb = signal.firwin(41, cutoff, pass_zero=pass_zero, fs=2)
464
+ ... ff, HH = signal.freqz(bb, fs=2)
465
+ ... ax.plot(ff, abs(HH), 'C0-', label="FIR Response")
466
+ ...
467
+ ... f_d = np.hstack(([0], np.atleast_1d(cutoff), [1]))
468
+ ... H_d = np.tile([1, 0] if pass_zero else [0, 1], 2)[:len(f_d)]
469
+ ... H_d[-1] = H_d[-2] # account for symmetry at Nyquist frequency
470
+ ... ax.step(f_d, H_d, 'k--', where='post', alpha=.3, label="Desired Response")
471
+ >>> axx[-1].set(xlabel=r"Frequency $f\,$ in hertz (sampling frequency $f_S=2\,$Hz)",
472
+ ... xlim=(0, 1))
473
+ >>> plt.show()
474
+ """
475
+ # NB: scipy's version of array_namespace returns `np_compat` for int or floats
476
+ xp = array_namespace(cutoff)
477
+
478
+ # The major enhancements to this function added in November 2010 were
479
+ # developed by Tom Krauss (see ticket #902).
480
+ fs = _validate_fs(fs, allow_none=True)
481
+ fs = 2 if fs is None else fs
482
+
483
+ nyq = 0.5 * fs
484
+
485
+ cutoff = xp.asarray(cutoff, dtype=xp_default_dtype(xp))
486
+ cutoff = xpx.atleast_nd(cutoff, ndim=1, xp=xp) / float(nyq)
487
+
488
+ # Check for invalid input.
489
+ if cutoff.ndim > 1:
490
+ raise ValueError("The cutoff argument must be at most "
491
+ "one-dimensional.")
492
+ if xp_size(cutoff) == 0:
493
+ raise ValueError("At least one cutoff frequency must be given.")
494
+ if xp.min(cutoff) <= 0 or xp.max(cutoff) >= 1:
495
+ raise ValueError("Invalid cutoff frequency: frequencies must be "
496
+ "greater than 0 and less than fs/2.")
497
+ if xp.any(cutoff[1:] - cutoff[:-1] <= 0):
498
+ raise ValueError("Invalid cutoff frequencies: the frequencies "
499
+ "must be strictly increasing.")
500
+
501
+ if width is not None:
502
+ # A width was given. Find the beta parameter of the Kaiser window
503
+ # and set `window`. This overrides the value of `window` passed in.
504
+ atten = kaiser_atten(numtaps, float(width) / nyq)
505
+ beta = kaiser_beta(atten)
506
+ window = ('kaiser', beta)
507
+
508
+ if pass_zero in ('bandstop', 'lowpass'):
509
+ if pass_zero == 'lowpass':
510
+ if xp_size(cutoff) != 1:
511
+ raise ValueError('cutoff must have one element if '
512
+ f'pass_zero=="lowpass", got {cutoff.shape}')
513
+ elif xp_size(cutoff) <= 1:
514
+ raise ValueError('cutoff must have at least two elements if '
515
+ f'pass_zero=="bandstop", got {cutoff.shape}')
516
+ pass_zero = True
517
+ elif pass_zero in ('bandpass', 'highpass'):
518
+ if pass_zero == 'highpass':
519
+ if xp_size(cutoff) != 1:
520
+ raise ValueError('cutoff must have one element if '
521
+ f'pass_zero=="highpass", got {cutoff.shape}')
522
+ elif xp_size(cutoff) <= 1:
523
+ raise ValueError('cutoff must have at least two elements if '
524
+ f'pass_zero=="bandpass", got {cutoff.shape}')
525
+ pass_zero = False
526
+ elif not (pass_zero is True or pass_zero is False):
527
+ raise ValueError(f"Parameter {pass_zero=} not in (True, False, 'bandpass', " +
528
+ "'lowpass', 'highpass', 'bandstop')")
529
+
530
+ pass_nyquist = (xp_size(cutoff) % 2 == 0) == pass_zero
531
+ if pass_nyquist and numtaps % 2 == 0:
532
+ raise ValueError("A filter with an even number of coefficients must "
533
+ "have zero response at the Nyquist frequency.")
534
+
535
+ # Insert 0 and/or 1 at the ends of cutoff so that the length of cutoff
536
+ # is even, and each pair in cutoff corresponds to passband.
537
+ cutoff = xp.concat((xp.zeros(int(pass_zero)), cutoff, xp.ones(int(pass_nyquist))))
538
+
539
+
540
+ # `bands` is a 2-D array; each row gives the left and right edges of
541
+ # a passband.
542
+ bands = xp.reshape(cutoff, (-1, 2))
543
+
544
+ # Build up the coefficients.
545
+ alpha = 0.5 * (numtaps - 1)
546
+ m = xp.arange(0, numtaps, dtype=cutoff.dtype) - alpha
547
+ h = 0
548
+ for j in range(bands.shape[0]):
549
+ left, right = bands[j, 0], bands[j, 1]
550
+ h += right * xpx.sinc(right * m, xp=xp)
551
+ h -= left * xpx.sinc(left * m, xp=xp)
552
+
553
+ # Get and apply the window function.
554
+ win = get_window(window, numtaps, fftbins=False, xp=xp)
555
+ h *= win
556
+
557
+ # Now handle scaling if desired.
558
+ if scale:
559
+ # Get the first passband.
560
+ left, right = bands[0, ...]
561
+ if left == 0:
562
+ scale_frequency = 0.0
563
+ elif right == 1:
564
+ scale_frequency = 1.0
565
+ else:
566
+ scale_frequency = 0.5 * (left + right)
567
+ c = xp.cos(xp.pi * m * scale_frequency)
568
+ s = xp.sum(h * c)
569
+ h /= s
570
+
571
+ return h
572
+
573
+
574
+ # Original version of firwin2 from scipy ticket #457, submitted by "tash".
575
+ #
576
+ # Rewritten by Warren Weckesser, 2010.
577
+ def firwin2(numtaps, freq, gain, *, nfreqs=None, window='hamming',
578
+ antisymmetric=False, fs=None):
579
+ """
580
+ FIR filter design using the window method.
581
+
582
+ From the given frequencies `freq` and corresponding gains `gain`,
583
+ this function constructs an FIR filter with linear phase and
584
+ (approximately) the given frequency response.
585
+
586
+ Parameters
587
+ ----------
588
+ numtaps : int
589
+ The number of taps in the FIR filter. `numtaps` must be less than
590
+ `nfreqs`.
591
+ freq : array_like, 1-D
592
+ The frequency sampling points. Typically 0.0 to 1.0 with 1.0 being
593
+ Nyquist. The Nyquist frequency is half `fs`.
594
+ The values in `freq` must be nondecreasing. A value can be repeated
595
+ once to implement a discontinuity. The first value in `freq` must
596
+ be 0, and the last value must be ``fs/2``. Values 0 and ``fs/2`` must
597
+ not be repeated.
598
+ gain : array_like
599
+ The filter gains at the frequency sampling points. Certain
600
+ constraints to gain values, depending on the filter type, are applied,
601
+ see Notes for details.
602
+ nfreqs : int, optional
603
+ The size of the interpolation mesh used to construct the filter.
604
+ For most efficient behavior, this should be a power of 2 plus 1
605
+ (e.g, 129, 257, etc). The default is one more than the smallest
606
+ power of 2 that is not less than `numtaps`. `nfreqs` must be greater
607
+ than `numtaps`.
608
+ window : str or (str, float) or float, or None, optional
609
+ Desired window to use. Default is ``'hamming'``. The window will be symmetric,
610
+ unless a suffix ``'_periodic'`` is appended to the window name (e.g.,
611
+ ``'hamming_perodic'``) Consult `~scipy.signal.get_window` for a list of windows
612
+ and required parameters. If ``None``, no window function is applied.
613
+ antisymmetric : bool, optional
614
+ Whether resulting impulse response is symmetric/antisymmetric.
615
+ See Notes for more details.
616
+ fs : float, optional
617
+ The sampling frequency of the signal. Each frequency in `cutoff`
618
+ must be between 0 and ``fs/2``. Default is 2.
619
+
620
+ Returns
621
+ -------
622
+ taps : ndarray
623
+ The filter coefficients of the FIR filter, as a 1-D array of length
624
+ `numtaps`.
625
+
626
+ See Also
627
+ --------
628
+ firls
629
+ firwin
630
+ minimum_phase
631
+ remez
632
+
633
+ Notes
634
+ -----
635
+ From the given set of frequencies and gains, the desired response is
636
+ constructed in the frequency domain. The inverse FFT is applied to the
637
+ desired response to create the associated convolution kernel, and the
638
+ first `numtaps` coefficients of this kernel, scaled by `window`, are
639
+ returned.
640
+
641
+ The FIR filter will have linear phase. The type of filter is determined by
642
+ the value of 'numtaps` and `antisymmetric` flag.
643
+ There are four possible combinations:
644
+
645
+ - odd `numtaps`, `antisymmetric` is False, type I filter is produced
646
+ - even `numtaps`, `antisymmetric` is False, type II filter is produced
647
+ - odd `numtaps`, `antisymmetric` is True, type III filter is produced
648
+ - even `numtaps`, `antisymmetric` is True, type IV filter is produced
649
+
650
+ Magnitude response of all but type I filters are subjects to following
651
+ constraints:
652
+
653
+ - type II -- zero at the Nyquist frequency
654
+ - type III -- zero at zero and Nyquist frequencies
655
+ - type IV -- zero at zero frequency
656
+
657
+ .. versionadded:: 0.9.0
658
+
659
+ References
660
+ ----------
661
+ .. [1] Oppenheim, A. V. and Schafer, R. W., "Discrete-Time Signal
662
+ Processing", Prentice-Hall, Englewood Cliffs, New Jersey (1989).
663
+ (See, for example, Section 7.4.)
664
+
665
+ .. [2] Smith, Steven W., "The Scientist and Engineer's Guide to Digital
666
+ Signal Processing", Ch. 17. http://www.dspguide.com/ch17/1.htm
667
+
668
+ Examples
669
+ --------
670
+ A lowpass FIR filter with a response that is 1 on [0.0, 0.5], and
671
+ that decreases linearly on [0.5, 1.0] from 1 to 0:
672
+
673
+ >>> from scipy import signal
674
+ >>> taps = signal.firwin2(150, [0.0, 0.5, 1.0], [1.0, 1.0, 0.0])
675
+ >>> print(taps[72:78])
676
+ [-0.02286961 -0.06362756 0.57310236 0.57310236 -0.06362756 -0.02286961]
677
+
678
+ """
679
+ xp = array_namespace(freq, gain)
680
+ freq, gain = xp.asarray(freq), xp.asarray(gain)
681
+
682
+ fs = _validate_fs(fs, allow_none=True)
683
+ fs = 2 if fs is None else fs
684
+ nyq = 0.5 * fs
685
+
686
+ if freq.shape[0] != gain.shape[0]:
687
+ raise ValueError('freq and gain must be of same length.')
688
+
689
+ if nfreqs is not None and numtaps >= nfreqs:
690
+ raise ValueError(
691
+ f'ntaps must be less than nfreqs, but firwin2 was called with '
692
+ f'ntaps={numtaps} and nfreqs={nfreqs}'
693
+ )
694
+
695
+ if freq[0] != 0 or freq[-1] != nyq:
696
+ raise ValueError('freq must start with 0 and end with fs/2.')
697
+ d = freq[1:] - freq[:-1]
698
+ if xp.any(d < 0):
699
+ raise ValueError('The values in freq must be nondecreasing.')
700
+ d2 = d[:-1] + d[1:]
701
+ if xp.any(d2 == 0):
702
+ raise ValueError('A value in freq must not occur more than twice.')
703
+ if freq[1] == 0:
704
+ raise ValueError('Value 0 must not be repeated in freq')
705
+ if freq[-2] == nyq:
706
+ raise ValueError('Value fs/2 must not be repeated in freq')
707
+
708
+ if antisymmetric:
709
+ if numtaps % 2 == 0:
710
+ ftype = 4
711
+ else:
712
+ ftype = 3
713
+ else:
714
+ if numtaps % 2 == 0:
715
+ ftype = 2
716
+ else:
717
+ ftype = 1
718
+
719
+ if ftype == 2 and gain[-1] != 0.0:
720
+ raise ValueError("A Type II filter must have zero gain at the "
721
+ "Nyquist frequency.")
722
+ elif ftype == 3 and (gain[0] != 0.0 or gain[-1] != 0.0):
723
+ raise ValueError("A Type III filter must have zero gain at zero "
724
+ "and Nyquist frequencies.")
725
+ elif ftype == 4 and gain[0] != 0.0:
726
+ raise ValueError("A Type IV filter must have zero gain at zero "
727
+ "frequency.")
728
+
729
+ if nfreqs is None:
730
+ nfreqs = 1 + 2 ** int(ceil(log(numtaps, 2)))
731
+
732
+ if xp.any(d == 0):
733
+ # Tweak any repeated values in freq so that interp works.
734
+ freq = xp.asarray(freq, copy=True)
735
+ eps = xp.finfo(xp_default_dtype(xp)).eps * nyq
736
+ for k in range(freq.shape[0] - 1):
737
+ if freq[k] == freq[k + 1]:
738
+ freq[k] = freq[k] - eps
739
+ freq[k + 1] = freq[k + 1] + eps
740
+ # Check if freq is strictly increasing after tweak
741
+ d = freq[1:] - freq[:-1]
742
+ if xp.any(d <= 0):
743
+ raise ValueError("freq cannot contain numbers that are too close "
744
+ "(within eps * (fs/2): "
745
+ f"{eps}) to a repeated value")
746
+
747
+ # Linearly interpolate the desired response on a uniform mesh `x`.
748
+ x = np.linspace(0.0, nyq, nfreqs)
749
+ fx = np.interp(x, np.asarray(freq), np.asarray(gain)) # XXX array-api-extra#193
750
+ x = xp.asarray(x)
751
+ fx = xp.asarray(fx)
752
+
753
+ # Adjust the phases of the coefficients so that the first `ntaps` of the
754
+ # inverse FFT are the desired filter coefficients.
755
+ shift = xp.exp(-(numtaps - 1) / 2. * 1j * xp.pi * x / nyq)
756
+ if ftype > 2:
757
+ shift *= 1j
758
+
759
+ fx2 = fx * shift
760
+
761
+ # Use irfft to compute the inverse FFT.
762
+ out_full = irfft(fx2)
763
+
764
+ if window is not None:
765
+ # Create the window to apply to the filter coefficients.
766
+ wind = get_window(window, numtaps, fftbins=False, xp=xp)
767
+ else:
768
+ wind = 1
769
+
770
+ # Keep only the first `numtaps` coefficients in `out`, and multiply by
771
+ # the window.
772
+ out = out_full[:numtaps] * wind
773
+
774
+ if ftype == 3:
775
+ out[xp_size(out) // 2] = 0.0
776
+
777
+ return out
778
+
779
+
780
+ def remez(numtaps, bands, desired, *, weight=None, type='bandpass',
781
+ maxiter=25, grid_density=16, fs=None):
782
+ """
783
+ Calculate the minimax optimal filter using the Remez exchange algorithm.
784
+
785
+ Calculate the filter-coefficients for the finite impulse response
786
+ (FIR) filter whose transfer function minimizes the maximum error
787
+ between the desired gain and the realized gain in the specified
788
+ frequency bands using the Remez exchange algorithm.
789
+
790
+ Parameters
791
+ ----------
792
+ numtaps : int
793
+ The desired number of taps in the filter. The number of taps is
794
+ the number of terms in the filter, or the filter order plus one.
795
+ bands : array_like
796
+ A monotonic sequence containing the band edges.
797
+ All elements must be non-negative and less than half the sampling
798
+ frequency as given by `fs`.
799
+ desired : array_like
800
+ A sequence half the size of bands containing the desired gain
801
+ in each of the specified bands.
802
+ weight : array_like, optional
803
+ A relative weighting to give to each band region. The length of
804
+ `weight` has to be half the length of `bands`.
805
+ type : {'bandpass', 'differentiator', 'hilbert'}, optional
806
+ The type of filter:
807
+
808
+ * 'bandpass' : flat response in bands. This is the default.
809
+ * 'differentiator' : frequency proportional response in bands.
810
+ * 'hilbert' : filter with odd symmetry, that is, type III
811
+ (for even order) or type IV (for odd order) linear phase filters.
812
+
813
+ maxiter : int, optional
814
+ Maximum number of iterations of the algorithm. Default is 25.
815
+ grid_density : int, optional
816
+ Grid density. The dense grid used in `remez` is of size
817
+ ``(numtaps + 1) * grid_density``. Default is 16.
818
+ fs : float, optional
819
+ The sampling frequency of the signal. Default is 1.
820
+
821
+ Returns
822
+ -------
823
+ out : ndarray
824
+ A rank-1 array containing the coefficients of the optimal
825
+ (in a minimax sense) filter.
826
+
827
+ See Also
828
+ --------
829
+ firls
830
+ firwin
831
+ firwin2
832
+ minimum_phase
833
+
834
+ References
835
+ ----------
836
+ .. [1] J. H. McClellan and T. W. Parks, "A unified approach to the
837
+ design of optimum FIR linear phase digital filters",
838
+ IEEE Trans. Circuit Theory, vol. CT-20, pp. 697-701, 1973.
839
+ .. [2] J. H. McClellan, T. W. Parks and L. R. Rabiner, "A Computer
840
+ Program for Designing Optimum FIR Linear Phase Digital
841
+ Filters", IEEE Trans. Audio Electroacoust., vol. AU-21,
842
+ pp. 506-525, 1973.
843
+
844
+ Examples
845
+ --------
846
+ In these examples, `remez` is used to design low-pass, high-pass,
847
+ band-pass and band-stop filters. The parameters that define each filter
848
+ are the filter order, the band boundaries, the transition widths of the
849
+ boundaries, the desired gains in each band, and the sampling frequency.
850
+
851
+ We'll use a sample frequency of 22050 Hz in all the examples. In each
852
+ example, the desired gain in each band is either 0 (for a stop band)
853
+ or 1 (for a pass band).
854
+
855
+ `freqz` is used to compute the frequency response of each filter, and
856
+ the utility function ``plot_response`` defined below is used to plot
857
+ the response.
858
+
859
+ >>> import numpy as np
860
+ >>> from scipy import signal
861
+ >>> import matplotlib.pyplot as plt
862
+
863
+ >>> fs = 22050 # Sample rate, Hz
864
+
865
+ >>> def plot_response(w, h, title):
866
+ ... "Utility function to plot response functions"
867
+ ... fig = plt.figure()
868
+ ... ax = fig.add_subplot(111)
869
+ ... ax.plot(w, 20*np.log10(np.abs(h)))
870
+ ... ax.set_ylim(-40, 5)
871
+ ... ax.grid(True)
872
+ ... ax.set_xlabel('Frequency (Hz)')
873
+ ... ax.set_ylabel('Gain (dB)')
874
+ ... ax.set_title(title)
875
+
876
+ The first example is a low-pass filter, with cutoff frequency 8 kHz.
877
+ The filter length is 325, and the transition width from pass to stop
878
+ is 100 Hz.
879
+
880
+ >>> cutoff = 8000.0 # Desired cutoff frequency, Hz
881
+ >>> trans_width = 100 # Width of transition from pass to stop, Hz
882
+ >>> numtaps = 325 # Size of the FIR filter.
883
+ >>> taps = signal.remez(numtaps, [0, cutoff, cutoff + trans_width, 0.5*fs],
884
+ ... [1, 0], fs=fs)
885
+ >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs)
886
+ >>> plot_response(w, h, "Low-pass Filter")
887
+ >>> plt.show()
888
+
889
+ This example shows a high-pass filter:
890
+
891
+ >>> cutoff = 2000.0 # Desired cutoff frequency, Hz
892
+ >>> trans_width = 250 # Width of transition from pass to stop, Hz
893
+ >>> numtaps = 125 # Size of the FIR filter.
894
+ >>> taps = signal.remez(numtaps, [0, cutoff - trans_width, cutoff, 0.5*fs],
895
+ ... [0, 1], fs=fs)
896
+ >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs)
897
+ >>> plot_response(w, h, "High-pass Filter")
898
+ >>> plt.show()
899
+
900
+ This example shows a band-pass filter with a pass-band from 2 kHz to
901
+ 5 kHz. The transition width is 260 Hz and the length of the filter
902
+ is 63, which is smaller than in the other examples:
903
+
904
+ >>> band = [2000, 5000] # Desired pass band, Hz
905
+ >>> trans_width = 260 # Width of transition from pass to stop, Hz
906
+ >>> numtaps = 63 # Size of the FIR filter.
907
+ >>> edges = [0, band[0] - trans_width, band[0], band[1],
908
+ ... band[1] + trans_width, 0.5*fs]
909
+ >>> taps = signal.remez(numtaps, edges, [0, 1, 0], fs=fs)
910
+ >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs)
911
+ >>> plot_response(w, h, "Band-pass Filter")
912
+ >>> plt.show()
913
+
914
+ The low order leads to higher ripple and less steep transitions.
915
+
916
+ The next example shows a band-stop filter.
917
+
918
+ >>> band = [6000, 8000] # Desired stop band, Hz
919
+ >>> trans_width = 200 # Width of transition from pass to stop, Hz
920
+ >>> numtaps = 175 # Size of the FIR filter.
921
+ >>> edges = [0, band[0] - trans_width, band[0], band[1],
922
+ ... band[1] + trans_width, 0.5*fs]
923
+ >>> taps = signal.remez(numtaps, edges, [1, 0, 1], fs=fs)
924
+ >>> w, h = signal.freqz(taps, [1], worN=2000, fs=fs)
925
+ >>> plot_response(w, h, "Band-stop Filter")
926
+ >>> plt.show()
927
+
928
+ """
929
+ xp = array_namespace(bands, desired, weight)
930
+ bands = np.asarray(bands)
931
+ desired = np.asarray(desired)
932
+ if weight is not None:
933
+ weight = np.asarray(weight)
934
+
935
+ fs = _validate_fs(fs, allow_none=True)
936
+ fs = 1.0 if fs is None else fs
937
+
938
+ # Convert type
939
+ try:
940
+ tnum = {'bandpass': 1, 'differentiator': 2, 'hilbert': 3}[type]
941
+ except KeyError as e:
942
+ raise ValueError("Type must be 'bandpass', 'differentiator', "
943
+ "or 'hilbert'") from e
944
+
945
+ # Convert weight
946
+ if weight is None:
947
+ weight = [1] * len(desired)
948
+
949
+ bands = np.asarray(bands).copy()
950
+ result = _sigtools._remez(numtaps, bands, desired, weight, tnum, fs,
951
+ maxiter, grid_density)
952
+ return xp.asarray(result)
953
+
954
+
955
+ def firls(numtaps, bands, desired, *, weight=None, fs=None):
956
+ """
957
+ FIR filter design using least-squares error minimization.
958
+
959
+ Calculate the filter coefficients for the linear-phase finite
960
+ impulse response (FIR) filter which has the best approximation
961
+ to the desired frequency response described by `bands` and
962
+ `desired` in the least squares sense (i.e., the integral of the
963
+ weighted mean-squared error within the specified bands is
964
+ minimized).
965
+
966
+ Parameters
967
+ ----------
968
+ numtaps : int
969
+ The number of taps in the FIR filter. `numtaps` must be odd.
970
+ bands : array_like
971
+ A monotonic nondecreasing sequence containing the band edges in
972
+ Hz. All elements must be non-negative and less than or equal to
973
+ the Nyquist frequency given by `nyq`. The bands are specified as
974
+ frequency pairs, thus, if using a 1D array, its length must be
975
+ even, e.g., `np.array([0, 1, 2, 3, 4, 5])`. Alternatively, the
976
+ bands can be specified as an nx2 sized 2D array, where n is the
977
+ number of bands, e.g, `np.array([[0, 1], [2, 3], [4, 5]])`.
978
+ desired : array_like
979
+ A sequence the same size as `bands` containing the desired gain
980
+ at the start and end point of each band.
981
+ weight : array_like, optional
982
+ A relative weighting to give to each band region when solving
983
+ the least squares problem. `weight` has to be half the size of
984
+ `bands`.
985
+ fs : float, optional
986
+ The sampling frequency of the signal. Each frequency in `bands`
987
+ must be between 0 and ``fs/2`` (inclusive). Default is 2.
988
+
989
+ Returns
990
+ -------
991
+ coeffs : ndarray
992
+ Coefficients of the optimal (in a least squares sense) FIR filter.
993
+
994
+ See Also
995
+ --------
996
+ firwin
997
+ firwin2
998
+ minimum_phase
999
+ remez
1000
+
1001
+ Notes
1002
+ -----
1003
+ This implementation follows the algorithm given in [1]_.
1004
+ As noted there, least squares design has multiple advantages:
1005
+
1006
+ 1. Optimal in a least-squares sense.
1007
+ 2. Simple, non-iterative method.
1008
+ 3. The general solution can obtained by solving a linear
1009
+ system of equations.
1010
+ 4. Allows the use of a frequency dependent weighting function.
1011
+
1012
+ This function constructs a Type I linear phase FIR filter, which
1013
+ contains an odd number of `coeffs` satisfying for :math:`n < numtaps`:
1014
+
1015
+ .. math:: coeffs(n) = coeffs(numtaps - 1 - n)
1016
+
1017
+ The odd number of coefficients and filter symmetry avoid boundary
1018
+ conditions that could otherwise occur at the Nyquist and 0 frequencies
1019
+ (e.g., for Type II, III, or IV variants).
1020
+
1021
+ .. versionadded:: 0.18
1022
+
1023
+ References
1024
+ ----------
1025
+ .. [1] Ivan Selesnick, Linear-Phase Fir Filter Design By Least Squares.
1026
+ OpenStax CNX. Aug 9, 2005.
1027
+ https://eeweb.engineering.nyu.edu/iselesni/EL713/firls/firls.pdf
1028
+
1029
+ Examples
1030
+ --------
1031
+ We want to construct a band-pass filter. Note that the behavior in the
1032
+ frequency ranges between our stop bands and pass bands is unspecified,
1033
+ and thus may overshoot depending on the parameters of our filter:
1034
+
1035
+ >>> import numpy as np
1036
+ >>> from scipy import signal
1037
+ >>> import matplotlib.pyplot as plt
1038
+ >>> fig, axs = plt.subplots(2)
1039
+ >>> fs = 10.0 # Hz
1040
+ >>> desired = (0, 0, 1, 1, 0, 0)
1041
+ >>> for bi, bands in enumerate(((0, 1, 2, 3, 4, 5), (0, 1, 2, 4, 4.5, 5))):
1042
+ ... fir_firls = signal.firls(73, bands, desired, fs=fs)
1043
+ ... fir_remez = signal.remez(73, bands, desired[::2], fs=fs)
1044
+ ... fir_firwin2 = signal.firwin2(73, bands, desired, fs=fs)
1045
+ ... hs = list()
1046
+ ... ax = axs[bi]
1047
+ ... for fir in (fir_firls, fir_remez, fir_firwin2):
1048
+ ... freq, response = signal.freqz(fir)
1049
+ ... hs.append(ax.semilogy(0.5*fs*freq/np.pi, np.abs(response))[0])
1050
+ ... for band, gains in zip(zip(bands[::2], bands[1::2]),
1051
+ ... zip(desired[::2], desired[1::2])):
1052
+ ... ax.semilogy(band, np.maximum(gains, 1e-7), 'k--', linewidth=2)
1053
+ ... if bi == 0:
1054
+ ... ax.legend(hs, ('firls', 'remez', 'firwin2'),
1055
+ ... loc='lower center', frameon=False)
1056
+ ... else:
1057
+ ... ax.set_xlabel('Frequency (Hz)')
1058
+ ... ax.grid(True)
1059
+ ... ax.set(title='Band-pass %d-%d Hz' % bands[2:4], ylabel='Magnitude')
1060
+ ...
1061
+ >>> fig.tight_layout()
1062
+ >>> plt.show()
1063
+
1064
+ """
1065
+ xp = array_namespace(bands, desired)
1066
+ bands = np.asarray(bands)
1067
+ desired = np.asarray(desired)
1068
+
1069
+ fs = _validate_fs(fs, allow_none=True)
1070
+ fs = 2 if fs is None else fs
1071
+ nyq = 0.5 * fs
1072
+
1073
+ numtaps = int(numtaps)
1074
+ if numtaps % 2 == 0 or numtaps < 1:
1075
+ raise ValueError("numtaps must be odd and >= 1")
1076
+ M = (numtaps-1) // 2
1077
+
1078
+ # normalize bands 0->1 and make it 2 columns
1079
+ nyq = float(nyq)
1080
+ if nyq <= 0:
1081
+ raise ValueError(f'nyq must be positive, got {nyq} <= 0.')
1082
+ bands = np.asarray(bands).flatten() / nyq
1083
+ if len(bands) % 2 != 0:
1084
+ raise ValueError("bands must contain frequency pairs.")
1085
+ if (bands < 0).any() or (bands > 1).any():
1086
+ raise ValueError("bands must be between 0 and 1 relative to Nyquist")
1087
+ bands = bands.reshape((-1, 2))
1088
+
1089
+ # check remaining params
1090
+ desired = np.asarray(desired).flatten()
1091
+ if bands.size != desired.size:
1092
+ raise ValueError(
1093
+ f"desired must have one entry per frequency, got {desired.size} "
1094
+ f"gains for {bands.size} frequencies."
1095
+ )
1096
+ desired = desired.reshape((-1, 2))
1097
+ if (np.diff(bands) <= 0).any() or (np.diff(bands[:, 0]) < 0).any():
1098
+ raise ValueError("bands must be monotonically nondecreasing and have "
1099
+ "width > 0.")
1100
+ if (bands[:-1, 1] > bands[1:, 0]).any():
1101
+ raise ValueError("bands must not overlap.")
1102
+ if (desired < 0).any():
1103
+ raise ValueError("desired must be non-negative.")
1104
+ if weight is None:
1105
+ weight = np.ones(len(desired))
1106
+ weight = np.asarray(weight).flatten()
1107
+ if len(weight) != len(desired):
1108
+ raise ValueError("weight must be the same size as the number of "
1109
+ f"band pairs ({len(bands)}).")
1110
+ if (weight < 0).any():
1111
+ raise ValueError("weight must be non-negative.")
1112
+
1113
+ # Set up the linear matrix equation to be solved, Qa = b
1114
+
1115
+ # We can express Q(k,n) = 0.5 Q1(k,n) + 0.5 Q2(k,n)
1116
+ # where Q1(k,n)=q(k-n) and Q2(k,n)=q(k+n), i.e. a Toeplitz plus Hankel.
1117
+
1118
+ # We omit the factor of 0.5 above, instead adding it during coefficient
1119
+ # calculation.
1120
+
1121
+ # We also omit the 1/π from both Q and b equations, as they cancel
1122
+ # during solving.
1123
+
1124
+ # We have that:
1125
+ # q(n) = 1/π ∫W(ω)cos(nω)dω (over 0->π)
1126
+ # Using our normalization ω=πf and with a constant weight W over each
1127
+ # interval f1->f2 we get:
1128
+ # q(n) = W∫cos(πnf)df (0->1) = Wf sin(πnf)/πnf
1129
+ # integrated over each f1->f2 pair (i.e., value at f2 - value at f1).
1130
+ n = np.arange(numtaps)[:, np.newaxis, np.newaxis]
1131
+ q = np.dot(np.diff(np.sinc(bands * n) * bands, axis=2)[:, :, 0], weight)
1132
+
1133
+ # Now we assemble our sum of Toeplitz and Hankel
1134
+ Q1 = toeplitz(q[:M+1])
1135
+ Q2 = hankel(q[:M+1], q[M:])
1136
+ Q = Q1 + Q2
1137
+
1138
+ # Now for b(n) we have that:
1139
+ # b(n) = 1/π ∫ W(ω)D(ω)cos(nω)dω (over 0->π)
1140
+ # Using our normalization ω=πf and with a constant weight W over each
1141
+ # interval and a linear term for D(ω) we get (over each f1->f2 interval):
1142
+ # b(n) = W ∫ (mf+c)cos(πnf)df
1143
+ # = f(mf+c)sin(πnf)/πnf + mf**2 cos(nπf)/(πnf)**2
1144
+ # integrated over each f1->f2 pair (i.e., value at f2 - value at f1).
1145
+ n = n[:M + 1] # only need this many coefficients here
1146
+ # Choose m and c such that we are at the start and end weights
1147
+ m = (np.diff(desired, axis=1) / np.diff(bands, axis=1))
1148
+ c = desired[:, [0]] - bands[:, [0]] * m
1149
+ b = bands * (m*bands + c) * np.sinc(bands * n)
1150
+ # Use L'Hospital's rule here for cos(nπf)/(πnf)**2 @ n=0
1151
+ b[0] -= m * bands * bands / 2.
1152
+ b[1:] += m * np.cos(n[1:] * np.pi * bands) / (np.pi * n[1:]) ** 2
1153
+ b = np.dot(np.diff(b, axis=2)[:, :, 0], weight)
1154
+
1155
+ # Now we can solve the equation
1156
+ try: # try the fast way
1157
+ with warnings.catch_warnings(record=True) as w:
1158
+ warnings.simplefilter('always')
1159
+ a = solve(Q, b, assume_a="pos", check_finite=False)
1160
+ for ww in w:
1161
+ if (ww.category == LinAlgWarning and
1162
+ str(ww.message).startswith('Ill-conditioned matrix')):
1163
+ raise LinAlgError(str(ww.message))
1164
+ except LinAlgError: # in case Q is rank deficient
1165
+ # This is faster than pinvh, even though we don't explicitly use
1166
+ # the symmetry here. gelsy was faster than gelsd and gelss in
1167
+ # some non-exhaustive tests.
1168
+ a = lstsq(Q, b, lapack_driver='gelsy')[0]
1169
+
1170
+ # make coefficients symmetric (linear phase)
1171
+ coeffs = np.hstack((a[:0:-1], 2 * a[0], a[1:]))
1172
+ return xp.asarray(coeffs)
1173
+
1174
+
1175
+ def _dhtm(mag, xp):
1176
+ """Compute the modified 1-D discrete Hilbert transform
1177
+
1178
+ Parameters
1179
+ ----------
1180
+ mag : ndarray
1181
+ The magnitude spectrum. Should be 1-D with an even length, and
1182
+ preferably a fast length for FFT/IFFT.
1183
+ """
1184
+ # Adapted based on code by Niranjan Damera-Venkata,
1185
+ # Brian L. Evans and Shawn R. McCaslin (see refs for `minimum_phase`)
1186
+ sig = xp.zeros(mag.shape[0])
1187
+ # Leave Nyquist and DC at 0, knowing np.abs(fftfreq(N)[midpt]) == 0.5
1188
+ midpt = mag.shape[0] // 2
1189
+ sig = xpx.at(sig)[1:midpt].set(1.0)
1190
+ sig = xpx.at(sig)[midpt+1:].set(-1.0)
1191
+ # eventually if we want to support complex filters, we will need a
1192
+ # np.abs() on the mag inside the log, and should remove the .real
1193
+ recon = xp.real(ifft(mag * xp.exp(fft(sig * ifft(xp.log(mag))))))
1194
+ return recon
1195
+
1196
+
1197
+ def minimum_phase(h,
1198
+ method: Literal['homomorphic', 'hilbert'] = 'homomorphic',
1199
+ n_fft: int | None = None, *, half: bool = True):
1200
+ """Convert a linear-phase FIR filter to minimum phase.
1201
+
1202
+ Parameters
1203
+ ----------
1204
+ h : array
1205
+ Linear-phase FIR filter coefficients.
1206
+ method : {'hilbert', 'homomorphic'}
1207
+ The provided methods are:
1208
+
1209
+ - 'homomorphic' (default):
1210
+ This method [4]_ [5]_ works best with filters with an
1211
+ odd number of taps, and the resulting minimum phase filter
1212
+ will have a magnitude response that approximates the square
1213
+ root of the original filter's magnitude response using half
1214
+ the number of taps when ``half=True`` (default), or the
1215
+ original magnitude spectrum using the same number of taps
1216
+ when ``half=False``.
1217
+ - 'hilbert'
1218
+ This method [1]_ is designed to be used with equiripple
1219
+ filters (e.g., from `remez`) with unity or zero gain
1220
+ regions.
1221
+
1222
+ n_fft : int
1223
+ The number of points to use for the FFT. Should be at least a
1224
+ few times larger than the signal length (see Notes).
1225
+ half : bool
1226
+ If ``True``, create a filter that is half the length of the original, with a
1227
+ magnitude spectrum that is the square root of the original. If ``False``,
1228
+ create a filter that is the same length as the original, with a magnitude
1229
+ spectrum that is designed to match the original (only supported when
1230
+ ``method='homomorphic'``).
1231
+
1232
+ .. versionadded:: 1.14.0
1233
+
1234
+ Returns
1235
+ -------
1236
+ h_minimum : array
1237
+ The minimum-phase version of the filter, with length
1238
+ ``(len(h) + 1) // 2`` when ``half is True`` or ``len(h)`` otherwise.
1239
+
1240
+ See Also
1241
+ --------
1242
+ firwin
1243
+ firwin2
1244
+ remez
1245
+
1246
+ Notes
1247
+ -----
1248
+ Both the Hilbert [1]_ or homomorphic [4]_ [5]_ methods require selection
1249
+ of an FFT length to estimate the complex cepstrum of the filter.
1250
+
1251
+ In the case of the Hilbert method, the deviation from the ideal
1252
+ spectrum ``epsilon`` is related to the number of stopband zeros
1253
+ ``n_stop`` and FFT length ``n_fft`` as::
1254
+
1255
+ epsilon = 2. * n_stop / n_fft
1256
+
1257
+ For example, with 100 stopband zeros and a FFT length of 2048,
1258
+ ``epsilon = 0.0976``. If we conservatively assume that the number of
1259
+ stopband zeros is one less than the filter length, we can take the FFT
1260
+ length to be the next power of 2 that satisfies ``epsilon=0.01`` as::
1261
+
1262
+ n_fft = 2 ** int(np.ceil(np.log2(2 * (len(h) - 1) / 0.01)))
1263
+
1264
+ This gives reasonable results for both the Hilbert and homomorphic
1265
+ methods, and gives the value used when ``n_fft=None``.
1266
+
1267
+ Alternative implementations exist for creating minimum-phase filters,
1268
+ including zero inversion [2]_ and spectral factorization [3]_ [4]_.
1269
+ For more information, see `this DSPGuru page
1270
+ <http://dspguru.com/dsp/howtos/how-to-design-minimum-phase-fir-filters>`__.
1271
+
1272
+ References
1273
+ ----------
1274
+ .. [1] N. Damera-Venkata and B. L. Evans, "Optimal design of real and
1275
+ complex minimum phase digital FIR filters," Acoustics, Speech,
1276
+ and Signal Processing, 1999. Proceedings., 1999 IEEE International
1277
+ Conference on, Phoenix, AZ, 1999, pp. 1145-1148 vol.3.
1278
+ :doi:`10.1109/ICASSP.1999.756179`
1279
+ .. [2] X. Chen and T. W. Parks, "Design of optimal minimum phase FIR
1280
+ filters by direct factorization," Signal Processing,
1281
+ vol. 10, no. 4, pp. 369-383, Jun. 1986.
1282
+ .. [3] T. Saramaki, "Finite Impulse Response Filter Design," in
1283
+ Handbook for Digital Signal Processing, chapter 4,
1284
+ New York: Wiley-Interscience, 1993.
1285
+ .. [4] J. S. Lim, Advanced Topics in Signal Processing.
1286
+ Englewood Cliffs, N.J.: Prentice Hall, 1988.
1287
+ .. [5] A. V. Oppenheim, R. W. Schafer, and J. R. Buck,
1288
+ "Discrete-Time Signal Processing," 3rd edition.
1289
+ Upper Saddle River, N.J.: Pearson, 2009.
1290
+
1291
+ Examples
1292
+ --------
1293
+ Create an optimal linear-phase low-pass filter `h` with a transition band of
1294
+ [0.2, 0.3] (assuming a Nyquist frequency of 1):
1295
+
1296
+ >>> import numpy as np
1297
+ >>> from scipy.signal import remez, minimum_phase, freqz, group_delay
1298
+ >>> import matplotlib.pyplot as plt
1299
+ >>> freq = [0, 0.2, 0.3, 1.0]
1300
+ >>> desired = [1, 0]
1301
+ >>> h_linear = remez(151, freq, desired, fs=2)
1302
+
1303
+ Convert it to minimum phase:
1304
+
1305
+ >>> h_hil = minimum_phase(h_linear, method='hilbert')
1306
+ >>> h_hom = minimum_phase(h_linear, method='homomorphic')
1307
+ >>> h_hom_full = minimum_phase(h_linear, method='homomorphic', half=False)
1308
+
1309
+ Compare the impulse and frequency response of the four filters:
1310
+
1311
+ >>> fig0, ax0 = plt.subplots(figsize=(6, 3), tight_layout=True)
1312
+ >>> fig1, axs = plt.subplots(3, sharex='all', figsize=(6, 6), tight_layout=True)
1313
+ >>> ax0.set_title("Impulse response")
1314
+ >>> ax0.set(xlabel='Samples', ylabel='Amplitude', xlim=(0, len(h_linear) - 1))
1315
+ >>> axs[0].set_title("Frequency Response")
1316
+ >>> axs[0].set(xlim=(0, .65), ylabel="Magnitude / dB")
1317
+ >>> axs[1].set(ylabel="Phase / rad")
1318
+ >>> axs[2].set(ylabel="Group Delay / samples", ylim=(-31, 81),
1319
+ ... xlabel='Normalized Frequency (Nyqist frequency: 1)')
1320
+ >>> for h, lb in ((h_linear, f'Linear ({len(h_linear)})'),
1321
+ ... (h_hil, f'Min-Hilbert ({len(h_hil)})'),
1322
+ ... (h_hom, f'Min-Homomorphic ({len(h_hom)})'),
1323
+ ... (h_hom_full, f'Min-Homom. Full ({len(h_hom_full)})')):
1324
+ ... w_H, H = freqz(h, fs=2)
1325
+ ... w_gd, gd = group_delay((h, 1), fs=2)
1326
+ ...
1327
+ ... alpha = 1.0 if lb == 'linear' else 0.5 # full opacity for 'linear' line
1328
+ ... ax0.plot(h, '.-', alpha=alpha, label=lb)
1329
+ ... axs[0].plot(w_H, 20 * np.log10(np.abs(H)), alpha=alpha)
1330
+ ... axs[1].plot(w_H, np.unwrap(np.angle(H)), alpha=alpha, label=lb)
1331
+ ... axs[2].plot(w_gd, gd, alpha=alpha)
1332
+ >>> ax0.grid(True)
1333
+ >>> ax0.legend(title='Filter Phase (Order)')
1334
+ >>> axs[1].legend(title='Filter Phase (Order)', loc='lower right')
1335
+ >>> for ax_ in axs: # shade transition band:
1336
+ ... ax_.axvspan(freq[1], freq[2], color='y', alpha=.25)
1337
+ ... ax_.grid(True)
1338
+ >>> plt.show()
1339
+
1340
+ The impulse response and group delay plot depict the 75 sample delay of the linear
1341
+ phase filter `h`. The phase should also be linear in the stop band--due to the small
1342
+ magnitude, numeric noise dominates there. Furthermore, the plots show that the
1343
+ minimum phase filters clearly show a reduced (negative) phase slope in the pass and
1344
+ transition band. The plots also illustrate that the filter with parameters
1345
+ ``method='homomorphic', half=False`` has same order and magnitude response as the
1346
+ linear filter `h` whereas the other minimum phase filters have only half the order
1347
+ and the square root of the magnitude response.
1348
+ """
1349
+ xp = array_namespace(h)
1350
+
1351
+ h = xp.asarray(h)
1352
+ if xp.isdtype(h.dtype, "complex floating"):
1353
+ raise ValueError('Complex filters not supported')
1354
+ if h.ndim != 1 or h.shape[0] <= 2:
1355
+ raise ValueError('h must be 1-D and at least 2 samples long')
1356
+ n_half = h.shape[0] // 2
1357
+
1358
+ if not xp.any(xp.flip(h[-n_half:]) - h[:n_half] <= 1e-8 + 1e-6*abs(h[:n_half])):
1359
+ warnings.warn('h does not appear to by symmetric, conversion may fail',
1360
+ RuntimeWarning, stacklevel=2)
1361
+ if not isinstance(method, str) or method not in \
1362
+ ('homomorphic', 'hilbert',):
1363
+ raise ValueError(f'method must be "homomorphic" or "hilbert", got {method!r}')
1364
+ if method == "hilbert" and not half:
1365
+ raise ValueError("`half=False` is only supported when `method='homomorphic'`")
1366
+ if n_fft is None:
1367
+ n_fft = 2 ** int(ceil(log2(2 * (h.shape[0] - 1) / 0.01)))
1368
+ n_fft = int(n_fft)
1369
+ if n_fft < h.shape[0]:
1370
+ raise ValueError(f'n_fft must be at least len(h)=={len(h)}')
1371
+
1372
+ if method == 'hilbert':
1373
+ w = xp.arange(n_fft, dtype=xp.float64) * (2 * xp.pi / n_fft * n_half)
1374
+ H = xp.real(fft(h, n_fft) * xp.exp(1j * w))
1375
+ dp = max(H) - 1
1376
+ ds = 0 - min(H)
1377
+ S = 4. / (xp.sqrt(1+dp+ds) + xp.sqrt(1-dp+ds)) ** 2
1378
+ H += ds
1379
+ H *= S
1380
+ H = xp.sqrt(H)
1381
+ H += 1e-10 # ensure that the log does not explode
1382
+ h_minimum = _dhtm(H, xp)
1383
+ else: # method == 'homomorphic'
1384
+ # zero-pad; calculate the DFT
1385
+ h_temp = xp.abs(fft(h, n_fft))
1386
+ # take 0.25*log(|H|**2) = 0.5*log(|H|)
1387
+ h_temp += 1e-7 * xp.min(h_temp[h_temp > 0]) # don't let log blow up
1388
+ h_temp = xp.log(h_temp)
1389
+ if half: # halving of magnitude spectrum optional
1390
+ h_temp *= 0.5
1391
+ # IDFT
1392
+ h_temp = xp.real(ifft(h_temp))
1393
+ # multiply pointwise by the homomorphic filter
1394
+ # lmin[n] = 2u[n] - d[n]
1395
+ # i.e., double the positive frequencies and zero out the negative ones;
1396
+ # Oppenheim+Shafer 3rd ed p991 eq13.42b and p1004 fig13.7
1397
+ win = xp.zeros(n_fft, dtype=h_temp.dtype)
1398
+ win = xpx.at(win)[0].set(1)
1399
+ stop = n_fft // 2
1400
+ win = xpx.at(win)[1:stop].set(2)
1401
+ # Nyquist freq: odd use 2, even use 1
1402
+ win = xpx.at(win)[stop].set(1 + (n_fft % 2))
1403
+ h_temp *= win
1404
+ h_temp = ifft(xp.exp(fft(h_temp)))
1405
+ h_minimum = xp.real(h_temp)
1406
+ n_out = (n_half + h.shape[0] % 2) if half else h.shape[0]
1407
+ return h_minimum[:n_out]
1408
+
1409
+
1410
+ def firwin_2d(hsize, window, *, fc=None, fs=2, circular=False,
1411
+ pass_zero=True, scale=True):
1412
+ """
1413
+ 2D FIR filter design using the window method.
1414
+
1415
+ This function computes the coefficients of a 2D finite impulse response
1416
+ filter. The filter is separable with linear phase; it will be designed
1417
+ as a product of two 1D filters with dimensions defined by `hsize`.
1418
+ Additionally, it can create approximately circularly symmetric 2-D windows.
1419
+
1420
+ Parameters
1421
+ ----------
1422
+ hsize : tuple or list of length 2
1423
+ Lengths of the filter in each dimension. `hsize[0]` specifies the
1424
+ number of coefficients in the row direction and `hsize[1]` specifies
1425
+ the number of coefficients in the column direction.
1426
+ window : tuple or list of length 2 or str
1427
+ Desired window to use for each 1D filter or a single window type for creating
1428
+ circularly symmetric 2-D windows. Each element should be a string or tuple of
1429
+ string and parameter values. The generated windows will be symmetric, unless a
1430
+ suffix ``'_periodic'`` is appended to the window name (e.g.,
1431
+ ``'hamming_perodic'``). Consult `~scipy.signal.get_window` for a list of windows
1432
+ and required parameters.
1433
+ fc : float or 1-D array_like, optional
1434
+ Cutoff frequency of the filter in the same units as `fs`. This defines
1435
+ the frequency at which the filter's gain drops to approximately -6 dB
1436
+ (half power) in a low-pass or high-pass filter. For multi-band filters,
1437
+ `fc` can be an array of cutoff frequencies (i.e., band edges) in the
1438
+ range [0, fs/2], with each band specified in pairs. Required if
1439
+ `circular` is False.
1440
+ fs : float, optional
1441
+ The sampling frequency of the signal. Default is 2.
1442
+ circular : bool, optional
1443
+ Whether to create a circularly symmetric 2-D window. Default is ``False``.
1444
+ pass_zero : {True, False, 'bandpass', 'lowpass', 'highpass', 'bandstop'}, optional
1445
+ This parameter is directly passed to `firwin` for each scalar frequency axis.
1446
+ Hence, if ``True``, the DC gain, i.e., the gain at frequency (0, 0), is 1.
1447
+ If ``False``, the DC gain is 0 at frequency (0, 0) if `circular` is ``True``.
1448
+ If `circular` is ``False`` the frequencies (0, f1) and (f0, 0) will
1449
+ have gain 0.
1450
+ It can also be a string argument for the desired filter type
1451
+ (equivalent to ``btype`` in IIR design functions).
1452
+ scale : bool, optional
1453
+ This parameter is directly passed to `firwin` for each scalar frequency axis.
1454
+ Set to ``True`` to scale the coefficients so that the frequency
1455
+ response is exactly unity at a certain frequency on one frequency axis.
1456
+ That frequency is either:
1457
+
1458
+ - 0 (DC) if the first passband starts at 0 (i.e. pass_zero is ``True``)
1459
+ - `fs`/2 (the Nyquist frequency) if the first passband ends at `fs`/2
1460
+ (i.e., the filter is a single band highpass filter);
1461
+ center of first passband otherwise
1462
+
1463
+ Returns
1464
+ -------
1465
+ filter_2d : (hsize[0], hsize[1]) ndarray
1466
+ Coefficients of 2D FIR filter.
1467
+
1468
+ Raises
1469
+ ------
1470
+ ValueError
1471
+ - If `hsize` and `window` are not 2-element tuples or lists.
1472
+ - If `cutoff` is None when `circular` is True.
1473
+ - If `cutoff` is outside the range [0, `fs`/2] and `circular` is ``False``.
1474
+ - If any of the elements in `window` are not recognized.
1475
+ RuntimeError
1476
+ If `firwin` fails to converge when designing the filter.
1477
+
1478
+ See Also
1479
+ --------
1480
+ firwin: FIR filter design using the window method for 1d arrays.
1481
+ get_window: Return a window of a given length and type.
1482
+
1483
+ Examples
1484
+ --------
1485
+ Generate a 5x5 low-pass filter with cutoff frequency 0.1:
1486
+
1487
+ >>> import numpy as np
1488
+ >>> from scipy.signal import get_window
1489
+ >>> from scipy.signal import firwin_2d
1490
+ >>> hsize = (5, 5)
1491
+ >>> window = (("kaiser", 5.0), ("kaiser", 5.0))
1492
+ >>> fc = 0.1
1493
+ >>> filter_2d = firwin_2d(hsize, window, fc=fc)
1494
+ >>> filter_2d
1495
+ array([[0.00025366, 0.00401662, 0.00738617, 0.00401662, 0.00025366],
1496
+ [0.00401662, 0.06360159, 0.11695714, 0.06360159, 0.00401662],
1497
+ [0.00738617, 0.11695714, 0.21507283, 0.11695714, 0.00738617],
1498
+ [0.00401662, 0.06360159, 0.11695714, 0.06360159, 0.00401662],
1499
+ [0.00025366, 0.00401662, 0.00738617, 0.00401662, 0.00025366]])
1500
+
1501
+ Generate a circularly symmetric 5x5 low-pass filter with Hamming window:
1502
+
1503
+ >>> filter_2d = firwin_2d((5, 5), 'hamming', fc=fc, circular=True)
1504
+ >>> filter_2d
1505
+ array([[-0.00020354, -0.00020354, -0.00020354, -0.00020354, -0.00020354],
1506
+ [-0.00020354, 0.01506844, 0.09907658, 0.01506844, -0.00020354],
1507
+ [-0.00020354, 0.09907658, -0.00020354, 0.09907658, -0.00020354],
1508
+ [-0.00020354, 0.01506844, 0.09907658, 0.01506844, -0.00020354],
1509
+ [-0.00020354, -0.00020354, -0.00020354, -0.00020354, -0.00020354]])
1510
+
1511
+ Generate Plots comparing the product of two 1d filters with a circular
1512
+ symmetric filter:
1513
+
1514
+ >>> import matplotlib.pyplot as plt
1515
+ >>> hsize, fc = (50, 50), 0.05
1516
+ >>> window = (("kaiser", 5.0), ("kaiser", 5.0))
1517
+ >>> filter0_2d = firwin_2d(hsize, window, fc=fc)
1518
+ >>> filter1_2d = firwin_2d((50, 50), 'hamming', fc=fc, circular=True)
1519
+ ...
1520
+ >>> fg, (ax0, ax1) = plt.subplots(1, 2, tight_layout=True, figsize=(6.5, 3.5))
1521
+ >>> ax0.set_title("Product of 2 Windows")
1522
+ >>> im0 = ax0.imshow(filter0_2d, cmap='viridis', origin='lower', aspect='equal')
1523
+ >>> fg.colorbar(im0, ax=ax0, shrink=0.7)
1524
+ >>> ax1.set_title("Circular Window")
1525
+ >>> im1 = ax1.imshow(filter1_2d, cmap='plasma', origin='lower', aspect='equal')
1526
+ >>> fg.colorbar(im1, ax=ax1, shrink=0.7)
1527
+ >>> plt.show()
1528
+ """
1529
+ if len(hsize) != 2:
1530
+ raise ValueError("hsize must be a 2-element tuple or list")
1531
+
1532
+ if circular:
1533
+ if fc is None:
1534
+ raise ValueError("Cutoff frequency `fc` must be "
1535
+ "provided when `circular` is True")
1536
+
1537
+ n_r = max(hsize[0], hsize[1]) * 8 # oversample 1d window by factor 8
1538
+
1539
+ win_r = firwin(n_r, cutoff=fc, window=window, fs=fs)
1540
+
1541
+ f1, f2 = np.meshgrid(np.linspace(-1, 1, hsize[0]), np.linspace(-1, 1, hsize[1]))
1542
+ r = np.sqrt(f1**2 + f2**2)
1543
+
1544
+ win_2d = np.interp(r, np.linspace(0, 1, n_r), win_r)
1545
+ return win_2d
1546
+
1547
+ if len(window) != 2:
1548
+ raise ValueError("window must be a 2-element tuple or list")
1549
+
1550
+ row_filter = firwin(hsize[0], cutoff=fc, window=window[0], fs=fs)
1551
+ col_filter = firwin(hsize[1], cutoff=fc, window=window[1], fs=fs)
1552
+
1553
+ return np.outer(row_filter, col_filter)
venv/lib/python3.12/site-packages/scipy/signal/_lti_conversion.py ADDED
@@ -0,0 +1,583 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ltisys -- a collection of functions to convert linear time invariant systems
3
+ from one representation to another.
4
+ """
5
+
6
+ import numpy as np
7
+ from numpy import (r_, eye, atleast_2d, poly, dot,
8
+ asarray, zeros, array, outer)
9
+ from scipy import linalg
10
+
11
+ from scipy._lib._array_api import (array_namespace, xp_size, xp_promote,
12
+ xp_result_type)
13
+ import scipy._external.array_api_extra as xpx
14
+ from ._filter_design import tf2zpk, zpk2tf, normalize
15
+
16
+
17
+ __all__ = ['tf2ss', 'abcd_normalize', 'ss2tf', 'zpk2ss', 'ss2zpk',
18
+ 'cont2discrete']
19
+
20
+
21
+ def tf2ss(num, den):
22
+ r"""Transfer function to state-space representation.
23
+
24
+ Parameters
25
+ ----------
26
+ num, den : array_like
27
+ Sequences representing the coefficients of the numerator and
28
+ denominator polynomials, in order of descending degree. The
29
+ denominator needs to be at least as long as the numerator.
30
+
31
+ Returns
32
+ -------
33
+ A, B, C, D : ndarray
34
+ State space representation of the system, in controller canonical
35
+ form.
36
+
37
+ Examples
38
+ --------
39
+ Convert the transfer function:
40
+
41
+ .. math:: H(s) = \frac{s^2 + 3s + 3}{s^2 + 2s + 1}
42
+
43
+ >>> num = [1, 3, 3]
44
+ >>> den = [1, 2, 1]
45
+
46
+ to the state-space representation:
47
+
48
+ .. math::
49
+
50
+ \dot{\textbf{x}}(t) =
51
+ \begin{bmatrix} -2 & -1 \\ 1 & 0 \end{bmatrix} \textbf{x}(t) +
52
+ \begin{bmatrix} 1 \\ 0 \end{bmatrix} \textbf{u}(t) \\
53
+
54
+ \textbf{y}(t) = \begin{bmatrix} 1 & 2 \end{bmatrix} \textbf{x}(t) +
55
+ \begin{bmatrix} 1 \end{bmatrix} \textbf{u}(t)
56
+
57
+ >>> from scipy.signal import tf2ss
58
+ >>> A, B, C, D = tf2ss(num, den)
59
+ >>> A
60
+ array([[-2., -1.],
61
+ [ 1., 0.]])
62
+ >>> B
63
+ array([[ 1.],
64
+ [ 0.]])
65
+ >>> C
66
+ array([[ 1., 2.]])
67
+ >>> D
68
+ array([[ 1.]])
69
+ """
70
+ # Controller canonical state-space representation.
71
+ # if M+1 = len(num) and K+1 = len(den) then we must have M <= K
72
+ # states are found by asserting that X(s) = U(s) / D(s)
73
+ # then Y(s) = N(s) * X(s)
74
+ #
75
+ # A, B, C, and D follow quite naturally.
76
+ #
77
+ num, den = normalize(num, den) # Strips zeros, checks arrays
78
+ nn = len(num.shape)
79
+ if nn == 1:
80
+ num = asarray([num], num.dtype)
81
+ M = num.shape[1]
82
+ K = len(den)
83
+ if M > K:
84
+ msg = "Improper transfer function. `num` is longer than `den`."
85
+ raise ValueError(msg)
86
+ if M == 0 or K == 0: # Null system
87
+ return (array([], float), array([], float), array([], float),
88
+ array([], float))
89
+
90
+ # pad numerator to have same number of columns has denominator
91
+ num = np.hstack((np.zeros((num.shape[0], K - M), dtype=num.dtype), num))
92
+
93
+ if num.shape[-1] > 0:
94
+ D = atleast_2d(num[:, 0])
95
+
96
+ else:
97
+ # We don't assign it an empty array because this system
98
+ # is not 'null'. It just doesn't have a non-zero D
99
+ # matrix. Thus, it should have a non-zero shape so that
100
+ # it can be operated on by functions like 'ss2tf'
101
+ D = array([[0]], float)
102
+
103
+ if K == 1:
104
+ D = D.reshape(num.shape)
105
+
106
+ return (zeros((1, 1)), zeros((1, D.shape[1])),
107
+ zeros((D.shape[0], 1)), D)
108
+
109
+ frow = -array([den[1:]])
110
+ A = r_[frow, eye(K - 2, K - 1)]
111
+ B = eye(K - 1, 1)
112
+ C = num[:, 1:] - outer(num[:, 0], den[1:])
113
+ D = D.reshape((C.shape[0], B.shape[1]))
114
+
115
+ return A, B, C, D
116
+
117
+
118
+ def abcd_normalize(A=None, B=None, C=None, D=None):
119
+ r"""Check state-space matrices compatibility and ensure they are 2d arrays.
120
+
121
+ First, the input matrices are converted into two-dimensional arrays with
122
+ appropriate dtype as needed. Then the dimensions n, q, p are determined by
123
+ investigating the array shapes. If an input is ``None``, or has size zero, it is
124
+ set to an array of zeros of compatible shape. Finally, it is verified that all
125
+ parameter shapes are compatible with each other. If that fails, a ``ValueError`` is
126
+ raised. Note that the dimensions n, q, p are allowed to be zero.
127
+
128
+ Parameters
129
+ ----------
130
+ A : array_like, optional
131
+ Two-dimensional array of shape (n, n).
132
+ B : array_like, optional
133
+ Two-dimensional array of shape (n, p).
134
+ C : array_like, optional
135
+ Two-dimensional array of shape (q, n).
136
+ D : array_like, optional
137
+ Two-dimensional array of shape (q, p).
138
+
139
+ Returns
140
+ -------
141
+ A, B, C, D : array
142
+ State-space matrices as two-dimensional arrays with identical dtype.
143
+ The result dtype is determined based on the standard
144
+ `dtype promotion rules <https://numpy.org/doc/2.3/reference/arrays.promotion.html>`_
145
+ except for when the inputs are all of integer dtype, in which case the returned
146
+ arrays will have the default floating point dtype of ``float64``.
147
+
148
+ Raises
149
+ ------
150
+ ValueError
151
+ If the dimensions n, q, or p could not be determined or if the shapes are
152
+ incompatible with each other.
153
+
154
+ Notes
155
+ -----
156
+ If a matrix is not modified, the original matrix (not a copy) is returned.
157
+
158
+ The :ref:`tutorial_signal_state_space_representation` section of the
159
+ :ref:`user_guide` presents the corresponding definitions of continuous-time and
160
+ disrcete time state space systems.
161
+
162
+ See Also
163
+ --------
164
+ StateSpace: Linear Time Invariant system in state-space form.
165
+ dlti: Discrete-time linear time invariant system base class.
166
+ tf2ss: Transfer function to state-space representation.
167
+ ss2tf: State-space to transfer function.
168
+ ss2zpk: State-space representation to zero-pole-gain representation.
169
+ cont2discrete: Transform a continuous to a discrete state-space system.
170
+
171
+ Examples
172
+ --------
173
+ The following example demonstrates that the passed lists are converted into
174
+ two-dimensional arrays:
175
+
176
+ >>> from scipy.signal import abcd_normalize
177
+ >>> AA, BB, CC, DD = abcd_normalize(A=[[1, 2], [3, 4]], B=[[-1], [5]],
178
+ ... C=[[4, 5]], D=2.5)
179
+ >>> AA.shape, BB.shape, CC.shape, DD.shape
180
+ ((2, 2), (2, 1), (1, 2), (1, 1))
181
+
182
+ In the following, the missing parameter C is assumed to be an array of zeros
183
+ with shape (1, 2):
184
+
185
+ >>> from scipy.signal import abcd_normalize
186
+ >>> AA, BB, CC, DD = abcd_normalize(A=[[1, 2], [3, 4]], B=[[-1], [5]], D=2.5)
187
+ >>> AA.shape, BB.shape, CC.shape, DD.shape
188
+ ((2, 2), (2, 1), (1, 2), (1, 1))
189
+ >>> CC
190
+ array([[0., 0.]])
191
+
192
+ """
193
+ if A is None and B is None and C is None:
194
+ raise ValueError("Dimension n is undefined for parameters A = B = C = None!")
195
+ if B is None and D is None:
196
+ raise ValueError("Dimension p is undefined for parameters B = D = None!")
197
+ if C is None and D is None:
198
+ raise ValueError("Dimension q is undefined for parameters C = D = None!")
199
+
200
+ xp = array_namespace(A, B, C, D)
201
+ A, B, C, D = xp_promote(A, B, C, D, xp=xp, force_floating=True)
202
+ dtype = xp_result_type(A, B, C, D, xp=xp)
203
+
204
+ # convert inputs into 2d arrays (zero-size 2d array if None):
205
+ A, B, C, D = (
206
+ xpx.atleast_nd(xp.asarray(M_), ndim=2, xp=xp)
207
+ if M_ is not None else xp.zeros((0, 0), dtype=dtype)
208
+ for M_ in (A, B, C, D)
209
+ )
210
+
211
+ n = A.shape[0] or B.shape[0] or C.shape[1] # try finding non-zero dimensions
212
+ p = B.shape[1] or D.shape[1]
213
+ q = C.shape[0] or D.shape[0]
214
+
215
+ # Create zero matrices as needed:
216
+ A = xp.zeros((n, n), dtype=dtype) if xp_size(A) == 0 else A
217
+ B = xp.zeros((n, p), dtype=dtype) if xp_size(B) == 0 else B
218
+ C = xp.zeros((q, n), dtype=dtype) if xp_size(C) == 0 else C
219
+ D = xp.zeros((q, p), dtype=dtype) if xp_size(D) == 0 else D
220
+
221
+ if A.shape != (n, n):
222
+ raise ValueError(f"Parameter A has shape {A.shape} but should be ({n}, {n})!")
223
+ if B.shape != (n, p):
224
+ raise ValueError(f"Parameter B has shape {B.shape} but should be ({n}, {p})!")
225
+ if C.shape != (q, n):
226
+ raise ValueError(f"Parameter C has shape {C.shape} but should be ({q}, {n})!")
227
+ if D.shape != (q, p):
228
+ raise ValueError(f"Parameter D has shape {D.shape} but should be ({q}, {p})!")
229
+
230
+ return A, B, C, D
231
+
232
+
233
+ def ss2tf(A, B, C, D, input=0):
234
+ r"""State-space to transfer function.
235
+
236
+ A, B, C, D defines a linear state-space system with `p` inputs,
237
+ `q` outputs, and `n` state variables.
238
+
239
+ Parameters
240
+ ----------
241
+ A : array_like
242
+ State (or system) matrix of shape ``(n, n)``
243
+ B : array_like
244
+ Input matrix of shape ``(n, p)``
245
+ C : array_like
246
+ Output matrix of shape ``(q, n)``
247
+ D : array_like
248
+ Feedthrough (or feedforward) matrix of shape ``(q, p)``
249
+ input : int, optional
250
+ For multiple-input systems, the index of the input to use.
251
+
252
+ Returns
253
+ -------
254
+ num : 2-D ndarray
255
+ Numerator(s) of the resulting transfer function(s). `num` has one row
256
+ for each of the system's outputs. Each row is a sequence representation
257
+ of the numerator polynomial.
258
+ den : 1-D ndarray
259
+ Denominator of the resulting transfer function(s). `den` is a sequence
260
+ representation of the denominator polynomial.
261
+
262
+ Notes
263
+ -----
264
+ Before calculating `num` and `den`, the function `abcd_normalize` is called to
265
+ convert the parameters `A`, `B`, `C`, `D` into two-dimesional arrays of the
266
+ same dtype. The resulting dtype will be based on NumPy's dtype promotion rules,
267
+ except in the case where each of `A`, `B`, `C`, and `D` has integer dtype, in which
268
+ case the resulting dtype will be the default floating point dtype of ``float64``.
269
+
270
+ The :ref:`tutorial_signal_state_space_representation` section of the
271
+ :ref:`user_guide` presents the corresponding definitions of continuous-time and
272
+ disrcete time state space systems.
273
+
274
+ Examples
275
+ --------
276
+ Convert the state-space representation:
277
+
278
+ .. math::
279
+
280
+ \dot{\textbf{x}}(t) =
281
+ \begin{bmatrix} -2 & -1 \\ 1 & 0 \end{bmatrix} \textbf{x}(t) +
282
+ \begin{bmatrix} 1 \\ 0 \end{bmatrix} \textbf{u}(t) \\
283
+
284
+ \textbf{y}(t) = \begin{bmatrix} 1 & 2 \end{bmatrix} \textbf{x}(t) +
285
+ \begin{bmatrix} 1 \end{bmatrix} \textbf{u}(t)
286
+
287
+ >>> A = [[-2, -1], [1, 0]]
288
+ >>> B = [[1], [0]] # 2-D column vector
289
+ >>> C = [[1, 2]] # 2-D row vector
290
+ >>> D = 1
291
+
292
+ to the transfer function:
293
+
294
+ .. math:: H(s) = \frac{s^2 + 3s + 3}{s^2 + 2s + 1}
295
+
296
+ >>> from scipy.signal import ss2tf
297
+ >>> ss2tf(A, B, C, D)
298
+ (array([[1., 3., 3.]]), array([ 1., 2., 1.]))
299
+ """
300
+ # transfer function is C (sI - A)**(-1) B + D
301
+
302
+ # Check consistency and make them all floating point 2d arrays:
303
+ A, B, C, D = abcd_normalize(A, B, C, D)
304
+
305
+ nout, nin = D.shape
306
+ if input >= nin:
307
+ raise ValueError("System does not have the input specified.")
308
+
309
+ # make SIMO from possibly MIMO system.
310
+ B = B[:, input:input + 1]
311
+ D = D[:, input:input + 1]
312
+
313
+ try:
314
+ den = poly(A)
315
+ except ValueError:
316
+ den = 1
317
+
318
+ if (B.size == 0) and (C.size == 0):
319
+ num = np.ravel(D)
320
+ if (D.size == 0) and (A.size == 0):
321
+ den = []
322
+ return num, den
323
+
324
+ num_states = A.shape[0]
325
+ type_test = A[:, 0] + B[:, 0] + C[0, :] + D + 0.0
326
+ num = np.empty((nout, num_states + 1), type_test.dtype)
327
+ for k in range(nout):
328
+ Ck = atleast_2d(C[k, :])
329
+ num[k] = poly(A - dot(B, Ck)) + (D[k] - 1) * den
330
+
331
+ return num, den
332
+
333
+
334
+ def zpk2ss(z, p, k):
335
+ """Zero-pole-gain representation to state-space representation.
336
+
337
+ Parameters
338
+ ----------
339
+ z, p : sequence
340
+ Zeros and poles.
341
+ k : float
342
+ System gain.
343
+
344
+ Returns
345
+ -------
346
+ A, B, C, D : ndarray
347
+ State space representation of the system, in controller canonical
348
+ form.
349
+
350
+ """
351
+ return tf2ss(*zpk2tf(z, p, k))
352
+
353
+
354
+ def ss2zpk(A, B, C, D, input=0):
355
+ """State-space representation to zero-pole-gain representation.
356
+
357
+ A, B, C, D defines a linear state-space system with `p` inputs,
358
+ `q` outputs, and `n` state variables.
359
+
360
+ Parameters
361
+ ----------
362
+ A : array_like
363
+ State (or system) matrix of shape ``(n, n)``
364
+ B : array_like
365
+ Input matrix of shape ``(n, p)``
366
+ C : array_like
367
+ Output matrix of shape ``(q, n)``
368
+ D : array_like
369
+ Feedthrough (or feedforward) matrix of shape ``(q, p)``
370
+ input : int, optional
371
+ For multiple-input systems, the index of the input to use.
372
+
373
+ Returns
374
+ -------
375
+ z, p : sequence
376
+ Zeros and poles.
377
+ k : float
378
+ System gain.
379
+
380
+ """
381
+ return tf2zpk(*ss2tf(A, B, C, D, input=input))
382
+
383
+
384
+ def cont2discrete(system, dt, method="zoh", alpha=None):
385
+ """
386
+ Transform a continuous to a discrete state-space system.
387
+
388
+ Parameters
389
+ ----------
390
+ system : a tuple describing the system or an instance of `lti`
391
+ The following gives the number of elements in the tuple and
392
+ the interpretation:
393
+
394
+ * 1: (instance of `lti`)
395
+ * 2: (num, den)
396
+ * 3: (zeros, poles, gain)
397
+ * 4: (A, B, C, D)
398
+
399
+ dt : float
400
+ The discretization time step.
401
+ method : str, optional
402
+ Which method to use:
403
+
404
+ * gbt: generalized bilinear transformation
405
+ * bilinear: Tustin's approximation ("gbt" with alpha=0.5)
406
+ * euler: Euler (or forward differencing) method ("gbt" with alpha=0)
407
+ * backward_diff: Backwards differencing ("gbt" with alpha=1.0)
408
+ * zoh: zero-order hold (default)
409
+ * foh: first-order hold (*versionadded: 1.3.0*)
410
+ * impulse: equivalent impulse response (*versionadded: 1.3.0*)
411
+
412
+ alpha : float within [0, 1], optional
413
+ The generalized bilinear transformation weighting parameter, which
414
+ should only be specified with method="gbt", and is ignored otherwise
415
+
416
+ Returns
417
+ -------
418
+ sysd : tuple containing the discrete system
419
+ Based on the input type, the output will be of the form
420
+
421
+ * (num, den, dt) for transfer function input
422
+ * (zeros, poles, gain, dt) for zeros-poles-gain input
423
+ * (A, B, C, D, dt) for state-space system input
424
+
425
+ Notes
426
+ -----
427
+ By default, the routine uses a Zero-Order Hold (zoh) method to perform
428
+ the transformation. Alternatively, a generalized bilinear transformation
429
+ may be used, which includes the common Tustin's bilinear approximation,
430
+ an Euler's method technique, or a backwards differencing technique.
431
+
432
+ The Zero-Order Hold (zoh) method is based on [1]_, the generalized bilinear
433
+ approximation is based on [2]_ and [3]_, the First-Order Hold (foh) method
434
+ is based on [4]_.
435
+
436
+ References
437
+ ----------
438
+ .. [1] https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models
439
+
440
+ .. [2] http://techteach.no/publications/discretetime_signals_systems/discrete.pdf
441
+
442
+ .. [3] G. Zhang, X. Chen, and T. Chen, Digital redesign via the generalized
443
+ bilinear transformation, Int. J. Control, vol. 82, no. 4, pp. 741-754,
444
+ 2009.
445
+ (https://www.mypolyuweb.hk/~magzhang/Research/ZCC09_IJC.pdf)
446
+
447
+ .. [4] G. F. Franklin, J. D. Powell, and M. L. Workman, Digital control
448
+ of dynamic systems, 3rd ed. Menlo Park, Calif: Addison-Wesley,
449
+ pp. 204-206, 1998.
450
+
451
+ Examples
452
+ --------
453
+ We can transform a continuous state-space system to a discrete one:
454
+
455
+ >>> import numpy as np
456
+ >>> import matplotlib.pyplot as plt
457
+ >>> from scipy.signal import cont2discrete, lti, dlti, dstep
458
+
459
+ Define a continuous state-space system.
460
+
461
+ >>> A = np.array([[0, 1],[-10., -3]])
462
+ >>> B = np.array([[0],[10.]])
463
+ >>> C = np.array([[1., 0]])
464
+ >>> D = np.array([[0.]])
465
+ >>> l_system = lti(A, B, C, D)
466
+ >>> t, x = l_system.step(T=np.linspace(0, 5, 100))
467
+ >>> fig, ax = plt.subplots()
468
+ >>> ax.plot(t, x, label='Continuous', linewidth=3)
469
+
470
+ Transform it to a discrete state-space system using several methods.
471
+
472
+ >>> dt = 0.1
473
+ >>> for method in ['zoh', 'bilinear', 'euler', 'backward_diff', 'foh', 'impulse']:
474
+ ... d_system = cont2discrete((A, B, C, D), dt, method=method)
475
+ ... s, x_d = dstep(d_system)
476
+ ... ax.step(s, np.squeeze(x_d), label=method, where='post')
477
+ >>> ax.axis([t[0], t[-1], x[0], 1.4])
478
+ >>> ax.legend(loc='best')
479
+ >>> fig.tight_layout()
480
+ >>> plt.show()
481
+
482
+ """
483
+ if hasattr(system, 'to_discrete') and callable(system.to_discrete):
484
+ return system.to_discrete(dt=dt, method=method, alpha=alpha)
485
+
486
+ if len(system) == 2:
487
+ sysd = cont2discrete(tf2ss(system[0], system[1]), dt, method=method,
488
+ alpha=alpha)
489
+ return ss2tf(sysd[0], sysd[1], sysd[2], sysd[3]) + (dt,)
490
+ elif len(system) == 3:
491
+ sysd = cont2discrete(zpk2ss(system[0], system[1], system[2]), dt,
492
+ method=method, alpha=alpha)
493
+ return ss2zpk(sysd[0], sysd[1], sysd[2], sysd[3]) + (dt,)
494
+ elif len(system) == 4:
495
+ a, b, c, d = system
496
+ else:
497
+ raise ValueError("First argument must either be a tuple of 2 (tf), "
498
+ "3 (zpk), or 4 (ss) arrays.")
499
+
500
+ if method == 'gbt':
501
+ if alpha is None:
502
+ raise ValueError("Alpha parameter must be specified for the "
503
+ "generalized bilinear transform (gbt) method")
504
+ elif alpha < 0 or alpha > 1:
505
+ raise ValueError("Alpha parameter must be within the interval "
506
+ "[0,1] for the gbt method")
507
+
508
+ if method == 'gbt':
509
+ # This parameter is used repeatedly - compute once here
510
+ ima = np.eye(a.shape[0]) - alpha*dt*a
511
+ ad = linalg.solve(ima, np.eye(a.shape[0]) + (1.0-alpha)*dt*a)
512
+ bd = linalg.solve(ima, dt*b)
513
+
514
+ # Similarly solve for the output equation matrices
515
+ cd = linalg.solve(ima.transpose(), c.transpose())
516
+ cd = cd.transpose()
517
+ dd = d + alpha*np.dot(c, bd)
518
+
519
+ elif method == 'bilinear' or method == 'tustin':
520
+ return cont2discrete(system, dt, method="gbt", alpha=0.5)
521
+
522
+ elif method == 'euler' or method == 'forward_diff':
523
+ return cont2discrete(system, dt, method="gbt", alpha=0.0)
524
+
525
+ elif method == 'backward_diff':
526
+ return cont2discrete(system, dt, method="gbt", alpha=1.0)
527
+
528
+ elif method == 'zoh':
529
+ # Build an exponential matrix
530
+ em_upper = np.hstack((a, b))
531
+
532
+ # Need to stack zeros under the a and b matrices
533
+ em_lower = np.hstack((np.zeros((b.shape[1], a.shape[0])),
534
+ np.zeros((b.shape[1], b.shape[1]))))
535
+
536
+ em = np.vstack((em_upper, em_lower))
537
+ ms = linalg.expm(dt * em)
538
+
539
+ # Dispose of the lower rows
540
+ ms = ms[:a.shape[0], :]
541
+
542
+ ad = ms[:, 0:a.shape[1]]
543
+ bd = ms[:, a.shape[1]:]
544
+
545
+ cd = c
546
+ dd = d
547
+
548
+ elif method == 'foh':
549
+ # Size parameters for convenience
550
+ n = a.shape[0]
551
+ m = b.shape[1]
552
+
553
+ # Build an exponential matrix similar to 'zoh' method
554
+ em_upper = linalg.block_diag(np.block([a, b]) * dt, np.eye(m))
555
+ em_lower = zeros((m, n + 2 * m))
556
+ em = np.block([[em_upper], [em_lower]])
557
+
558
+ ms = linalg.expm(em)
559
+
560
+ # Get the three blocks from upper rows
561
+ ms11 = ms[:n, 0:n]
562
+ ms12 = ms[:n, n:n + m]
563
+ ms13 = ms[:n, n + m:]
564
+
565
+ ad = ms11
566
+ bd = ms12 - ms13 + ms11 @ ms13
567
+ cd = c
568
+ dd = d + c @ ms13
569
+
570
+ elif method == 'impulse':
571
+ if not np.allclose(d, 0):
572
+ raise ValueError("Impulse method is only applicable "
573
+ "to strictly proper systems")
574
+
575
+ ad = linalg.expm(a * dt)
576
+ bd = ad @ b * dt
577
+ cd = c
578
+ dd = c @ b * dt
579
+
580
+ else:
581
+ raise ValueError(f"Unknown transformation method '{method}'")
582
+
583
+ return ad, bd, cd, dd, dt
venv/lib/python3.12/site-packages/scipy/signal/_ltisys.py ADDED
The diff for this file is too large to render. See raw diff
 
venv/lib/python3.12/site-packages/scipy/signal/_max_len_seq.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Author: Eric Larson
2
+ # 2014
3
+
4
+ """Tools for MLS generation"""
5
+
6
+ import numpy as np
7
+
8
+ from ._max_len_seq_inner import _max_len_seq_inner
9
+
10
+ __all__ = ['max_len_seq']
11
+
12
+
13
+ # These are definitions of linear shift register taps for use in max_len_seq()
14
+ _mls_taps = {2: [1], 3: [2], 4: [3], 5: [3], 6: [5], 7: [6], 8: [7, 6, 1],
15
+ 9: [5], 10: [7], 11: [9], 12: [11, 10, 4], 13: [12, 11, 8],
16
+ 14: [13, 12, 2], 15: [14], 16: [15, 13, 4], 17: [14],
17
+ 18: [11], 19: [18, 17, 14], 20: [17], 21: [19], 22: [21],
18
+ 23: [18], 24: [23, 22, 17], 25: [22], 26: [25, 24, 20],
19
+ 27: [26, 25, 22], 28: [25], 29: [27], 30: [29, 28, 7],
20
+ 31: [28], 32: [31, 30, 10]}
21
+
22
+ def max_len_seq(nbits, state=None, length=None, taps=None):
23
+ """
24
+ Maximum length sequence (MLS) generator.
25
+
26
+ Parameters
27
+ ----------
28
+ nbits : int
29
+ Number of bits to use. Length of the resulting sequence will
30
+ be ``(2**nbits) - 1``. Note that generating long sequences
31
+ (e.g., greater than ``nbits == 16``) can take a long time.
32
+ state : array_like, optional
33
+ If array, must be of length ``nbits``, and will be cast to binary
34
+ (bool) representation. If None, a seed of ones will be used,
35
+ producing a repeatable representation. If ``state`` is all
36
+ zeros, an error is raised as this is invalid. Default: None.
37
+ length : int, optional
38
+ Number of samples to compute. If None, the entire length
39
+ ``(2**nbits) - 1`` is computed.
40
+ taps : array_like, optional
41
+ Polynomial taps to use (e.g., ``[7, 6, 1]`` for an 8-bit sequence).
42
+ If None, taps will be automatically selected (for up to
43
+ ``nbits == 32``).
44
+
45
+ Returns
46
+ -------
47
+ seq : array
48
+ Resulting MLS sequence of 0's and 1's.
49
+ state : array
50
+ The final state of the shift register.
51
+
52
+ Notes
53
+ -----
54
+ The algorithm for MLS generation is generically described in:
55
+
56
+ https://en.wikipedia.org/wiki/Maximum_length_sequence
57
+
58
+ The default values for taps are specifically taken from the first
59
+ option listed for each value of ``nbits`` in:
60
+
61
+ https://web.archive.org/web/20181001062252/http://www.newwaveinstruments.com/resources/articles/m_sequence_linear_feedback_shift_register_lfsr.htm
62
+
63
+ .. versionadded:: 0.15.0
64
+
65
+ Examples
66
+ --------
67
+ MLS uses binary convention:
68
+
69
+ >>> from scipy.signal import max_len_seq
70
+ >>> max_len_seq(4)[0]
71
+ array([1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0], dtype=int8)
72
+
73
+ MLS has a white spectrum (except for DC):
74
+
75
+ >>> import numpy as np
76
+ >>> import matplotlib.pyplot as plt
77
+ >>> from numpy.fft import fft, ifft, fftshift, fftfreq
78
+ >>> seq = max_len_seq(6)[0]*2-1 # +1 and -1
79
+ >>> spec = fft(seq)
80
+ >>> N = len(seq)
81
+ >>> plt.plot(fftshift(fftfreq(N)), fftshift(np.abs(spec)), '.-')
82
+ >>> plt.margins(0.1, 0.1)
83
+ >>> plt.grid(True)
84
+ >>> plt.show()
85
+
86
+ Circular autocorrelation of MLS is an impulse:
87
+
88
+ >>> acorrcirc = ifft(spec * np.conj(spec)).real
89
+ >>> plt.figure()
90
+ >>> plt.plot(np.arange(-N/2+1, N/2+1), fftshift(acorrcirc), '.-')
91
+ >>> plt.margins(0.1, 0.1)
92
+ >>> plt.grid(True)
93
+ >>> plt.show()
94
+
95
+ Linear autocorrelation of MLS is approximately an impulse:
96
+
97
+ >>> acorr = np.correlate(seq, seq, 'full')
98
+ >>> plt.figure()
99
+ >>> plt.plot(np.arange(-N+1, N), acorr, '.-')
100
+ >>> plt.margins(0.1, 0.1)
101
+ >>> plt.grid(True)
102
+ >>> plt.show()
103
+
104
+ """
105
+ taps_dtype = np.int32 if np.intp().itemsize == 4 else np.int64
106
+ if taps is None:
107
+ if nbits not in _mls_taps:
108
+ known_taps = np.array(list(_mls_taps.keys()))
109
+ raise ValueError(f'nbits must be between {known_taps.min()} and '
110
+ f'{known_taps.max()} if taps is None')
111
+ taps = np.array(_mls_taps[nbits], taps_dtype)
112
+ else:
113
+ taps = np.unique(np.array(taps, taps_dtype))[::-1]
114
+ if np.any(taps < 0) or np.any(taps > nbits) or taps.size < 1:
115
+ raise ValueError('taps must be non-empty with values between '
116
+ 'zero and nbits (inclusive)')
117
+ taps = np.array(taps) # needed for Cython and Pythran
118
+ n_max = (2**nbits) - 1
119
+ if length is None:
120
+ length = n_max
121
+ else:
122
+ length = int(length)
123
+ if length < 0:
124
+ raise ValueError('length must be greater than or equal to 0')
125
+ # We use int8 instead of bool here because NumPy arrays of bools
126
+ # don't seem to work nicely with Cython
127
+ if state is None:
128
+ state = np.ones(nbits, dtype=np.int8, order='c')
129
+ else:
130
+ # makes a copy if need be, ensuring it's 0's and 1's
131
+ state = np.array(state, dtype=bool, order='c').astype(np.int8)
132
+ if state.ndim != 1 or state.size != nbits:
133
+ raise ValueError('state must be a 1-D array of size nbits')
134
+ if np.all(state == 0):
135
+ raise ValueError('state must not be all zeros')
136
+
137
+ seq = np.empty(length, dtype=np.int8, order='c')
138
+ state = _max_len_seq_inner(taps, state, nbits, length, seq)
139
+ return seq, state
venv/lib/python3.12/site-packages/scipy/signal/_max_len_seq_inner.cpython-312-x86_64-linux-gnu.so ADDED
Binary file (57.3 kB). View file
 
venv/lib/python3.12/site-packages/scipy/signal/_peak_finding.py ADDED
@@ -0,0 +1,1307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Functions for identifying peaks in signals.
3
+ """
4
+ import math
5
+ import numpy as np
6
+
7
+ from scipy.signal._wavelets import _cwt, _ricker
8
+ from scipy.stats import scoreatpercentile
9
+
10
+ from ._peak_finding_utils import (
11
+ _local_maxima_1d,
12
+ _select_by_peak_distance,
13
+ _peak_prominences,
14
+ _peak_widths
15
+ )
16
+
17
+
18
+ __all__ = ['argrelmin', 'argrelmax', 'argrelextrema', 'peak_prominences',
19
+ 'peak_widths', 'find_peaks', 'find_peaks_cwt']
20
+
21
+
22
+ def _boolrelextrema(data, comparator, axis=0, order=1, mode='clip'):
23
+ """
24
+ Calculate the relative extrema of `data`.
25
+
26
+ Relative extrema are calculated by finding locations where
27
+ ``comparator(data[n], data[n+1:n+order+1])`` is True.
28
+
29
+ Parameters
30
+ ----------
31
+ data : ndarray
32
+ Array in which to find the relative extrema.
33
+ comparator : callable
34
+ Function to use to compare two data points.
35
+ Should take two arrays as arguments.
36
+ axis : int, optional
37
+ Axis over which to select from `data`. Default is 0.
38
+ order : int, optional
39
+ How many points on each side to use for the comparison
40
+ to consider ``comparator(n,n+x)`` to be True.
41
+ mode : str, optional
42
+ How the edges of the vector are treated. 'wrap' (wrap around) or
43
+ 'clip' (treat overflow as the same as the last (or first) element).
44
+ Default 'clip'. See numpy.take.
45
+
46
+ Returns
47
+ -------
48
+ extrema : ndarray
49
+ Boolean array of the same shape as `data` that is True at an extrema,
50
+ False otherwise.
51
+
52
+ See Also
53
+ --------
54
+ argrelmax, argrelmin
55
+
56
+ Examples
57
+ --------
58
+ >>> import numpy as np
59
+ >>> from scipy.signal._peak_finding import _boolrelextrema
60
+ >>> testdata = np.array([1,2,3,2,1])
61
+ >>> _boolrelextrema(testdata, np.greater, axis=0)
62
+ array([False, False, True, False, False], dtype=bool)
63
+
64
+ """
65
+ if (int(order) != order) or (order < 1):
66
+ raise ValueError('Order must be an int >= 1')
67
+
68
+ datalen = data.shape[axis]
69
+ locs = np.arange(0, datalen)
70
+
71
+ results = np.ones(data.shape, dtype=bool)
72
+ main = data.take(locs, axis=axis, mode=mode)
73
+ for shift in range(1, order + 1):
74
+ plus = data.take(locs + shift, axis=axis, mode=mode)
75
+ minus = data.take(locs - shift, axis=axis, mode=mode)
76
+ results &= comparator(main, plus)
77
+ results &= comparator(main, minus)
78
+ if ~results.any():
79
+ return results
80
+ return results
81
+
82
+
83
+ def argrelmin(data, axis=0, order=1, mode='clip'):
84
+ """
85
+ Calculate the relative minima of `data`.
86
+
87
+ Parameters
88
+ ----------
89
+ data : ndarray
90
+ Array in which to find the relative minima.
91
+ axis : int, optional
92
+ Axis over which to select from `data`. Default is 0.
93
+ order : int, optional
94
+ How many points on each side to use for the comparison
95
+ to consider ``comparator(n, n+x)`` to be True.
96
+ mode : str, optional
97
+ How the edges of the vector are treated.
98
+ Available options are 'wrap' (wrap around) or 'clip' (treat overflow
99
+ as the same as the last (or first) element).
100
+ Default 'clip'. See numpy.take.
101
+
102
+ Returns
103
+ -------
104
+ extrema : tuple of ndarrays
105
+ Indices of the minima in arrays of integers. ``extrema[k]`` is
106
+ the array of indices of axis `k` of `data`. Note that the
107
+ return value is a tuple even when `data` is 1-D.
108
+
109
+ See Also
110
+ --------
111
+ argrelextrema, argrelmax, find_peaks
112
+
113
+ Notes
114
+ -----
115
+ This function uses `argrelextrema` with np.less as comparator. Therefore, it
116
+ requires a strict inequality on both sides of a value to consider it a
117
+ minimum. This means flat minima (more than one sample wide) are not detected.
118
+ In case of 1-D `data` `find_peaks` can be used to detect all
119
+ local minima, including flat ones, by calling it with negated `data`.
120
+
121
+ .. versionadded:: 0.11.0
122
+
123
+ Examples
124
+ --------
125
+ >>> import numpy as np
126
+ >>> from scipy.signal import argrelmin
127
+ >>> x = np.array([2, 1, 2, 3, 2, 0, 1, 0])
128
+ >>> argrelmin(x)
129
+ (array([1, 5]),)
130
+ >>> y = np.array([[1, 2, 1, 2],
131
+ ... [2, 2, 0, 0],
132
+ ... [5, 3, 4, 4]])
133
+ ...
134
+ >>> argrelmin(y, axis=1)
135
+ (array([0, 2]), array([2, 1]))
136
+
137
+ """
138
+ return argrelextrema(data, np.less, axis, order, mode)
139
+
140
+
141
+ def argrelmax(data, axis=0, order=1, mode='clip'):
142
+ """
143
+ Calculate the relative maxima of `data`.
144
+
145
+ Parameters
146
+ ----------
147
+ data : ndarray
148
+ Array in which to find the relative maxima.
149
+ axis : int, optional
150
+ Axis over which to select from `data`. Default is 0.
151
+ order : int, optional
152
+ How many points on each side to use for the comparison
153
+ to consider ``comparator(n, n+x)`` to be True.
154
+ mode : str, optional
155
+ How the edges of the vector are treated.
156
+ Available options are 'wrap' (wrap around) or 'clip' (treat overflow
157
+ as the same as the last (or first) element).
158
+ Default 'clip'. See `numpy.take`.
159
+
160
+ Returns
161
+ -------
162
+ extrema : tuple of ndarrays
163
+ Indices of the maxima in arrays of integers. ``extrema[k]`` is
164
+ the array of indices of axis `k` of `data`. Note that the
165
+ return value is a tuple even when `data` is 1-D.
166
+
167
+ See Also
168
+ --------
169
+ argrelextrema, argrelmin, find_peaks
170
+
171
+ Notes
172
+ -----
173
+ This function uses `argrelextrema` with np.greater as comparator. Therefore,
174
+ it requires a strict inequality on both sides of a value to consider it a
175
+ maximum. This means flat maxima (more than one sample wide) are not detected.
176
+ In case of 1-D `data` `find_peaks` can be used to detect all
177
+ local maxima, including flat ones.
178
+
179
+ .. versionadded:: 0.11.0
180
+
181
+ Examples
182
+ --------
183
+ >>> import numpy as np
184
+ >>> from scipy.signal import argrelmax
185
+ >>> x = np.array([2, 1, 2, 3, 2, 0, 1, 0])
186
+ >>> argrelmax(x)
187
+ (array([3, 6]),)
188
+ >>> y = np.array([[1, 2, 1, 2],
189
+ ... [2, 2, 0, 0],
190
+ ... [5, 3, 4, 4]])
191
+ ...
192
+ >>> argrelmax(y, axis=1)
193
+ (array([0]), array([1]))
194
+ """
195
+ return argrelextrema(data, np.greater, axis, order, mode)
196
+
197
+
198
+ def argrelextrema(data, comparator, axis=0, order=1, mode='clip'):
199
+ """
200
+ Calculate the relative extrema of `data`.
201
+
202
+ Parameters
203
+ ----------
204
+ data : ndarray
205
+ Array in which to find the relative extrema.
206
+ comparator : callable
207
+ Function to use to compare two data points.
208
+ Should take two arrays as arguments.
209
+ axis : int, optional
210
+ Axis over which to select from `data`. Default is 0.
211
+ order : int, optional
212
+ How many points on each side to use for the comparison
213
+ to consider ``comparator(n, n+x)`` to be True.
214
+ mode : str, optional
215
+ How the edges of the vector are treated. 'wrap' (wrap around) or
216
+ 'clip' (treat overflow as the same as the last (or first) element).
217
+ Default is 'clip'. See `numpy.take`.
218
+
219
+ Returns
220
+ -------
221
+ extrema : tuple of ndarrays
222
+ Indices of the maxima in arrays of integers. ``extrema[k]`` is
223
+ the array of indices of axis `k` of `data`. Note that the
224
+ return value is a tuple even when `data` is 1-D.
225
+
226
+ See Also
227
+ --------
228
+ argrelmin, argrelmax
229
+
230
+ Notes
231
+ -----
232
+
233
+ .. versionadded:: 0.11.0
234
+
235
+ Examples
236
+ --------
237
+ >>> import numpy as np
238
+ >>> from scipy.signal import argrelextrema
239
+ >>> x = np.array([2, 1, 2, 3, 2, 0, 1, 0])
240
+ >>> argrelextrema(x, np.greater)
241
+ (array([3, 6]),)
242
+ >>> y = np.array([[1, 2, 1, 2],
243
+ ... [2, 2, 0, 0],
244
+ ... [5, 3, 4, 4]])
245
+ ...
246
+ >>> argrelextrema(y, np.less, axis=1)
247
+ (array([0, 2]), array([2, 1]))
248
+
249
+ """
250
+ results = _boolrelextrema(data, comparator,
251
+ axis, order, mode)
252
+ return np.nonzero(results)
253
+
254
+
255
+ def _arg_x_as_expected(value):
256
+ """Ensure argument `x` is a 1-D C-contiguous array of dtype('float64').
257
+
258
+ Used in `find_peaks`, `peak_prominences` and `peak_widths` to make `x`
259
+ compatible with the signature of the wrapped Cython functions.
260
+
261
+ Returns
262
+ -------
263
+ value : ndarray
264
+ A 1-D C-contiguous array with dtype('float64').
265
+ """
266
+ value = np.asarray(value, order='C', dtype=np.float64)
267
+ if value.ndim != 1:
268
+ raise ValueError('`x` must be a 1-D array')
269
+ return value
270
+
271
+
272
+ def _arg_peaks_as_expected(value):
273
+ """Ensure argument `peaks` is a 1-D C-contiguous array of dtype('intp').
274
+
275
+ Used in `peak_prominences` and `peak_widths` to make `peaks` compatible
276
+ with the signature of the wrapped Cython functions.
277
+
278
+ Returns
279
+ -------
280
+ value : ndarray
281
+ A 1-D C-contiguous array with dtype('intp').
282
+ """
283
+ value = np.asarray(value)
284
+ if value.size == 0:
285
+ # Empty arrays default to np.float64 but are valid input
286
+ value = np.array([], dtype=np.intp)
287
+ try:
288
+ # Safely convert to C-contiguous array of type np.intp
289
+ value = value.astype(np.intp, order='C', casting='safe',
290
+ subok=False, copy=False)
291
+ except TypeError as e:
292
+ raise TypeError("cannot safely cast `peaks` to dtype('intp')") from e
293
+ if value.ndim != 1:
294
+ raise ValueError('`peaks` must be a 1-D array')
295
+ return value
296
+
297
+
298
+ def _arg_wlen_as_expected(value):
299
+ """Ensure argument `wlen` is of type `np.intp` and larger than 1.
300
+
301
+ Used in `peak_prominences` and `peak_widths`.
302
+
303
+ Returns
304
+ -------
305
+ value : np.intp
306
+ The original `value` rounded up to an integer or -1 if `value` was
307
+ None.
308
+ """
309
+ if value is None:
310
+ # _peak_prominences expects an intp; -1 signals that no value was
311
+ # supplied by the user
312
+ value = -1
313
+ elif 1 < value:
314
+ # Round up to a positive integer
315
+ if isinstance(value, float):
316
+ value = math.ceil(value)
317
+ value = np.intp(value)
318
+ else:
319
+ raise ValueError(f'`wlen` must be larger than 1, was {value}')
320
+ return value
321
+
322
+
323
+ def peak_prominences(x, peaks, wlen=None):
324
+ """
325
+ Calculate the prominence of each peak in a signal.
326
+
327
+ The prominence of a peak measures how much a peak stands out from the
328
+ surrounding baseline of the signal and is defined as the vertical distance
329
+ between the peak and its lowest contour line.
330
+
331
+ Parameters
332
+ ----------
333
+ x : sequence
334
+ A signal with peaks.
335
+ peaks : sequence
336
+ Indices of peaks in `x`.
337
+ wlen : int, optional
338
+ A window length in samples that optionally limits the evaluated area for
339
+ each peak to a subset of `x`. The peak is always placed in the middle of
340
+ the window therefore the given length is rounded up to the next odd
341
+ integer. This parameter can speed up the calculation (see Notes).
342
+
343
+ Returns
344
+ -------
345
+ prominences : ndarray
346
+ The calculated prominences for each peak in `peaks`.
347
+ left_bases, right_bases : ndarray
348
+ The peaks' bases as indices in `x` to the left and right of each peak.
349
+ The higher base of each pair is a peak's lowest contour line.
350
+
351
+ Raises
352
+ ------
353
+ ValueError
354
+ If a value in `peaks` is an invalid index for `x`.
355
+
356
+ Warns
357
+ -----
358
+ PeakPropertyWarning
359
+ For indices in `peaks` that don't point to valid local maxima in `x`,
360
+ the returned prominence will be 0 and this warning is raised. This
361
+ also happens if `wlen` is smaller than the plateau size of a peak.
362
+
363
+ Warnings
364
+ --------
365
+ This function may return unexpected results for data containing NaNs. To
366
+ avoid this, NaNs should either be removed or replaced.
367
+
368
+ See Also
369
+ --------
370
+ find_peaks
371
+ Find peaks inside a signal based on peak properties.
372
+ peak_widths
373
+ Calculate the width of peaks.
374
+
375
+ Notes
376
+ -----
377
+ Strategy to compute a peak's prominence:
378
+
379
+ 1. Extend a horizontal line from the current peak to the left and right
380
+ until the line either reaches the window border (see `wlen`) or
381
+ intersects the signal again at the slope of a higher peak. An
382
+ intersection with a peak of the same height is ignored.
383
+ 2. On each side find the minimal signal value within the interval defined
384
+ above. These points are the peak's bases.
385
+ 3. The higher one of the two bases marks the peak's lowest contour line. The
386
+ prominence can then be calculated as the vertical difference between the
387
+ peaks height itself and its lowest contour line.
388
+
389
+ Searching for the peak's bases can be slow for large `x` with periodic
390
+ behavior because large chunks or even the full signal need to be evaluated
391
+ for the first algorithmic step. This evaluation area can be limited with the
392
+ parameter `wlen` which restricts the algorithm to a window around the
393
+ current peak and can shorten the calculation time if the window length is
394
+ short in relation to `x`.
395
+ However, this may stop the algorithm from finding the true global contour
396
+ line if the peak's true bases are outside this window. Instead, a higher
397
+ contour line is found within the restricted window leading to a smaller
398
+ calculated prominence. In practice, this is only relevant for the highest set
399
+ of peaks in `x`. This behavior may even be used intentionally to calculate
400
+ "local" prominences.
401
+
402
+ .. versionadded:: 1.1.0
403
+
404
+ References
405
+ ----------
406
+ .. [1] Wikipedia Article for Topographic Prominence:
407
+ https://en.wikipedia.org/wiki/Topographic_prominence
408
+
409
+ Examples
410
+ --------
411
+ >>> import numpy as np
412
+ >>> from scipy.signal import find_peaks, peak_prominences
413
+ >>> import matplotlib.pyplot as plt
414
+
415
+ Create a test signal with two overlaid harmonics
416
+
417
+ >>> x = np.linspace(0, 6 * np.pi, 1000)
418
+ >>> x = np.sin(x) + 0.6 * np.sin(2.6 * x)
419
+
420
+ Find all peaks and calculate prominences
421
+
422
+ >>> peaks, _ = find_peaks(x)
423
+ >>> prominences = peak_prominences(x, peaks)[0]
424
+ >>> prominences
425
+ array([1.24159486, 0.47840168, 0.28470524, 3.10716793, 0.284603 ,
426
+ 0.47822491, 2.48340261, 0.47822491])
427
+
428
+ Calculate the height of each peak's contour line and plot the results
429
+
430
+ >>> contour_heights = x[peaks] - prominences
431
+ >>> plt.plot(x)
432
+ >>> plt.plot(peaks, x[peaks], "x")
433
+ >>> plt.vlines(x=peaks, ymin=contour_heights, ymax=x[peaks])
434
+ >>> plt.show()
435
+
436
+ Let's evaluate a second example that demonstrates several edge cases for
437
+ one peak at index 5.
438
+
439
+ >>> x = np.array([0, 1, 0, 3, 1, 3, 0, 4, 0])
440
+ >>> peaks = np.array([5])
441
+ >>> plt.plot(x)
442
+ >>> plt.plot(peaks, x[peaks], "x")
443
+ >>> plt.show()
444
+ >>> peak_prominences(x, peaks) # -> (prominences, left_bases, right_bases)
445
+ (array([3.]), array([2]), array([6]))
446
+
447
+ Note how the peak at index 3 of the same height is not considered as a
448
+ border while searching for the left base. Instead, two minima at 0 and 2
449
+ are found in which case the one closer to the evaluated peak is always
450
+ chosen. On the right side, however, the base must be placed at 6 because the
451
+ higher peak represents the right border to the evaluated area.
452
+
453
+ >>> peak_prominences(x, peaks, wlen=3.1)
454
+ (array([2.]), array([4]), array([6]))
455
+
456
+ Here, we restricted the algorithm to a window from 3 to 7 (the length is 5
457
+ samples because `wlen` was rounded up to the next odd integer). Thus, the
458
+ only two candidates in the evaluated area are the two neighboring samples
459
+ and a smaller prominence is calculated.
460
+ """
461
+ x = _arg_x_as_expected(x)
462
+ peaks = _arg_peaks_as_expected(peaks)
463
+ wlen = _arg_wlen_as_expected(wlen)
464
+ return _peak_prominences(x, peaks, wlen)
465
+
466
+
467
+ def peak_widths(x, peaks, rel_height=0.5, prominence_data=None, wlen=None):
468
+ """
469
+ Calculate the width of each peak in a signal.
470
+
471
+ This function calculates the width of a peak in samples at a relative
472
+ distance to the peak's height and prominence.
473
+
474
+ Parameters
475
+ ----------
476
+ x : sequence
477
+ A signal with peaks.
478
+ peaks : sequence
479
+ Indices of peaks in `x`.
480
+ rel_height : float, optional
481
+ Chooses the relative height at which the peak width is measured as a
482
+ percentage of its prominence. 1.0 calculates the width of the peak at
483
+ its lowest contour line while 0.5 evaluates at half the prominence
484
+ height. Must be at least 0. See notes for further explanation.
485
+ prominence_data : tuple, optional
486
+ A tuple of three arrays matching the output of `peak_prominences` when
487
+ called with the same arguments `x` and `peaks`. This data are calculated
488
+ internally if not provided.
489
+ wlen : int, optional
490
+ A window length in samples passed to `peak_prominences` as an optional
491
+ argument for internal calculation of `prominence_data`. This argument
492
+ is ignored if `prominence_data` is given.
493
+
494
+ Returns
495
+ -------
496
+ widths : ndarray
497
+ The widths for each peak in samples.
498
+ width_heights : ndarray
499
+ The height of the contour lines at which the `widths` where evaluated.
500
+ left_ips, right_ips : ndarray
501
+ Interpolated positions of left and right intersection points of a
502
+ horizontal line at the respective evaluation height.
503
+
504
+ Raises
505
+ ------
506
+ ValueError
507
+ If `prominence_data` is supplied but doesn't satisfy the condition
508
+ ``0 <= left_base <= peak <= right_base < x.shape[0]`` for each peak,
509
+ has the wrong dtype, is not C-contiguous or does not have the same
510
+ shape.
511
+
512
+ Warns
513
+ -----
514
+ PeakPropertyWarning
515
+ Raised if any calculated width is 0. This may stem from the supplied
516
+ `prominence_data` or if `rel_height` is set to 0.
517
+
518
+ Warnings
519
+ --------
520
+ This function may return unexpected results for data containing NaNs. To
521
+ avoid this, NaNs should either be removed or replaced.
522
+
523
+ See Also
524
+ --------
525
+ find_peaks
526
+ Find peaks inside a signal based on peak properties.
527
+ peak_prominences
528
+ Calculate the prominence of peaks.
529
+
530
+ Notes
531
+ -----
532
+ The basic algorithm to calculate a peak's width is as follows:
533
+
534
+ * Calculate the evaluation height :math:`h_{eval}` with the formula
535
+ :math:`h_{eval} = h_{Peak} - P \\cdot R`, where :math:`h_{Peak}` is the
536
+ height of the peak itself, :math:`P` is the peak's prominence and
537
+ :math:`R` a positive ratio specified with the argument `rel_height`.
538
+ * Draw a horizontal line at the evaluation height to both sides, starting at
539
+ the peak's current vertical position until the lines either intersect a
540
+ slope, the signal border or cross the vertical position of the peak's
541
+ base (see `peak_prominences` for a definition). For the first case,
542
+ intersection with the signal, the true intersection point is estimated
543
+ with linear interpolation.
544
+ * Calculate the width as the horizontal distance between the chosen
545
+ endpoints on both sides. As a consequence of this the maximal possible
546
+ width for each peak is the horizontal distance between its bases.
547
+
548
+ As shown above to calculate a peak's width its prominence and bases must be
549
+ known. You can supply these yourself with the argument `prominence_data`.
550
+ Otherwise, they are internally calculated (see `peak_prominences`).
551
+
552
+ .. versionadded:: 1.1.0
553
+
554
+ Examples
555
+ --------
556
+ >>> import numpy as np
557
+ >>> from scipy.signal import chirp, find_peaks, peak_widths
558
+ >>> import matplotlib.pyplot as plt
559
+
560
+ Create a test signal with two overlaid harmonics
561
+
562
+ >>> x = np.linspace(0, 6 * np.pi, 1000)
563
+ >>> x = np.sin(x) + 0.6 * np.sin(2.6 * x)
564
+
565
+ Find all peaks and calculate their widths at the relative height of 0.5
566
+ (contour line at half the prominence height) and 1 (at the lowest contour
567
+ line at full prominence height).
568
+
569
+ >>> peaks, _ = find_peaks(x)
570
+ >>> results_half = peak_widths(x, peaks, rel_height=0.5)
571
+ >>> results_half[0] # widths
572
+ array([ 64.25172825, 41.29465463, 35.46943289, 104.71586081,
573
+ 35.46729324, 41.30429622, 181.93835853, 45.37078546])
574
+ >>> results_full = peak_widths(x, peaks, rel_height=1)
575
+ >>> results_full[0] # widths
576
+ array([181.9396084 , 72.99284945, 61.28657872, 373.84622694,
577
+ 61.78404617, 72.48822812, 253.09161876, 79.36860878])
578
+
579
+ Plot signal, peaks and contour lines at which the widths where calculated
580
+
581
+ >>> plt.plot(x)
582
+ >>> plt.plot(peaks, x[peaks], "x")
583
+ >>> plt.hlines(*results_half[1:], color="C2")
584
+ >>> plt.hlines(*results_full[1:], color="C3")
585
+ >>> plt.show()
586
+ """
587
+ x = _arg_x_as_expected(x)
588
+ peaks = _arg_peaks_as_expected(peaks)
589
+ if prominence_data is None:
590
+ # Calculate prominence if not supplied and use wlen if supplied.
591
+ wlen = _arg_wlen_as_expected(wlen)
592
+ prominence_data = _peak_prominences(x, peaks, wlen)
593
+ return _peak_widths(x, peaks, rel_height, *prominence_data)
594
+
595
+
596
+ def _unpack_condition_args(interval, x, peaks):
597
+ """
598
+ Parse condition arguments for `find_peaks`.
599
+
600
+ Parameters
601
+ ----------
602
+ interval : number or ndarray or sequence
603
+ Either a number or ndarray or a 2-element sequence of the former. The
604
+ first value is always interpreted as `imin` and the second, if supplied,
605
+ as `imax`.
606
+ x : ndarray
607
+ The signal with `peaks`.
608
+ peaks : ndarray
609
+ An array with indices used to reduce `imin` and / or `imax` if those are
610
+ arrays.
611
+
612
+ Returns
613
+ -------
614
+ imin, imax : number or ndarray or None
615
+ Minimal and maximal value in `argument`.
616
+
617
+ Raises
618
+ ------
619
+ ValueError :
620
+ If interval border is given as array and its size does not match the size
621
+ of `x`.
622
+
623
+ Notes
624
+ -----
625
+
626
+ .. versionadded:: 1.1.0
627
+ """
628
+ try:
629
+ imin, imax = interval
630
+ except (TypeError, ValueError):
631
+ imin, imax = (interval, None)
632
+
633
+ # Reduce arrays if arrays
634
+ if isinstance(imin, np.ndarray):
635
+ if imin.size != x.size:
636
+ raise ValueError('array size of lower interval border must match x')
637
+ imin = imin[peaks]
638
+ if isinstance(imax, np.ndarray):
639
+ if imax.size != x.size:
640
+ raise ValueError('array size of upper interval border must match x')
641
+ imax = imax[peaks]
642
+
643
+ return imin, imax
644
+
645
+
646
+ def _select_by_property(peak_properties, pmin, pmax):
647
+ """
648
+ Evaluate where the generic property of peaks confirms to an interval.
649
+
650
+ Parameters
651
+ ----------
652
+ peak_properties : ndarray
653
+ An array with properties for each peak.
654
+ pmin : None or number or ndarray
655
+ Lower interval boundary for `peak_properties`. ``None`` is interpreted as
656
+ an open border.
657
+ pmax : None or number or ndarray
658
+ Upper interval boundary for `peak_properties`. ``None`` is interpreted as
659
+ an open border.
660
+
661
+ Returns
662
+ -------
663
+ keep : bool
664
+ A boolean mask evaluating to true where `peak_properties` confirms to the
665
+ interval.
666
+
667
+ See Also
668
+ --------
669
+ find_peaks
670
+
671
+ Notes
672
+ -----
673
+
674
+ .. versionadded:: 1.1.0
675
+ """
676
+ keep = np.ones(peak_properties.size, dtype=bool)
677
+ if pmin is not None:
678
+ keep &= (pmin <= peak_properties)
679
+ if pmax is not None:
680
+ keep &= (peak_properties <= pmax)
681
+ return keep
682
+
683
+
684
+ def _select_by_peak_threshold(x, peaks, tmin, tmax):
685
+ """
686
+ Evaluate which peaks fulfill the threshold condition.
687
+
688
+ Parameters
689
+ ----------
690
+ x : ndarray
691
+ A 1-D array which is indexable by `peaks`.
692
+ peaks : ndarray
693
+ Indices of peaks in `x`.
694
+ tmin, tmax : scalar or ndarray or None
695
+ Minimal and / or maximal required thresholds. If supplied as ndarrays
696
+ their size must match `peaks`. ``None`` is interpreted as an open
697
+ border.
698
+
699
+ Returns
700
+ -------
701
+ keep : bool
702
+ A boolean mask evaluating to true where `peaks` fulfill the threshold
703
+ condition.
704
+ left_thresholds, right_thresholds : ndarray
705
+ Array matching `peak` containing the thresholds of each peak on
706
+ both sides.
707
+
708
+ Notes
709
+ -----
710
+
711
+ .. versionadded:: 1.1.0
712
+ """
713
+ # Stack thresholds on both sides to make min / max operations easier:
714
+ # tmin is compared with the smaller, and tmax with the greater threshold to
715
+ # each peak's side
716
+ stacked_thresholds = np.vstack([x[peaks] - x[peaks - 1],
717
+ x[peaks] - x[peaks + 1]])
718
+ keep = np.ones(peaks.size, dtype=bool)
719
+ if tmin is not None:
720
+ min_thresholds = np.min(stacked_thresholds, axis=0)
721
+ keep &= (tmin <= min_thresholds)
722
+ if tmax is not None:
723
+ max_thresholds = np.max(stacked_thresholds, axis=0)
724
+ keep &= (max_thresholds <= tmax)
725
+
726
+ return keep, stacked_thresholds[0], stacked_thresholds[1]
727
+
728
+
729
+ def find_peaks(x, height=None, threshold=None, distance=None,
730
+ prominence=None, width=None, wlen=None, rel_height=0.5,
731
+ plateau_size=None):
732
+ """
733
+ Find peaks inside a signal based on peak properties.
734
+
735
+ This function takes a 1-D array and finds all local maxima by
736
+ simple comparison of neighboring values. Optionally, a subset of these
737
+ peaks can be selected by specifying conditions for a peak's properties.
738
+
739
+ Parameters
740
+ ----------
741
+ x : sequence
742
+ A signal with peaks.
743
+ height : number or ndarray or sequence, optional
744
+ Required height of peaks. Either a number, ``None``, an array matching
745
+ `x` or a 2-element sequence of the former. The first element is
746
+ always interpreted as the minimal and the second, if supplied, as the
747
+ maximal required height.
748
+ threshold : number or ndarray or sequence, optional
749
+ Required threshold of peaks, the vertical distance to its neighboring
750
+ samples. Either a number, ``None``, an array matching `x` or a
751
+ 2-element sequence of the former. The first element is always
752
+ interpreted as the minimal and the second, if supplied, as the maximal
753
+ required threshold.
754
+ distance : number, optional
755
+ Required minimal horizontal distance (>= 1) in samples between
756
+ neighbouring peaks. Smaller peaks are removed first until the condition
757
+ is fulfilled for all remaining peaks.
758
+ prominence : number or ndarray or sequence, optional
759
+ Required prominence of peaks. Either a number, ``None``, an array
760
+ matching `x` or a 2-element sequence of the former. The first
761
+ element is always interpreted as the minimal and the second, if
762
+ supplied, as the maximal required prominence.
763
+ width : number or ndarray or sequence, optional
764
+ Required width of peaks in samples. Either a number, ``None``, an array
765
+ matching `x` or a 2-element sequence of the former. The first
766
+ element is always interpreted as the minimal and the second, if
767
+ supplied, as the maximal required width.
768
+ wlen : int, optional
769
+ Used for calculation of the peaks prominences, thus it is only used if
770
+ one of the arguments `prominence` or `width` is given. See argument
771
+ `wlen` in `peak_prominences` for a full description of its effects.
772
+ rel_height : float, optional
773
+ Used for calculation of the peaks width, thus it is only used if `width`
774
+ is given. See argument `rel_height` in `peak_widths` for a full
775
+ description of its effects.
776
+ plateau_size : number or ndarray or sequence, optional
777
+ Required size of the flat top of peaks in samples. Either a number,
778
+ ``None``, an array matching `x` or a 2-element sequence of the former.
779
+ The first element is always interpreted as the minimal and the second,
780
+ if supplied as the maximal required plateau size.
781
+
782
+ .. versionadded:: 1.2.0
783
+
784
+ Returns
785
+ -------
786
+ peaks : ndarray
787
+ Indices of peaks in `x` that satisfy all given conditions.
788
+ properties : dict
789
+ A dictionary containing properties of the returned peaks which were
790
+ calculated as intermediate results during evaluation of the specified
791
+ conditions:
792
+
793
+ * 'peak_heights'
794
+ If `height` is given, the height of each peak in `x`.
795
+ * 'left_thresholds', 'right_thresholds'
796
+ If `threshold` is given, these keys contain a peaks vertical
797
+ distance to its neighbouring samples.
798
+ * 'prominences', 'right_bases', 'left_bases'
799
+ If `prominence` is given, these keys are accessible. See
800
+ `peak_prominences` for a description of their content.
801
+ * 'widths', 'width_heights', 'left_ips', 'right_ips'
802
+ If `width` is given, these keys are accessible. See `peak_widths`
803
+ for a description of their content.
804
+ * 'plateau_sizes', left_edges', 'right_edges'
805
+ If `plateau_size` is given, these keys are accessible and contain
806
+ the indices of a peak's edges (edges are still part of the
807
+ plateau) and the calculated plateau sizes.
808
+
809
+ .. versionadded:: 1.2.0
810
+
811
+ To calculate and return properties without excluding peaks, provide the
812
+ open interval ``(None, None)`` as a value to the appropriate argument
813
+ (excluding `distance`).
814
+
815
+ Warns
816
+ -----
817
+ PeakPropertyWarning
818
+ Raised if a peak's properties have unexpected values (see
819
+ `peak_prominences` and `peak_widths`).
820
+
821
+ Warnings
822
+ --------
823
+ This function may return unexpected results for data containing NaNs. To
824
+ avoid this, NaNs should either be removed or replaced.
825
+
826
+ See Also
827
+ --------
828
+ find_peaks_cwt
829
+ Find peaks using the wavelet transformation.
830
+ peak_prominences
831
+ Directly calculate the prominence of peaks.
832
+ peak_widths
833
+ Directly calculate the width of peaks.
834
+
835
+ Notes
836
+ -----
837
+ In the context of this function, a peak or local maximum is defined as any
838
+ sample whose two direct neighbours have a smaller amplitude. For flat peaks
839
+ (more than one sample of equal amplitude wide) the index of the middle
840
+ sample is returned (rounded down in case the number of samples is even).
841
+ For noisy signals the peak locations can be off because the noise might
842
+ change the position of local maxima. In those cases consider smoothing the
843
+ signal before searching for peaks or use other peak finding and fitting
844
+ methods (like `find_peaks_cwt`).
845
+
846
+ Some additional comments on specifying conditions:
847
+
848
+ * Almost all conditions (excluding `distance`) can be given as half-open or
849
+ closed intervals, e.g., ``1`` or ``(1, None)`` defines the half-open
850
+ interval :math:`[1, \\infty]` while ``(None, 1)`` defines the interval
851
+ :math:`[-\\infty, 1]`. The open interval ``(None, None)`` can be specified
852
+ as well, which returns the matching properties without exclusion of peaks.
853
+ * The border is always included in the interval used to select valid peaks.
854
+ * For several conditions the interval borders can be specified with
855
+ arrays matching `x` in shape which enables dynamic constrains based on
856
+ the sample position.
857
+ * The conditions are evaluated in the following order: `plateau_size`,
858
+ `height`, `threshold`, `distance`, `prominence`, `width`. In most cases
859
+ this order is the fastest one because faster operations are applied first
860
+ to reduce the number of peaks that need to be evaluated later.
861
+ * While indices in `peaks` are guaranteed to be at least `distance` samples
862
+ apart, edges of flat peaks may be closer than the allowed `distance`.
863
+ * Use `wlen` to reduce the time it takes to evaluate the conditions for
864
+ `prominence` or `width` if `x` is large or has many local maxima
865
+ (see `peak_prominences`).
866
+
867
+ .. versionadded:: 1.1.0
868
+
869
+ Examples
870
+ --------
871
+ To demonstrate this function's usage we use a signal `x` supplied with
872
+ SciPy (see `scipy.datasets.electrocardiogram`). Let's find all peaks (local
873
+ maxima) in `x` whose amplitude lies above 0.
874
+
875
+ >>> import numpy as np
876
+ >>> import matplotlib.pyplot as plt
877
+ >>> from scipy.datasets import electrocardiogram
878
+ >>> from scipy.signal import find_peaks
879
+ >>> x = electrocardiogram()[2000:4000]
880
+ >>> peaks, _ = find_peaks(x, height=0)
881
+ >>> plt.plot(x)
882
+ >>> plt.plot(peaks, x[peaks], "x")
883
+ >>> plt.plot(np.zeros_like(x), "--", color="gray")
884
+ >>> plt.show()
885
+
886
+ We can select peaks below 0 with ``height=(None, 0)`` or use arrays matching
887
+ `x` in size to reflect a changing condition for different parts of the
888
+ signal.
889
+
890
+ >>> border = np.sin(np.linspace(0, 3 * np.pi, x.size))
891
+ >>> peaks, _ = find_peaks(x, height=(-border, border))
892
+ >>> plt.plot(x)
893
+ >>> plt.plot(-border, "--", color="gray")
894
+ >>> plt.plot(border, ":", color="gray")
895
+ >>> plt.plot(peaks, x[peaks], "x")
896
+ >>> plt.show()
897
+
898
+ Another useful condition for periodic signals can be given with the
899
+ `distance` argument. In this case, we can easily select the positions of
900
+ QRS complexes within the electrocardiogram (ECG) by demanding a distance of
901
+ at least 150 samples.
902
+
903
+ >>> peaks, _ = find_peaks(x, distance=150)
904
+ >>> np.diff(peaks)
905
+ array([186, 180, 177, 171, 177, 169, 167, 164, 158, 162, 172])
906
+ >>> plt.plot(x)
907
+ >>> plt.plot(peaks, x[peaks], "x")
908
+ >>> plt.show()
909
+
910
+ Especially for noisy signals peaks can be easily grouped by their
911
+ prominence (see `peak_prominences`). E.g., we can select all peaks except
912
+ for the mentioned QRS complexes by limiting the allowed prominence to 0.6.
913
+
914
+ >>> peaks, properties = find_peaks(x, prominence=(None, 0.6))
915
+ >>> properties["prominences"].max()
916
+ 0.5049999999999999
917
+ >>> plt.plot(x)
918
+ >>> plt.plot(peaks, x[peaks], "x")
919
+ >>> plt.show()
920
+
921
+ And, finally, let's examine a different section of the ECG which contains
922
+ beat forms of different shape. To select only the atypical heart beats, we
923
+ combine two conditions: a minimal prominence of 1 and width of at least 20
924
+ samples.
925
+
926
+ >>> x = electrocardiogram()[17000:18000]
927
+ >>> peaks, properties = find_peaks(x, prominence=1, width=20)
928
+ >>> properties["prominences"], properties["widths"]
929
+ (array([1.495, 2.3 ]), array([36.93773946, 39.32723577]))
930
+ >>> plt.plot(x)
931
+ >>> plt.plot(peaks, x[peaks], "x")
932
+ >>> plt.vlines(x=peaks, ymin=x[peaks] - properties["prominences"],
933
+ ... ymax = x[peaks], color = "C1")
934
+ >>> plt.hlines(y=properties["width_heights"], xmin=properties["left_ips"],
935
+ ... xmax=properties["right_ips"], color = "C1")
936
+ >>> plt.show()
937
+ """
938
+ # _argmaxima1d expects array of dtype 'float64'
939
+ x = _arg_x_as_expected(x)
940
+ if distance is not None and distance < 1:
941
+ raise ValueError('`distance` must be greater or equal to 1')
942
+
943
+ peaks, left_edges, right_edges = _local_maxima_1d(x)
944
+ properties = {}
945
+
946
+ if plateau_size is not None:
947
+ # Evaluate plateau size
948
+ plateau_sizes = right_edges - left_edges + 1
949
+ pmin, pmax = _unpack_condition_args(plateau_size, x, peaks)
950
+ keep = _select_by_property(plateau_sizes, pmin, pmax)
951
+ peaks = peaks[keep]
952
+ properties["plateau_sizes"] = plateau_sizes
953
+ properties["left_edges"] = left_edges
954
+ properties["right_edges"] = right_edges
955
+ properties = {key: array[keep] for key, array in properties.items()}
956
+
957
+ if height is not None:
958
+ # Evaluate height condition
959
+ peak_heights = x[peaks]
960
+ hmin, hmax = _unpack_condition_args(height, x, peaks)
961
+ keep = _select_by_property(peak_heights, hmin, hmax)
962
+ peaks = peaks[keep]
963
+ properties["peak_heights"] = peak_heights
964
+ properties = {key: array[keep] for key, array in properties.items()}
965
+
966
+ if threshold is not None:
967
+ # Evaluate threshold condition
968
+ tmin, tmax = _unpack_condition_args(threshold, x, peaks)
969
+ keep, left_thresholds, right_thresholds = _select_by_peak_threshold(
970
+ x, peaks, tmin, tmax)
971
+ peaks = peaks[keep]
972
+ properties["left_thresholds"] = left_thresholds
973
+ properties["right_thresholds"] = right_thresholds
974
+ properties = {key: array[keep] for key, array in properties.items()}
975
+
976
+ if distance is not None:
977
+ # Evaluate distance condition
978
+ keep = _select_by_peak_distance(peaks, x[peaks], distance)
979
+ peaks = peaks[keep]
980
+ properties = {key: array[keep] for key, array in properties.items()}
981
+
982
+ if prominence is not None or width is not None:
983
+ # Calculate prominence (required for both conditions)
984
+ wlen = _arg_wlen_as_expected(wlen)
985
+ properties.update(zip(
986
+ ['prominences', 'left_bases', 'right_bases'],
987
+ _peak_prominences(x, peaks, wlen=wlen)
988
+ ))
989
+
990
+ if prominence is not None:
991
+ # Evaluate prominence condition
992
+ pmin, pmax = _unpack_condition_args(prominence, x, peaks)
993
+ keep = _select_by_property(properties['prominences'], pmin, pmax)
994
+ peaks = peaks[keep]
995
+ properties = {key: array[keep] for key, array in properties.items()}
996
+
997
+ if width is not None:
998
+ # Calculate widths
999
+ properties.update(zip(
1000
+ ['widths', 'width_heights', 'left_ips', 'right_ips'],
1001
+ _peak_widths(x, peaks, rel_height, properties['prominences'],
1002
+ properties['left_bases'], properties['right_bases'])
1003
+ ))
1004
+ # Evaluate width condition
1005
+ wmin, wmax = _unpack_condition_args(width, x, peaks)
1006
+ keep = _select_by_property(properties['widths'], wmin, wmax)
1007
+ peaks = peaks[keep]
1008
+ properties = {key: array[keep] for key, array in properties.items()}
1009
+
1010
+ return peaks, properties
1011
+
1012
+
1013
+ def _identify_ridge_lines(matr, max_distances, gap_thresh):
1014
+ """
1015
+ Identify ridges in the 2-D matrix.
1016
+
1017
+ Expect that the width of the wavelet feature increases with increasing row
1018
+ number.
1019
+
1020
+ Parameters
1021
+ ----------
1022
+ matr : 2-D ndarray
1023
+ Matrix in which to identify ridge lines.
1024
+ max_distances : 1-D sequence
1025
+ At each row, a ridge line is only connected
1026
+ if the relative max at row[n] is within
1027
+ `max_distances`[n] from the relative max at row[n+1].
1028
+ gap_thresh : int
1029
+ If a relative maximum is not found within `max_distances`,
1030
+ there will be a gap. A ridge line is discontinued if
1031
+ there are more than `gap_thresh` points without connecting
1032
+ a new relative maximum.
1033
+
1034
+ Returns
1035
+ -------
1036
+ ridge_lines : tuple
1037
+ Tuple of 2 1-D sequences. `ridge_lines`[ii][0] are the rows of the
1038
+ ii-th ridge-line, `ridge_lines`[ii][1] are the columns. Empty if none
1039
+ found. Each ridge-line will be sorted by row (increasing), but the
1040
+ order of the ridge lines is not specified.
1041
+
1042
+ References
1043
+ ----------
1044
+ .. [1] Bioinformatics (2006) 22 (17): 2059-2065.
1045
+ :doi:`10.1093/bioinformatics/btl355`
1046
+
1047
+ Examples
1048
+ --------
1049
+ >>> import numpy as np
1050
+ >>> from scipy.signal._peak_finding import _identify_ridge_lines
1051
+ >>> rng = np.random.default_rng()
1052
+ >>> data = rng.random((5,5))
1053
+ >>> max_dist = 3
1054
+ >>> max_distances = np.full(20, max_dist)
1055
+ >>> ridge_lines = _identify_ridge_lines(data, max_distances, 1)
1056
+
1057
+ Notes
1058
+ -----
1059
+ This function is intended to be used in conjunction with `cwt`
1060
+ as part of `find_peaks_cwt`.
1061
+
1062
+ """
1063
+ if len(max_distances) < matr.shape[0]:
1064
+ raise ValueError('Max_distances must have at least as many rows '
1065
+ 'as matr')
1066
+
1067
+ all_max_cols = _boolrelextrema(matr, np.greater, axis=1, order=1)
1068
+ # Highest row for which there are any relative maxima
1069
+ has_relmax = np.nonzero(all_max_cols.any(axis=1))[0]
1070
+ if len(has_relmax) == 0:
1071
+ return []
1072
+ start_row = has_relmax[-1]
1073
+ # Each ridge line is a 3-tuple:
1074
+ # rows, cols,Gap number
1075
+ ridge_lines = [[[start_row],
1076
+ [col],
1077
+ 0] for col in np.nonzero(all_max_cols[start_row])[0]]
1078
+ final_lines = []
1079
+ rows = np.arange(start_row - 1, -1, -1)
1080
+ cols = np.arange(0, matr.shape[1])
1081
+ for row in rows:
1082
+ this_max_cols = cols[all_max_cols[row]]
1083
+
1084
+ # Increment gap number of each line,
1085
+ # set it to zero later if appropriate
1086
+ for line in ridge_lines:
1087
+ line[2] += 1
1088
+
1089
+ # XXX These should always be all_max_cols[row]
1090
+ # But the order might be different. Might be an efficiency gain
1091
+ # to make sure the order is the same and avoid this iteration
1092
+ prev_ridge_cols = np.array([line[1][-1] for line in ridge_lines])
1093
+ # Look through every relative maximum found at current row
1094
+ # Attempt to connect them with existing ridge lines.
1095
+ for ind, col in enumerate(this_max_cols):
1096
+ # If there is a previous ridge line within
1097
+ # the max_distance to connect to, do so.
1098
+ # Otherwise start a new one.
1099
+ line = None
1100
+ if len(prev_ridge_cols) > 0:
1101
+ diffs = np.abs(col - prev_ridge_cols)
1102
+ closest = np.argmin(diffs)
1103
+ if diffs[closest] <= max_distances[row]:
1104
+ line = ridge_lines[closest]
1105
+ if line is not None:
1106
+ # Found a point close enough, extend current ridge line
1107
+ line[1].append(col)
1108
+ line[0].append(row)
1109
+ line[2] = 0
1110
+ else:
1111
+ new_line = [[row],
1112
+ [col],
1113
+ 0]
1114
+ ridge_lines.append(new_line)
1115
+
1116
+ # Remove the ridge lines with gap_number too high
1117
+ # XXX Modifying a list while iterating over it.
1118
+ # Should be safe, since we iterate backwards, but
1119
+ # still tacky.
1120
+ for ind in range(len(ridge_lines) - 1, -1, -1):
1121
+ line = ridge_lines[ind]
1122
+ if line[2] > gap_thresh:
1123
+ final_lines.append(line)
1124
+ del ridge_lines[ind]
1125
+
1126
+ out_lines = []
1127
+ for line in (final_lines + ridge_lines):
1128
+ sortargs = np.argsort(line[0])
1129
+ rows = np.array(line[0])[sortargs]
1130
+ cols = np.array(line[1])[sortargs]
1131
+ out_lines.append([rows, cols])
1132
+
1133
+ return out_lines
1134
+
1135
+
1136
+ def _filter_ridge_lines(cwt, ridge_lines, window_size=None, min_length=None,
1137
+ min_snr=1, noise_perc=10):
1138
+ """
1139
+ Filter ridge lines according to prescribed criteria. Intended
1140
+ to be used for finding relative maxima.
1141
+
1142
+ Parameters
1143
+ ----------
1144
+ cwt : 2-D ndarray
1145
+ Continuous wavelet transform from which the `ridge_lines` were defined.
1146
+ ridge_lines : 1-D sequence
1147
+ Each element should contain 2 sequences, the rows and columns
1148
+ of the ridge line (respectively).
1149
+ window_size : int, optional
1150
+ Size of window to use to calculate noise floor.
1151
+ Default is ``cwt.shape[1] / 20``.
1152
+ min_length : int, optional
1153
+ Minimum length a ridge line needs to be acceptable.
1154
+ Default is ``cwt.shape[0] / 4``, ie 1/4-th the number of widths.
1155
+ min_snr : float, optional
1156
+ Minimum SNR ratio. Default 1. The signal is the value of
1157
+ the cwt matrix at the shortest length scale (``cwt[0, loc]``), the
1158
+ noise is the `noise_perc`\\ th percentile of datapoints contained within a
1159
+ window of `window_size` around ``cwt[0, loc]``.
1160
+ noise_perc : float, optional
1161
+ When calculating the noise floor, percentile of data points
1162
+ examined below which to consider noise. Calculated using
1163
+ scipy.stats.scoreatpercentile.
1164
+
1165
+ References
1166
+ ----------
1167
+ .. [1] Bioinformatics (2006) 22 (17): 2059-2065.
1168
+ :doi:`10.1093/bioinformatics/btl355`
1169
+
1170
+ """
1171
+ num_points = cwt.shape[1]
1172
+ if min_length is None:
1173
+ min_length = np.ceil(cwt.shape[0] / 4)
1174
+ if window_size is None:
1175
+ window_size = np.ceil(num_points / 20)
1176
+
1177
+ window_size = int(window_size)
1178
+ hf_window, odd = divmod(window_size, 2)
1179
+
1180
+ # Filter based on SNR
1181
+ row_one = cwt[0, :]
1182
+ noises = np.empty_like(row_one)
1183
+ for ind, val in enumerate(row_one):
1184
+ window_start = max(ind - hf_window, 0)
1185
+ window_end = min(ind + hf_window + odd, num_points)
1186
+ noises[ind] = scoreatpercentile(row_one[window_start:window_end],
1187
+ per=noise_perc)
1188
+
1189
+ def filt_func(line):
1190
+ if len(line[0]) < min_length:
1191
+ return False
1192
+ snr = abs(cwt[line[0][0], line[1][0]] / noises[line[1][0]])
1193
+ if snr < min_snr:
1194
+ return False
1195
+ return True
1196
+
1197
+ return list(filter(filt_func, ridge_lines))
1198
+
1199
+
1200
+ def find_peaks_cwt(vector, widths, wavelet=None, max_distances=None,
1201
+ gap_thresh=None, min_length=None,
1202
+ min_snr=1, noise_perc=10, window_size=None):
1203
+ """
1204
+ Find peaks in a 1-D array with wavelet transformation.
1205
+
1206
+ The general approach is to smooth `vector` by convolving it with
1207
+ `wavelet(width)` for each width in `widths`. Relative maxima which
1208
+ appear at enough length scales, and with sufficiently high SNR, are
1209
+ accepted.
1210
+
1211
+ Parameters
1212
+ ----------
1213
+ vector : ndarray
1214
+ 1-D array in which to find the peaks.
1215
+ widths : float or sequence
1216
+ Single width or 1-D array-like of widths to use for calculating
1217
+ the CWT matrix. In general,
1218
+ this range should cover the expected width of peaks of interest.
1219
+ wavelet : callable, optional
1220
+ Should take two parameters and return a 1-D array to convolve
1221
+ with `vector`. The first parameter determines the number of points
1222
+ of the returned wavelet array, the second parameter is the scale
1223
+ (`width`) of the wavelet. Should be normalized and symmetric.
1224
+ Default is the ricker wavelet.
1225
+ max_distances : ndarray, optional
1226
+ At each row, a ridge line is only connected if the relative max at
1227
+ ``row[n]`` is within ``max_distances[n]`` from the relative max at
1228
+ ``row[n+1]``. Default value is ``widths/4``.
1229
+ gap_thresh : float, optional
1230
+ If a relative maximum is not found within `max_distances`,
1231
+ there will be a gap. A ridge line is discontinued if there are more
1232
+ than `gap_thresh` points without connecting a new relative maximum.
1233
+ Default is the first value of the widths array i.e. ``widths[0]``.
1234
+ min_length : int, optional
1235
+ Minimum length a ridge line needs to be acceptable.
1236
+ Default is ``cwt.shape[0] / 4``, ie 1/4-th the number of widths.
1237
+ min_snr : float, optional
1238
+ Minimum SNR ratio. Default 1. The signal is the maximum CWT coefficient
1239
+ on the largest ridge line. The noise is `noise_perc` th percentile of
1240
+ datapoints contained within the same ridge line.
1241
+ noise_perc : float, optional
1242
+ When calculating the noise floor, percentile of data points
1243
+ examined below which to consider noise. Calculated using
1244
+ `stats.scoreatpercentile`. Default is 10.
1245
+ window_size : int, optional
1246
+ Size of window to use to calculate noise floor.
1247
+ Default is ``cwt.shape[1] / 20``.
1248
+
1249
+ Returns
1250
+ -------
1251
+ peaks_indices : ndarray
1252
+ Indices of the locations in the `vector` where peaks were found.
1253
+ The list is sorted.
1254
+
1255
+ See Also
1256
+ --------
1257
+ find_peaks
1258
+ Find peaks inside a signal based on peak properties.
1259
+
1260
+ Notes
1261
+ -----
1262
+ This approach was designed for finding sharp peaks among noisy data,
1263
+ however with proper parameter selection it should function well for
1264
+ different peak shapes.
1265
+
1266
+ The algorithm is as follows:
1267
+ 1. Perform a continuous wavelet transform on `vector`, for the supplied
1268
+ `widths`. This is a convolution of `vector` with `wavelet(width)` for
1269
+ each width in `widths`.
1270
+ 2. Identify "ridge lines" in the cwt matrix. These are relative maxima
1271
+ at each row, connected across adjacent rows. See identify_ridge_lines
1272
+ 3. Filter the ridge_lines using filter_ridge_lines.
1273
+
1274
+ References
1275
+ ----------
1276
+ .. [1] Bioinformatics (2006) 22 (17): 2059-2065.
1277
+ :doi:`10.1093/bioinformatics/btl355`
1278
+
1279
+ Examples
1280
+ --------
1281
+ >>> import numpy as np
1282
+ >>> from scipy import signal
1283
+ >>> xs = np.arange(0, np.pi, 0.05)
1284
+ >>> data = np.sin(xs)
1285
+ >>> peakind = signal.find_peaks_cwt(data, np.arange(1,10))
1286
+ >>> peakind, xs[peakind], data[peakind]
1287
+ ([32], array([ 1.6]), array([ 0.9995736]))
1288
+
1289
+ """
1290
+ widths = np.atleast_1d(np.asarray(widths))
1291
+
1292
+ if gap_thresh is None:
1293
+ gap_thresh = np.ceil(widths[0])
1294
+ if max_distances is None:
1295
+ max_distances = widths / 4.0
1296
+ if wavelet is None:
1297
+ wavelet = _ricker
1298
+
1299
+ cwt_dat = _cwt(vector, wavelet, widths)
1300
+ ridge_lines = _identify_ridge_lines(cwt_dat, max_distances, gap_thresh)
1301
+ filtered = _filter_ridge_lines(cwt_dat, ridge_lines, min_length=min_length,
1302
+ window_size=window_size, min_snr=min_snr,
1303
+ noise_perc=noise_perc)
1304
+ max_locs = np.asarray([x[1][0] for x in filtered])
1305
+ max_locs.sort()
1306
+
1307
+ return max_locs