PySDKit 0.4.29__tar.gz → 0.4.30__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.
- {pysdkit-0.4.29 → pysdkit-0.4.30}/PKG-INFO +2 -1
- {pysdkit-0.4.29 → pysdkit-0.4.30}/PySDKit.egg-info/PKG-INFO +2 -1
- {pysdkit-0.4.29 → pysdkit-0.4.30}/PySDKit.egg-info/SOURCES.txt +2 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/README.md +1 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/__init__.py +7 -1
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_jmd/__init__.py +2 -1
- pysdkit-0.4.30/pysdkit/_jmd/sjmd.py +464 -0
- pysdkit-0.4.30/pysdkit/tests/test_sjmd.py +160 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/LICENSE +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/PySDKit.egg-info/dependency_links.txt +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/PySDKit.egg-info/requires.txt +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/PySDKit.egg-info/top_level.txt +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pyproject.toml +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_alif/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_alif/alif.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_alif/data/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_alif/data/prefixed_double_filter.npy +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_alif/iterative_filtering.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_apmd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_apmd/apmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/_find_extrema.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/_prepare_points.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/_splines.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/ceemdan.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/eemd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/efd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/emd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/hht/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/hht/frequency.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/hht/hht.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/memd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/remd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd/tvf_emd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd2d/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd2d/bemd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd2d/bmemd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_emd2d/emd2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_esmd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_esmd/esmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_ewt/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_ewt/ewt.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_ewt/ewt2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_faemd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_faemd/extrema.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_faemd/faemd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_faemd/faemd2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_faemd/faemd3d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_faemd/filter.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_fmd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_fmd/fmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_fmd/nfmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_hvd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_hvd/hvd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_itd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_itd/itd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_jmd/jmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_jmd/mjmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_lmd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_lmd/lmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_lmd/rlmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_osd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_ssa/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_ssa/ssa.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/acmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/avmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/ba_acmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/base.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/mvmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/ovmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/svmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/vmd_c.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/vmd_f.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd/vme.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd2d/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd2d/cvmd2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vmd2d/vmd2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vncmd/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vncmd/avncmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vncmd/incmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vncmd/mncmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/_vncmd/vncmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_add_noise.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_cube.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_generator.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_image.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_models.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_test_univariate.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/_time_series.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/data/texture.txt +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/entropy/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/entropy/_approxiamte_entropy.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/entropy/_permutation_entropy.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/entropy/_sample_entropy.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/models/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/models/_base.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/models/_kmeans.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/models/_knn.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/models/_pca.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/plot/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/plot/_fourier_spectra.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/plot/_functions.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/plot/_plot_images.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/plot/_plot_imfs.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/plot/_plot_signal.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/data/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/data/test_generator_cube.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/data/test_generator_image.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/data/test_generator_signal.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/data/test_generator_univariate_signal.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/models/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/models/test_knn.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/models/test_pca.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/run_all.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_alif.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_apmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_avncmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_ceemdan.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_emd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_ewt.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_faemd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_faemd2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_faemd3d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_itd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_jmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_lmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_moving_decomp.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_ovmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_rlmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_stl.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_vmd.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tests/test_vmd2d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tsa/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tsa/_dtw.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tsa/_moving_decomp.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tsa/_mstl.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/tsa/_stl.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/__init__.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_cite.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_correlation.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_diagnalization.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_differ.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_fft.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_function.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_hilbert.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_instantaneous.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_kernel_matrix.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_mirror.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_process.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/pysdkit/utils/_smooth1d.py +0 -0
- {pysdkit-0.4.29 → pysdkit-0.4.30}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySDKit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.30
|
|
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>
|
|
@@ -183,6 +183,7 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
|
|
|
183
183
|
| [`BA-ACMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ba_acmd.py) (Bandwidth-aware adaptive chirp mode decomposition) | [[paper]](https://journals.sagepub.com/doi/abs/10.1177/14759217231174699) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/132792-bandwidth-aware-adaptive-chirp-mode-decomposition-ba-acmd?s_tid=srchtitle) | ✖️ |
|
|
184
184
|
| [`JMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/jmd.py) (Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169388-jump-plus-am-fm-mode-decomposition-jmd?s_tid=prof_contriblnk) | ️✔️ |
|
|
185
185
|
| [`MJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/mjmd.py) (Multivariate Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169393-multivariate-jump-plus-am-fm-mode-decomposition-mjmd?s_tid=prof_contriblnk) | ✔️ |
|
|
186
|
+
| [`SJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/sjmd.py) / `SMJMD` (Successive Jump and Mode Decomposition) | [[paper]](https://arxiv.org/abs/2504.08453) | [[code]]() | ✔️ |
|
|
186
187
|
| [`ESMD`]() (Extreme-Point Symmetric Mode Decomposition) | [[paper]](https://arxiv.org/abs/1303.6540) | [[code]](https://github.com/WuShichao/esmd) | ✖️ |
|
|
187
188
|
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) | ✖️ |
|
|
188
189
|
| [`STL`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/tsa/_stl.py) (Seasonal-Trend decomposition using LOESS) | [[paper]](https://www.nniiem.ru/file/news/2016/stl-statistical-model.pdf) | [[code]](https://www.statsmodels.org/stable/examples/notebooks/generated/stl_decomposition.html) | ✔️ |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: PySDKit
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.30
|
|
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>
|
|
@@ -183,6 +183,7 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
|
|
|
183
183
|
| [`BA-ACMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ba_acmd.py) (Bandwidth-aware adaptive chirp mode decomposition) | [[paper]](https://journals.sagepub.com/doi/abs/10.1177/14759217231174699) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/132792-bandwidth-aware-adaptive-chirp-mode-decomposition-ba-acmd?s_tid=srchtitle) | ✖️ |
|
|
184
184
|
| [`JMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/jmd.py) (Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169388-jump-plus-am-fm-mode-decomposition-jmd?s_tid=prof_contriblnk) | ️✔️ |
|
|
185
185
|
| [`MJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/mjmd.py) (Multivariate Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169393-multivariate-jump-plus-am-fm-mode-decomposition-mjmd?s_tid=prof_contriblnk) | ✔️ |
|
|
186
|
+
| [`SJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/sjmd.py) / `SMJMD` (Successive Jump and Mode Decomposition) | [[paper]](https://arxiv.org/abs/2504.08453) | [[code]]() | ✔️ |
|
|
186
187
|
| [`ESMD`]() (Extreme-Point Symmetric Mode Decomposition) | [[paper]](https://arxiv.org/abs/1303.6540) | [[code]](https://github.com/WuShichao/esmd) | ✖️ |
|
|
187
188
|
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) | ✖️ |
|
|
188
189
|
| [`STL`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/tsa/_stl.py) (Seasonal-Trend decomposition using LOESS) | [[paper]](https://www.nniiem.ru/file/news/2016/stl-statistical-model.pdf) | [[code]](https://www.statsmodels.org/stable/examples/notebooks/generated/stl_decomposition.html) | ✔️ |
|
|
@@ -53,6 +53,7 @@ pysdkit/_itd/itd.py
|
|
|
53
53
|
pysdkit/_jmd/__init__.py
|
|
54
54
|
pysdkit/_jmd/jmd.py
|
|
55
55
|
pysdkit/_jmd/mjmd.py
|
|
56
|
+
pysdkit/_jmd/sjmd.py
|
|
56
57
|
pysdkit/_lmd/__init__.py
|
|
57
58
|
pysdkit/_lmd/lmd.py
|
|
58
59
|
pysdkit/_lmd/rlmd.py
|
|
@@ -119,6 +120,7 @@ pysdkit/tests/test_lmd.py
|
|
|
119
120
|
pysdkit/tests/test_moving_decomp.py
|
|
120
121
|
pysdkit/tests/test_ovmd.py
|
|
121
122
|
pysdkit/tests/test_rlmd.py
|
|
123
|
+
pysdkit/tests/test_sjmd.py
|
|
122
124
|
pysdkit/tests/test_stl.py
|
|
123
125
|
pysdkit/tests/test_vmd.py
|
|
124
126
|
pysdkit/tests/test_vmd2d.py
|
|
@@ -129,6 +129,7 @@ plot_IMFs_amplitude_spectra(IMFs, smooth="exp") # use exp smooth
|
|
|
129
129
|
| [`BA-ACMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_vmd/ba_acmd.py) (Bandwidth-aware adaptive chirp mode decomposition) | [[paper]](https://journals.sagepub.com/doi/abs/10.1177/14759217231174699) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/132792-bandwidth-aware-adaptive-chirp-mode-decomposition-ba-acmd?s_tid=srchtitle) | ✖️ |
|
|
130
130
|
| [`JMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/jmd.py) (Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169388-jump-plus-am-fm-mode-decomposition-jmd?s_tid=prof_contriblnk) | ️✔️ |
|
|
131
131
|
| [`MJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/mjmd.py) (Multivariate Jump Plus AM-FM Mode Decomposition) | [[paper]](https://arxiv.org/abs/2407.07800) | [[code]](https://www.mathworks.com/matlabcentral/fileexchange/169393-multivariate-jump-plus-am-fm-mode-decomposition-mjmd?s_tid=prof_contriblnk) | ✔️ |
|
|
132
|
+
| [`SJMD`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/_jmd/sjmd.py) / `SMJMD` (Successive Jump and Mode Decomposition) | [[paper]](https://arxiv.org/abs/2504.08453) | [[code]]() | ✔️ |
|
|
132
133
|
| [`ESMD`]() (Extreme-Point Symmetric Mode Decomposition) | [[paper]](https://arxiv.org/abs/1303.6540) | [[code]](https://github.com/WuShichao/esmd) | ✖️ |
|
|
133
134
|
| [`STNBMD`]() (Short-Time Narrow-Band Mode Decomposition) | [[paper]](https://www.sciencedirect.com/science/article/pii/S0022460X16002443?via%3Dihub) | [[code]](https://ww2.mathworks.cn/matlabcentral/fileexchange/56226-short-time-narrow-band-mode-decomposition-stnbmd-toolbox) | ✖️ |
|
|
134
135
|
| [`STL`](https://github.com/wwhenxuan/PySDKit/blob/main/pysdkit/tsa/_stl.py) (Seasonal-Trend decomposition using LOESS) | [[paper]](https://www.nniiem.ru/file/news/2016/stl-statistical-model.pdf) | [[code]](https://www.statsmodels.org/stable/examples/notebooks/generated/stl_decomposition.html) | ✔️ |
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
A Python library for signal decomposition algorithms.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
__version__ = "0.4.
|
|
5
|
+
__version__ = "0.4.30"
|
|
6
6
|
|
|
7
7
|
# Empirical Mode Decomposition
|
|
8
8
|
from ._emd import EMD
|
|
@@ -91,6 +91,9 @@ from ._jmd import JMD
|
|
|
91
91
|
# Multivariate Jump Plus AM-FM Mode Decomposition
|
|
92
92
|
from ._jmd import MJMD
|
|
93
93
|
|
|
94
|
+
# Successive Jump and Mode Decomposition (univariate / multivariate)
|
|
95
|
+
from ._jmd import SJMD, SMJMD
|
|
96
|
+
|
|
94
97
|
# Feature Mode Decomposition
|
|
95
98
|
from ._fmd import FMD
|
|
96
99
|
|
|
@@ -155,6 +158,7 @@ Adaptive Polymorphic Mode Decomposition | APMD
|
|
|
155
158
|
Empirical Wavelet Transform | EWT
|
|
156
159
|
Jump Plus AM-FM Mode Decomposition | JMD
|
|
157
160
|
Multivariate Jump Plus AM-FM Mode Decomposition | MJMD
|
|
161
|
+
Successive Jump and Mode Decomposition | SJMD / SMJMD
|
|
158
162
|
Feature Mode Decomposition | FMD
|
|
159
163
|
Moving Average Decomposition | Moving
|
|
160
164
|
Seasonal-Trend decomposition using LOESS | STL
|
|
@@ -196,6 +200,8 @@ __all__ = [
|
|
|
196
200
|
"EWT",
|
|
197
201
|
"JMD",
|
|
198
202
|
"MJMD",
|
|
203
|
+
"SJMD",
|
|
204
|
+
"SMJMD",
|
|
199
205
|
"FMD",
|
|
200
206
|
"Moving_Decomp",
|
|
201
207
|
"STL",
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Created on 2025/07/20
|
|
4
|
+
@author: Rongkun Zhu
|
|
5
|
+
@email: 25481568@life.hkbu.edu.hk
|
|
6
|
+
|
|
7
|
+
Successive Jump and Mode Decomposition (SJMD / SMJMD).
|
|
8
|
+
|
|
9
|
+
Faithful port of the authors' MATLAB ``SJMD.m``.
|
|
10
|
+
One implementation covers both univariate (SJMD) and multivariate (SMJMD).
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from typing import List, Optional, Tuple, Union
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
import scipy.sparse as sp
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SJMD(object):
|
|
21
|
+
"""
|
|
22
|
+
Successive Jump and Mode Decomposition (SJMD / SMJMD).
|
|
23
|
+
|
|
24
|
+
Successively extracts AM–FM modes and a jump component without a
|
|
25
|
+
predefined mode count ``K``. The same routine accepts:
|
|
26
|
+
|
|
27
|
+
- univariate data ``(N,)`` or ``(1, N)`` → SJMD
|
|
28
|
+
- multivariate data ``(C, N)`` (or ``(N, C)`` if ``N > C``) → SMJMD
|
|
29
|
+
|
|
30
|
+
Nazari, Korshøj, ur Rehman, "Successive Jump and Mode Decomposition,"
|
|
31
|
+
arXiv:2504.08453.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
max_alpha: float = 80000.0,
|
|
37
|
+
tau: float = 50.0,
|
|
38
|
+
beta: float = 0.5,
|
|
39
|
+
b_bar: float = 0.9,
|
|
40
|
+
stopc: int = 4,
|
|
41
|
+
tol: float = 1e-5,
|
|
42
|
+
kdm: Optional[int] = None,
|
|
43
|
+
max_inner: int = 100,
|
|
44
|
+
min_alpha: float = 10.0,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""
|
|
47
|
+
:param max_alpha: upper bound on the bandwidth parameter α
|
|
48
|
+
:param tau: jump-ADMM step used to form ``gamma``
|
|
49
|
+
:param beta: jump-constraint weight
|
|
50
|
+
:param b_bar: expected minimal jump height
|
|
51
|
+
:param stopc: stopping criterion
|
|
52
|
+
1 exact reconstruction, 2 Bayesian, 3 k most dominant modes,
|
|
53
|
+
4 power of the last mode (recommended / default)
|
|
54
|
+
:param tol: inner-loop convergence tolerance
|
|
55
|
+
:param kdm: number of dominant modes when ``stopc == 3``
|
|
56
|
+
:param max_inner: max ADMM iterations per mode / alpha stage
|
|
57
|
+
:param min_alpha: initial alpha for each successive mode
|
|
58
|
+
"""
|
|
59
|
+
self.max_alpha = float(max_alpha)
|
|
60
|
+
self.tau = float(tau)
|
|
61
|
+
self.beta = float(beta)
|
|
62
|
+
self.b_bar = float(b_bar)
|
|
63
|
+
self.stopc = int(stopc)
|
|
64
|
+
self.tol = float(tol)
|
|
65
|
+
self.kdm = kdm
|
|
66
|
+
self.max_inner = int(max_inner)
|
|
67
|
+
self.min_alpha = float(min_alpha)
|
|
68
|
+
|
|
69
|
+
self.u: Optional[np.ndarray] = None
|
|
70
|
+
self.j: Optional[np.ndarray] = None
|
|
71
|
+
self.omega: Optional[np.ndarray] = None
|
|
72
|
+
self.n_channels: Optional[int] = None
|
|
73
|
+
|
|
74
|
+
def __call__(
|
|
75
|
+
self, signal: np.ndarray, return_all: bool = False
|
|
76
|
+
) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
|
|
77
|
+
return self.fit_transform(signal=signal, return_all=return_all)
|
|
78
|
+
|
|
79
|
+
def __str__(self) -> str:
|
|
80
|
+
return "Successive Jump and Mode Decomposition (SJMD / SMJMD)"
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _resolve_input(signal: np.ndarray) -> Tuple[np.ndarray, int, int, bool]:
|
|
84
|
+
"""Return ``(data CxN, C, N, univariate)`` matching MATLAB layout."""
|
|
85
|
+
arr = np.asarray(signal, dtype=float)
|
|
86
|
+
if arr.ndim == 1:
|
|
87
|
+
data = arr.reshape(1, -1)
|
|
88
|
+
uni = True
|
|
89
|
+
elif arr.ndim == 2:
|
|
90
|
+
x, y = arr.shape
|
|
91
|
+
if x > y:
|
|
92
|
+
data = arr.T.copy()
|
|
93
|
+
uni = data.shape[0] == 1
|
|
94
|
+
else:
|
|
95
|
+
data = arr.copy()
|
|
96
|
+
uni = data.shape[0] == 1
|
|
97
|
+
else:
|
|
98
|
+
raise ValueError("SJMD expects 1-D or 2-D input")
|
|
99
|
+
|
|
100
|
+
# MATLAB: if mod(2*save_T,2)~=0 → trim (always false for integer);
|
|
101
|
+
# keep even-length guard used elsewhere in the toolbox.
|
|
102
|
+
if data.shape[1] % 2 == 1:
|
|
103
|
+
data = data[:, :-1]
|
|
104
|
+
c, n = data.shape
|
|
105
|
+
if n < 8:
|
|
106
|
+
raise ValueError("signal length must be at least 8 samples")
|
|
107
|
+
return data, c, n, uni
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _mirror(signal: np.ndarray) -> np.ndarray:
|
|
111
|
+
"""Mirror extension as in MATLAB ``SJMD.m`` (overlap + append last)."""
|
|
112
|
+
t = signal.shape[1]
|
|
113
|
+
half = t // 2
|
|
114
|
+
f_mir = np.zeros((signal.shape[0], 2 * t - 1), dtype=float)
|
|
115
|
+
# f_mir(:,1:T/2) = signal(:,T/2:-1:1)
|
|
116
|
+
f_mir[:, 0:half] = signal[:, half - 1 :: -1]
|
|
117
|
+
# f_mir(:,T/2:3*T/2-1) = signal (overwrites column T/2)
|
|
118
|
+
f_mir[:, half - 1 : half - 1 + t] = signal
|
|
119
|
+
# f_mir(:,3*T/2:2*T-1) = signal(:,T:-1:T/2+1)
|
|
120
|
+
f_mir[:, 3 * half - 1 : 2 * t - 1] = signal[:, t - 1 : half - 1 : -1]
|
|
121
|
+
# f_mir = [f_mir f_mir(:,end)]
|
|
122
|
+
return np.concatenate([f_mir, f_mir[:, -1:]], axis=1)
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _soft_jump(h: np.ndarray, mu: float, b: float) -> np.ndarray:
|
|
126
|
+
abs_h = np.abs(h) + np.finfo(float).eps
|
|
127
|
+
scale = (1.0 / (1.0 - mu * b)) - (
|
|
128
|
+
mu * np.sqrt(2.0 * b) / (1.0 - mu * b)
|
|
129
|
+
) / abs_h
|
|
130
|
+
return np.clip(scale, 0.0, 1.0) * h
|
|
131
|
+
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _hermitian_from_onesided(
|
|
134
|
+
onesided: np.ndarray, half: int, t_len: int
|
|
135
|
+
) -> np.ndarray:
|
|
136
|
+
"""Build full Hermitian spectrum from one-sided FFT bins."""
|
|
137
|
+
out = np.zeros((t_len,) + onesided.shape[1:], dtype=complex)
|
|
138
|
+
out[half:t_len, ...] = onesided[half:t_len, ...]
|
|
139
|
+
out[half:0:-1, ...] = np.conj(onesided[half:t_len, ...])
|
|
140
|
+
out[0, ...] = np.conj(out[-1, ...])
|
|
141
|
+
return out
|
|
142
|
+
|
|
143
|
+
def _check_stop(
|
|
144
|
+
self,
|
|
145
|
+
k: int,
|
|
146
|
+
f_mir: np.ndarray,
|
|
147
|
+
sum_u: np.ndarray,
|
|
148
|
+
u_hat_i: np.ndarray,
|
|
149
|
+
f_hat_temp: np.ndarray,
|
|
150
|
+
polm: List[float],
|
|
151
|
+
polm_temp: Optional[float],
|
|
152
|
+
bic: List[float],
|
|
153
|
+
t_len: int,
|
|
154
|
+
) -> Tuple[bool, List[float], Optional[float], List[float]]:
|
|
155
|
+
"""MATLAB Part 5 stopping criteria."""
|
|
156
|
+
stop = False
|
|
157
|
+
stopc = self.stopc
|
|
158
|
+
|
|
159
|
+
if stopc == 1:
|
|
160
|
+
numer = (1.0 / t_len) * (np.linalg.norm(f_mir - sum_u) ** 2)
|
|
161
|
+
denom = (1.0 / t_len) * (np.linalg.norm(f_mir) ** 2) + np.finfo(float).eps
|
|
162
|
+
if numer / denom <= 0.02:
|
|
163
|
+
stop = True
|
|
164
|
+
elif stopc == 2:
|
|
165
|
+
if u_hat_i.shape[2] == 1:
|
|
166
|
+
err = np.linalg.norm(f_hat_temp - u_hat_i[:, :, 0].T) ** 2
|
|
167
|
+
else:
|
|
168
|
+
err = np.linalg.norm(f_hat_temp - np.sum(u_hat_i, axis=2).T) ** 2
|
|
169
|
+
bic.append(
|
|
170
|
+
2 * t_len * np.log(err + np.finfo(float).eps)
|
|
171
|
+
+ (3 * k) * np.log(2 * t_len)
|
|
172
|
+
)
|
|
173
|
+
if k > 1 and bic[-1] > bic[-2]:
|
|
174
|
+
stop = True
|
|
175
|
+
elif stopc == 3:
|
|
176
|
+
if self.kdm is None:
|
|
177
|
+
raise ValueError("kdm must be set when stopc == 3")
|
|
178
|
+
if k == self.kdm:
|
|
179
|
+
stop = True
|
|
180
|
+
else:
|
|
181
|
+
# case 4 / default: power of the last mode
|
|
182
|
+
power = float(
|
|
183
|
+
np.mean(
|
|
184
|
+
np.abs(
|
|
185
|
+
np.sum(
|
|
186
|
+
(1.0 / t_len)
|
|
187
|
+
* u_hat_i[:, :, k - 1]
|
|
188
|
+
* np.conj(u_hat_i[:, :, k - 1]),
|
|
189
|
+
axis=0,
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
)
|
|
193
|
+
)
|
|
194
|
+
if k < 2:
|
|
195
|
+
polm_temp = power
|
|
196
|
+
polm.append(1.0)
|
|
197
|
+
else:
|
|
198
|
+
polm.append(power / (polm_temp + np.finfo(float).eps))
|
|
199
|
+
if abs(polm[-1] - polm[-2]) < 0.01:
|
|
200
|
+
stop = True
|
|
201
|
+
|
|
202
|
+
return stop, polm, polm_temp, bic
|
|
203
|
+
|
|
204
|
+
def fit_transform(
|
|
205
|
+
self, signal: np.ndarray, return_all: bool = False
|
|
206
|
+
) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
|
|
207
|
+
"""
|
|
208
|
+
Run SJMD / SMJMD.
|
|
209
|
+
|
|
210
|
+
:param signal: ``(N,)``, ``(1, N)``, or ``(C, N)``
|
|
211
|
+
:param return_all: if True, return ``(modes, jump)``
|
|
212
|
+
:return:
|
|
213
|
+
- univariate: modes ``(K, N)``, jump ``(N,)``
|
|
214
|
+
- multivariate: modes ``(K, N, C)``, jump ``(C, N)``
|
|
215
|
+
"""
|
|
216
|
+
data, n_ch, save_t, univariate = self._resolve_input(signal)
|
|
217
|
+
self.n_channels = n_ch
|
|
218
|
+
|
|
219
|
+
shift = data.mean(axis=1, keepdims=True)
|
|
220
|
+
data = data - shift
|
|
221
|
+
|
|
222
|
+
f_mir = self._mirror(data)
|
|
223
|
+
f = f_mir.copy()
|
|
224
|
+
t_len = f.shape[1]
|
|
225
|
+
half = t_len // 2
|
|
226
|
+
t = np.arange(1, t_len + 1, dtype=float) / t_len
|
|
227
|
+
omega_freqs = t - 0.5 - 1.0 / t_len
|
|
228
|
+
|
|
229
|
+
f_hat = np.fft.fftshift(np.fft.fft(f, axis=1), axes=1)
|
|
230
|
+
f_hat_onesided = f_hat.copy()
|
|
231
|
+
f_hat_onesided[:, :half] = 0.0
|
|
232
|
+
f_hat_temp = f_hat_onesided.copy()
|
|
233
|
+
|
|
234
|
+
n_max = self.max_inner
|
|
235
|
+
b = 2.0 / (self.b_bar**2)
|
|
236
|
+
gamma = self.tau * (0.5 * b * self.beta)
|
|
237
|
+
d = np.ones(t_len)
|
|
238
|
+
D = sp.diags([-d, d], [0, 1], shape=(t_len, t_len), format="lil")
|
|
239
|
+
D[-1, :] = 0
|
|
240
|
+
D = D.tocsr()
|
|
241
|
+
D_arr = D.toarray()
|
|
242
|
+
DTD = (D.T @ D).tocsc()
|
|
243
|
+
coef1 = 1.0 / gamma
|
|
244
|
+
mu = 2.0 * self.beta / gamma
|
|
245
|
+
A_jump = sp.eye(t_len, format="csc") + gamma * DTD
|
|
246
|
+
solve_jump = sp.linalg.factorized(A_jump)
|
|
247
|
+
|
|
248
|
+
x = np.zeros((t_len, n_ch), dtype=float)
|
|
249
|
+
rho = np.zeros((t_len, n_ch), dtype=float)
|
|
250
|
+
j_comp = np.zeros((n_ch, t_len), dtype=float)
|
|
251
|
+
j_hat_plus = np.zeros((t_len, n_ch, n_max), dtype=complex)
|
|
252
|
+
g_hat_plus = np.zeros((t_len, n_ch, n_max), dtype=complex)
|
|
253
|
+
|
|
254
|
+
u_hat_temp_list: List[np.ndarray] = []
|
|
255
|
+
omega_d_temp: List[float] = []
|
|
256
|
+
u_hat_i = np.zeros((t_len, n_ch, 0), dtype=complex)
|
|
257
|
+
|
|
258
|
+
sum_u = np.zeros((n_ch, t_len), dtype=float)
|
|
259
|
+
sc2 = False
|
|
260
|
+
k = 1
|
|
261
|
+
polm: List[float] = []
|
|
262
|
+
polm_temp: Optional[float] = None
|
|
263
|
+
bic: List[float] = []
|
|
264
|
+
|
|
265
|
+
while not sc2:
|
|
266
|
+
# ---- Part 2/3: extract one mode while increasing alpha ----
|
|
267
|
+
omega_l = np.zeros(n_max, dtype=float)
|
|
268
|
+
u_hat_l = np.zeros((t_len, n_ch, n_max), dtype=complex)
|
|
269
|
+
udiff = self.tol + np.spacing(1)
|
|
270
|
+
n = 0 # 0-based; MATLAB starts at 1 and writes to n+1
|
|
271
|
+
m = 0.0
|
|
272
|
+
bf = 0
|
|
273
|
+
alpha_val = self.min_alpha
|
|
274
|
+
|
|
275
|
+
while alpha_val < (self.max_alpha + 1):
|
|
276
|
+
while udiff > self.tol and n < n_max - 1:
|
|
277
|
+
for c in range(n_ch):
|
|
278
|
+
denom = 1.0 + alpha_val * (omega_freqs - omega_l[n]) ** 2
|
|
279
|
+
u_hat_l[:, c, n + 1] = (
|
|
280
|
+
f_hat_onesided[c, :]
|
|
281
|
+
- j_hat_plus[:, c, n]
|
|
282
|
+
- g_hat_plus[:, c, n]
|
|
283
|
+
) / denom
|
|
284
|
+
|
|
285
|
+
num_g = (
|
|
286
|
+
(alpha_val**2)
|
|
287
|
+
* (omega_freqs - omega_l[n]) ** 4
|
|
288
|
+
* (
|
|
289
|
+
f_hat_onesided[c, :]
|
|
290
|
+
- j_hat_plus[:, c, n]
|
|
291
|
+
- u_hat_l[:, c, n + 1]
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
den_g = (
|
|
295
|
+
1.0 + 2.0 * (alpha_val**2) * (omega_freqs - omega_l[n]) ** 4
|
|
296
|
+
)
|
|
297
|
+
g_hat_plus[:, c, n + 1] = num_g / den_g
|
|
298
|
+
|
|
299
|
+
# shared / single center frequency
|
|
300
|
+
abs2 = np.abs(u_hat_l[half:t_len, :, n + 1]) ** 2
|
|
301
|
+
num_w = omega_freqs[half:t_len] @ abs2
|
|
302
|
+
omega_l[n + 1] = float(
|
|
303
|
+
np.sum(num_w) / (np.sum(abs2) + np.finfo(float).eps)
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# back to time domain
|
|
307
|
+
u_hat = self._hermitian_from_onesided(
|
|
308
|
+
u_hat_l[:, :, n + 1], half=half, t_len=t_len
|
|
309
|
+
)
|
|
310
|
+
g_hat = self._hermitian_from_onesided(
|
|
311
|
+
g_hat_plus[:, :, n + 1], half=half, t_len=t_len
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
u_td = np.zeros((n_ch, t_len), dtype=float)
|
|
315
|
+
fr_td = np.zeros((n_ch, t_len), dtype=float)
|
|
316
|
+
for c in range(n_ch):
|
|
317
|
+
u_td[c, :] = np.real(np.fft.ifft(np.fft.ifftshift(u_hat[:, c])))
|
|
318
|
+
fr_td[c, :] = np.real(
|
|
319
|
+
np.fft.ifft(np.fft.ifftshift(g_hat[:, c]))
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
for c in range(n_ch):
|
|
323
|
+
temp = u_td[c, :] + fr_td[c, :]
|
|
324
|
+
f1 = f[c, :]
|
|
325
|
+
rhs = (
|
|
326
|
+
gamma * (D_arr.T @ x[:, c])
|
|
327
|
+
- (D_arr.T @ rho[:, c])
|
|
328
|
+
+ f1
|
|
329
|
+
- temp
|
|
330
|
+
)
|
|
331
|
+
v = solve_jump(rhs)
|
|
332
|
+
dv = D_arr @ v
|
|
333
|
+
h = dv + coef1 * rho[:, c]
|
|
334
|
+
x[:, c] = self._soft_jump(h, mu=mu, b=b)
|
|
335
|
+
rho[:, c] = rho[:, c] - gamma * (x[:, c] - dv)
|
|
336
|
+
v = v - (np.mean(v) - np.mean(f1))
|
|
337
|
+
j_comp[c, :] = v
|
|
338
|
+
j_hat_plus[:, c, n + 1] = np.fft.fftshift(np.fft.fft(v))
|
|
339
|
+
j_hat_plus[:half, c, n + 1] = 0.0
|
|
340
|
+
|
|
341
|
+
n += 1
|
|
342
|
+
ud = u_hat_l[:, :, n] - u_hat_l[:, :, n - 1]
|
|
343
|
+
udiff_m = (1.0 / t_len) * (ud.conj().T @ ud)
|
|
344
|
+
jd = j_hat_plus[:, :, n] - j_hat_plus[:, :, n - 1]
|
|
345
|
+
jdiff_m = (1.0 / t_len) * (jd.conj().T @ jd)
|
|
346
|
+
udiff = float(
|
|
347
|
+
np.spacing(1)
|
|
348
|
+
+ np.abs(np.sum(udiff_m))
|
|
349
|
+
+ np.abs(np.sum(jdiff_m))
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
# ---- Part 3: increase alpha ----
|
|
353
|
+
if abs(m - np.log(self.max_alpha)) > 1:
|
|
354
|
+
m = m + 1.0
|
|
355
|
+
else:
|
|
356
|
+
m = m + 0.05
|
|
357
|
+
bf += 1
|
|
358
|
+
if bf >= 2:
|
|
359
|
+
alpha_val = alpha_val + 1.0
|
|
360
|
+
|
|
361
|
+
if alpha_val <= (self.max_alpha - 1):
|
|
362
|
+
if bf == 1:
|
|
363
|
+
alpha_val = self.max_alpha - 1
|
|
364
|
+
else:
|
|
365
|
+
alpha_val = float(np.exp(m))
|
|
366
|
+
|
|
367
|
+
# MATLAB: omega_L = omega_L(n) → keep current omega as scalar start
|
|
368
|
+
omega_keep = float(omega_l[n])
|
|
369
|
+
udiff = self.tol + np.spacing(1)
|
|
370
|
+
temp_ud = u_hat_l[:, :, n].copy()
|
|
371
|
+
n = 0
|
|
372
|
+
u_hat_l = np.zeros((t_len, n_ch, n_max), dtype=complex)
|
|
373
|
+
u_hat_l[:, :, 0] = temp_ud
|
|
374
|
+
omega_l = np.zeros(n_max, dtype=float)
|
|
375
|
+
omega_l[0] = omega_keep
|
|
376
|
+
|
|
377
|
+
# ---- Part 4: save mode ----
|
|
378
|
+
# MATLAB: omega_L=omega_L(omega_L>0); omega_d_Temp(k)=omega_L(n-1)
|
|
379
|
+
# With a leading zero at omega_l[0], filtering + (n-1) selects the last
|
|
380
|
+
# positive center frequency written during ADMM.
|
|
381
|
+
omega_pos = omega_l[omega_l > 0]
|
|
382
|
+
omega_save = float(omega_pos[-1]) if omega_pos.size else 0.0
|
|
383
|
+
|
|
384
|
+
mode_spec = u_hat_l[:, :, n].copy()
|
|
385
|
+
u_hat_temp_list.append(mode_spec)
|
|
386
|
+
omega_d_temp.append(omega_save)
|
|
387
|
+
|
|
388
|
+
u_hat_i = np.concatenate([u_hat_i, mode_spec[:, :, None]], axis=2)
|
|
389
|
+
|
|
390
|
+
u_hat = self._hermitian_from_onesided(mode_spec, half=half, t_len=t_len)
|
|
391
|
+
u_td = np.zeros((n_ch, t_len), dtype=float)
|
|
392
|
+
for c in range(n_ch):
|
|
393
|
+
u_td[c, :] = np.real(np.fft.ifft(np.fft.ifftshift(u_hat[:, c])))
|
|
394
|
+
|
|
395
|
+
f = f - u_td
|
|
396
|
+
sum_u = sum_u + u_td
|
|
397
|
+
|
|
398
|
+
f_hat = np.fft.fftshift(np.fft.fft(f, axis=1), axes=1)
|
|
399
|
+
f_hat_onesided = f_hat.copy()
|
|
400
|
+
f_hat_onesided[:, :half] = 0.0
|
|
401
|
+
|
|
402
|
+
x = np.zeros((t_len, n_ch), dtype=float)
|
|
403
|
+
rho = np.zeros((t_len, n_ch), dtype=float)
|
|
404
|
+
|
|
405
|
+
sc2, polm, polm_temp, bic = self._check_stop(
|
|
406
|
+
k=k,
|
|
407
|
+
f_mir=f_mir,
|
|
408
|
+
sum_u=sum_u,
|
|
409
|
+
u_hat_i=u_hat_i,
|
|
410
|
+
f_hat_temp=f_hat_temp,
|
|
411
|
+
polm=polm,
|
|
412
|
+
polm_temp=polm_temp,
|
|
413
|
+
bic=bic,
|
|
414
|
+
t_len=t_len,
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
# Part 6: reset for next mode
|
|
418
|
+
k += 1
|
|
419
|
+
if k > 50:
|
|
420
|
+
sc2 = True
|
|
421
|
+
|
|
422
|
+
# ---- Part 7: reconstruct ----
|
|
423
|
+
n_modes = len(omega_d_temp)
|
|
424
|
+
if n_modes == 0:
|
|
425
|
+
raise RuntimeError("SJMD failed to extract any mode")
|
|
426
|
+
|
|
427
|
+
u_hat_temp = np.stack(u_hat_temp_list, axis=2) # (T, C, K)
|
|
428
|
+
omega = np.asarray(omega_d_temp, dtype=float)
|
|
429
|
+
|
|
430
|
+
u_hat = self._hermitian_from_onesided(u_hat_temp, half=half, t_len=t_len)
|
|
431
|
+
|
|
432
|
+
u_time = np.zeros((t_len, n_ch, n_modes), dtype=float)
|
|
433
|
+
for c in range(n_ch):
|
|
434
|
+
for ki in range(n_modes):
|
|
435
|
+
u_time[:, c, ki] = np.real(
|
|
436
|
+
np.fft.ifft(np.fft.ifftshift(u_hat[:, c, ki]))
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
order = np.argsort(omega)
|
|
440
|
+
u_time = u_time[:, :, order]
|
|
441
|
+
omega = omega[order]
|
|
442
|
+
|
|
443
|
+
# remove mirror: MATLAB T/4+1:3*T/4
|
|
444
|
+
u_crop = u_time[t_len // 4 : 3 * t_len // 4, :, :]
|
|
445
|
+
j_crop = j_comp[:, t_len // 4 : 3 * t_len // 4] + shift
|
|
446
|
+
|
|
447
|
+
if univariate:
|
|
448
|
+
modes = np.transpose(u_crop[:, 0, :]) # (K, N)
|
|
449
|
+
jump = j_crop[0, :]
|
|
450
|
+
else:
|
|
451
|
+
modes = np.transpose(u_crop, (2, 0, 1)) # (K, N, C)
|
|
452
|
+
jump = j_crop
|
|
453
|
+
|
|
454
|
+
self.u = modes
|
|
455
|
+
self.j = jump
|
|
456
|
+
self.omega = omega
|
|
457
|
+
|
|
458
|
+
if return_all:
|
|
459
|
+
return modes, jump
|
|
460
|
+
return modes
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
# Paper name for the multivariate case (same implementation)
|
|
464
|
+
SMJMD = SJMD
|