PySDKit 0.4.31__tar.gz → 0.4.32__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 (157) hide show
  1. {pysdkit-0.4.31 → pysdkit-0.4.32}/PKG-INFO +3 -3
  2. {pysdkit-0.4.31 → pysdkit-0.4.32}/PySDKit.egg-info/PKG-INFO +3 -3
  3. {pysdkit-0.4.31 → pysdkit-0.4.32}/PySDKit.egg-info/SOURCES.txt +1 -0
  4. {pysdkit-0.4.31 → pysdkit-0.4.32}/README.md +2 -2
  5. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/__init__.py +7 -1
  6. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/__init__.py +2 -0
  7. pysdkit-0.4.32/pysdkit/_vmd/svmd.py +486 -0
  8. pysdkit-0.4.32/pysdkit/tests/test_svmd.py +118 -0
  9. pysdkit-0.4.31/pysdkit/_vmd/svmd.py +0 -401
  10. {pysdkit-0.4.31 → pysdkit-0.4.32}/LICENSE +0 -0
  11. {pysdkit-0.4.31 → pysdkit-0.4.32}/PySDKit.egg-info/dependency_links.txt +0 -0
  12. {pysdkit-0.4.31 → pysdkit-0.4.32}/PySDKit.egg-info/requires.txt +0 -0
  13. {pysdkit-0.4.31 → pysdkit-0.4.32}/PySDKit.egg-info/top_level.txt +0 -0
  14. {pysdkit-0.4.31 → pysdkit-0.4.32}/pyproject.toml +0 -0
  15. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_alif/__init__.py +0 -0
  16. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_alif/alif.py +0 -0
  17. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_alif/data/__init__.py +0 -0
  18. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_alif/data/prefixed_double_filter.npy +0 -0
  19. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_alif/iterative_filtering.py +0 -0
  20. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_apmd/__init__.py +0 -0
  21. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_apmd/apmd.py +0 -0
  22. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/__init__.py +0 -0
  23. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/_find_extrema.py +0 -0
  24. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/_prepare_points.py +0 -0
  25. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/_splines.py +0 -0
  26. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/ceemdan.py +0 -0
  27. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/eemd.py +0 -0
  28. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/efd.py +0 -0
  29. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/emd.py +0 -0
  30. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/hht/__init__.py +0 -0
  31. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/hht/frequency.py +0 -0
  32. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/hht/hht.py +0 -0
  33. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/memd.py +0 -0
  34. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/remd.py +0 -0
  35. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd/tvf_emd.py +0 -0
  36. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd2d/__init__.py +0 -0
  37. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd2d/bemd.py +0 -0
  38. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd2d/bmemd.py +0 -0
  39. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_emd2d/emd2d.py +0 -0
  40. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_esmd/__init__.py +0 -0
  41. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_esmd/esmd.py +0 -0
  42. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_ewt/__init__.py +0 -0
  43. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_ewt/ewt.py +0 -0
  44. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_ewt/ewt2d.py +0 -0
  45. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_faemd/__init__.py +0 -0
  46. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_faemd/extrema.py +0 -0
  47. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_faemd/faemd.py +0 -0
  48. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_faemd/faemd2d.py +0 -0
  49. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_faemd/faemd3d.py +0 -0
  50. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_faemd/filter.py +0 -0
  51. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_fmd/__init__.py +0 -0
  52. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_fmd/fmd.py +0 -0
  53. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_fmd/nfmd.py +0 -0
  54. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_hvd/__init__.py +0 -0
  55. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_hvd/hvd.py +0 -0
  56. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_itd/__init__.py +0 -0
  57. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_itd/itd.py +0 -0
  58. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_jmd/__init__.py +0 -0
  59. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_jmd/jmd.py +0 -0
  60. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_jmd/mjmd.py +0 -0
  61. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_jmd/sjmd.py +0 -0
  62. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_lmd/__init__.py +0 -0
  63. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_lmd/lmd.py +0 -0
  64. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_lmd/rlmd.py +0 -0
  65. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_osd/__init__.py +0 -0
  66. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_ssa/__init__.py +0 -0
  67. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_ssa/ssa.py +0 -0
  68. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/acmd.py +0 -0
  69. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/avmd.py +0 -0
  70. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/ba_acmd.py +0 -0
  71. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/base.py +0 -0
  72. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/mvmd.py +0 -0
  73. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/ovmd.py +0 -0
  74. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/vmd_c.py +0 -0
  75. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/vmd_f.py +0 -0
  76. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd/vme.py +0 -0
  77. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd2d/__init__.py +0 -0
  78. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd2d/cvmd2d.py +0 -0
  79. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vmd2d/vmd2d.py +0 -0
  80. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vncmd/__init__.py +0 -0
  81. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vncmd/avncmd.py +0 -0
  82. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vncmd/incmd.py +0 -0
  83. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vncmd/mncmd.py +0 -0
  84. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/_vncmd/vncmd.py +0 -0
  85. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/__init__.py +0 -0
  86. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_add_noise.py +0 -0
  87. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_cube.py +0 -0
  88. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_generator.py +0 -0
  89. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_image.py +0 -0
  90. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_models.py +0 -0
  91. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_test_univariate.py +0 -0
  92. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/_time_series.py +0 -0
  93. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/data/texture.txt +0 -0
  94. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/entropy/__init__.py +0 -0
  95. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
  96. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/entropy/_permutation_entropy.py +0 -0
  97. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/entropy/_sample_entropy.py +0 -0
  98. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/models/__init__.py +0 -0
  99. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/models/_base.py +0 -0
  100. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/models/_kmeans.py +0 -0
  101. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/models/_knn.py +0 -0
  102. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/models/_pca.py +0 -0
  103. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/plot/__init__.py +0 -0
  104. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/plot/_fourier_spectra.py +0 -0
  105. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/plot/_functions.py +0 -0
  106. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/plot/_plot_images.py +0 -0
  107. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/plot/_plot_imfs.py +0 -0
  108. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/plot/_plot_signal.py +0 -0
  109. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/__init__.py +0 -0
  110. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/data/__init__.py +0 -0
  111. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/data/test_generator_cube.py +0 -0
  112. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/data/test_generator_image.py +0 -0
  113. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/data/test_generator_signal.py +0 -0
  114. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
  115. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/models/__init__.py +0 -0
  116. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/models/test_knn.py +0 -0
  117. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/models/test_pca.py +0 -0
  118. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/run_all.py +0 -0
  119. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_alif.py +0 -0
  120. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_apmd.py +0 -0
  121. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_avncmd.py +0 -0
  122. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_ceemdan.py +0 -0
  123. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_emd.py +0 -0
  124. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_ewt.py +0 -0
  125. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_ewt2d.py +0 -0
  126. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_faemd.py +0 -0
  127. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_faemd2d.py +0 -0
  128. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_faemd3d.py +0 -0
  129. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_itd.py +0 -0
  130. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_jmd.py +0 -0
  131. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_lmd.py +0 -0
  132. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_moving_decomp.py +0 -0
  133. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_ovmd.py +0 -0
  134. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_rlmd.py +0 -0
  135. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_sjmd.py +0 -0
  136. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_stl.py +0 -0
  137. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_vmd.py +0 -0
  138. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tests/test_vmd2d.py +0 -0
  139. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tsa/__init__.py +0 -0
  140. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tsa/_dtw.py +0 -0
  141. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tsa/_moving_decomp.py +0 -0
  142. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tsa/_mstl.py +0 -0
  143. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/tsa/_stl.py +0 -0
  144. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/__init__.py +0 -0
  145. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_cite.py +0 -0
  146. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_correlation.py +0 -0
  147. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_diagnalization.py +0 -0
  148. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_differ.py +0 -0
  149. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_fft.py +0 -0
  150. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_function.py +0 -0
  151. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_hilbert.py +0 -0
  152. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_instantaneous.py +0 -0
  153. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_kernel_matrix.py +0 -0
  154. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_mirror.py +0 -0
  155. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_process.py +0 -0
  156. {pysdkit-0.4.31 → pysdkit-0.4.32}/pysdkit/utils/_smooth1d.py +0 -0
  157. {pysdkit-0.4.31 → pysdkit-0.4.32}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.31
3
+ Version: 0.4.32
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Author: josefinez, Deeksha Manjunath, Yuan Feng, JacktheFowler
6
6
  Author-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>, WenTong Zhao <23049200290@stu.xidian.edu.cn>
@@ -167,14 +167,14 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
167
167
  | [`NFMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_fmd/nfmd.py) (Non-stationary Feature Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/9732251) | [[code]](https://github.com/sheadan/NFMD-ExtractionInstantaneous/blob/master/nfmd/NFMD.py) | ✖️ |
168
168
  | [`SSA`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ssa/ssa.py) (Singular Spectral Analysis) | [[paper]](https://orca.cardiff.ac.uk/id/eprint/15208/1/Zhiglavsky_SSA_encyclopedia.pdf) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/58967-singular-spectrum-analysis-beginners-guide) | ✔️ |
169
169
  | [`EWT`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt.py) (Empirical Wavelet Transform) | [[paper]](https://ieeexplore.ieee.org/document/6522142) | [[code]](https://www.mathworks.com/help/wavelet/ug/empirical-wavelet-transform.html) | ✔️ |
170
- | [`EWT2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt2d.py) (2D Empirical Wavelet Transform) | [[paper]](https://arxiv.org/abs/2405.06188) | [[code]](https://github.com/bhurat/EWT-Python) | ✖️ |
170
+ | [`EWT2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt2d.py) (2D Empirical Wavelet Transform) | [[paper]](https://arxiv.org/abs/2405.06188) | [[code]](https://github.com/bhurat/EWT-Python) | ✔️ |
171
171
  | [`VMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/vmd_c.py) (Variational Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/6655981) | [[code]](https://github.com/vrcarva/vmdpy) | ✔️ |
172
172
  | [`MVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/mvmd.py) (Multivariate Variational Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/8890883) | [[code]](https://github.com/yunyueye/MVMD) | ✔️ |
173
173
  | [`VMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd2d/vmd2d.py) (Two-Dimensional Variational Mode Decomposition) | [[paper]](https://ww3.math.ucla.edu/camreport/cam14-16.pdf) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/45918-two-dimensional-variational-mode-decomposition?s_tid=srchtitle) | ✔️ |
174
174
  | [`CVMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd2d/cvmd2d.py) (Two-Dimensional Compact Variational Mode Decomposition) | [[paper]](https://link.springer.com/article/10.1007/s10851-017-0710-z) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/67285-two-dimensional-compact-variational-mode-decomposition-2d-tv-vmd?s_tid=FX_rc2_behav) | ✔️ |
175
175
  | [`VME`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/vme.py) (Variational Mode Extraction) | [[paper]](https://ieeexplore.ieee.org/document/7997854) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/76003-variational-mode-extraction-vme-m?s_tid=srchtitle) | ✔️ |
176
176
  | [`OVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ovmd.py) (Orthogonalized Variational Mode Decomposition) | [[paper]](https://doi.org/10.1016/j.sigpro.2025.110251) | [[code]](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ovmd.py) | ✔️ |
177
- | [`SVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/svmd.py) (Successive Variational Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168420301535) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/98649-successive-variational-mode-decomposition-svmd-m?s_tid=FX_rc3_behav) | ✖️ |
177
+ | [`SVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/svmd.py) (Successive Variational Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168420301535) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/98649-successive-variational-mode-decomposition-svmd-m?s_tid=FX_rc3_behav) | ✔️ |
178
178
  | [`VNCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/vncmd.py) (Variational Nonlinear Chirp Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/7990179) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/64292-variational-nonlinear-chirp-mode-decomposition) | ✔️ |
179
179
  | [`INCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/incmd.py) (Iterative Nonlinear Chirp Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X2030403X?via%3Dihub) | [[code]](https://github.com/sheadan/IterativeNCMD) | ✔️ |
180
180
  | [`MNCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/mncmd.py) (Multivariate Nonlinear Chirp Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0165168420302103) | [[code]]() | ✖️ |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PySDKit
3
- Version: 0.4.31
3
+ Version: 0.4.32
4
4
  Summary: A Python library for signal decomposition algorithms with a unified interface.
5
5
  Author: josefinez, Deeksha Manjunath, Yuan Feng, JacktheFowler
6
6
  Author-email: Whenxuan Wang <wwhenxuan@gmail.com>, RuiZhe Wang <3133986068@qq.com>, WenTong Zhao <23049200290@stu.xidian.edu.cn>
@@ -167,14 +167,14 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
167
167
  | [`NFMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_fmd/nfmd.py) (Non-stationary Feature Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/9732251) | [[code]](https://github.com/sheadan/NFMD-ExtractionInstantaneous/blob/master/nfmd/NFMD.py) | ✖️ |
168
168
  | [`SSA`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ssa/ssa.py) (Singular Spectral Analysis) | [[paper]](https://orca.cardiff.ac.uk/id/eprint/15208/1/Zhiglavsky_SSA_encyclopedia.pdf) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/58967-singular-spectrum-analysis-beginners-guide) | ✔️ |
169
169
  | [`EWT`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt.py) (Empirical Wavelet Transform) | [[paper]](https://ieeexplore.ieee.org/document/6522142) | [[code]](https://www.mathworks.com/help/wavelet/ug/empirical-wavelet-transform.html) | ✔️ |
170
- | [`EWT2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt2d.py) (2D Empirical Wavelet Transform) | [[paper]](https://arxiv.org/abs/2405.06188) | [[code]](https://github.com/bhurat/EWT-Python) | ✖️ |
170
+ | [`EWT2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt2d.py) (2D Empirical Wavelet Transform) | [[paper]](https://arxiv.org/abs/2405.06188) | [[code]](https://github.com/bhurat/EWT-Python) | ✔️ |
171
171
  | [`VMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/vmd_c.py) (Variational Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/6655981) | [[code]](https://github.com/vrcarva/vmdpy) | ✔️ |
172
172
  | [`MVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/mvmd.py) (Multivariate Variational Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/8890883) | [[code]](https://github.com/yunyueye/MVMD) | ✔️ |
173
173
  | [`VMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd2d/vmd2d.py) (Two-Dimensional Variational Mode Decomposition) | [[paper]](https://ww3.math.ucla.edu/camreport/cam14-16.pdf) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/45918-two-dimensional-variational-mode-decomposition?s_tid=srchtitle) | ✔️ |
174
174
  | [`CVMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd2d/cvmd2d.py) (Two-Dimensional Compact Variational Mode Decomposition) | [[paper]](https://link.springer.com/article/10.1007/s10851-017-0710-z) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/67285-two-dimensional-compact-variational-mode-decomposition-2d-tv-vmd?s_tid=FX_rc2_behav) | ✔️ |
175
175
  | [`VME`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/vme.py) (Variational Mode Extraction) | [[paper]](https://ieeexplore.ieee.org/document/7997854) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/76003-variational-mode-extraction-vme-m?s_tid=srchtitle) | ✔️ |
176
176
  | [`OVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ovmd.py) (Orthogonalized Variational Mode Decomposition) | [[paper]](https://doi.org/10.1016/j.sigpro.2025.110251) | [[code]](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ovmd.py) | ✔️ |
177
- | [`SVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/svmd.py) (Successive Variational Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168420301535) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/98649-successive-variational-mode-decomposition-svmd-m?s_tid=FX_rc3_behav) | ✖️ |
177
+ | [`SVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/svmd.py) (Successive Variational Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168420301535) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/98649-successive-variational-mode-decomposition-svmd-m?s_tid=FX_rc3_behav) | ✔️ |
178
178
  | [`VNCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/vncmd.py) (Variational Nonlinear Chirp Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/7990179) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/64292-variational-nonlinear-chirp-mode-decomposition) | ✔️ |
179
179
  | [`INCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/incmd.py) (Iterative Nonlinear Chirp Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X2030403X?via%3Dihub) | [[code]](https://github.com/sheadan/IterativeNCMD) | ✔️ |
180
180
  | [`MNCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/mncmd.py) (Multivariate Nonlinear Chirp Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0165168420302103) | [[code]]() | ✖️ |
@@ -123,6 +123,7 @@ pysdkit/tests/test_ovmd.py
123
123
  pysdkit/tests/test_rlmd.py
124
124
  pysdkit/tests/test_sjmd.py
125
125
  pysdkit/tests/test_stl.py
126
+ pysdkit/tests/test_svmd.py
126
127
  pysdkit/tests/test_vmd.py
127
128
  pysdkit/tests/test_vmd2d.py
128
129
  pysdkit/tests/data/__init__.py
@@ -113,14 +113,14 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
113
113
  | [`NFMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_fmd/nfmd.py) (Non-stationary Feature Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/9732251) | [[code]](https://github.com/sheadan/NFMD-ExtractionInstantaneous/blob/master/nfmd/NFMD.py) | ✖️ |
114
114
  | [`SSA`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ssa/ssa.py) (Singular Spectral Analysis) | [[paper]](https://orca.cardiff.ac.uk/id/eprint/15208/1/Zhiglavsky_SSA_encyclopedia.pdf) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/58967-singular-spectrum-analysis-beginners-guide) | ✔️ |
115
115
  | [`EWT`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt.py) (Empirical Wavelet Transform) | [[paper]](https://ieeexplore.ieee.org/document/6522142) | [[code]](https://www.mathworks.com/help/wavelet/ug/empirical-wavelet-transform.html) | ✔️ |
116
- | [`EWT2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt2d.py) (2D Empirical Wavelet Transform) | [[paper]](https://arxiv.org/abs/2405.06188) | [[code]](https://github.com/bhurat/EWT-Python) | ✖️ |
116
+ | [`EWT2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_ewt/ewt2d.py) (2D Empirical Wavelet Transform) | [[paper]](https://arxiv.org/abs/2405.06188) | [[code]](https://github.com/bhurat/EWT-Python) | ✔️ |
117
117
  | [`VMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/vmd_c.py) (Variational Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/6655981) | [[code]](https://github.com/vrcarva/vmdpy) | ✔️ |
118
118
  | [`MVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/mvmd.py) (Multivariate Variational Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/8890883) | [[code]](https://github.com/yunyueye/MVMD) | ✔️ |
119
119
  | [`VMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd2d/vmd2d.py) (Two-Dimensional Variational Mode Decomposition) | [[paper]](https://ww3.math.ucla.edu/camreport/cam14-16.pdf) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/45918-two-dimensional-variational-mode-decomposition?s_tid=srchtitle) | ✔️ |
120
120
  | [`CVMD2D`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd2d/cvmd2d.py) (Two-Dimensional Compact Variational Mode Decomposition) | [[paper]](https://link.springer.com/article/10.1007/s10851-017-0710-z) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/67285-two-dimensional-compact-variational-mode-decomposition-2d-tv-vmd?s_tid=FX_rc2_behav) | ✔️ |
121
121
  | [`VME`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/vme.py) (Variational Mode Extraction) | [[paper]](https://ieeexplore.ieee.org/document/7997854) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/76003-variational-mode-extraction-vme-m?s_tid=srchtitle) | ✔️ |
122
122
  | [`OVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ovmd.py) (Orthogonalized Variational Mode Decomposition) | [[paper]](https://doi.org/10.1016/j.sigpro.2025.110251) | [[code]](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ovmd.py) | ✔️ |
123
- | [`SVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/svmd.py) (Successive Variational Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168420301535) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/98649-successive-variational-mode-decomposition-svmd-m?s_tid=FX_rc3_behav) | ✖️ |
123
+ | [`SVMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/svmd.py) (Successive Variational Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0165168420301535) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/98649-successive-variational-mode-decomposition-svmd-m?s_tid=FX_rc3_behav) | ✔️ |
124
124
  | [`VNCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/vncmd.py) (Variational Nonlinear Chirp Mode Decomposition) | [[paper]](https://ieeexplore.ieee.org/document/7990179) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/64292-variational-nonlinear-chirp-mode-decomposition) | ✔️ |
125
125
  | [`INCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/incmd.py) (Iterative Nonlinear Chirp Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X2030403X?via%3Dihub) | [[code]](https://github.com/sheadan/IterativeNCMD) | ✔️ |
126
126
  | [`MNCMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vncmd/mncmd.py) (Multivariate Nonlinear Chirp Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/abs/pii/S0165168420302103) | [[code]]() | ✖️ |
@@ -2,7 +2,7 @@
2
2
  A Python library for signal decomposition algorithms.
3
3
  """
4
4
 
5
- __version__ = "0.4.31"
5
+ __version__ = "0.4.32"
6
6
 
7
7
  # Empirical Mode Decomposition
8
8
  from ._emd import EMD
@@ -61,6 +61,9 @@ from ._vmd import VME
61
61
  # Orthogonalized Variational Mode Decomposition
62
62
  from ._vmd import OVMD
63
63
 
64
+ # Successive Variational Mode Decomposition
65
+ from ._vmd import svmd, SVMD
66
+
64
67
  # Variational Mode Decomposition for 2D Image
65
68
  from ._vmd2d import VMD2D
66
69
 
@@ -151,6 +154,7 @@ Variational Mode Decomposition | VMD
151
154
  Multivariate Variational Mode Decomposition | MVMD
152
155
  Variational Mode Extraction | VME
153
156
  Orthogonalized Variational Mode Decomposition | OVMD
157
+ Successive Variational Mode Decomposition | SVMD
154
158
  Variational Mode Decomposition for 2D Image | VMD2D
155
159
  Compact VMD for 2D Image | CVMD2D
156
160
  Variational Nonlinear Chirp Mode Decomposition | VNCMD
@@ -193,6 +197,8 @@ __all__ = [
193
197
  "MVMD",
194
198
  "VME",
195
199
  "OVMD",
200
+ "svmd",
201
+ "SVMD",
196
202
  "VMD2D",
197
203
  "CVMD2D",
198
204
  "VNCMD",
@@ -16,3 +16,5 @@ from .acmd import ACMD
16
16
  from .vme import VME
17
17
 
18
18
  from .ovmd import OVMD
19
+
20
+ from .svmd import svmd, SVMD
@@ -0,0 +1,486 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on 2024/6/2 17:54
4
+ @author: Whenxuan Wang
5
+ @email: wwhenxuan@gmail.com
6
+
7
+ Successive Variational Mode Decomposition (SVMD).
8
+
9
+ Unlike classical VMD, which extracts K modes concurrently and therefore
10
+ requires a known mode count, SVMD extracts compact-spectrum modes
11
+ **one by one**. At each stage the residual / previously extracted modes
12
+ are discouraged from overlapping the mode of interest, and a stopping
13
+ criterion decides when to halt.
14
+
15
+ Mojtaba Nazari, Sayed Mahmoud Sakhaei.
16
+ Successive Variational Mode Decomposition,
17
+ Signal Processing 174 (2020) 107610.
18
+ https://doi.org/10.1016/j.sigpro.2020.107610
19
+ """
20
+ from __future__ import annotations
21
+
22
+ from typing import List, Optional, Tuple, Union
23
+
24
+ import numpy as np
25
+ from numpy.linalg import norm
26
+ from scipy.signal import savgol_filter
27
+
28
+ from .base import Base
29
+
30
+
31
+ class SVMD(Base):
32
+ """
33
+ Successive Variational Mode Decomposition.
34
+
35
+ SVMD extends the variational framework of VMD by extracting modes
36
+ successively. Each new mode ``u_L`` is obtained by solving a constrained
37
+ optimization problem that enforces:
38
+
39
+ 1. a compact spectrum around a center frequency ``ω_L`` (as in VMD);
40
+ 2. little spectral overlap with previously extracted modes;
41
+ 3. little spectral overlap with the residual signal.
42
+
43
+ Because modes are obtained one after another, the algorithm does **not**
44
+ require the number of modes ``K`` a priori. Several stopping criteria
45
+ (noise power, exact reconstruction, BIC, power of the last mode) are
46
+ available.
47
+
48
+ Nazari & Sakhaei, Signal Processing, 174:107610, 2020.
49
+ """
50
+
51
+ def __init__(
52
+ self,
53
+ max_alpha: float = 20000,
54
+ tau: float = 0.0,
55
+ tol: float = 1e-6,
56
+ stopc: int = 4,
57
+ init_omega: int = 0,
58
+ max_iter: int = 300,
59
+ max_modes: int = 30,
60
+ poly_order: int = 8,
61
+ window_length: int = 25,
62
+ random_seed: int = 42,
63
+ ) -> None:
64
+ """
65
+ :param max_alpha: balancing parameter of the data-fidelity constraint
66
+ (compactness of each mode). Typical values: 1e3–2e4.
67
+ :param tau: dual-ascent time step. Set to 0 under high-level noise.
68
+ :param tol: relative convergence tolerance for each mode (≈ 1e-6).
69
+ :param stopc: stopping-criterion type
70
+ 1 – residual power vs. estimated noise (compact spectra / EEG);
71
+ 2 – exact reconstruction (clean signals);
72
+ 3 – Bayesian information criterion (BIC);
73
+ 4 – power of the last mode (default, as in the MATLAB toolbox).
74
+ :param init_omega: center-frequency initialisation
75
+ 0 – start each mode at DC (default, usually sufficient);
76
+ 1 – random initialisation avoiding previously found frequencies.
77
+ :param max_iter: maximum ADMM iterations used to refine each mode.
78
+ :param max_modes: hard upper bound on the number of extracted modes
79
+ (safety guard against non-termination).
80
+ :param poly_order: Savitzky–Golay polynomial order used for noise
81
+ estimation (stopc = 1).
82
+ :param window_length: Savitzky–Golay window length (must be odd).
83
+ :param random_seed: RNG seed for ``init_omega = 1``.
84
+ """
85
+ super().__init__()
86
+ self.max_alpha = float(max_alpha)
87
+ self.tau = float(tau)
88
+ self.tol = float(tol)
89
+ self.stopc = int(stopc)
90
+ self.init_omega = int(init_omega)
91
+ self.max_iter = int(max_iter)
92
+ self.max_modes = int(max_modes)
93
+ self.poly_order = int(poly_order)
94
+ self.window_length = int(window_length)
95
+ self.eps = np.finfo(np.float64).eps
96
+ self.rng = np.random.RandomState(seed=random_seed)
97
+
98
+ if self.stopc not in (1, 2, 3, 4):
99
+ raise ValueError("stopc must be one of {1, 2, 3, 4}")
100
+ if self.init_omega not in (0, 1):
101
+ raise ValueError("init_omega must be 0 or 1")
102
+
103
+ self.signal: Optional[np.ndarray] = None
104
+ self.u: Optional[np.ndarray] = None
105
+ self.u_hat: Optional[np.ndarray] = None
106
+ self.omega: Optional[np.ndarray] = None
107
+
108
+ def __call__(
109
+ self, signal: np.ndarray, return_all: bool = False
110
+ ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray, np.ndarray]]:
111
+ """Allow instances to be called like functions."""
112
+ return self.fit_transform(signal=signal, return_all=return_all)
113
+
114
+ def __str__(self) -> str:
115
+ return "Successive Variational Mode Decomposition (SVMD)"
116
+
117
+ # ------------------------------------------------------------------ #
118
+ # Helpers
119
+ # ------------------------------------------------------------------ #
120
+ def _sgolay(
121
+ self,
122
+ signal: np.ndarray,
123
+ poly_order: Optional[int] = None,
124
+ window_length: Optional[int] = None,
125
+ ) -> np.ndarray:
126
+ """Savitzky–Golay smoother used to estimate the noise floor."""
127
+ poly_order = self.poly_order if poly_order is None else int(poly_order)
128
+ window_length = (
129
+ self.window_length if window_length is None else int(window_length)
130
+ )
131
+ if window_length % 2 == 0:
132
+ window_length += 1
133
+ if window_length > signal.size:
134
+ window_length = signal.size if signal.size % 2 == 1 else signal.size - 1
135
+ if window_length < poly_order + 2:
136
+ poly_order = max(1, window_length - 2)
137
+ return savgol_filter(signal, window_length=window_length, polyorder=poly_order)
138
+
139
+ def _init_omega_scalar(self, fs: float) -> float:
140
+ """Return the initial center frequency for a new mode."""
141
+ if self.init_omega == 0:
142
+ return 0.0
143
+ return float(np.exp(np.log(fs) + (np.log(0.5) - np.log(fs)) * self.rng.rand()))
144
+
145
+ def _reinit_omega(self, fs: float, omega_d: List[float]) -> Tuple[float, int]:
146
+ """
147
+ Draw a random ω that is at least ≈ 0.02 (normalised Hz) away from
148
+ previously extracted center frequencies. Returns ``(omega, n2)``.
149
+ """
150
+ n2 = 0
151
+ omega = 0.0
152
+ while n2 < 300:
153
+ omega = float(
154
+ np.exp(np.log(fs) + (np.log(0.5) - np.log(fs)) * self.rng.rand())
155
+ )
156
+ if len(omega_d) == 0 or np.min(np.abs(np.asarray(omega_d) - omega)) >= 0.02:
157
+ return omega, n2 + 1
158
+ n2 += 1
159
+ return omega, n2
160
+
161
+ # ------------------------------------------------------------------ #
162
+ # Main API
163
+ # ------------------------------------------------------------------ #
164
+ def fit_transform(
165
+ self,
166
+ signal: np.ndarray,
167
+ return_all: bool = False,
168
+ poly_order: Optional[int] = None,
169
+ window_length: Optional[int] = None,
170
+ ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray, np.ndarray]]:
171
+ """
172
+ Decompose a 1-D signal with SVMD.
173
+
174
+ :param signal: real-valued 1-D array (even length preferred; odd
175
+ length is truncated by one sample, matching the MATLAB code).
176
+ :param return_all: if True, also return spectra and center frequencies.
177
+ :param poly_order: optional override of the Savitzky–Golay order.
178
+ :param window_length: optional override of the Savitzky–Golay window.
179
+ :return: modes ``(L, N)``, optionally ``(u, u_hat, omega)``.
180
+ """
181
+ signal = np.asarray(signal, dtype=float).ravel()
182
+ if signal.size < 4:
183
+ raise ValueError("signal length must be at least 4 samples")
184
+
185
+ # MATLAB: discard the last sample if the length is odd
186
+ if signal.size % 2 == 1:
187
+ signal = signal[:-1]
188
+ save_T = signal.size
189
+ self.signal = signal
190
+
191
+ # ---- Part 1: initialisation -----------------------------------
192
+ y = self._sgolay(signal, poly_order=poly_order, window_length=window_length)
193
+ signoise = signal - y
194
+ fs = 1.0 / save_T
195
+
196
+ # Mirror extension of the signal and of the estimated noise
197
+ f = self.fmirror(signal, save_T // 2)
198
+ fnoise = self.fmirror(signoise, save_T // 2)
199
+
200
+ T = f.size
201
+ t = np.arange(1, T + 1, dtype=float) / T
202
+ omega_freqs = t - 0.5 - 1.0 / T
203
+ half = T // 2
204
+
205
+ # One-sided FFT (Hilbert concept used by VMD / SVMD)
206
+ f_hat = self.fftshift(self.fft(f))
207
+ f_hat_onesided = f_hat.copy()
208
+ f_hat_onesided[:half] = 0.0
209
+
210
+ f_hat_n = self.fftshift(self.fft(fnoise))
211
+ f_hat_n_onesided = f_hat_n.copy()
212
+ f_hat_n_onesided[:half] = 0.0
213
+ noisepe = float(norm(f_hat_n_onesided, ord=2) ** 2)
214
+
215
+ N = self.max_iter
216
+ min_alpha = 10.0
217
+
218
+ # Accumulated modes / filters / frequencies
219
+ modes_hat: List[np.ndarray] = [] # each: (T,) complex
220
+ omega_d: List[float] = []
221
+ alpha_hist: List[float] = []
222
+ h_rows: List[np.ndarray] = [] # filter rows for previously extracted modes
223
+
224
+ # Stopping-criterion auxiliaries
225
+ polm_list: List[float] = []
226
+ polm_temp = 1.0
227
+ bic_list: List[float] = []
228
+ sigerror_list: List[float] = []
229
+ normind_list: List[float] = []
230
+
231
+ SC2 = 0
232
+ mode_idx = 0 # 0-based mode counter (= MATLAB l-1)
233
+
234
+ # ---- Part 2–6: successive extraction --------------------------
235
+ while SC2 != 1 and mode_idx < self.max_modes:
236
+ # Fresh ADMM state for the new mode
237
+ Alpha = min_alpha
238
+ omega_L = np.zeros(N, dtype=float)
239
+ n2 = 0
240
+ if self.init_omega == 0 or mode_idx == 0:
241
+ omega_L[0] = self._init_omega_scalar(fs)
242
+ else:
243
+ omega_L[0], n2 = self._reinit_omega(fs, omega_d)
244
+
245
+ lambda_hat = np.zeros((N, T), dtype=complex)
246
+ u_hat_L = np.zeros((N, T), dtype=complex)
247
+
248
+ udiff = self.tol + self.eps
249
+ n = 0 # 0-based iteration index (MATLAB n starts at 1)
250
+ m = 0.0
251
+ bf = 0
252
+
253
+ h_sum = (
254
+ np.sum(np.vstack(h_rows), axis=0)
255
+ if len(h_rows) > 0
256
+ else np.zeros(T, dtype=float)
257
+ )
258
+ u_sum = (
259
+ np.sum(np.vstack(modes_hat), axis=0)
260
+ if len(modes_hat) > 0
261
+ else np.zeros(T, dtype=complex)
262
+ )
263
+
264
+ # Outer α-growth loop --------------------------------------
265
+ while Alpha < self.max_alpha + 1:
266
+ # Inner ADMM loop --------------------------------------
267
+ while udiff > self.tol and n < N - 1:
268
+ dw = omega_freqs - omega_L[n]
269
+ dw2 = dw**2
270
+ dw4 = dw2**2
271
+ A2 = Alpha**2
272
+
273
+ # Update mode spectrum û_L (Eq. derived via ADMM)
274
+ numer = (
275
+ f_hat_onesided + (A2 * dw4) * u_hat_L[n] + lambda_hat[n] / 2.0
276
+ )
277
+ denom = 1.0 + (A2 * dw4) * (1.0 + 2.0 * Alpha * dw2) + h_sum
278
+ u_hat_L[n + 1] = numer / denom
279
+
280
+ # Update center frequency ω_L (positive frequencies only)
281
+ power = np.abs(u_hat_L[n + 1, half:]) ** 2
282
+ power_sum = np.sum(power)
283
+ if power_sum > 0:
284
+ omega_L[n + 1] = float(
285
+ np.dot(omega_freqs[half:], power) / power_sum
286
+ )
287
+ else:
288
+ omega_L[n + 1] = omega_L[n]
289
+
290
+ # Dual ascent for λ
291
+ if self.tau != 0.0:
292
+ filter_num = (
293
+ A2
294
+ * dw4
295
+ * (
296
+ f_hat_onesided
297
+ - u_hat_L[n + 1]
298
+ - u_sum
299
+ + lambda_hat[n] / 2.0
300
+ )
301
+ - u_sum
302
+ )
303
+ filter_den = 1.0 + A2 * dw4
304
+ residual = f_hat_onesided - (
305
+ u_hat_L[n + 1] + filter_num / filter_den + u_sum
306
+ )
307
+ lambda_hat[n + 1] = lambda_hat[n] + self.tau * residual
308
+ else:
309
+ lambda_hat[n + 1] = lambda_hat[n]
310
+
311
+ # Relative spectral update
312
+ diff = u_hat_L[n + 1] - u_hat_L[n]
313
+ num = np.vdot(diff, diff).real / T
314
+ den = np.vdot(u_hat_L[n], u_hat_L[n]).real / T + self.eps
315
+ udiff = abs(num / den)
316
+ n += 1
317
+
318
+ # ---- Part 3: increase α toward max_alpha -------------
319
+ if abs(m - np.log(self.max_alpha)) > 1.0:
320
+ m = m + 1.0
321
+ else:
322
+ m = m + 0.05
323
+ bf = bf + 1
324
+
325
+ if bf >= 2:
326
+ Alpha = Alpha + 1.0
327
+
328
+ if Alpha <= (self.max_alpha - 1.0):
329
+ if bf == 1:
330
+ Alpha = self.max_alpha - 1.0
331
+ else:
332
+ Alpha = float(np.exp(m))
333
+
334
+ # Keep the current ω / spectrum and restart ADMM
335
+ omega_keep = float(omega_L[n])
336
+ temp_ud = u_hat_L[n].copy()
337
+
338
+ udiff = self.tol + self.eps
339
+ n = 0
340
+ lambda_hat = np.zeros((N, T), dtype=complex)
341
+ u_hat_L = np.zeros((N, T), dtype=complex)
342
+ u_hat_L[0] = temp_ud
343
+ omega_L = np.zeros(N, dtype=float)
344
+ omega_L[0] = omega_keep
345
+
346
+ # ---- Part 4: store the converged mode --------------------
347
+ # Last written iterate is index n (MATLAB uses u_hat_L(n,:)
348
+ # after the while, with ω from the previous step).
349
+ mode_spectrum = u_hat_L[n].copy()
350
+ omega_val = float(omega_L[max(n - 1, 0)])
351
+ if omega_val <= 0.0 and n > 0:
352
+ # Fall back to the last strictly positive ω, matching
353
+ # MATLAB ``omega_L = omega_L(omega_L > 0)``.
354
+ pos = omega_L[omega_L > 0]
355
+ omega_val = float(pos[-1]) if pos.size > 0 else float(omega_L[n])
356
+
357
+ modes_hat.append(mode_spectrum)
358
+ omega_d.append(omega_val)
359
+ alpha_hist.append(float(Alpha))
360
+
361
+ # Filter that suppresses this mode in subsequent extractions
362
+ gamma = 1.0
363
+ h_row = gamma / (
364
+ (alpha_hist[-1] ** 2) * (omega_freqs - omega_d[-1]) ** 4 + self.eps
365
+ )
366
+ h_rows.append(h_row)
367
+
368
+ # ---- Part 5: stopping criteria ---------------------------
369
+ u_stack = np.vstack(modes_hat)
370
+ residual = f_hat_onesided - np.sum(u_stack, axis=0)
371
+
372
+ if self.stopc == 1:
373
+ # Residual power vs. estimated noise power
374
+ sigerror = float(norm(residual, ord=2) ** 2)
375
+ sigerror_list.append(sigerror)
376
+ if n2 >= 300 or sigerror <= round(noisepe):
377
+ SC2 = 1
378
+
379
+ elif self.stopc == 2:
380
+ # Exact reconstruction (relative residual energy)
381
+ sum_u = np.sum(u_stack, axis=0)
382
+ ni = (norm(sum_u - f_hat_onesided) ** 2 / T) / (
383
+ norm(f_hat_onesided) ** 2 / T + self.eps
384
+ )
385
+ normind_list.append(float(ni))
386
+ if n2 >= 300 or ni < 0.005:
387
+ SC2 = 1
388
+
389
+ elif self.stopc == 3:
390
+ # Bayesian information criterion
391
+ sigerror = float(norm(residual, ord=2) ** 2)
392
+ sigerror_list.append(sigerror)
393
+ # MATLAB: BIC(l) = 2*T*log(sigerror) + (3*l)*log(2*T)
394
+ # with 1-based l = mode_idx + 1
395
+ bic = 2 * T * np.log(max(sigerror, self.eps)) + (
396
+ 3 * (mode_idx + 1)
397
+ ) * np.log(2 * T)
398
+ bic_list.append(float(bic))
399
+ if mode_idx >= 1 and bic_list[-1] > bic_list[-2]:
400
+ SC2 = 1
401
+
402
+ else:
403
+ # stopc == 4: power of the last mode (default)
404
+ mode_l = modes_hat[-1]
405
+ omega_l = omega_d[-1]
406
+ # After α-growth, Alpha sits near / above max_alpha
407
+ A = float(Alpha)
408
+ filt = (4.0 * A * mode_l) / (
409
+ 1.0 + 2.0 * A * (omega_freqs - omega_l) ** 2
410
+ )
411
+ polm_val = float(norm(filt * np.conj(mode_l), ord=2))
412
+ if mode_idx < 1:
413
+ polm_temp = polm_val if polm_val != 0 else 1.0
414
+ polm_list.append(1.0)
415
+ else:
416
+ polm_list.append(polm_val / (polm_temp + self.eps))
417
+ if abs(polm_list[-1] - polm_list[-2]) < 0.001:
418
+ SC2 = 1
419
+
420
+ # ---- Part 6: prepare the next mode -----------------------
421
+ mode_idx += 1
422
+
423
+ if len(modes_hat) == 0:
424
+ raise RuntimeError("SVMD failed to extract any mode")
425
+
426
+ # ---- Part 7: signal reconstruction ---------------------------
427
+ L = len(modes_hat)
428
+ omega = np.asarray(omega_d, dtype=float)
429
+
430
+ u_hat_full = np.zeros((T, L), dtype=complex)
431
+ stacked = np.vstack(modes_hat).T # (T, L)
432
+ # Hermitian completion identical to VMD / MATLAB SVMD
433
+ idxs = np.flip(np.arange(1, half + 1), axis=0)
434
+ u_hat_full[half:T, :] = stacked[half:T, :]
435
+ u_hat_full[idxs, :] = np.conj(stacked[half:T, :])
436
+ u_hat_full[0, :] = np.conj(u_hat_full[-1, :])
437
+
438
+ u = np.zeros((L, T), dtype=float)
439
+ for ell in range(L):
440
+ u[ell, :] = np.real(self.ifft(self.ifftshift(u_hat_full[:, ell])))
441
+
442
+ # Sort by ascending center frequency
443
+ order = np.argsort(omega)
444
+ omega = omega[order]
445
+ u = u[order, :]
446
+
447
+ # Remove the mirrored borders
448
+ u = u[:, T // 4 : 3 * T // 4]
449
+
450
+ # Recompute spectra on the cropped modes
451
+ u_hat = np.zeros((save_T, L), dtype=complex)
452
+ for ell in range(L):
453
+ u_hat[:, ell] = self.fftshift(self.fft(u[ell, :]))
454
+
455
+ self.u = u
456
+ self.u_hat = u_hat
457
+ self.omega = omega
458
+
459
+ if return_all:
460
+ return u, u_hat, omega
461
+ return u
462
+
463
+
464
+ def svmd(
465
+ signal: np.ndarray,
466
+ max_alpha: float = 20000,
467
+ tau: float = 0.0,
468
+ tol: float = 1e-6,
469
+ stopc: int = 4,
470
+ init_omega: int = 0,
471
+ max_iter: int = 300,
472
+ return_all: bool = False,
473
+ ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray, np.ndarray]]:
474
+ """
475
+ Functional interface to Successive Variational Mode Decomposition.
476
+
477
+ See :class:`SVMD` for parameter descriptions.
478
+ """
479
+ return SVMD(
480
+ max_alpha=max_alpha,
481
+ tau=tau,
482
+ tol=tol,
483
+ stopc=stopc,
484
+ init_omega=init_omega,
485
+ max_iter=max_iter,
486
+ ).fit_transform(signal=signal, return_all=return_all)