Modal-Decomposition 0.1.1__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.1 → modal_decomposition-0.1.3}/PKG-INFO +2 -26
  2. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/README.md +1 -25
  3. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/pyproject.toml +1 -1
  4. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEFD.py +5 -2
  5. modal_decomposition-0.1.3/src/Modal_Decomposition/EEMD.py +64 -0
  6. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EFD.py +8 -6
  7. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EMD.py +14 -5
  8. modal_decomposition-0.1.3/src/Modal_Decomposition/Error/RealizationError.py +2 -0
  9. modal_decomposition-0.1.3/src/Modal_Decomposition/LMD.py +243 -0
  10. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/MEMD.py +13 -11
  11. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/SSA.py +2 -2
  12. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/SVMD.py +9 -4
  13. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/__init__.py +32 -19
  14. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/PKG-INFO +2 -26
  15. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/SOURCES.txt +1 -1
  16. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/tests/test.py +2 -5
  17. modal_decomposition-0.1.1/src/Modal_Decomposition/EEMD.py +0 -130
  18. modal_decomposition-0.1.1/src/Modal_Decomposition/LMD.py +0 -178
  19. modal_decomposition-0.1.1/src/Modal_Decomposition/help_function.py +0 -64
  20. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/LICENSE +0 -0
  21. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/setup.cfg +0 -0
  22. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEMD.py +0 -0
  23. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/CEEMDAN.py +0 -0
  24. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/__init__.py +0 -0
  25. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/color_define.py +0 -0
  26. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/COLOR/colorful_print.py +0 -0
  27. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/EWT.py +0 -0
  28. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Error/CycleError.py +0 -0
  29. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/FMD.py +0 -0
  30. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/ICEEMDAN.py +0 -0
  31. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/RPSEMD.py +0 -0
  32. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/EnvironmentMemory.py +0 -0
  33. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/LazyImport.py +0 -0
  34. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/Monotonicity.py +0 -0
  35. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/NumpyNdarray_MemoryCalculator.py +0 -0
  36. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/OneDimArray.py +0 -0
  37. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/Utils/__init__.py +0 -0
  38. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition/VMD.py +0 -0
  39. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/dependency_links.txt +0 -0
  40. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/requires.txt +0 -0
  41. {modal_decomposition-0.1.1 → modal_decomposition-0.1.3}/src/Modal_Decomposition.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Modal-Decomposition
3
- Version: 0.1.1
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.1"
7
+ version = "0.1.3"
8
8
  requires-python = ">=3.8"
9
9
 
10
10
  authors = [
@@ -14,6 +14,7 @@ Description: (if None write None)
14
14
  Modify: (must)
15
15
  2026.3.25 - Create
16
16
  2026.3.30 - Desperate the CEEMDAN and the CEEFD, and rename the Cyclic_CEEFD as CEEFD, del the origin CEEFD.
17
+ 2026.4.7 - Fix the problem when the freq_bins include 0 will make the idx out of list. And change _extract_imf to staticmethod.
17
18
  """
18
19
 
19
20
  import numpy as np
@@ -44,13 +45,15 @@ class ceefd:
44
45
  envelope = np.maximum(envelope, mag_spectrum)
45
46
  return envelope
46
47
 
47
- def _extract_imf(self, signal, freq_bins):
48
+ @staticmethod
49
+ def _extract_imf(signal, freq_bins):
48
50
  N = len(signal)
49
51
  fft_signal = np.fft.fft(signal)
50
52
 
51
53
  mask = np.zeros(N, dtype=bool)
52
54
  mask[freq_bins] = True
53
- mask[N - np.array(freq_bins)] = True
55
+ # mask[N - np.array(freq_bins)] = True -> \ # 2026.4.7
56
+ mask[(N - np.array(freq_bins)) % N] = True
54
57
 
55
58
  imf_fft = fft_signal * mask
56
59
  imf = np.fft.ifft(imf_fft).real
@@ -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,11 +18,10 @@ 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
 
25
- def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind: str = "cubic", nbsym: int = 2, max_imf=-1, fs=None)\
24
+ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind: str = "cubic", nbsym: int = 2, max_imf=-1, verbose: bool=False)\
26
25
  -> Tuple[np.ndarray, np.ndarray]:
27
26
  """
28
27
  EMD: Empirical Mode Decomposition
@@ -32,7 +31,7 @@ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind
32
31
  :param spline_kind: the kind of spline. default cubic.
33
32
  :param nbsym:
34
33
  :param max_imf: the max num of IMFs
35
- :param fs: the f of T. default 1.
34
+ :param verbose: True will print info, else no.
36
35
  :return: IMFs (2-dim), Res (1-dim)
37
36
  """
38
37
  if not isinstance(S, np.ndarray):
@@ -52,7 +51,8 @@ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind
52
51
 
53
52
  if T is None: # if T is None, default generate uniform T-axis.
54
53
  T = np.arange(N) # default fs = 1
55
- print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
54
+ if verbose:
55
+ print(f"Warn: T is None,default T = [0, 1, 2, ..., {N - 1}]")
56
56
 
57
57
  else:
58
58
  if not isinstance(T, np.ndarray):
@@ -62,4 +62,13 @@ def emd(S: Union[list, np.ndarray], T: Union[list, np.ndarray]=None, spline_kind
62
62
 
63
63
  IMFs = EMD_cls.emd(S, T, max_imf=max_imf)
64
64
 
65
- return IMFs[:-1, :], IMFs[-1, :]
65
+ Res = IMFs[-1, :]
66
+ IMFs = IMFs[:-1, :]
67
+
68
+ if IMFs.ndim == 1:
69
+ IMFs = IMFs.reshape(1, -1)
70
+
71
+ elif IMFs.ndim == 0:
72
+ IMFs = np.zeros((1, Res.shape[1]))
73
+
74
+ return IMFs, Res
@@ -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