Modal-Decomposition 0.1.2__tar.gz → 0.1.3__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 (41) hide show
  1. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/PKG-INFO +2 -26
  2. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/README.md +1 -25
  3. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/pyproject.toml +1 -1
  4. modal_decomposition-0.1.3/src/Modal_Decomposition/EEMD.py +64 -0
  5. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EFD.py +8 -6
  6. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EMD.py +0 -1
  7. modal_decomposition-0.1.3/src/Modal_Decomposition/Error/RealizationError.py +2 -0
  8. modal_decomposition-0.1.3/src/Modal_Decomposition/LMD.py +243 -0
  9. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/MEMD.py +13 -11
  10. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/SSA.py +2 -2
  11. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/SVMD.py +9 -4
  12. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/__init__.py +8 -15
  13. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/PKG-INFO +2 -26
  14. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/SOURCES.txt +1 -1
  15. modal_decomposition-0.1.2/src/Modal_Decomposition/EEMD.py +0 -130
  16. modal_decomposition-0.1.2/src/Modal_Decomposition/LMD.py +0 -178
  17. modal_decomposition-0.1.2/src/Modal_Decomposition/help_function.py +0 -64
  18. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/LICENSE +0 -0
  19. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/setup.cfg +0 -0
  20. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEFD.py +0 -0
  21. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEMD.py +0 -0
  22. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEMDAN.py +0 -0
  23. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/__init__.py +0 -0
  24. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/color_define.py +0 -0
  25. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/colorful_print.py +0 -0
  26. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EWT.py +0 -0
  27. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Error/CycleError.py +0 -0
  28. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/FMD.py +0 -0
  29. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/ICEEMDAN.py +0 -0
  30. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/RPSEMD.py +0 -0
  31. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/EnvironmentMemory.py +0 -0
  32. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/LazyImport.py +0 -0
  33. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/Monotonicity.py +0 -0
  34. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/NumpyNdarray_MemoryCalculator.py +0 -0
  35. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/OneDimArray.py +0 -0
  36. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/__init__.py +0 -0
  37. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition/VMD.py +0 -0
  38. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/dependency_links.txt +0 -0
  39. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/requires.txt +0 -0
  40. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/top_level.txt +0 -0
  41. {modal_decomposition-0.1.2 → modal_decomposition-0.1.3}/tests/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Modal-Decomposition
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: a package for modal decomposition
5
5
  Author-email: Mao_HaoChuan <2215269365@qq.com>
6
6
  License: Apache-2.0
@@ -55,7 +55,7 @@ All entrance of functions or class are stored in `Modal_Decomposition/__init__.p
55
55
  | EFD | Empirical Fourier Decomposition | `Function.EFD(signal)` | [10.1016/j.ymssp.2021.108155](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355?via%3Dihub) |
56
56
  | EMD | Empirical Mode Decomposition | `Function.EMD(signal)` | [10.1098/rspa.1998.0193](https://www.semanticscholar.org/paper/The-empirical-mode-decomposition-and-the-Hilbert-Huang-Shen/3842d81b0375dae8ae92734aa2a5d4aeed7a91d1) |
57
57
  | EWT | Empirical Wavelet Transform | `Function.EWT(signal)` | [10.48550/arXiv.2304.06274](https://arxiv.org/abs/2304.06274) |
58
- | FMD | Filtered Mode Decomposition | `Function.MEMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
58
+ | FMD | Filtered Mode Decomposition | `Function.FMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
59
59
  | ICEEMDAN | Improved Complete Ensemble Empirical Mode Decomposition with Adaptive Noise | `Function.ICEEMDAN(signal)` | [10.1007/s10470-021-01901-3](https://link.springer.com/article/10.1007/s10470-021-01901-3#citeas) |
60
60
  | LMD | Local Mean Decomposition | `Function.LMD(signal)` | [10.1098/rsif.2005.0058](https://royalsocietypublishing.org/doi/10.1098/rsif.2005.0058) |
61
61
  | MEMD | Multivariate Empirical Mode Decomposition | `Function.MEMD(signal)` | [10.48550/arXiv.2206.00926](https://arxiv.org/abs/2206.00926) |
@@ -79,42 +79,18 @@ pip install -r requirements.txt
79
79
  pip install Modal-Decomposition
80
80
  ```
81
81
 
82
- *Well, some libs should be installed by yourself, don't use `pip install -r requirements.txt`, because `Triton` need installed by GitHub.*
83
-
84
- *`Triton` please visite this url: https://github.com/woct0rdho/triton-windows/releases*
85
-
86
82
  ## Dependence
87
83
 
88
84
  This lib's dependence are:
89
85
 
90
86
  ***Python: 3.10***
91
87
 
92
- - [antropy](https://github.com/raphaelvallat/antropy)
93
- - [colorama](https://github.com/tartley/colorama)
94
- - [einops](https://github.com/arogozhnikov/einops)
95
88
  - [EMD-signal](https://github.com/laszukdawid/PyEMD)
96
89
  - [ewtpy](https://github.com/vrcarva/ewtpy)
97
- - [numba](https://github.com/numba/numba)
98
- - [numpy](https://github.com/numpy/numpy)
99
- - [scipy](https://github.com/scipy/scipy)
100
90
  - [vmdpy](https://github.com/vrcarva/vmdpy)
101
- - [psutil](https://github.com/giampaolo/psutil)
102
91
 
103
92
  *Other dependence please read "requirements.txt"*
104
93
 
105
- ## Codes Resource
106
-
107
- All codes from :
108
-
109
- - Github:
110
- - EMD-signal -> EMD, CEEFD, CEEMDAN, EEMD
111
- - ewtpy -> EWT
112
- - vmdpy -> VMD
113
-
114
-
115
- - Myself:
116
- - CEEMD, EFD, FMD, ICEEMDAN, LMD, MEMD, RPSEMD, SSA, SVMD
117
-
118
94
  ## Url
119
95
 
120
96
  This lib's url is: https://github.com/a-raining-day/Modal-Decomposition
@@ -23,7 +23,7 @@ All entrance of functions or class are stored in `Modal_Decomposition/__init__.p
23
23
  | EFD | Empirical Fourier Decomposition | `Function.EFD(signal)` | [10.1016/j.ymssp.2021.108155](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355?via%3Dihub) |
24
24
  | EMD | Empirical Mode Decomposition | `Function.EMD(signal)` | [10.1098/rspa.1998.0193](https://www.semanticscholar.org/paper/The-empirical-mode-decomposition-and-the-Hilbert-Huang-Shen/3842d81b0375dae8ae92734aa2a5d4aeed7a91d1) |
25
25
  | EWT | Empirical Wavelet Transform | `Function.EWT(signal)` | [10.48550/arXiv.2304.06274](https://arxiv.org/abs/2304.06274) |
26
- | FMD | Filtered Mode Decomposition | `Function.MEMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
26
+ | FMD | Filtered Mode Decomposition | `Function.FMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
27
27
  | ICEEMDAN | Improved Complete Ensemble Empirical Mode Decomposition with Adaptive Noise | `Function.ICEEMDAN(signal)` | [10.1007/s10470-021-01901-3](https://link.springer.com/article/10.1007/s10470-021-01901-3#citeas) |
28
28
  | LMD | Local Mean Decomposition | `Function.LMD(signal)` | [10.1098/rsif.2005.0058](https://royalsocietypublishing.org/doi/10.1098/rsif.2005.0058) |
29
29
  | MEMD | Multivariate Empirical Mode Decomposition | `Function.MEMD(signal)` | [10.48550/arXiv.2206.00926](https://arxiv.org/abs/2206.00926) |
@@ -47,42 +47,18 @@ pip install -r requirements.txt
47
47
  pip install Modal-Decomposition
48
48
  ```
49
49
 
50
- *Well, some libs should be installed by yourself, don't use `pip install -r requirements.txt`, because `Triton` need installed by GitHub.*
51
-
52
- *`Triton` please visite this url: https://github.com/woct0rdho/triton-windows/releases*
53
-
54
50
  ## Dependence
55
51
 
56
52
  This lib's dependence are:
57
53
 
58
54
  ***Python: 3.10***
59
55
 
60
- - [antropy](https://github.com/raphaelvallat/antropy)
61
- - [colorama](https://github.com/tartley/colorama)
62
- - [einops](https://github.com/arogozhnikov/einops)
63
56
  - [EMD-signal](https://github.com/laszukdawid/PyEMD)
64
57
  - [ewtpy](https://github.com/vrcarva/ewtpy)
65
- - [numba](https://github.com/numba/numba)
66
- - [numpy](https://github.com/numpy/numpy)
67
- - [scipy](https://github.com/scipy/scipy)
68
58
  - [vmdpy](https://github.com/vrcarva/vmdpy)
69
- - [psutil](https://github.com/giampaolo/psutil)
70
59
 
71
60
  *Other dependence please read "requirements.txt"*
72
61
 
73
- ## Codes Resource
74
-
75
- All codes from :
76
-
77
- - Github:
78
- - EMD-signal -> EMD, CEEFD, CEEMDAN, EEMD
79
- - ewtpy -> EWT
80
- - vmdpy -> VMD
81
-
82
-
83
- - Myself:
84
- - CEEMD, EFD, FMD, ICEEMDAN, LMD, MEMD, RPSEMD, SSA, SVMD
85
-
86
62
  ## Url
87
63
 
88
64
  This lib's url is: https://github.com/a-raining-day/Modal-Decomposition
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "Modal-Decomposition"
7
- version = "0.1.2"
7
+ version = "0.1.3"
8
8
  requires-python = ">=3.8"
9
9
 
10
10
  authors = [
@@ -0,0 +1,64 @@
1
+ """
2
+ Python version: (must)
3
+ 3.10.11
4
+
5
+ Lib and Version: (if None write None)
6
+ EMD-S - 1.9.0
7
+
8
+ Only accessed by: (must)
9
+ Only __init__.py
10
+
11
+ Description: (if None write None)
12
+ Realize the EEMD.
13
+
14
+ Modify: (must)
15
+ 2026.3.25 - Create.
16
+ 2026.3.27 - Change the EEMD class' usage. EEMD(parallel=True) (default) -> EEMD(parallel=False). Now, it will not use parallel default.
17
+ 2026.4.2 - Finish the Optimization of the EEMD. Correct the logic.
18
+ 2026.5.1 - Fix: use PyEMD.EEMD to ensure standard algorithm.
19
+ """
20
+
21
+ import numpy as np
22
+ from typing import Union, Tuple, Optional
23
+
24
+ def eemd(
25
+ S: Union[list, np.ndarray],
26
+ T: Optional[Union[list, np.ndarray]] = None,
27
+ trials: int = 100,
28
+ noise_width: float = 0.05,
29
+ max_imf: int = -1,
30
+ **kwargs
31
+ ) -> Tuple[np.ndarray, np.ndarray]:
32
+ """
33
+ EEMD: Ensemble Empirical Mode Decomposition
34
+ (Standard implementation via PyEMD)
35
+
36
+ :param S: Signal (1-dim)
37
+ :param T: Time axis (1-dim). Default uniform.
38
+ :param trials: Number of white noise realizations (ensemble size).
39
+ :param noise_width: Standard deviation of added white noise relative to signal std.
40
+ :param max_imf: Max number of IMFs to extract. -1 for all.
41
+ :param kwargs: Additional parameters passed to PyEMD.EEMD.
42
+ :return: IMFs (n_IMFs, N), Res (N,)
43
+ """
44
+ from PyEMD import EEMD
45
+
46
+ if not isinstance(S, np.ndarray):
47
+ S = np.array(S, dtype=np.float64).ravel()
48
+ else:
49
+ S = S.ravel().astype(np.float64)
50
+
51
+ N = len(S)
52
+
53
+ if T is None:
54
+ T = np.arange(N, dtype=np.float64)
55
+ else:
56
+ T = np.asarray(T, dtype=np.float64)
57
+
58
+ decomposer = EEMD(trials=trials, noise_width=noise_width, **kwargs)
59
+ result = decomposer.eemd(S, T, max_imf=max_imf)
60
+
61
+ IMFs = result[:-1, :] # shape [n_imfs, N]
62
+ Res = result[-1, :] # shape [N,]
63
+
64
+ return IMFs, Res
@@ -17,6 +17,7 @@ Description: (if None write None)
17
17
  Modify: (must)
18
18
  2026.3.25 - Create
19
19
  2026.4.2 - Finish the Optimization of the EFD. Del the origin efd function.
20
+ 2026.4.7 - Correct the error of the use of the np.concentrate and the construction of the wn.
20
21
  """
21
22
 
22
23
  import numpy as np
@@ -24,13 +25,14 @@ from .COLOR import printc
24
25
  from typing import Union, Tuple
25
26
 
26
27
 
27
- def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: int=-1) -> Tuple[np.ndarray, np.ndarray]:
28
+ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: int=-1, verbose: bool=False) -> Tuple[np.ndarray, np.ndarray]:
28
29
  """
29
30
  EFD: Empirical Fourier Decomposition
30
31
 
31
32
  :param S: Signal (1-dim)
32
33
  :param T: Time axis (1-dim)
33
34
  :param max_IMFs: the num of the IMFs. -1 means return all IMFs
35
+ :param verbose: if print the info?
34
36
  :return: IMFs (n_IMFs, N), Res: (N,)
35
37
  """
36
38
 
@@ -60,7 +62,8 @@ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: i
60
62
 
61
63
  if T is None: # if T is None, default generate uniform T-axis.
62
64
  T = np.arange(N) # default fs = 1
63
- print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
65
+ if verbose:
66
+ print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
64
67
 
65
68
  else:
66
69
  if not isinstance(T, np.ndarray):
@@ -86,14 +89,13 @@ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: i
86
89
  uniform_freq = np.linspace(0, np.pi, N // 2)
87
90
  freq_N = len(uniform_freq)
88
91
 
89
- local_maximum_points = argrelmax(edge_magnitude)
92
+ local_maximum_points_tuple = argrelmax(edge_magnitude)
93
+ local_maximum_points = local_maximum_points_tuple[0]
90
94
  local_maximum = edge_magnitude[local_maximum_points]
91
95
 
92
96
  if max_IMFs != -1:
93
97
  local_maximum_zip = [(point, value) for point, value in zip(local_maximum_points, local_maximum)]
94
-
95
98
  local_maximum_zip = sorted(local_maximum_zip, reverse=True, key=lambda x: x[1])
96
-
97
99
  local_maximum_points = list(map(lambda x: x[0], local_maximum_zip[:max_IMFs]))
98
100
 
99
101
  local_maximum_points = np.concatenate(([0], local_maximum_points, [freq_N - 1]))
@@ -112,7 +114,7 @@ def efd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, max_IMFs: i
112
114
  else:
113
115
  # wn.append(np.argmin(edge_magnitude[current_point:next_point + 1]))
114
116
  wn.append(current_point + np.argmin(edge_magnitude[current_point:next_point + 1]))
115
- wn = np.concatenate(([0], wn, freq_N - 1))
117
+ wn = np.concatenate(([0], wn, [freq_N - 1]))
116
118
 
117
119
  filters_arr = []
118
120
  for edge in range(1, len(wn)):
@@ -18,7 +18,6 @@ Modify: (must)
18
18
 
19
19
  from PyEMD import EMD
20
20
  import numpy as np
21
- from .COLOR.colorful_print import printc
22
21
  from typing import Tuple, Union
23
22
 
24
23
 
@@ -0,0 +1,2 @@
1
+ class RealizationError(Exception):
2
+ pass
@@ -0,0 +1,243 @@
1
+ """
2
+ Python version: (must)
3
+ 3.10.11
4
+
5
+ Lib and Version: (if None write None)
6
+ numpy - 2.2.6
7
+ scipy - 1.15.3
8
+
9
+ Only accessed by: (must)
10
+ Only __init__.py
11
+
12
+ Modify: (must)
13
+ 2026.3.25
14
+
15
+ Description: (if None write None)
16
+ Realize the LMD
17
+
18
+ Modify:
19
+ 2026.3.25 - Optimize the use of scipy
20
+ 2026.4.3 - Finish the Optimization of the LMD.
21
+ 2026.4.7 - LMD can support the decompose completely now.
22
+ 2026.4.9 - Change some hardcode parameters to args.
23
+ 2026.5.1 - Fix the dependence of "help_function.py".
24
+ """
25
+
26
+ import numpy as np
27
+ from .Utils import is_monotonic
28
+ from typing import Union, Tuple
29
+
30
+
31
+ def lmd(
32
+ S: Union[list, np.ndarray],
33
+ max_pf: Union[int, None] = None,
34
+ max_iter: int = 37,
35
+ eps: float = 0.05,
36
+ eps_stable: float = 1e-12,
37
+ min_amp: float = 1e-12,
38
+ max_amp: float = 1e12,
39
+ converge_mean: float = 1e-3,
40
+ smooth_window: int = 5
41
+ ) -> Tuple[np.ndarray, np.ndarray]:
42
+ """
43
+ LMD: Local mean decomposition
44
+
45
+ :param S: Signal (1-dim)
46
+ :param max_pf: the max num of the PFs. Default log2(N), -1 means decompose completely.
47
+ :param max_iter: max iteration for each iter of decomposing the PFs. Default 37, according the paper.
48
+ :param eps: Envelope convergence threshold
49
+ :param eps_stable:
50
+ :param min_amp:
51
+ :param max_amp:
52
+ :param converge_mean:
53
+ :param smooth_window:
54
+
55
+ Returns:
56
+ PFs: (n_pf, N), Res: (N,)
57
+ """
58
+ from scipy.signal import argrelextrema, savgol_filter
59
+
60
+ if S.ndim == 0:
61
+ raise ValueError("The dim of the S must be 1-dim, not 0")
62
+
63
+ elif S.ndim > 1:
64
+ if 1 in S.shape:
65
+ S = S.reshape(-1)
66
+
67
+ else:
68
+ raise ValueError(f"The dim of S must be 1-dim, not {S.ndim}")
69
+
70
+ if not isinstance(S, np.ndarray):
71
+ S = np.asarray(S, dtype=np.float64).ravel()
72
+ n_samples = S.size
73
+
74
+ if n_samples < 8:
75
+ raise ValueError("LMD requires signal length ≥ 8 (Smith 2005 standard)")
76
+
77
+ t = np.arange(n_samples, dtype=np.float64)
78
+ max_pf = int(np.log2(n_samples)) if max_pf is None else max_pf
79
+ residue = S.copy()
80
+ PFs: list[np.ndarray] = []
81
+
82
+ if max_pf != -1:
83
+ for _ in range(max_pf):
84
+ h = residue.copy()
85
+ a_total = np.ones(n_samples, dtype=np.float64)
86
+ converged = False
87
+
88
+ for __ in range(max_iter):
89
+ max_loc = argrelextrema(h, np.greater)[0]
90
+ min_loc = argrelextrema(h, np.less)[0]
91
+ ext_idx = np.unique(np.concatenate([max_loc, min_loc]))
92
+
93
+ if len(ext_idx) < 3:
94
+ break
95
+
96
+ ext_idx, ext_vals = _mirror_extend_real(h, ext_idx, n_samples)
97
+ ext_idx = np.sort(ext_idx)
98
+
99
+ t_mid = (ext_idx[:-1] + ext_idx[1:]) / 2.0
100
+ m_vals = (ext_vals[:-1] + ext_vals[1:]) / 2.0
101
+ a_vals = np.abs(ext_vals[:-1] - ext_vals[1:]) / 2.0
102
+
103
+ m_t = _safe_interpolate(t_mid, m_vals, t)
104
+ a_t = _safe_interpolate(t_mid, a_vals, t)
105
+
106
+ a_t = np.clip(a_t, min_amp, max_amp)
107
+ a_t = savgol_filter(a_t, smooth_window, 2)
108
+
109
+ if _check_convergence(m_t, a_t, eps, converge_mean):
110
+ converged = True
111
+ break
112
+
113
+ s_new = (h - m_t) / a_t
114
+ a_total = np.clip(a_total * a_t, min_amp, max_amp)
115
+ h = s_new
116
+
117
+ if not converged:
118
+ break
119
+
120
+ current_pf = np.clip(a_total * s_new, -max_amp, max_amp)
121
+
122
+ if np.any(np.isnan(current_pf)) or np.any(np.isinf(current_pf)):
123
+ break
124
+
125
+ if np.sum(current_pf ** 2) < eps_stable:
126
+ break
127
+
128
+ PFs.append(current_pf)
129
+ residue -= current_pf
130
+
131
+ if (
132
+ is_monotonic(residue) or
133
+ np.sum(residue ** 2) < eps_stable or
134
+ len(argrelextrema(residue, np.greater)[0]) + len(argrelextrema(residue, np.less)[0]) <= 2
135
+ ):
136
+ break
137
+
138
+ else:
139
+ while True:
140
+ h = residue.copy()
141
+ a_total = np.ones(n_samples, dtype=np.float64)
142
+ converged = False
143
+
144
+ for __ in range(max_iter):
145
+ max_loc = argrelextrema(h, np.greater)[0]
146
+ min_loc = argrelextrema(h, np.less)[0]
147
+ ext_idx = np.unique(np.concatenate([max_loc, min_loc]))
148
+
149
+ if len(ext_idx) < 3:
150
+ break
151
+
152
+ ext_idx, ext_vals = _mirror_extend_real(h, ext_idx, n_samples)
153
+ ext_idx = np.sort(ext_idx)
154
+
155
+ t_mid = (ext_idx[:-1] + ext_idx[1:]) / 2.0
156
+ m_vals = (ext_vals[:-1] + ext_vals[1:]) / 2.0
157
+ a_vals = np.abs(ext_vals[:-1] - ext_vals[1:]) / 2.0
158
+
159
+ m_t = _safe_interpolate(t_mid, m_vals, t)
160
+ a_t = _safe_interpolate(t_mid, a_vals, t)
161
+
162
+ a_t = np.clip(a_t, min_amp, max_amp)
163
+ a_t = savgol_filter(a_t, smooth_window, 2)
164
+
165
+ if _check_convergence(m_t, a_t, eps, converge_mean):
166
+ converged = True
167
+ break
168
+
169
+ s_new = (h - m_t) / a_t
170
+ a_total = np.clip(a_total * a_t, min_amp, max_amp)
171
+ h = s_new
172
+
173
+ if not converged:
174
+ break
175
+
176
+ current_pf = np.clip(a_total * s_new, -max_amp, max_amp)
177
+
178
+ if np.any(np.isnan(current_pf)) or np.any(np.isinf(current_pf)):
179
+ break
180
+
181
+ if np.sum(current_pf ** 2) < eps_stable:
182
+ break
183
+
184
+ PFs.append(current_pf)
185
+ residue -= current_pf
186
+
187
+ if (is_increasing(residue) or is_increasing(-residue) or
188
+ np.sum(residue ** 2) < eps_stable or
189
+ len(argrelextrema(residue, np.greater)[0]) + len(argrelextrema(residue, np.less)[0]) <= 2):
190
+ break
191
+
192
+ del h, a_total, t
193
+ return np.array(PFs, dtype=np.float64), residue
194
+
195
+ def _mirror_extend_real(signal: np.ndarray, ext_idx: np.ndarray, n_samples: int) -> Tuple[np.ndarray, np.ndarray]:
196
+ ext = ext_idx.copy()
197
+ vals = signal[ext].copy()
198
+ last_idx = n_samples - 1
199
+
200
+ if ext[0] > 0:
201
+ mirror_pos = 0
202
+ mirror_val = 2 * signal[0] - vals[0]
203
+ ext = np.insert(ext, 0, mirror_pos)
204
+ vals = np.insert(vals, 0, mirror_val)
205
+
206
+ if ext[-1] < last_idx:
207
+ mirror_pos = last_idx
208
+ mirror_val = 2 * signal[-1] - vals[-1]
209
+ ext = np.append(ext, mirror_pos)
210
+ vals = np.append(vals, mirror_val)
211
+
212
+ return ext, vals
213
+
214
+ def _safe_interpolate(x: np.ndarray, y: np.ndarray, x_new: np.ndarray) -> np.ndarray:
215
+ from scipy import interpolate
216
+
217
+ try:
218
+ interp = interpolate.CubicSpline(x, y, bc_type="natural")
219
+ res = interp(x_new)
220
+ except:
221
+ res = np.interp(x_new, x, y)
222
+
223
+ left_mask = x_new < x[0]
224
+ right_mask = x_new > x[-1]
225
+ if np.any(left_mask):
226
+ slope = (y[1] - y[0]) / (x[1] - x[0])
227
+ res[left_mask] = y[0] + slope * (x_new[left_mask] - x[0])
228
+ if np.any(right_mask):
229
+ slope = (y[-1] - y[-2]) / (x[-1] - x[-2])
230
+ res[right_mask] = y[-1] + slope * (x_new[right_mask] - x[-1])
231
+
232
+ return res
233
+
234
+ def _check_convergence(m_t: np.ndarray, a_t: np.ndarray, eps: float, converge_mean) -> bool:
235
+ mean_ok = np.max(np.abs(m_t)) < converge_mean
236
+ envelope_ok = np.max(np.abs(a_t - 1.0)) < eps
237
+ return mean_ok and envelope_ok
238
+
239
+ def compute_envelope(signal: Union[list, np.ndarray]) -> np.ndarray:
240
+ from scipy.linalg import hilbert
241
+
242
+ signal = np.asarray(signal, dtype=np.float64).ravel()
243
+ return np.abs(hilbert(signal))
@@ -9,20 +9,21 @@ Lib and Version: (if None write None)
9
9
  Only accessed by: (must)
10
10
  Only __init__.py
11
11
 
12
- Modify: (must)
13
- 2026.3.25
14
-
15
12
  Description: (if None write None)
16
13
  Realize the MEMD
14
+
15
+ Modify: (must)
16
+ 2026.3.25 - Create.
17
+ 2026.5.1 - Fix the problem when calculating projections.
17
18
  """
18
19
 
19
20
  import numpy as np
20
21
  from typing import Union, Tuple
21
22
 
22
23
 
23
- def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2, max_iter=10) -> Tuple[np.ndarray, np.ndarray]:
24
+ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2, max_iter=10, spline_kind: str="linear") -> Tuple[np.ndarray, np.ndarray]:
24
25
  """
25
- MEMD: Multimodal Experience Modeling Decomposition
26
+ MEMD: Multimodal Empirical Mode Decomposition
26
27
 
27
28
  :param S: Signal (2-dim), (d, N) | d -> channels,N -> time points
28
29
  :param d: channels or dimensions
@@ -30,7 +31,8 @@ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2
30
31
  :param max_imf: max num of IMFs
31
32
  :param sd_thresh: therahold
32
33
  :param max_iter: max iterations of each IMF
33
- :return: IMFs (d, N), Res (2-dim)
34
+ :param spline_kind: spline kind.
35
+ :return: IMFs (IMFs_num, d, N), Res (2-dim)
34
36
  """
35
37
 
36
38
  if not isinstance(S, np.ndarray):
@@ -56,7 +58,7 @@ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2
56
58
 
57
59
  vectors = generate_hammersley_points(k, d) # dhape: (d, k)
58
60
 
59
- projections = np.dot(S.T, vectors) # shape: (N, k)
61
+ # projections = np.dot(S.T, vectors) # shape: (N, k)
60
62
 
61
63
  imfs = []
62
64
  residue = S.copy()
@@ -66,7 +68,7 @@ def memd(S: Union[list, np.ndarray], d=None, k=None, max_imf=None, sd_thresh=0.2
66
68
 
67
69
  for iter_num in range(max_iter):
68
70
  h_old = h
69
- mean_envelope = compute_local_mean(h, vectors, projections, T)
71
+ mean_envelope = compute_local_mean(h, vectors, T, spline_kind=spline_kind)
70
72
 
71
73
  h_new = h - mean_envelope
72
74
  sd = np.sum((h_old - h_new) ** 2) / np.sum(h_old ** 2)
@@ -153,7 +155,7 @@ def generate_primes(n):
153
155
  return primes
154
156
 
155
157
 
156
- def compute_local_mean(signal, vectors, T):
158
+ def compute_local_mean(signal, vectors, T, spline_kind: str="linear"):
157
159
  """
158
160
  S: now S (d, N)
159
161
  vectors: directional vector (d, k)
@@ -196,7 +198,7 @@ def compute_local_mean(signal, vectors, T):
196
198
  max_values = signal[ch, max_indices]
197
199
  if len(max_indices) >= 4:
198
200
  try:
199
- max_spline = interp1d(max_indices, max_values, kind='cubic',fill_value='extrapolate')
201
+ max_spline = interp1d(max_indices, max_values, kind=spline_kind,fill_value='extrapolate')
200
202
  max_env = max_spline(T)
201
203
  except:
202
204
  max_env = np.interp(T, max_indices, max_values)
@@ -206,7 +208,7 @@ def compute_local_mean(signal, vectors, T):
206
208
  min_values = signal[ch, min_indices]
207
209
  if len(min_indices) >= 4:
208
210
  try:
209
- min_spline = interp1d(min_indices, min_values, kind='cubic',fill_value='extrapolate')
211
+ min_spline = interp1d(min_indices, min_values, kind=spline_kind,fill_value='extrapolate')
210
212
  min_env = min_spline(T)
211
213
  except:
212
214
  min_env = np.interp(T, min_indices, min_values)
@@ -15,7 +15,7 @@ Description: (if None write None)
15
15
 
16
16
  Modify: (must)
17
17
  2026.3.25 - Create.
18
- 2026.3.29 - Optimize the SSA.decompose function, it's faster now. Please use SSA() which means SSA.decompose_fast().
18
+ 2026.3.29 - Optimize the SSA.decompose function, it's use_JIT now. Please use SSA() which means SSA.decompose_fast().
19
19
  """
20
20
 
21
21
  import numpy as np
@@ -165,7 +165,7 @@ class SSA:
165
165
  :param S: Signal (1-dim)
166
166
  :param L:
167
167
  :param groups: group information, such as: [[0], [1,2], [3,4]] means which components will be merged. If None, return all
168
- :param faster: if you choose True, function will be faster with wasting memory.
168
+ :param faster: if you choose True, function will be use_JIT with wasting memory.
169
169
  :return: IMFs (2-dim)
170
170
  """
171
171
 
@@ -15,6 +15,7 @@ Description: (if None write None)
15
15
  Modify: (must)
16
16
  2026.3.25 - Create.
17
17
  2026.4.3 - Finish the Optimization of the SVMD. Give two kind of SVMD.
18
+ 2026.5.1 - The decomposition with JIT is Error.
18
19
  """
19
20
 
20
21
  import numpy as np
@@ -134,6 +135,9 @@ class SVMD:
134
135
  return modes, residual
135
136
 
136
137
  def give_svmd_JIT():
138
+ from .Error.RealizationError import RealizationError
139
+ raise RealizationError("this method' realization is error.")
140
+
137
141
  from scipy.fft import fft, ifft, fftfreq
138
142
  from numba import jit, prange, complex128, float64
139
143
  import numba as nb
@@ -204,7 +208,7 @@ def give_svmd_JIT():
204
208
  for n in range(N):
205
209
  re = np.real(sum_modes_hat[n]) - prev_recon[n]
206
210
  recon_error += re * re
207
- sig_norm += signal_hat[n].real * signal_hat[n].real
211
+ sig_norm += np.sum(np.abs(signal_hat)**2) / N
208
212
 
209
213
  if it > 1 and np.sqrt(recon_error) / (np.sqrt(sig_norm) + eps) < tol:
210
214
  break
@@ -225,6 +229,7 @@ def give_svmd_JIT():
225
229
  residual[n] = np.real(signal_hat[n]) - recon_val
226
230
 
227
231
  return modes, residual
232
+
228
233
  class NumbaSVMD(SVMD):
229
234
 
230
235
  def decompose(self, signal: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
@@ -254,7 +259,7 @@ def give_svmd_JIT():
254
259
  def svmd \
255
260
  (
256
261
  S: Union[list, np.ndarray],
257
- faster: bool = False,
262
+ use_JIT: bool = False,
258
263
  num_modes: int = 3,
259
264
  alpha: float = 2000,
260
265
  tau: float = 0.0,
@@ -264,7 +269,7 @@ def svmd \
264
269
  """
265
270
 
266
271
  :param S: Signal (N,)
267
- :param faster: if True, use Numba version.
272
+ :param use_JIT: if True, use Numba version.
268
273
  :param num_modes: the num of modes
269
274
  :param alpha:
270
275
  :param tau:
@@ -273,7 +278,7 @@ def svmd \
273
278
  :return: IMFs(num_modes, N), Res(N,)
274
279
  """
275
280
 
276
- if not faster:
281
+ if not use_JIT:
277
282
  Cls = SVMD(num_modes=num_modes, alpha=alpha, tau=tau, tol=tol, max_iter=max_iter)
278
283
  IMFs, Res = Cls(S)
279
284
 
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Modal Decomposition:
3
- LMDCEEMDANEFDCEEFDVMDEEMDFMDEWTSSARPSEMDCEEMDMEMDICEEMDANEMD
3
+ LMD, CEEMDAN, EFD, CEEFD, VMD, EEMD, FMD, EWT, SSA, RPSEMD, CEEMD, MEMD, ICEEMDAN, EMD
4
4
 
5
5
  GitHub url: https://github.com/a-raining-day/Modal-Decomposition
6
6
 
@@ -17,9 +17,6 @@ Lib and Version: (if None write None)
17
17
  Only accessed by: (must)
18
18
  All
19
19
 
20
- Modify: (must)
21
- 2026.3.25
22
-
23
20
  Description: (if None write None)
24
21
  As the entrance of the lib
25
22
 
@@ -43,12 +40,14 @@ Modify:
43
40
  2026.4.3 - Finish the Optimization of the modal decomposition method. Except the MEMD method.
44
41
  2026.4.4 - Add the parameter to describe lib.
45
42
  2026.4.6 - Change the position of the entrance of importing 'threading'. Try to reduce the cost of the import.
43
+ 2026.4.9 - Fix the import error of Class.VMD. From "vmdpy.EWT1D" to "vmdpy.VMD".
44
+ 2026.5.1 - Delete the "help_function.py". Stop use decomposition with JIT in "SVMD.py". EEMD use PyEMD now. Fix the MEMD.
46
45
  """
47
46
 
48
47
 
49
- __version__ = "0.1.1"
48
+ __version__ = "0.1.3"
50
49
  __author__ = "a-raining-day(Mao)"
51
- __email__ = "None, Only QQ email."
50
+ __email__ = "2215269365@qq.com"
52
51
  __license__ = "Apache 2.0"
53
52
  __url__ = "https://github.com/a-raining-day/Modal-Decomposition"
54
53
  __description__ = "A comprehensive modal decomposition library"
@@ -72,11 +71,6 @@ from .SSA import SSA, ssa
72
71
  from .SVMD import svmd
73
72
  from .VMD import vmd
74
73
 
75
-
76
- import warnings
77
- warnings.warn("The MEMD is rebuilding...")
78
-
79
-
80
74
  __all__ = \
81
75
  [
82
76
  "Function", "Class",
@@ -129,7 +123,7 @@ class Class:
129
123
  def VMD(cls, **kwargs):
130
124
  if "EWT1D" not in cls.__cache:
131
125
  try:
132
- Module = import_module("vmdpy").EWT1D
126
+ Module = import_module("vmdpy").VMD
133
127
  cls.__cache["VMD"] = Module
134
128
  return Module(**kwargs)
135
129
  except ImportError:
@@ -139,9 +133,8 @@ class Class:
139
133
  return cls.__cache["VMD"](**kwargs)
140
134
 
141
135
  class Function:
142
- # function | default function for modal decomposition
143
- # the IMFs (2-dim) means: (K, len(Signal)) (K is the num of IMFs)
144
- # CEEFD = ceefd_real_cls.ceefd
136
+ """ function | default function for modal decomposition
137
+ the IMFs (2-dim) means: (K, len(Signal)) (K is the num of IMFs)"""
145
138
 
146
139
  CEEFD = Class.CEEFD(fs=1.0, min_peak_distance=10, envelop_iter=3)
147
140
  CEEMD = ceemd
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Modal-Decomposition
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: a package for modal decomposition
5
5
  Author-email: Mao_HaoChuan <2215269365@qq.com>
6
6
  License: Apache-2.0
@@ -55,7 +55,7 @@ All entrance of functions or class are stored in `Modal_Decomposition/__init__.p
55
55
  | EFD | Empirical Fourier Decomposition | `Function.EFD(signal)` | [10.1016/j.ymssp.2021.108155](https://www.sciencedirect.com/science/article/abs/pii/S0888327021005355?via%3Dihub) |
56
56
  | EMD | Empirical Mode Decomposition | `Function.EMD(signal)` | [10.1098/rspa.1998.0193](https://www.semanticscholar.org/paper/The-empirical-mode-decomposition-and-the-Hilbert-Huang-Shen/3842d81b0375dae8ae92734aa2a5d4aeed7a91d1) |
57
57
  | EWT | Empirical Wavelet Transform | `Function.EWT(signal)` | [10.48550/arXiv.2304.06274](https://arxiv.org/abs/2304.06274) |
58
- | FMD | Filtered Mode Decomposition | `Function.MEMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
58
+ | FMD | Filtered Mode Decomposition | `Function.FMD(signal)` | [10.1109/TIE.2022.3156156](https://ieeexplore.ieee.org/document/9732251) |
59
59
  | ICEEMDAN | Improved Complete Ensemble Empirical Mode Decomposition with Adaptive Noise | `Function.ICEEMDAN(signal)` | [10.1007/s10470-021-01901-3](https://link.springer.com/article/10.1007/s10470-021-01901-3#citeas) |
60
60
  | LMD | Local Mean Decomposition | `Function.LMD(signal)` | [10.1098/rsif.2005.0058](https://royalsocietypublishing.org/doi/10.1098/rsif.2005.0058) |
61
61
  | MEMD | Multivariate Empirical Mode Decomposition | `Function.MEMD(signal)` | [10.48550/arXiv.2206.00926](https://arxiv.org/abs/2206.00926) |
@@ -79,42 +79,18 @@ pip install -r requirements.txt
79
79
  pip install Modal-Decomposition
80
80
  ```
81
81
 
82
- *Well, some libs should be installed by yourself, don't use `pip install -r requirements.txt`, because `Triton` need installed by GitHub.*
83
-
84
- *`Triton` please visite this url: https://github.com/woct0rdho/triton-windows/releases*
85
-
86
82
  ## Dependence
87
83
 
88
84
  This lib's dependence are:
89
85
 
90
86
  ***Python: 3.10***
91
87
 
92
- - [antropy](https://github.com/raphaelvallat/antropy)
93
- - [colorama](https://github.com/tartley/colorama)
94
- - [einops](https://github.com/arogozhnikov/einops)
95
88
  - [EMD-signal](https://github.com/laszukdawid/PyEMD)
96
89
  - [ewtpy](https://github.com/vrcarva/ewtpy)
97
- - [numba](https://github.com/numba/numba)
98
- - [numpy](https://github.com/numpy/numpy)
99
- - [scipy](https://github.com/scipy/scipy)
100
90
  - [vmdpy](https://github.com/vrcarva/vmdpy)
101
- - [psutil](https://github.com/giampaolo/psutil)
102
91
 
103
92
  *Other dependence please read "requirements.txt"*
104
93
 
105
- ## Codes Resource
106
-
107
- All codes from :
108
-
109
- - Github:
110
- - EMD-signal -> EMD, CEEFD, CEEMDAN, EEMD
111
- - ewtpy -> EWT
112
- - vmdpy -> VMD
113
-
114
-
115
- - Myself:
116
- - CEEMD, EFD, FMD, ICEEMDAN, LMD, MEMD, RPSEMD, SSA, SVMD
117
-
118
94
  ## Url
119
95
 
120
96
  This lib's url is: https://github.com/a-raining-day/Modal-Decomposition
@@ -17,7 +17,6 @@ src/Modal_Decomposition/SSA.py
17
17
  src/Modal_Decomposition/SVMD.py
18
18
  src/Modal_Decomposition/VMD.py
19
19
  src/Modal_Decomposition/__init__.py
20
- src/Modal_Decomposition/help_function.py
21
20
  src/Modal_Decomposition.egg-info/PKG-INFO
22
21
  src/Modal_Decomposition.egg-info/SOURCES.txt
23
22
  src/Modal_Decomposition.egg-info/dependency_links.txt
@@ -27,6 +26,7 @@ src/Modal_Decomposition/COLOR/__init__.py
27
26
  src/Modal_Decomposition/COLOR/color_define.py
28
27
  src/Modal_Decomposition/COLOR/colorful_print.py
29
28
  src/Modal_Decomposition/Error/CycleError.py
29
+ src/Modal_Decomposition/Error/RealizationError.py
30
30
  src/Modal_Decomposition/Utils/EnvironmentMemory.py
31
31
  src/Modal_Decomposition/Utils/LazyImport.py
32
32
  src/Modal_Decomposition/Utils/Monotonicity.py
@@ -1,130 +0,0 @@
1
- """
2
- Python version: (must)
3
- 3.10.11
4
-
5
- Lib and Version: (if None write None)
6
- EMD-S - 1.9.0
7
-
8
- Only accessed by: (must)
9
- Only __init__.py
10
-
11
- Description: (if None write None)
12
- Realize the EEMD.
13
-
14
- Modify: (must)
15
- 2026.3.25 - Create.
16
- 2026.3.27 - Change the EEMD class' usage. EEMD(parallel=True) (default) -> EEMD(parallel=False). Now, it will not use parallel default.
17
- 2026.4.2 - Finish the Optimization of the EEMD. Correct the logic.
18
- """
19
-
20
- import numpy as np
21
- from typing import Union, Tuple, Optional
22
- from .Utils import monotonic_increasing, monotonic_decreasing
23
- from .Error import CycleError
24
- from time import sleep
25
- from warnings import warn
26
- from .EMD import emd
27
-
28
-
29
- def eemd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, N_whitenoise=300, beta=0.3, max_imf: Optional[int]=None, dead_line: int=10, verbose: bool=False) \
30
- -> Tuple[np.ndarray, np.ndarray]:
31
- """
32
- EEMD: Ensemble Empirical Mode Decomposition
33
-
34
- :param S: Signal (1-dim)
35
- :param T: the time axis.
36
- :param N_whitenoise: the num of the added whitenoise.
37
- :param beta:
38
- :param max_imf: -1, None or other int | -1 means decompose completely, None means give a int auto, other int means the num of the IMFs
39
- :param dead_line: Sometime it'll be in unuseful cycle, when the average of the N's sequence with added whitenoise is empty([]). It'll be forced exit when the time of the cycle above the deadline.
40
- :param verbose:
41
- :return: IMFs (n_IMFs, N), Res (N,)
42
- """
43
- if beta <= 0:
44
- raise ValueError("The beta should > 0")
45
-
46
- if N_whitenoise <= 0 or not isinstance(N_whitenoise, int):
47
- raise TypeError("N_whitenoise must be int type or > 0")
48
-
49
- if not isinstance(S, np.ndarray):
50
- S = np.array(S)
51
-
52
- if S.ndim == 0:
53
- raise ValueError("The dim of the S must be 1-dim, not 0")
54
-
55
- elif S.ndim > 1:
56
- if 1 in S.shape:
57
- S = S.reshape(-1)
58
-
59
- else:
60
- raise ValueError(f"The dim of S must be 1-dim, not {S.ndim}")
61
-
62
- N = len(S)
63
-
64
- if T is None: # if T is None, default generate uniform T-axis.
65
- T = np.arange(N) # default fs = 1
66
- print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
67
-
68
- else:
69
- if not isinstance(T, np.ndarray):
70
- T = np.array(T)
71
-
72
- if len(T) != len(S):
73
- raise ValueError("The length of T must be equal to Signal.")
74
-
75
- if not np.all(np.diff(T) > 0):
76
- raise ValueError("T should be monotonic increasing!")
77
-
78
- if np.any(np.allclose(np.diff(np.diff(T)))):
79
- warn("The T is not uniform! Some error may happen.")
80
-
81
- if max_imf is None:
82
- max_imf = int(np.log2(len(S))) + 2
83
-
84
- Res = S
85
- IMFs = []
86
-
87
- # std_dev = np.std(S)
88
-
89
- count = 0
90
- dead_cycle = 0
91
- while True:
92
- std_dev = np.std(Res)
93
-
94
- _IMFs = []
95
- for n in range(N_whitenoise):
96
- white_noise = np.random.normal(0, std_dev * beta, N)
97
- _S = Res + white_noise
98
- _IMFs_, _ = emd(_S, T, max_imf=1)
99
- if not _IMFs_:
100
- continue
101
- _IMFs.append(_IMFs_[0])
102
-
103
- if not _IMFs:
104
- dead_cycle += 1
105
- continue
106
-
107
- if dead_cycle >= dead_line:
108
- raise RuntimeError("Trapped in a vicious cycle")
109
- else:
110
- dead_cycle = 0
111
-
112
- IMF = np.mean(_IMFs, axis=0)
113
-
114
- IMFs.append(IMF)
115
-
116
- Res -= IMF
117
- count += 1
118
-
119
- if verbose:
120
- if count % 10 == 0:
121
- print(f"has get {count} IMFs...")
122
- sleep(1)
123
-
124
- if max_imf != -1:
125
- if count >= max_imf or monotonic_increasing(Res) or monotonic_decreasing(Res):
126
- return np.array(IMFs), np.array(Res)
127
-
128
- else:
129
- if monotonic_increasing(Res) or monotonic_decreasing(Res):
130
- return np.array(IMFs), np.array(Res)
@@ -1,178 +0,0 @@
1
- """
2
- Python version: (must)
3
- 3.10.11
4
-
5
- Lib and Version: (if None write None)
6
- numpy - 2.2.6
7
- scipy - 1.15.3
8
-
9
- Only accessed by: (must)
10
- Only __init__.py
11
-
12
- Modify: (must)
13
- 2026.3.25
14
-
15
- Description: (if None write None)
16
- Realize the LMD
17
-
18
- Modify:
19
- 2026.3.25 - Optimize the use of scipy
20
- 2026.4.3 - Finish the Optimization of the LMD.
21
- """
22
-
23
- import numpy as np
24
- from .help_function import is_increasing
25
- from typing import Union, Tuple
26
-
27
- EPS_STABLE = 1e-12
28
- MIN_AMP = 1e-12
29
- MAX_AMP = 1e12
30
- CONVERGE_MEAN = 1e-3
31
- SMOOTH_WINDOW = 5
32
-
33
- def lmd(
34
- S: Union[list, np.ndarray],
35
- max_pf: int | None = None,
36
- max_iter: int = 37,
37
- eps: float = 0.05
38
- ) -> Tuple[np.ndarray, np.ndarray]:
39
- """
40
- LMD: Local mean decomposition
41
-
42
- :param S: Signal (1-dim)
43
- :param max_pf: the max num of the PFs. Default log2(N)
44
- :param max_iter: max iteration for each iter of decomposing the PFs. Default 37, according the paper.
45
- :param eps: Envelope convergence threshold
46
-
47
- Returns:
48
- PFs: (n_pf, N), Res: (N,)
49
- """
50
- from scipy.signal import argrelextrema, hilbert, savgol_filter
51
-
52
- if S.ndim == 0:
53
- raise ValueError("The dim of the S must be 1-dim, not 0")
54
-
55
- elif S.ndim > 1:
56
- if 1 in S.shape:
57
- S = S.reshape(-1)
58
-
59
- else:
60
- raise ValueError(f"The dim of S must be 1-dim, not {S.ndim}")
61
-
62
- if not isinstance(S, np.ndarray):
63
- S = np.asarray(S, dtype=np.float64).ravel()
64
- n_samples = S.size
65
-
66
- if n_samples < 8:
67
- raise ValueError("LMD requires signal length ≥ 8 (Smith 2005 standard)")
68
-
69
- t = np.arange(n_samples, dtype=np.float64)
70
- max_pf = int(np.log2(n_samples)) if max_pf is None else max_pf
71
- residue = S.copy()
72
- PFs: list[np.ndarray] = []
73
-
74
- for _ in range(max_pf):
75
- h = residue.copy()
76
- a_total = np.ones(n_samples, dtype=np.float64)
77
- converged = False
78
-
79
- for __ in range(max_iter):
80
- max_loc = argrelextrema(h, np.greater)[0]
81
- min_loc = argrelextrema(h, np.less)[0]
82
- ext_idx = np.unique(np.concatenate([max_loc, min_loc]))
83
-
84
- if len(ext_idx) < 3:
85
- break
86
-
87
- ext_idx, ext_vals = _mirror_extend_real(h, ext_idx, n_samples)
88
- ext_idx = np.sort(ext_idx)
89
-
90
- t_mid = (ext_idx[:-1] + ext_idx[1:]) / 2.0
91
- m_vals = (ext_vals[:-1] + ext_vals[1:]) / 2.0
92
- a_vals = np.abs(ext_vals[:-1] - ext_vals[1:]) / 2.0
93
-
94
- m_t = _safe_interpolate(t_mid, m_vals, t)
95
- a_t = _safe_interpolate(t_mid, a_vals, t)
96
-
97
- a_t = np.clip(a_t, MIN_AMP, MAX_AMP)
98
- a_t = savgol_filter(a_t, SMOOTH_WINDOW, 2)
99
-
100
- if _check_convergence(m_t, a_t, eps):
101
- converged = True
102
- break
103
-
104
- s_new = (h - m_t) / a_t
105
- a_total = np.clip(a_total * a_t, MIN_AMP, MAX_AMP)
106
- h = s_new
107
-
108
- if not converged:
109
- break
110
-
111
- current_pf = np.clip(a_total * s_new, -MAX_AMP, MAX_AMP)
112
-
113
- if np.any(np.isnan(current_pf)) or np.any(np.isinf(current_pf)):
114
- break
115
-
116
- if np.sum(current_pf ** 2) < EPS_STABLE:
117
- break
118
-
119
- PFs.append(current_pf)
120
- residue -= current_pf
121
-
122
- if (is_increasing(residue) or is_increasing(-residue) or
123
- np.sum(residue ** 2) < EPS_STABLE or
124
- len(argrelextrema(residue, np.greater)[0]) + len(argrelextrema(residue, np.less)[0]) <= 2):
125
- break
126
-
127
- del h, a_total, t
128
- return np.array(PFs, dtype=np.float64), residue
129
-
130
- def _mirror_extend_real(signal: np.ndarray, ext_idx: np.ndarray, n_samples: int) -> Tuple[np.ndarray, np.ndarray]:
131
- ext = ext_idx.copy()
132
- vals = signal[ext].copy()
133
- last_idx = n_samples - 1
134
-
135
- if ext[0] > 0:
136
- mirror_pos = 0
137
- mirror_val = 2 * signal[0] - vals[0]
138
- ext = np.insert(ext, 0, mirror_pos)
139
- vals = np.insert(vals, 0, mirror_val)
140
-
141
- if ext[-1] < last_idx:
142
- mirror_pos = last_idx
143
- mirror_val = 2 * signal[-1] - vals[-1]
144
- ext = np.append(ext, mirror_pos)
145
- vals = np.append(vals, mirror_val)
146
-
147
- return ext, vals
148
-
149
- def _safe_interpolate(x: np.ndarray, y: np.ndarray, x_new: np.ndarray) -> np.ndarray:
150
- from scipy import interpolate
151
-
152
- try:
153
- interp = interpolate.CubicSpline(x, y, bc_type="natural")
154
- res = interp(x_new)
155
- except:
156
- res = np.interp(x_new, x, y)
157
-
158
- left_mask = x_new < x[0]
159
- right_mask = x_new > x[-1]
160
- if np.any(left_mask):
161
- slope = (y[1] - y[0]) / (x[1] - x[0])
162
- res[left_mask] = y[0] + slope * (x_new[left_mask] - x[0])
163
- if np.any(right_mask):
164
- slope = (y[-1] - y[-2]) / (x[-1] - x[-2])
165
- res[right_mask] = y[-1] + slope * (x_new[right_mask] - x[-1])
166
-
167
- return res
168
-
169
- def _check_convergence(m_t: np.ndarray, a_t: np.ndarray, eps: float) -> bool:
170
- mean_ok = np.max(np.abs(m_t)) < CONVERGE_MEAN
171
- envelope_ok = np.max(np.abs(a_t - 1.0)) < eps
172
- return mean_ok and envelope_ok
173
-
174
- def compute_envelope(signal: Union[list, np.ndarray]) -> np.ndarray:
175
- from scipy.linalg import hilbert
176
-
177
- signal = np.asarray(signal, dtype=np.float64).ravel()
178
- return np.abs(hilbert(signal))
@@ -1,64 +0,0 @@
1
- """
2
- Python version: (must)
3
- 3.10.11
4
-
5
- Lib and Version: (if None write None)
6
- numpy - 2.2.6
7
- numba - 0.64.0
8
-
9
- Only accessed by: (must)
10
- All(for modal decomposition more)
11
-
12
- Modify: (must)
13
- 2026.3.25
14
-
15
- Description: (if None write None)
16
- This file stored the helpful function which can be used in other modal decomposition methods.
17
-
18
- Modify:
19
- Optimize the use of numba, accelerate the speed of import
20
- """
21
-
22
- import numpy as np
23
- from typing import Literal, Callable
24
-
25
- def is_increasing_1(S) -> bool:
26
- diff = np.ediff1d(S)
27
- epsilon = 1e-8
28
- return np.all(diff > epsilon)
29
-
30
- def give_is_increasing_2() -> Callable:
31
- from numba import njit
32
-
33
- @njit
34
- def is_increasing_2(S, rtol=1e-8, atol=1e-8):
35
- for i in range(len(S)-1):
36
- diff = S[i+1] - S[i]
37
- if diff <= atol + rtol * abs(S[i]):
38
- return False
39
- return True
40
-
41
- return is_increasing_2
42
-
43
- def is_increasing(S, threshold=2, tolerance: Literal["high", "mid", "low"]="high") -> bool:
44
- def count_extrema(x):
45
- interior = x[1:-1]
46
- left = x[:-2]
47
- right = x[2:]
48
- maxima = (interior > left) & (interior > right)
49
- minima = (interior < left) & (interior < right)
50
- return np.sum(maxima) + np.sum(minima)
51
-
52
- if tolerance == "high":
53
- ans_3 = count_extrema(S) <= threshold
54
- return ans_3
55
-
56
- elif tolerance == "mid":
57
- diff = np.diff(S)
58
-
59
- sign_changes = np.diff(np.sign(diff))
60
-
61
- count = np.sum(np.abs(sign_changes) == 2)
62
-
63
- ans_1 = count <= threshold # one way to check
64
- return ans_1