PySDKit 0.4.44__tar.gz → 0.4.45__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. {pysdkit-0.4.44 → pysdkit-0.4.45}/PKG-INFO +31 -24
  2. {pysdkit-0.4.44 → pysdkit-0.4.45}/PySDKit.egg-info/PKG-INFO +31 -24
  3. {pysdkit-0.4.44 → pysdkit-0.4.45}/PySDKit.egg-info/SOURCES.txt +1 -0
  4. {pysdkit-0.4.44 → pysdkit-0.4.45}/README.md +30 -23
  5. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/__init__.py +1 -1
  6. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/vmd_c.py +116 -50
  7. pysdkit-0.4.45/pysdkit/_vmd/vmd_f.py +192 -0
  8. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/__init__.py +4 -0
  9. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_generator.py +24 -0
  10. pysdkit-0.4.45/pysdkit/data/vmd_example.npy +0 -0
  11. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/plot/__init__.py +1 -1
  12. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/plot/_plot_imfs.py +274 -15
  13. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_vmd.py +26 -0
  14. pysdkit-0.4.44/pysdkit/_vmd/vmd_f.py +0 -156
  15. {pysdkit-0.4.44 → pysdkit-0.4.45}/LICENSE +0 -0
  16. {pysdkit-0.4.44 → pysdkit-0.4.45}/PySDKit.egg-info/dependency_links.txt +0 -0
  17. {pysdkit-0.4.44 → pysdkit-0.4.45}/PySDKit.egg-info/requires.txt +0 -0
  18. {pysdkit-0.4.44 → pysdkit-0.4.45}/PySDKit.egg-info/top_level.txt +0 -0
  19. {pysdkit-0.4.44 → pysdkit-0.4.45}/pyproject.toml +0 -0
  20. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/__init__.py +0 -0
  21. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/_find_extrema.py +0 -0
  22. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/_prepare_points.py +0 -0
  23. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/_splines.py +0 -0
  24. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/ceemdan.py +0 -0
  25. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/eemd.py +0 -0
  26. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/efd.py +0 -0
  27. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/emd.py +0 -0
  28. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/esmd.py +0 -0
  29. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/hht/__init__.py +0 -0
  30. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/hht/frequency.py +0 -0
  31. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/hht/hht.py +0 -0
  32. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/memd.py +0 -0
  33. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/remd.py +0 -0
  34. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/semd.py +0 -0
  35. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd/tvf_emd.py +0 -0
  36. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd2d/__init__.py +0 -0
  37. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd2d/bemd.py +0 -0
  38. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd2d/bmemd.py +0 -0
  39. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_emd2d/emd2d.py +0 -0
  40. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_ewt/__init__.py +0 -0
  41. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_ewt/ewt.py +0 -0
  42. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_ewt/ewt2d.py +0 -0
  43. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_faemd/__init__.py +0 -0
  44. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_faemd/extrema.py +0 -0
  45. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_faemd/faemd.py +0 -0
  46. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_faemd/faemd2d.py +0 -0
  47. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_faemd/faemd3d.py +0 -0
  48. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_faemd/filter.py +0 -0
  49. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_fmd/__init__.py +0 -0
  50. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_fmd/fmd.py +0 -0
  51. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_fmd/nfmd.py +0 -0
  52. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_gdmd/__init__.py +0 -0
  53. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_gdmd/agncmd.py +0 -0
  54. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_gdmd/gdmd.py +0 -0
  55. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_gdmd/ivgnmd.py +0 -0
  56. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_gdmd/vgnmd.py +0 -0
  57. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_imd/__init__.py +0 -0
  58. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_imd/apmd.py +0 -0
  59. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_imd/data/gearbox_fault_snippet.npy +0 -0
  60. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_imd/data/input_sig.npy +0 -0
  61. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_imd/imd.py +0 -0
  62. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_jmd/__init__.py +0 -0
  63. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_jmd/jmd.py +0 -0
  64. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_jmd/mjmd.py +0 -0
  65. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_jmd/sjmd.py +0 -0
  66. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_lmd/__init__.py +0 -0
  67. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_lmd/lmd.py +0 -0
  68. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_lmd/rlmd.py +0 -0
  69. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_osd/__init__.py +0 -0
  70. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_osd/components.py +0 -0
  71. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_osd/osd.py +0 -0
  72. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_osd/swd.py +0 -0
  73. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/__init__.py +0 -0
  74. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/alif.py +0 -0
  75. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/data/__init__.py +0 -0
  76. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/data/prefixed_double_filter.npy +0 -0
  77. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/hvd.py +0 -0
  78. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/itd.py +0 -0
  79. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/iterative_filtering.py +0 -0
  80. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_tid/ssa.py +0 -0
  81. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/__init__.py +0 -0
  82. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/acmd.py +0 -0
  83. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/avmd.py +0 -0
  84. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/ba_acmd.py +0 -0
  85. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/base.py +0 -0
  86. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/mvmd.py +0 -0
  87. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/ovmd.py +0 -0
  88. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/stvmd.py +0 -0
  89. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/svmd.py +0 -0
  90. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd/vme.py +0 -0
  91. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd2d/__init__.py +0 -0
  92. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd2d/cvmd2d.py +0 -0
  93. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vmd2d/vmd2d.py +0 -0
  94. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vncmd/__init__.py +0 -0
  95. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vncmd/avncmd.py +0 -0
  96. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vncmd/incmd.py +0 -0
  97. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/_vncmd/vncmd.py +0 -0
  98. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_add_noise.py +0 -0
  99. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_cube.py +0 -0
  100. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_image.py +0 -0
  101. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_models.py +0 -0
  102. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_test_univariate.py +0 -0
  103. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/_time_series.py +0 -0
  104. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/fmd_demo.npy +0 -0
  105. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/data/texture.txt +0 -0
  106. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/entropy/__init__.py +0 -0
  107. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
  108. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/entropy/_permutation_entropy.py +0 -0
  109. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/entropy/_sample_entropy.py +0 -0
  110. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/models/__init__.py +0 -0
  111. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/models/_base.py +0 -0
  112. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/models/_kmeans.py +0 -0
  113. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/models/_knn.py +0 -0
  114. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/models/_pca.py +0 -0
  115. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/plot/_fourier_spectra.py +0 -0
  116. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/plot/_functions.py +0 -0
  117. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/plot/_plot_images.py +0 -0
  118. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/plot/_plot_signal.py +0 -0
  119. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/__init__.py +0 -0
  120. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/data/__init__.py +0 -0
  121. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/data/test_generator_cube.py +0 -0
  122. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/data/test_generator_image.py +0 -0
  123. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/data/test_generator_signal.py +0 -0
  124. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
  125. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/models/__init__.py +0 -0
  126. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/models/test_knn.py +0 -0
  127. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/models/test_pca.py +0 -0
  128. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/run_all.py +0 -0
  129. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_agncmd.py +0 -0
  130. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_alif.py +0 -0
  131. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_apmd.py +0 -0
  132. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_avncmd.py +0 -0
  133. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_bmemd.py +0 -0
  134. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_ceemdan.py +0 -0
  135. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_cvmd2d.py +0 -0
  136. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_eemd.py +0 -0
  137. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_emd.py +0 -0
  138. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_esmd.py +0 -0
  139. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_ewt.py +0 -0
  140. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_ewt2d.py +0 -0
  141. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_faemd.py +0 -0
  142. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_faemd2d.py +0 -0
  143. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_faemd3d.py +0 -0
  144. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_fmd.py +0 -0
  145. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_gdmd.py +0 -0
  146. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_hvd.py +0 -0
  147. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_imd.py +0 -0
  148. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_itd.py +0 -0
  149. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_ivgnmd.py +0 -0
  150. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_jmd.py +0 -0
  151. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_lmd.py +0 -0
  152. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_moving_decomp.py +0 -0
  153. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_mstl.py +0 -0
  154. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_osd.py +0 -0
  155. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_ovmd.py +0 -0
  156. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_rlmd.py +0 -0
  157. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_semd.py +0 -0
  158. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_sjmd.py +0 -0
  159. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_ssa.py +0 -0
  160. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_stl.py +0 -0
  161. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_stvmd.py +0 -0
  162. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_svmd.py +0 -0
  163. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_swd.py +0 -0
  164. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_vgnmd.py +0 -0
  165. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_vmd2d.py +0 -0
  166. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tests/test_vncmd.py +0 -0
  167. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tsa/__init__.py +0 -0
  168. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tsa/_dtw.py +0 -0
  169. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tsa/_moving_decomp.py +0 -0
  170. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tsa/_mstl.py +0 -0
  171. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/tsa/_stl.py +0 -0
  172. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/__init__.py +0 -0
  173. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_correlation.py +0 -0
  174. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_diagnalization.py +0 -0
  175. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_differ.py +0 -0
  176. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_fft.py +0 -0
  177. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_function.py +0 -0
  178. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_hilbert.py +0 -0
  179. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_instantaneous.py +0 -0
  180. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_kernel_matrix.py +0 -0
  181. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_mirror.py +0 -0
  182. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_process.py +0 -0
  183. {pysdkit-0.4.44 → pysdkit-0.4.45}/pysdkit/utils/_smooth1d.py +0 -0
  184. {pysdkit-0.4.44 → pysdkit-0.4.45}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.44
3
+ Version: 0.4.45
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Author: Whenxuan Wang, RuiZhe Wang, Rongkun Zhu, Kai Wu, Lei Wang, josefinez, Deeksha Manjunath, Yuan Feng, WenTong Zhao, JacktheFowler
6
6
  Maintainer-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>
@@ -51,10 +51,10 @@ Requires-Dist: tqdm>=4.66.5
51
51
  Requires-Dist: requests>=2.32.3
52
52
  Dynamic: license-file
53
53
 
54
- # PySDKit: signal decomposition in Python
55
-
56
54
  <div align="center">
57
55
 
56
+ # PySDKit: signal decomposition in Python
57
+
58
58
  [![PyPI version](https://badge.fury.io/py/PySDKit.svg)](https://pypi.org/project/PySDKit/)
59
59
  ![License](https://img.shields.io/github/license/wwhenxuan/PySDKit)
60
60
  [![Python](https://img.shields.io/badge/python-3.8+-blue?logo=python)](https://www.python.org/)
@@ -91,50 +91,57 @@ This project integrates simple signal processing methods, signal decomposition a
91
91
  4. Visualize and analyze the original signal and the intrinsic mode functions IMFs obtained by decomposition.
92
92
 
93
93
  ~~~python
94
+ import numpy as np
94
95
  from pysdkit import EMD
95
- from pysdkit.data import test_emd
96
96
  from pysdkit.plot import plot_IMFs
97
97
 
98
- t, signal = test_emd()
98
+ t = np.linspace(0, 1, 1000)
99
+ signal = np.sin(2 * np.pi * 5 * t) + 0.7 * np.sin(2 * np.pi * 25 * t) + 0.45 * np.sin(2 * np.pi * 80 * t)
99
100
 
100
- # create an instance for signal decomposition
101
101
  emd = EMD()
102
- # implement signal decomposition
103
- IMFs = emd.fit_transform(signal, max_imfs=2)
104
- plot_IMFs(signal, IMFs)
102
+ IMFs = emd.fit_transform(signal, max_imfs=3)
103
+ plot_IMFs(signal, IMFs, view="2d_freq", fs=1000, freq_max=150)
105
104
  ~~~
106
105
 
107
- ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/example.jpg)
106
+ ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/decomposition_demo.jpg)
108
107
 
109
108
  The EMD in the above example is the most classic [`empirical mode decomposition`](https://www.mathworks.com/help/signal/ref/emd.html) algorithm in signal decomposition. For more complex signals, you can try other algorithms such as variational mode decomposition ([`VMD`](https://ieeexplore.ieee.org/abstract/document/6655981)).
110
109
 
111
110
  ~~~python
112
- import numpy as np
113
111
  from pysdkit import VMD
112
+ from pysdkit.data import test_vmd
113
+ from pysdkit.plot import plot_IMFs
114
114
 
115
- # load new signal
116
- signal = np.load("./example/example.npy")
117
-
118
- # use variational mode decomposition
119
- vmd = VMD(alpha=500, K=3, tau=0.0, tol=1e-9)
120
- IMFs = vmd.fit_transform(signal=signal)
121
- print(IMFs.shape)
115
+ t, signal, fs = test_vmd()
122
116
 
123
- vmd.plot_IMFs(save_figure=True)
117
+ vmd = VMD(alpha=2000, K=4, tau=0.0, tol=1e-7)
118
+ IMFs = vmd.fit_transform(signal)
119
+ plot_IMFs(signal, IMFs, view="2d_freq", fs=fs, freq_max=fs / 2)
124
120
  ~~~
125
121
 
126
122
  ![vmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/vmd_example.jpg)
127
123
 
128
- Better observe the characteristics of the decomposed intrinsic mode function in the frequency domain.
124
+ For multichannel recordings, algorithms such as multivariate VMD ([`MVMD`](https://doi.org/10.1109/TSP.2019.2951223)) keep shared oscillations **mode-aligned** across channels:
129
125
 
130
126
  ~~~python
131
- from pysdkit.plot import plot_IMFs_amplitude_spectra
127
+ import numpy as np
128
+ from pysdkit import MVMD
129
+ from pysdkit.plot import plot_IMFs
132
130
 
133
- # frequency domain visualization
134
- plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
131
+ t = np.arange(0, 1, 0.001)
132
+ # ch1: 2+36 Hz, ch2: 24+36 Hz, ch3: 80+36 Hz (36 Hz shared)
133
+ signal = np.vstack([
134
+ np.cos(2*np.pi*2*t) + np.cos(2*np.pi*36*t),
135
+ np.cos(2*np.pi*24*t) + np.cos(2*np.pi*36*t),
136
+ np.cos(2*np.pi*80*t) + np.cos(2*np.pi*36*t),
137
+ ])
138
+
139
+ mvmd = MVMD(alpha=2000, K=4, tau=0.0, init="uniform")
140
+ IMFs = mvmd.fit_transform(signal) # shape: (K, T, C)
141
+ plot_IMFs(signal, IMFs) # per-channel panels
135
142
  ~~~
136
143
 
137
- ![frequency_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/frequency_example.jpg)
144
+ ![mvmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/mvmd_example.jpg)
138
145
 
139
146
  ## Target 🎯 <a id="Target"></a>
140
147
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.44
3
+ Version: 0.4.45
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Author: Whenxuan Wang, RuiZhe Wang, Rongkun Zhu, Kai Wu, Lei Wang, josefinez, Deeksha Manjunath, Yuan Feng, WenTong Zhao, JacktheFowler
6
6
  Maintainer-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>
@@ -51,10 +51,10 @@ Requires-Dist: tqdm>=4.66.5
51
51
  Requires-Dist: requests>=2.32.3
52
52
  Dynamic: license-file
53
53
 
54
- # PySDKit: signal decomposition in Python
55
-
56
54
  <div align="center">
57
55
 
56
+ # PySDKit: signal decomposition in Python
57
+
58
58
  [![PyPI version](https://badge.fury.io/py/PySDKit.svg)](https://pypi.org/project/PySDKit/)
59
59
  ![License](https://img.shields.io/github/license/wwhenxuan/PySDKit)
60
60
  [![Python](https://img.shields.io/badge/python-3.8+-blue?logo=python)](https://www.python.org/)
@@ -91,50 +91,57 @@ This project integrates simple signal processing methods, signal decomposition a
91
91
  4. Visualize and analyze the original signal and the intrinsic mode functions IMFs obtained by decomposition.
92
92
 
93
93
  ~~~python
94
+ import numpy as np
94
95
  from pysdkit import EMD
95
- from pysdkit.data import test_emd
96
96
  from pysdkit.plot import plot_IMFs
97
97
 
98
- t, signal = test_emd()
98
+ t = np.linspace(0, 1, 1000)
99
+ signal = np.sin(2 * np.pi * 5 * t) + 0.7 * np.sin(2 * np.pi * 25 * t) + 0.45 * np.sin(2 * np.pi * 80 * t)
99
100
 
100
- # create an instance for signal decomposition
101
101
  emd = EMD()
102
- # implement signal decomposition
103
- IMFs = emd.fit_transform(signal, max_imfs=2)
104
- plot_IMFs(signal, IMFs)
102
+ IMFs = emd.fit_transform(signal, max_imfs=3)
103
+ plot_IMFs(signal, IMFs, view="2d_freq", fs=1000, freq_max=150)
105
104
  ~~~
106
105
 
107
- ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/example.jpg)
106
+ ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/decomposition_demo.jpg)
108
107
 
109
108
  The EMD in the above example is the most classic [`empirical mode decomposition`](https://www.mathworks.com/help/signal/ref/emd.html) algorithm in signal decomposition. For more complex signals, you can try other algorithms such as variational mode decomposition ([`VMD`](https://ieeexplore.ieee.org/abstract/document/6655981)).
110
109
 
111
110
  ~~~python
112
- import numpy as np
113
111
  from pysdkit import VMD
112
+ from pysdkit.data import test_vmd
113
+ from pysdkit.plot import plot_IMFs
114
114
 
115
- # load new signal
116
- signal = np.load("./example/example.npy")
117
-
118
- # use variational mode decomposition
119
- vmd = VMD(alpha=500, K=3, tau=0.0, tol=1e-9)
120
- IMFs = vmd.fit_transform(signal=signal)
121
- print(IMFs.shape)
115
+ t, signal, fs = test_vmd()
122
116
 
123
- vmd.plot_IMFs(save_figure=True)
117
+ vmd = VMD(alpha=2000, K=4, tau=0.0, tol=1e-7)
118
+ IMFs = vmd.fit_transform(signal)
119
+ plot_IMFs(signal, IMFs, view="2d_freq", fs=fs, freq_max=fs / 2)
124
120
  ~~~
125
121
 
126
122
  ![vmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/vmd_example.jpg)
127
123
 
128
- Better observe the characteristics of the decomposed intrinsic mode function in the frequency domain.
124
+ For multichannel recordings, algorithms such as multivariate VMD ([`MVMD`](https://doi.org/10.1109/TSP.2019.2951223)) keep shared oscillations **mode-aligned** across channels:
129
125
 
130
126
  ~~~python
131
- from pysdkit.plot import plot_IMFs_amplitude_spectra
127
+ import numpy as np
128
+ from pysdkit import MVMD
129
+ from pysdkit.plot import plot_IMFs
132
130
 
133
- # frequency domain visualization
134
- plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
131
+ t = np.arange(0, 1, 0.001)
132
+ # ch1: 2+36 Hz, ch2: 24+36 Hz, ch3: 80+36 Hz (36 Hz shared)
133
+ signal = np.vstack([
134
+ np.cos(2*np.pi*2*t) + np.cos(2*np.pi*36*t),
135
+ np.cos(2*np.pi*24*t) + np.cos(2*np.pi*36*t),
136
+ np.cos(2*np.pi*80*t) + np.cos(2*np.pi*36*t),
137
+ ])
138
+
139
+ mvmd = MVMD(alpha=2000, K=4, tau=0.0, init="uniform")
140
+ IMFs = mvmd.fit_transform(signal) # shape: (K, T, C)
141
+ plot_IMFs(signal, IMFs) # per-channel panels
135
142
  ~~~
136
143
 
137
- ![frequency_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/frequency_example.jpg)
144
+ ![mvmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/mvmd_example.jpg)
138
145
 
139
146
  ## Target 🎯 <a id="Target"></a>
140
147
 
@@ -97,6 +97,7 @@ pysdkit/data/_test_univariate.py
97
97
  pysdkit/data/_time_series.py
98
98
  pysdkit/data/fmd_demo.npy
99
99
  pysdkit/data/texture.txt
100
+ pysdkit/data/vmd_example.npy
100
101
  pysdkit/entropy/__init__.py
101
102
  pysdkit/entropy/_approxiamte_entropy.py
102
103
  pysdkit/entropy/_permutation_entropy.py
@@ -1,7 +1,7 @@
1
- # PySDKit: signal decomposition in Python
2
-
3
1
  <div align="center">
4
2
 
3
+ # PySDKit: signal decomposition in Python
4
+
5
5
  [![PyPI version](https://badge.fury.io/py/PySDKit.svg)](https://pypi.org/project/PySDKit/)
6
6
  ![License](https://img.shields.io/github/license/wwhenxuan/PySDKit)
7
7
  [![Python](https://img.shields.io/badge/python-3.8+-blue?logo=python)](https://www.python.org/)
@@ -38,50 +38,57 @@ This project integrates simple signal processing methods, signal decomposition a
38
38
  4. Visualize and analyze the original signal and the intrinsic mode functions IMFs obtained by decomposition.
39
39
 
40
40
  ~~~python
41
+ import numpy as np
41
42
  from pysdkit import EMD
42
- from pysdkit.data import test_emd
43
43
  from pysdkit.plot import plot_IMFs
44
44
 
45
- t, signal = test_emd()
45
+ t = np.linspace(0, 1, 1000)
46
+ signal = np.sin(2 * np.pi * 5 * t) + 0.7 * np.sin(2 * np.pi * 25 * t) + 0.45 * np.sin(2 * np.pi * 80 * t)
46
47
 
47
- # create an instance for signal decomposition
48
48
  emd = EMD()
49
- # implement signal decomposition
50
- IMFs = emd.fit_transform(signal, max_imfs=2)
51
- plot_IMFs(signal, IMFs)
49
+ IMFs = emd.fit_transform(signal, max_imfs=3)
50
+ plot_IMFs(signal, IMFs, view="2d_freq", fs=1000, freq_max=150)
52
51
  ~~~
53
52
 
54
- ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/example.jpg)
53
+ ![example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/decomposition_demo.jpg)
55
54
 
56
55
  The EMD in the above example is the most classic [`empirical mode decomposition`](https://www.mathworks.com/help/signal/ref/emd.html) algorithm in signal decomposition. For more complex signals, you can try other algorithms such as variational mode decomposition ([`VMD`](https://ieeexplore.ieee.org/abstract/document/6655981)).
57
56
 
58
57
  ~~~python
59
- import numpy as np
60
58
  from pysdkit import VMD
59
+ from pysdkit.data import test_vmd
60
+ from pysdkit.plot import plot_IMFs
61
61
 
62
- # load new signal
63
- signal = np.load("./example/example.npy")
64
-
65
- # use variational mode decomposition
66
- vmd = VMD(alpha=500, K=3, tau=0.0, tol=1e-9)
67
- IMFs = vmd.fit_transform(signal=signal)
68
- print(IMFs.shape)
62
+ t, signal, fs = test_vmd()
69
63
 
70
- vmd.plot_IMFs(save_figure=True)
64
+ vmd = VMD(alpha=2000, K=4, tau=0.0, tol=1e-7)
65
+ IMFs = vmd.fit_transform(signal)
66
+ plot_IMFs(signal, IMFs, view="2d_freq", fs=fs, freq_max=fs / 2)
71
67
  ~~~
72
68
 
73
69
  ![vmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/vmd_example.jpg)
74
70
 
75
- Better observe the characteristics of the decomposed intrinsic mode function in the frequency domain.
71
+ For multichannel recordings, algorithms such as multivariate VMD ([`MVMD`](https://doi.org/10.1109/TSP.2019.2951223)) keep shared oscillations **mode-aligned** across channels:
76
72
 
77
73
  ~~~python
78
- from pysdkit.plot import plot_IMFs_amplitude_spectra
74
+ import numpy as np
75
+ from pysdkit import MVMD
76
+ from pysdkit.plot import plot_IMFs
79
77
 
80
- # frequency domain visualization
81
- plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
78
+ t = np.arange(0, 1, 0.001)
79
+ # ch1: 2+36 Hz, ch2: 24+36 Hz, ch3: 80+36 Hz (36 Hz shared)
80
+ signal = np.vstack([
81
+ np.cos(2*np.pi*2*t) + np.cos(2*np.pi*36*t),
82
+ np.cos(2*np.pi*24*t) + np.cos(2*np.pi*36*t),
83
+ np.cos(2*np.pi*80*t) + np.cos(2*np.pi*36*t),
84
+ ])
85
+
86
+ mvmd = MVMD(alpha=2000, K=4, tau=0.0, init="uniform")
87
+ IMFs = mvmd.fit_transform(signal) # shape: (K, T, C)
88
+ plot_IMFs(signal, IMFs) # per-channel panels
82
89
  ~~~
83
90
 
84
- ![frequency_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/frequency_example.jpg)
91
+ ![mvmd_example](https://raw.githubusercontent.com/wwhenxuan/PySDKit/main/images/mvmd_example.jpg)
85
92
 
86
93
  ## Target 🎯 <a id="Target"></a>
87
94
 
@@ -2,7 +2,7 @@
2
2
  A Python library for signal decomposition algorithms.
3
3
  """
4
4
 
5
- __version__ = "0.4.44"
5
+ __version__ = "0.4.45"
6
6
 
7
7
  # Empirical Mode Decomposition
8
8
  from ._emd import EMD
@@ -14,8 +14,8 @@ class VMD(Base):
14
14
  """
15
15
  Variational mode decomposition, object-oriented interface.
16
16
 
17
- Original paper: Dragomiretskiy, K. and Zosso, D. (2014) Variational Mode Decomposition’,
18
- IEEE Transactions on Signal Processing, 62(3), pp. 531544. doi: 10.1109/TSP.2013.2288675.
17
+ Original paper: Dragomiretskiy, K. and Zosso, D. (2014) "Variational Mode Decomposition",
18
+ IEEE Transactions on Signal Processing, 62(3), pp. 531-544. doi: 10.1109/TSP.2013.2288675.
19
19
 
20
20
  The goal of VMD is to decompose the input signal into a series of modes with sparse characteristics.
21
21
  The sparse characteristics here refer to the fact that all modes are narrowband signals concentrated near their respective center frequencies.
@@ -50,6 +50,7 @@ class VMD(Base):
50
50
  DC: bool = False,
51
51
  max_iter: int = 500,
52
52
  tol: float = 1e-6,
53
+ store_history: bool = True,
53
54
  ) -> None:
54
55
  """
55
56
  :param alpha: the balancing parameter of the data-fidelity constraint
@@ -61,6 +62,12 @@ class VMD(Base):
61
62
  :param DC: true if the first mode is put and kept at DC (0-freq)
62
63
  :param max_iter: Maximum number of iterations
63
64
  :param tol: tolerance of convergence criterion; typically around 1e-6
65
+ :param store_history: If True (default), keep spectral iterates for every
66
+ ADMM step with shape ``(max_iter, T, K)``. If False, keep only the
67
+ previous / current spectral buffers (``O(T * K)`` memory). Use
68
+ ``store_history=False`` for long signals to avoid large allocations.
69
+ Center-frequency history ``omega`` is always retained (cheap).
70
+ Decomposition quality is unchanged; only peak memory differs.
64
71
  """
65
72
  super().__init__()
66
73
  # parameters of VMD signal decomposition algorithm
@@ -71,6 +78,7 @@ class VMD(Base):
71
78
  self.DC = DC
72
79
  self.max_iter = max_iter
73
80
  self.tol = tol
81
+ self.store_history = bool(store_history)
74
82
 
75
83
  # The last input original signal
76
84
  self.signal = None
@@ -109,18 +117,22 @@ class VMD(Base):
109
117
  def plot_IMFs(
110
118
  self,
111
119
  max_imf: int = -1,
120
+ view: str = "2d",
112
121
  colors: Optional[List] = None,
113
122
  save_figure: bool = False,
114
123
  return_figure: bool = False,
115
- dpi: int = 500,
124
+ dpi: int = 256,
116
125
  spine_width: float = 2,
117
126
  labelpad: float = 10,
118
127
  save_name: Optional[str] = None,
128
+ fs: Optional[float] = None,
129
+ freq_max: Optional[float] = None,
119
130
  ) -> None:
120
131
  """
121
132
  An easy way to visualize signal decomposition results
122
133
 
123
134
  :param max_imf: The number of decomposition modes to be plotted
135
+ :param view: ``\"2d\"`` / ``\"3d\"`` / ``\"2d_freq\"``
124
136
  :param colors: List of color strings for plotting
125
137
  :param save_figure: Whether to save the figure as an image
126
138
  :param return_figure: Whether to return the figure object
@@ -128,13 +140,17 @@ class VMD(Base):
128
140
  :param spine_width: The width of the visible axes spines
129
141
  :param labelpad: Controls the filling distance of the y-axis coordinate
130
142
  :param save_name: The name of the saved image file
143
+ :param fs: sampling frequency for ``view=\"2d_freq\"``
144
+ :param freq_max: spectrum x-limit for ``view=\"2d_freq\"``
145
+
131
146
  :return: The figure object for the plot
132
147
  """
133
148
  if self.u is not None and self.signal is not None:
134
- plot_IMFs(
149
+ return plot_IMFs(
135
150
  signal=self.signal,
136
151
  IMFs=self.u,
137
152
  max_imfs=max_imf,
153
+ view=view,
138
154
  colors=colors,
139
155
  save_figure=save_figure,
140
156
  return_figure=return_figure,
@@ -142,6 +158,8 @@ class VMD(Base):
142
158
  spine_width=spine_width,
143
159
  labelpad=labelpad,
144
160
  save_name=save_name,
161
+ fs=fs,
162
+ freq_max=freq_max,
145
163
  )
146
164
  else:
147
165
  raise ValueError
@@ -182,70 +200,118 @@ class VMD(Base):
182
200
 
183
201
  # For future generalizations: individual alpha for each mode
184
202
  alpha = np.ones(self.K) * self.alpha
185
- # matrix keeping track of every iterant // could be discarded for mem
186
- u_hat_plus = np.zeros([self.max_iter, len(freqs), self.K], dtype=complex)
187
- # Initialization of omega_k
203
+ # Initialization of omega_k (always keep full history; it is cheap)
188
204
  omega_plus = self.__init_omega(fs=fs)
189
205
  if self.DC:
190
206
  omega_plus[0, 0] = 0
191
- # start with empty dual variables
192
- lambda_hat = np.zeros(shape=[self.max_iter, len(freqs)], dtype=complex)
193
207
 
194
208
  sum_uk = 0 # accumulator
195
209
  convergence = (
196
210
  np.spacing(1) + self.tol
197
211
  ) # Determine whether the algorithm converges
212
+ n = 0
198
213
 
199
- # Main loop for iterative updates
200
- for n in range(0, self.max_iter - 1):
201
- # update spectrum of first mode through Wiener filter of residuals
202
- sum_uk = u_hat_plus[n, :, self.K - 1] + sum_uk - u_hat_plus[n, :, 0]
203
- u_hat_plus[n + 1, :, 0] = (f_hat_plus - sum_uk - lambda_hat[n, :] / 2) / (
204
- 1.0 + alpha[0] * (freqs - omega_plus[n, 0]) ** 2
205
- )
214
+ if self.store_history:
215
+ # Full spectral history: O(max_iter * T * K) memory
216
+ u_hat_plus = np.zeros([self.max_iter, len(freqs), self.K], dtype=complex)
217
+ lambda_hat = np.zeros(shape=[self.max_iter, len(freqs)], dtype=complex)
206
218
 
207
- # update first omega if not held at 0
208
- if not self.DC:
209
- omega_plus[n + 1, 0] = np.dot(
210
- freqs[T // 2 : T], (abs(u_hat_plus[n + 1, T // 2 : T, 0]) ** 2)
211
- ) / np.sum(abs(u_hat_plus[n + 1, T // 2 : T, 0]) ** 2)
212
-
213
- # update of any other mode
214
- for k in range(1, self.K):
215
- # mode spectrum
216
- sum_uk = u_hat_plus[n + 1, :, k - 1] + sum_uk - u_hat_plus[n, :, k]
217
- u_hat_plus[n + 1, :, k] = (
219
+ for n in range(0, self.max_iter - 1):
220
+ sum_uk = u_hat_plus[n, :, self.K - 1] + sum_uk - u_hat_plus[n, :, 0]
221
+ u_hat_plus[n + 1, :, 0] = (
218
222
  f_hat_plus - sum_uk - lambda_hat[n, :] / 2
219
- ) / (1 + alpha[k] * (freqs - omega_plus[n, k]) ** 2)
220
- # center frequencies
221
- omega_plus[n + 1, k] = np.dot(
222
- freqs[T // 2 : T], (abs(u_hat_plus[n + 1, T // 2 : T, k]) ** 2)
223
- ) / np.sum(abs(u_hat_plus[n + 1, T // 2 : T, k]) ** 2)
224
-
225
- # Update Lagrange multipliers
226
- lambda_hat[n + 1, :] = lambda_hat[n, :] + self.tau * (
227
- np.sum(u_hat_plus[n + 1, :, :], axis=1) - f_hat_plus
228
- )
223
+ ) / (1.0 + alpha[0] * (freqs - omega_plus[n, 0]) ** 2)
224
+
225
+ if not self.DC:
226
+ omega_plus[n + 1, 0] = np.dot(
227
+ freqs[T // 2 : T],
228
+ (abs(u_hat_plus[n + 1, T // 2 : T, 0]) ** 2),
229
+ ) / np.sum(abs(u_hat_plus[n + 1, T // 2 : T, 0]) ** 2)
230
+
231
+ for k in range(1, self.K):
232
+ sum_uk = u_hat_plus[n + 1, :, k - 1] + sum_uk - u_hat_plus[n, :, k]
233
+ u_hat_plus[n + 1, :, k] = (
234
+ f_hat_plus - sum_uk - lambda_hat[n, :] / 2
235
+ ) / (1 + alpha[k] * (freqs - omega_plus[n, k]) ** 2)
236
+ omega_plus[n + 1, k] = np.dot(
237
+ freqs[T // 2 : T],
238
+ (abs(u_hat_plus[n + 1, T // 2 : T, k]) ** 2),
239
+ ) / np.sum(abs(u_hat_plus[n + 1, T // 2 : T, k]) ** 2)
240
+
241
+ lambda_hat[n + 1, :] = lambda_hat[n, :] + self.tau * (
242
+ np.sum(u_hat_plus[n + 1, :, :], axis=1) - f_hat_plus
243
+ )
229
244
 
230
- # Determine whether the algorithm has converged
231
- for i in range(self.K):
232
- convergence = convergence + (1 / T) * np.dot(
233
- (u_hat_plus[n, :, i] - u_hat_plus[n - 1, :, i]),
234
- np.conj((u_hat_plus[n, :, i] - u_hat_plus[n - 1, :, i])),
245
+ for i in range(self.K):
246
+ convergence = convergence + (1 / T) * np.dot(
247
+ (u_hat_plus[n, :, i] - u_hat_plus[n - 1, :, i]),
248
+ np.conj((u_hat_plus[n, :, i] - u_hat_plus[n - 1, :, i])),
249
+ )
250
+ convergence = np.abs(convergence)
251
+ if convergence <= self.tol:
252
+ break
253
+
254
+ niter = np.min([self.max_iter, n])
255
+ omega = omega_plus[:niter, :]
256
+ u_spec = u_hat_plus[niter - 1]
257
+
258
+ else:
259
+ # Low-memory path: only previous / current spectral buffers
260
+ # Memory: O(T * K) instead of O(max_iter * T * K)
261
+ u_old = np.zeros([len(freqs), self.K], dtype=complex)
262
+ u_new = np.zeros([len(freqs), self.K], dtype=complex)
263
+ lambda_hat = np.zeros(len(freqs), dtype=complex)
264
+ u_spec = u_old
265
+
266
+ for n in range(0, self.max_iter - 1):
267
+ sum_uk = u_old[:, self.K - 1] + sum_uk - u_old[:, 0]
268
+ u_new[:, 0] = (f_hat_plus - sum_uk - lambda_hat / 2) / (
269
+ 1.0 + alpha[0] * (freqs - omega_plus[n, 0]) ** 2
270
+ )
271
+
272
+ if not self.DC:
273
+ omega_plus[n + 1, 0] = np.dot(
274
+ freqs[T // 2 : T],
275
+ (abs(u_new[T // 2 : T, 0]) ** 2),
276
+ ) / np.sum(abs(u_new[T // 2 : T, 0]) ** 2)
277
+
278
+ for k in range(1, self.K):
279
+ sum_uk = u_new[:, k - 1] + sum_uk - u_old[:, k]
280
+ u_new[:, k] = (f_hat_plus - sum_uk - lambda_hat / 2) / (
281
+ 1 + alpha[k] * (freqs - omega_plus[n, k]) ** 2
282
+ )
283
+ omega_plus[n + 1, k] = np.dot(
284
+ freqs[T // 2 : T],
285
+ (abs(u_new[T // 2 : T, k]) ** 2),
286
+ ) / np.sum(abs(u_new[T // 2 : T, k]) ** 2)
287
+
288
+ lambda_hat = lambda_hat + self.tau * (
289
+ np.sum(u_new, axis=1) - f_hat_plus
235
290
  )
236
- convergence = np.abs(convergence)
237
- if convergence <= self.tol:
238
- break
239
291
 
240
- # discard empty space if converged early
241
- niter = np.min([self.max_iter, n])
242
- omega = omega_plus[:niter, :]
292
+ # Per-iteration residual between consecutive spectral iterates
293
+ convergence = np.spacing(1)
294
+ for i in range(self.K):
295
+ delta = u_new[:, i] - u_old[:, i]
296
+ convergence = convergence + (1 / T) * np.dot(delta, np.conj(delta))
297
+ convergence = np.abs(convergence)
298
+ u_spec = u_new
299
+
300
+ if convergence <= self.tol:
301
+ break
302
+
303
+ # Roll buffers for the next iteration
304
+ u_old, u_new = u_new, u_old
305
+
306
+ niter = np.min([self.max_iter, n + 1])
307
+ omega = omega_plus[:niter, :]
308
+
243
309
  idxs = np.flip(np.arange(1, T // 2 + 1), axis=0)
244
310
 
245
311
  # signal reconstruction
246
312
  u_hat = np.zeros([T, self.K], dtype=complex)
247
- u_hat[T // 2 : T, :] = u_hat_plus[niter - 1, T // 2 : T, :]
248
- u_hat[idxs, :] = np.conj(u_hat_plus[niter - 1, T // 2 : T, :])
313
+ u_hat[T // 2 : T, :] = u_spec[T // 2 : T, :]
314
+ u_hat[idxs, :] = np.conj(u_spec[T // 2 : T, :])
249
315
  u_hat[0, :] = np.conj(u_hat[-1, :])
250
316
 
251
317
  u = np.zeros([self.K, len(t)])