Skip to content

Commit d7b016c

Browse files
1 parent 048a39f commit d7b016c

20 files changed

+1418
-831
lines changed

README.md

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,17 @@
11
# Synchrosqueezing in Python
2-
Synchrosqueezing Toolbox ported to Python, authored by Eugene Brevdo, Gaurav Thakur. Original: https://github.com/ebrevdo/synchrosqueezing
2+
[Synchrosqueezing Toolbox](https://github.com/ebrevdo/synchrosqueezing) ported to Python.
33

4-
**Reviewers needed**; the repository is in a development stage - details below.
4+
`ssqueezepy`'s come out of retirement; see [Releases](https://github.com/OverLordGoldDragon/ssqueezepy/releases). I've validated all main methods; the repo's now production-ready. The rest is a matter of extending.
5+
6+
Significant changes to some code structure are expected until v0.6.0, but whatever's not explicitly marked as problematic will work as intended. README will also change soon.
57

68
## Features
79
- Forward & inverse CWT- and STFT-based Synchrosqueezing
810
- Forward & inverse discretized Continuous Wavelet Transform (CWT)
911
- Forward & inverse discretized Short-Time Fourier Transform (STFT)
1012
- Phase CWT & STFT
13+
- More
1114

12-
## Reviewers needed
13-
An eventual goal is a merged Pull Request to PyWavelets ([relevant Issue](https://github.com/PyWavelets/pywt/issues/258)). Points of review include:
14-
1. **Correctness**; plots generated from CWT transforms resemble those in the publications, but not entirely
15-
2. **Completeness**; parts of code in the original Github are missing or incorrect, and I'm unable to replace them all
16-
3. **Unit tests**; I'm not familiar with Synchrosqueezing itself, so I don't know how to validate its various functionalities
17-
4. **Licensing**; unsure how to proceed here; [original's](https://github.com/ebrevdo/synchrosqueezing/blob/master/LICENSE) says to "Redistributions in binary form must reproduce the above copyright notice" - but I'm not "redistributing" it, I'm distributing my rewriting of it
18-
5. <s>**Code style**</s>; I'm aware PyWavelets conforms with PEP8 (but I don't), so I'll edit PR code accordingly
19-
20-
## Review To-do:
21-
22-
**Correctness**:
23-
- [ ] 1. Example 1
24-
- [ ] 2. Example 2
25-
26-
**Completeness**:
27-
- [ ] 1. `freqband` in `synsq_cwt_inv` and `synsq_stft_inf` is defaulted to an integer, but indexed into as a 2D array; the two have nearly identical docstrings, and reference the same equation, but the equation appears completely irrelevant to both.
28-
- [ ] 2. `quadgk` has been ported as quadpy's [`quad`](https://github.com/nschloe/quadpy/blob/master/quadpy/line_segment/_tools.py#L16) (linked its wrapped function), which does not support infinite integration bounds, and has [trouble](https://github.com/nschloe/quadpy/issues/236) with computing `synsq_stft_inv`'s integral. Needs a workaround.
29-
- [ ] 3. As seen in examples, the y-axis shows "scales", not frequencies - and the relation between the two is neither clear nor linear; it also isn't linear w.r.t. `len(t)`, `nv`, or `fs`. Publications show frequencies instead.
30-
31-
**Unit tests**: Whatever suffices for PyWavelets will suffice for me
32-
33-
## Implementation To-do:
34-
One checkmark = code written; two = reviewed
35-
36-
| Status | Toolbox name | Repository name | Repository file |
37-
| --- | --- | --- | --- |
38-
| [ ] [**x**] | [`synsq_cwt_fw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_cwt_fw.m) | `synsq_cwt_fwd` | [synsq_cwt.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/synsq_cwt.py) |
39-
| [ ] [**x**] | [`synsq_cwt_iw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_cwt_iw.m) | `synsq_cwt_inv` | [synsq_cwt.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/synsq_cwt.py) |
40-
| [ ] [**x**] | [`synsq_stft_fw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_stft_fw.m) | `synsq_stft_fwd` | [synsq_stft.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/synsq_stft.py) |
41-
| [ ] [**x**] | [`synsq_stft_iw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_stft_iw.m) | `synsq_stft_inv` | [synsq_stft.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/synsq_stft.py) |
42-
| [ ] [**x**] | [`synsq_squeeze`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_squeeze.m) | `synsq_squeeze` | [wavelet_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/wavelet_transforms.py) |
43-
| [ ] [**x**] | [`synsq_cwt_squeeze`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_cwt_squeeze.m) | `synsq_cwt_squeeze` | [wavelet_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/wavelet_transforms.py) |
44-
| [ ] [**x**] | [`phase_cwt`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/phase_cwt.m) | `phase_cwt` | [wavelet_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/wavelet_transforms.py) |
45-
| [ ] [**x**] | [`phase_cwt_num`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/phase_cwt_num.m) | `phase_cwt_num` | [wavelet_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/wavelet_transforms.py) |
46-
| [ ] [**x**] | [`cwt_fw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/cwt_fw.m) | `cwt_fwd` | [wavelet_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/wavelet_transforms.py) |
47-
| [ ] [ ] | [`cwt_iw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/cwt_iw.m) |
48-
| [ ] [**x**] | [`stft_fw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/stft_fw.m) | `stft_fwd` | [stft_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/stft_transforms.py) |
49-
| [ ] [**x**] | [`stft_iw`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/stft_iw.m) | `stft_inv` | [stft_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/stft_transforms.py) |
50-
| [ ] [**x**] | [`phase_stft`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/phase_stft.m) | `phase_stft` | [stft_transforms.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/stft_transforms.py) |
51-
| [ ] [**x**] | [`padsignal`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/padsignal.m) | `padsignal` | [utils.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/utils.py) |
52-
| [ ] [**x**] | [`wfiltfn`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/wfiltfn.m) | `wfiltfn` | [utils.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/utils.py) |
53-
| [ ] [ ] | [`wfilth`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/wfilth.m) |
54-
| [ ] [**x**] | [`synsq_adm`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/synsq_adm.m) | `synsq_adm` | [utils.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/utils.py) |
55-
| [ ] [**x**] | [`buffer`](https://www.mathworks.com/help/signal/ref/buffer.html) | `buffer` | [utils.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/utils.py) |
56-
| [**x**] [**x**] | [`est_riskshrink_thresh`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/est_riskshrink_thresh.m) | `est_riskshrink_thresh` | [utils.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/utils.py) |
57-
| [**x**] [**x**] | [`p2up`](https://github.com/ebrevdo/synchrosqueezing/blob/master/synchrosqueezing/p2up.m) | `p2up` | [utils.py](https://github.com/OverLordGoldDragon/synchrosqueezing_python/blob/master/synchrosqueezing/utils.py) |
58-
59-
There are more unlisted (see original repo), but not all will be implemented, in particular GUI implementations.
6015

6116
## Differences w.r.t. original
6217

@@ -68,33 +23,11 @@ There are more unlisted (see original repo), but not all will be implemented, in
6823
- **Edited docstrings**; filled missing info, & few corrections
6924
- **Moved functions**; each no longer has its own file, but is grouped with other relevant functions
7025
- **Code style**; grouped parts of code as sub-functions for improved readability; indentation for vertical alignment; other
71-
- **Performance**; this repo may work faster or slower, as Numpy arrays are faster than C arrays, but some of original funcs use MEX-optimized code with no Numpy equivalent. Also using dense instead of sparse matrices (see below).
72-
- **Performance**; this repo _will_ work **10x+ faster** for some of the methods which were vectorized out of for-loops
26+
- **Performance**; this repo will work faster and for less memory for some methods (to be documented)
7327

7428
**Other**:
7529
- Dense instead of sparse matrices for `stft_fwd` in [stft_transforms.py](https://github.com/OverLordGoldDragon/ssqueezepy/blob/master/synchrosqueezing/stft_transforms.py), as Numpy doesn't handle latter in ops involved
7630

7731

7832

79-
## Examples
80-
81-
See [examples.py](https://github.com/OverLordGoldDragon/ssqueezepy/blob/master/examples.py). Links to: [paper [1]](https://sci-hub.se/https://doi.org/10.1016/j.sigpro.2012.11.029), [paper[2]](https://arxiv.org/pdf/0912.2437.pdf). `_inv` methods (reconstruction, inversion) have been omitted as they involve `freqband`.
82-
83-
**EX 1:** Paper [1], pg. 1086
84-
85-
Only real components shown; imaginary are nearly identical, sometimes sign-opposite.
86-
87-
<img src="https://user-images.githubusercontent.com/16495490/73328411-176e5380-4273-11ea-8bc1-502dfd107444.png" width="600">
88-
<img src="https://user-images.githubusercontent.com/16495490/73328308-b777ad00-4272-11ea-9b7a-89cca0042d1e.png" width="600">
89-
<img src="https://user-images.githubusercontent.com/16495490/73328370-e9890f00-4272-11ea-9418-9ff741de66f8.png" width="600">
90-
<img src="https://user-images.githubusercontent.com/16495490/73328515-8350bc00-4273-11ea-883b-1b253d4cd603.png" width="550">
91-
92-
synsq-CWT (`synsq_cwt_fwd`) appears to produce strongest agreement with paper (FIG 4), while none of STFT yield any resemblance of anything in the papers. It's also unclear whether `synsq_squeeze` was used for "synsq" in FIG 4 instead.
93-
94-
**EX 2:** Paper [2], pg. 18
95-
96-
<img src="https://user-images.githubusercontent.com/16495490/73328745-518c2500-4274-11ea-8140-489ee2403118.png" width="600">
97-
<img src="https://user-images.githubusercontent.com/16495490/73328804-8a2bfe80-4274-11ea-8c04-6a3b29791e52.png" width="600">
98-
<img src="https://user-images.githubusercontent.com/16495490/73328894-d8410200-4274-11ea-9731-9537ea9588bf.png" width="550">
9933

100-
Similar situation as EX 1; again CWT has close resemblance, and STFT is in a separate reality. The two apparent discrepancies w/ CWT are: (1) slope of the forking incline, steeper in FIG. 3; (2) position of horizontal line, lower in FIG. 3. As for the black lines in FIG 3, they seem to be the (manual) "markings" mentioned under the figure in the paper.

examples.py

Lines changed: 0 additions & 111 deletions
This file was deleted.

examples/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Examples
2+
3+
## Exponential chirp
4+
5+
<img src='imgs/ridge_chirp.png'>

examples/imgs/ridge_chirp.png

998 KB
Loading

examples/ridge_chirp.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import numpy as np
2+
from numpy.fft import rfft
3+
4+
from ssqueezepy import ssq_cwt, issq_cwt
5+
from ssqueezepy.toolkit import lin_band, cos_f, mad_rms
6+
from ssqueezepy.viz_toolkit import imshow, plot, scat
7+
8+
#%%###########################################################################
9+
def echirp(N):
10+
t = np.linspace(0, 10, N, False)
11+
return np.cos(2 * np.pi * np.exp(t / 3)), t
12+
#%%## Configure signal #######################################################
13+
N = 2048
14+
noise_var = 6 # noise variance; compare error against = 10
15+
16+
x, ts = echirp(N)
17+
x *= (1 + .3 * cos_f([1], N)) # amplitude modulation
18+
xo = x.copy()
19+
np.random.seed(4)
20+
x += np.sqrt(noise_var) * np.random.randn(len(x))
21+
22+
#### Show signal & its global spectrum #######################################
23+
axf = np.abs(rfft(x))
24+
25+
plot(xo); scat(xo, s=8, show=1)
26+
plot(x); scat(x, s=8, show=1)
27+
plot(axf, show=1)
28+
#%%# Synchrosqueeze ##########################################################
29+
wavelet = ('morlet', {'mu': 4.5})
30+
Tx, fs, Wx, scales, w = ssq_cwt(x, wavelet, t=ts, nv=32, scales='log')
31+
#%%# Visualize ###############################################################
32+
pkw = dict(abs=1, w=.86, h=.9, aspect='auto', cmap='bone')
33+
_Tx = np.pad(Tx, [[4, 4]]) # improve display of top- & bottom-most freqs
34+
imshow(Wx, **pkw)
35+
imshow(np.flipud(_Tx), norm=(0, 2e-1), **pkw)
36+
#%%# Estimate inversion ridge ###############################################
37+
bw, slope, offset = .035, .45, .45
38+
Cs, freqband = lin_band(Tx, slope, offset, bw, norm=(0, 2e-1))
39+
#%%###########################################################################
40+
xrec = issq_cwt(Tx, wavelet, Cs, freqband)[0]
41+
plot(xo)
42+
plot(xrec, show=1)
43+
44+
axof = np.abs(rfft(xo))
45+
axrecf = np.abs(rfft(xrec))
46+
plot(axof)
47+
plot(axrecf, show=1)
48+
49+
print("signal MAD/RMS: %.6f" % mad_rms(xo, xrec))
50+
print("spectrum MAD/RMS: %.6f" % mad_rms(axof, axrecf))

ssqueezepy/__init__.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1-
from synsq_cwt import *
2-
from synsq_stft import *
3-
from wavelet_transforms import *
4-
from stft_transforms import *
5-
from utils import *
1+
from . import ssqueezing
2+
from . import _ssq_cwt
3+
from . import _cwt
4+
from . import synsq_stft
5+
from . import utils
6+
from . import stft_transforms
7+
from . import experimental
68

9+
from .ssqueezing import *
10+
from ._ssq_cwt import *
11+
from ._cwt import *
12+
from .synsq_stft import *
13+
from .stft_transforms import *
14+
from .utils import *
715

8-
__version__ = '0.80'
16+
17+
__version__ = '0.5.0rc1'

0 commit comments

Comments
 (0)