From 4edcc2ccc5dcbd7ab244a0c28736b6295e5fa9d7 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 26 May 2025 12:42:58 +0200 Subject: [PATCH 01/41] Enhance _cwt.py by introducing a configurable hop size parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generating a scalogram from the full output of the Continuous Wavelet Transform (CWT) entails high computational cost while providing limited performance gains in acoustic recognition models based on deep learning. Therefore, this update proposes reducing the output size during the intermediate processing stage—rather than after CWT generation—to improve computational efficiency of CWT. This pull request reflects the research findings presented in the following paper. Phan, D. T., Huynh, T. A., Pham, V. T., Tran, C. M., Mai, V. T., & Tran, N. Q. (2025). Optimal Scalogram for Computational Complexity Reduction in Acoustic Recognition Using Deep Learning. arXiv preprint arXiv:2505.13017. --- pywt/_cwt.py | 56 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 0d69095b..569bea26 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -14,17 +14,27 @@ import numpy as np - -def next_fast_len(n): - """Round up size to the nearest power of two. - - Given a number of samples `n`, returns the next power of two - following this number to take advantage of FFT speedup. - """ - return 2**ceil(np.log2(n)) - - -def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1): +try: + import scipy + fftmodule = scipy.fft + next_fast_len = fftmodule.next_fast_len +except ImportError: + fftmodule = np.fft + + # provide a fallback so scipy is an optional requirement + # note: numpy.fft in numpy 2.0 is as fast as scipy.fft, so could be used + # unconditionally once the minimum supported numpy version is >=2.0 + def next_fast_len(n): + """Round up size to the nearest power of two. + + Given a number of samples `n`, returns the next power of two + following this number to take advantage of FFT speedup. + This fallback is less efficient than `scipy.fftpack.next_fast_len` + """ + return 2**ceil(np.log2(n)) + + +def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', axis=-1): """ cwt(data, scales, wavelet) @@ -114,7 +124,12 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1): raise AxisError("axis must be a scalar.") dt_out = dt_cplx if wavelet.complex_cwt else dt - out = np.empty((np.size(scales),) + data.shape, dtype=dt_out) + + # out length of transform when applying down sampling + downsampled_length = len(data) // hop_size + data_sampled = np.empty((1, downsampled_length)) + out = np.empty((np.size(scales), downsampled_length), dtype=dt_out) + precision = 10 int_psi, x = integrate_wavelet(wavelet, precision=precision) int_psi = np.conj(int_psi) if wavelet.complex_cwt else int_psi @@ -167,17 +182,22 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1): ) if size_scale != size_scale0: # Must recompute fft_data when the padding size changes. - fft_data = np.fft.fft(data, size_scale, axis=-1) + fft_data = fftmodule.fft(data, size_scale, axis=-1) size_scale0 = size_scale - fft_wav = np.fft.fft(int_psi_scale, size_scale, axis=-1) - conv = np.fft.ifft(fft_wav * fft_data, axis=-1) + fft_wav = fftmodule.fft(int_psi_scale, size_scale, axis=-1) + conv = fftmodule.ifft(fft_wav * fft_data, axis=-1) conv = conv[..., :data.shape[-1] + int_psi_scale.size - 1] - coef = - np.sqrt(scale) * np.diff(conv, axis=-1) + coef_temp = - np.sqrt(scale) * np.diff(conv, axis=-1) + + # Apply time downsampling + coef = coef_temp[::hop_size] # Selecting every `hop_size`-th sample + if out.dtype.kind != 'c': coef = coef.real + # transform axis is always -1 due to the data reshape above - d = (coef.shape[-1] - data.shape[-1]) / 2. + d = (coef.shape[-1] - data_sampled.shape[-1]) / 2. if d > 0: coef = coef[..., floor(d):-ceil(d)] elif d < 0: @@ -185,7 +205,7 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1): f"Selected scale of {scale} too small.") if data.ndim > 1: # restore original data shape and axis position - coef = coef.reshape(data_shape_pre) + coef = coef.reshape(data_sampled) coef = coef.swapaxes(axis, -1) out[i, ...] = coef From 9a8bf1a249aa3b6fe0ef1040c3d38e7af3575b44 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 1 Jun 2025 12:39:11 +0200 Subject: [PATCH 02/41] Update document for hop_size in _cwt.py --- pywt/_cwt.py | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 569bea26..6b345192 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -14,29 +14,19 @@ import numpy as np -try: - import scipy - fftmodule = scipy.fft - next_fast_len = fftmodule.next_fast_len -except ImportError: - fftmodule = np.fft - - # provide a fallback so scipy is an optional requirement - # note: numpy.fft in numpy 2.0 is as fast as scipy.fft, so could be used - # unconditionally once the minimum supported numpy version is >=2.0 - def next_fast_len(n): - """Round up size to the nearest power of two. - - Given a number of samples `n`, returns the next power of two - following this number to take advantage of FFT speedup. - This fallback is less efficient than `scipy.fftpack.next_fast_len` - """ - return 2**ceil(np.log2(n)) + +def next_fast_len(n): + """Round up size to the nearest power of two. + + Given a number of samples `n`, returns the next power of two + following this number to take advantage of FFT speedup. + """ + return 2**ceil(np.log2(n)) def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', axis=-1): """ - cwt(data, scales, wavelet) + cwt(data, scales, wavelet, hop_size) One dimensional Continuous Wavelet Transform. @@ -51,6 +41,14 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax ``sampling_period`` is given in seconds. wavelet : Wavelet object or name Wavelet to use + hop_size : int + Specifies the down-sampling factor applied on temporal axis during the transform. + The output is sampled every hop_size samples, rather than at every consecutive sample. + For example: + A signal of length 1024 yields 1024 output samples when hop_size=1; + 512 output samples when hop_size=2; + 256 output samples when hop_size=4. + hop_size must be a positive integer (≥1). sampling_period : float Sampling period for the frequencies output (optional). The values computed for ``coefs`` are independent of the choice of @@ -83,7 +81,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax Notes ----- - Size of coefficients arrays depends on the length of the input array and + Size of coefficients arrays depends on the length of the input array, the given hop_size and the length of given scales. Examples @@ -93,7 +91,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax >>> import matplotlib.pyplot as plt >>> x = np.arange(512) >>> y = np.sin(2*np.pi*x/32) - >>> coef, freqs=pywt.cwt(y,np.arange(1,129),'gaus1') + >>> coef, freqs=pywt.cwt(y,np.arange(1,129),1, 'gaus1') >>> plt.matshow(coef) >>> plt.show() @@ -103,7 +101,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax >>> t = np.linspace(-1, 1, 200, endpoint=False) >>> sig = np.cos(2 * np.pi * 7 * t) + np.real(np.exp(-7*(t-0.4)**2)*np.exp(1j*2*np.pi*2*(t-0.4))) >>> widths = np.arange(1, 31) - >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh') + >>> cwtmatr, freqs = pywt.cwt(sig, widths,2, 'mexh') >>> plt.imshow(cwtmatr, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) >>> plt.show() From ad4152165b1d43620480729b426f69c5327d1823 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 1 Jun 2025 14:56:36 +0200 Subject: [PATCH 03/41] Update _cwt.py to fix downsampled_length error --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 6b345192..17eb412d 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -124,7 +124,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax dt_out = dt_cplx if wavelet.complex_cwt else dt # out length of transform when applying down sampling - downsampled_length = len(data) // hop_size + downsampled_length = int(len(data) // hop_size) data_sampled = np.empty((1, downsampled_length)) out = np.empty((np.size(scales), downsampled_length), dtype=dt_out) From 6b7d7f1bfe3f04fc8548823fb1bd91e1591883a5 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 1 Jun 2025 15:16:48 +0200 Subject: [PATCH 04/41] Update _cwt.py with hop_size as integer --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 17eb412d..ce58c2e1 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -189,7 +189,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax coef_temp = - np.sqrt(scale) * np.diff(conv, axis=-1) # Apply time downsampling - coef = coef_temp[::hop_size] # Selecting every `hop_size`-th sample + coef = coef_temp[::int(hop_size)] # Selecting every `hop_size`-th sample if out.dtype.kind != 'c': coef = coef.real From 3c9289b01c656c74739bd6c2ae0947ccb3012d62 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:01:43 +0200 Subject: [PATCH 05/41] Fix fft lib for _cwt --- pywt/_cwt.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index ce58c2e1..1b13e978 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -180,10 +180,10 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax ) if size_scale != size_scale0: # Must recompute fft_data when the padding size changes. - fft_data = fftmodule.fft(data, size_scale, axis=-1) + fft_data = np.fft.fft(data, size_scale, axis=-1) size_scale0 = size_scale - fft_wav = fftmodule.fft(int_psi_scale, size_scale, axis=-1) - conv = fftmodule.ifft(fft_wav * fft_data, axis=-1) + fft_wav = np.fft.fft(int_psi_scale, size_scale, axis=-1) + conv = np.fft.ifft(fft_wav * fft_data, axis=-1) conv = conv[..., :data.shape[-1] + int_psi_scale.size - 1] coef_temp = - np.sqrt(scale) * np.diff(conv, axis=-1) From 0a53e697bc1efdabc83c99000af00d1e516e9cb3 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:14:29 +0200 Subject: [PATCH 06/41] Validation for hop_size in _cwt.py --- pywt/_cwt.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 1b13e978..ce16cc2d 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -120,6 +120,9 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax if not np.isscalar(axis): raise AxisError("axis must be a scalar.") + # Ensure hop_size is a positive integer + if not isinstance(hop_size, int) or hop_size <= 0: + raise ValueError(f"Invalid hop_size: {hop_size}. It must be a positive integer.") dt_out = dt_cplx if wavelet.complex_cwt else dt From 6776f9883e9a686ab5405c5e6085e374ae60fb4b Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:38:48 +0200 Subject: [PATCH 07/41] Add hop_size to test cases of _cwt --- pywt/tests/test_cwt_wavelets.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index f142404c..9067ecc7 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -379,9 +379,10 @@ def test_cwt_complex(dtype, tol, method): dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) + hop_size = 1 # real-valued tranfsorm as a reference - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method) + [cfs, f] = pywt.cwt(sst, scales, wavelet, hop_size, dt, method=method) # verify same precision assert_equal(cfs.real.dtype, sst.dtype) @@ -389,7 +390,7 @@ def test_cwt_complex(dtype, tol, method): # complex-valued transform equals sum of the transforms of the real # and imaginary components sst_complex = sst + 1j*sst - [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, + [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, hop_size, dt, method=method) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved @@ -409,10 +410,10 @@ def test_cwt_batch(axis, method): scales = np.arange(1, 32) # non-batch transform as reference - [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis) + [cfs1, f] = pywt.cwt(sst1, scales, wavelet, hop_size, dt, method=method, axis=axis) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis) + [cfs, f] = pywt.cwt(sst, scales, wavelet, hop_size, dt, method=method, axis=axis) # shape of input is not modified assert_equal(shape_in, sst.shape) From 82a4ef01826ee1839648a35adba64b90aa1eebb1 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Thu, 5 Jun 2025 10:48:04 +0200 Subject: [PATCH 08/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 9067ecc7..5e959e38 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -408,6 +408,7 @@ def test_cwt_batch(axis, method): dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) + hop_size = 1 # non-batch transform as reference [cfs1, f] = pywt.cwt(sst1, scales, wavelet, hop_size, dt, method=method, axis=axis) From 81cd1bb55eb633c1bfbb1638d83c828454d23103 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:59:50 +0200 Subject: [PATCH 09/41] Fix shape of output if data in n-dim Update _cwt.py --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index ce16cc2d..5ee407e6 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -128,7 +128,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax # out length of transform when applying down sampling downsampled_length = int(len(data) // hop_size) - data_sampled = np.empty((1, downsampled_length)) + data_sampled = np.empty((data.shape[0], downsampled_length)) out = np.empty((np.size(scales), downsampled_length), dtype=dt_out) precision = 10 From dc93c83bbb4e98186f7b50a203acd263a7680e11 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:12:04 +0200 Subject: [PATCH 10/41] Update _cwt.py with reshape the coef --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 5ee407e6..8afaf002 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -206,7 +206,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax f"Selected scale of {scale} too small.") if data.ndim > 1: # restore original data shape and axis position - coef = coef.reshape(data_sampled) + coef = coef.reshape(data_sampled.shape) coef = coef.swapaxes(axis, -1) out[i, ...] = coef From a8a9aee9c425c3c042230e03b48d8f69036c6ed7 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:22:58 +0200 Subject: [PATCH 11/41] Update _cwt.py to fix coef shape --- pywt/_cwt.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 8afaf002..6dfd2e36 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -149,9 +149,11 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax if data.ndim > 1: # move axis to be transformed last (so it is contiguous) data = data.swapaxes(-1, axis) + data_sampled = data_sampled.swapaxes(-1, axis) # reshape to (n_batch, data.shape[-1]) data_shape_pre = data.shape + data_sampled_shape_pre = data_sampled.shape data = data.reshape((-1, data.shape[-1])) for i, scale in enumerate(scales): From f607aa03bdcfab2a71f938a1d42c0b9aa34055cd Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:32:43 +0200 Subject: [PATCH 12/41] Update _cwt.py for fixing coef size --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 6dfd2e36..89dc28a9 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -208,7 +208,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax f"Selected scale of {scale} too small.") if data.ndim > 1: # restore original data shape and axis position - coef = coef.reshape(data_sampled.shape) + coef = coef.reshape(data_sampled_shape_pre.shape) coef = coef.swapaxes(axis, -1) out[i, ...] = coef From 5cda18e0767061914a29a1fd8b356c66292c5db6 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:51:42 +0200 Subject: [PATCH 13/41] Update _cwt.py --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 89dc28a9..3d28a58e 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -208,7 +208,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax f"Selected scale of {scale} too small.") if data.ndim > 1: # restore original data shape and axis position - coef = coef.reshape(data_sampled_shape_pre.shape) + coef = coef.reshape(data_sampled_shape_pre) coef = coef.swapaxes(axis, -1) out[i, ...] = coef From 6f4e57a7cf8d162a184bb36d6677839d6b446e48 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:39:31 +0200 Subject: [PATCH 14/41] Update _cwt.py --- pywt/_cwt.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 3d28a58e..ff057df2 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -127,9 +127,11 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax dt_out = dt_cplx if wavelet.complex_cwt else dt # out length of transform when applying down sampling - downsampled_length = int(len(data) // hop_size) - data_sampled = np.empty((data.shape[0], downsampled_length)) - out = np.empty((np.size(scales), downsampled_length), dtype=dt_out) + # hop_size is only applied for 1D data + if data.ndim == 1: + downsampled_length = int(len(data) // hop_size) + data_sampled = np.empty((data.shape[0], downsampled_length)) + out = np.empty((np.size(scales), downsampled_length), dtype=dt_out) precision = 10 int_psi, x = integrate_wavelet(wavelet, precision=precision) @@ -149,12 +151,12 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax if data.ndim > 1: # move axis to be transformed last (so it is contiguous) data = data.swapaxes(-1, axis) - data_sampled = data_sampled.swapaxes(-1, axis) # reshape to (n_batch, data.shape[-1]) data_shape_pre = data.shape - data_sampled_shape_pre = data_sampled.shape data = data.reshape((-1, data.shape[-1])) + if data.ndim == 1: + data_sampled_shape_pre = data_sampled.shape for i, scale in enumerate(scales): step = x[1] - x[0] @@ -194,8 +196,10 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax coef_temp = - np.sqrt(scale) * np.diff(conv, axis=-1) # Apply time downsampling - coef = coef_temp[::int(hop_size)] # Selecting every `hop_size`-th sample - + if data.ndim == 1: + coef = coef_temp[::int(hop_size)] # Selecting every `hop_size`-th sample + if data.ndim > 1: + coef = coef_temp if out.dtype.kind != 'c': coef = coef.real @@ -208,8 +212,12 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax f"Selected scale of {scale} too small.") if data.ndim > 1: # restore original data shape and axis position - coef = coef.reshape(data_sampled_shape_pre) + coef = coef.reshape(data_shape_pre) coef = coef.swapaxes(axis, -1) + # if data.ndim == 1: + # # restore original data shape and axis position + # coef = coef.reshape(data_sampled_shape_pre) + # coef = coef.swapaxes(axis, -1) out[i, ...] = coef frequencies = scale2frequency(wavelet, scales, precision) From 099f895b88cf17952e005198e65eb9b3ccc46f49 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:46:37 +0200 Subject: [PATCH 15/41] Update _cwt.py with correct sampling --- pywt/_cwt.py | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index ff057df2..692740f1 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -91,7 +91,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax >>> import matplotlib.pyplot as plt >>> x = np.arange(512) >>> y = np.sin(2*np.pi*x/32) - >>> coef, freqs=pywt.cwt(y,np.arange(1,129),1, 'gaus1') + >>> coef, freqs=pywt.cwt(y,np.arange(1,129),1,'gaus1') >>> plt.matshow(coef) >>> plt.show() @@ -101,7 +101,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax >>> t = np.linspace(-1, 1, 200, endpoint=False) >>> sig = np.cos(2 * np.pi * 7 * t) + np.real(np.exp(-7*(t-0.4)**2)*np.exp(1j*2*np.pi*2*(t-0.4))) >>> widths = np.arange(1, 31) - >>> cwtmatr, freqs = pywt.cwt(sig, widths,2, 'mexh') + >>> cwtmatr, freqs = pywt.cwt(sig, widths, 2, 'mexh') >>> plt.imshow(cwtmatr, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) >>> plt.show() @@ -120,19 +120,10 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax if not np.isscalar(axis): raise AxisError("axis must be a scalar.") - # Ensure hop_size is a positive integer - if not isinstance(hop_size, int) or hop_size <= 0: - raise ValueError(f"Invalid hop_size: {hop_size}. It must be a positive integer.") dt_out = dt_cplx if wavelet.complex_cwt else dt - - # out length of transform when applying down sampling - # hop_size is only applied for 1D data - if data.ndim == 1: - downsampled_length = int(len(data) // hop_size) - data_sampled = np.empty((data.shape[0], downsampled_length)) - out = np.empty((np.size(scales), downsampled_length), dtype=dt_out) - + ata_sampled = data[..., ::hop_size] + out = np.empty((np.size(scales),) + data_sampled.shape, dtype=dt_out) precision = 10 int_psi, x = integrate_wavelet(wavelet, precision=precision) int_psi = np.conj(int_psi) if wavelet.complex_cwt else int_psi @@ -155,8 +146,6 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax # reshape to (n_batch, data.shape[-1]) data_shape_pre = data.shape data = data.reshape((-1, data.shape[-1])) - if data.ndim == 1: - data_sampled_shape_pre = data_sampled.shape for i, scale in enumerate(scales): step = x[1] - x[0] @@ -193,18 +182,11 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax conv = np.fft.ifft(fft_wav * fft_data, axis=-1) conv = conv[..., :data.shape[-1] + int_psi_scale.size - 1] - coef_temp = - np.sqrt(scale) * np.diff(conv, axis=-1) - - # Apply time downsampling - if data.ndim == 1: - coef = coef_temp[::int(hop_size)] # Selecting every `hop_size`-th sample - if data.ndim > 1: - coef = coef_temp + coef = - np.sqrt(scale) * np.diff(conv, axis=-1) if out.dtype.kind != 'c': coef = coef.real - # transform axis is always -1 due to the data reshape above - d = (coef.shape[-1] - data_sampled.shape[-1]) / 2. + d = (coef.shape[-1] - data.shape[-1]) / 2. if d > 0: coef = coef[..., floor(d):-ceil(d)] elif d < 0: @@ -214,11 +196,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax # restore original data shape and axis position coef = coef.reshape(data_shape_pre) coef = coef.swapaxes(axis, -1) - # if data.ndim == 1: - # # restore original data shape and axis position - # coef = coef.reshape(data_sampled_shape_pre) - # coef = coef.swapaxes(axis, -1) - out[i, ...] = coef + out[i, ...] = coef[..., ::hop_size] frequencies = scale2frequency(wavelet, scales, precision) if np.isscalar(frequencies): From 7282d9f47976ca880ed8c7be79c0d308fa6a6608 Mon Sep 17 00:00:00 2001 From: phandangthoai <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 16 Jun 2025 15:54:52 +0200 Subject: [PATCH 16/41] Update _cwt.py --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 692740f1..a38627f5 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -122,7 +122,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax raise AxisError("axis must be a scalar.") dt_out = dt_cplx if wavelet.complex_cwt else dt - ata_sampled = data[..., ::hop_size] + data_sampled = data[..., ::hop_size] out = np.empty((np.size(scales),) + data_sampled.shape, dtype=dt_out) precision = 10 int_psi, x = integrate_wavelet(wavelet, precision=precision) From 20838a775186095cff23ae35114a4aaacfe517c3 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sat, 19 Jul 2025 10:05:30 +0200 Subject: [PATCH 17/41] Update _cwt.py Move hop_size to the end --- pywt/_cwt.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index a38627f5..2fd7d779 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -24,9 +24,8 @@ def next_fast_len(n): return 2**ceil(np.log2(n)) -def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', axis=-1): +def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, hop_size=1): """ - cwt(data, scales, wavelet, hop_size) One dimensional Continuous Wavelet Transform. @@ -41,14 +40,6 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax ``sampling_period`` is given in seconds. wavelet : Wavelet object or name Wavelet to use - hop_size : int - Specifies the down-sampling factor applied on temporal axis during the transform. - The output is sampled every hop_size samples, rather than at every consecutive sample. - For example: - A signal of length 1024 yields 1024 output samples when hop_size=1; - 512 output samples when hop_size=2; - 256 output samples when hop_size=4. - hop_size must be a positive integer (≥1). sampling_period : float Sampling period for the frequencies output (optional). The values computed for ``coefs`` are independent of the choice of @@ -68,6 +59,14 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax axis: int, optional Axis over which to compute the CWT. If not given, the last axis is used. + hop_size : int + Specifies the down-sampling factor applied on temporal axis during the transform. + The output is sampled every hop size samples, rather than at every consecutive sample. + For example: + A signal of length 1024 yields 1024 output samples when ``hop_size=1``; + 512 output samples when ``hop_size=2``; + 256 output samples when ``hop_size=4``. + ``hop_size`` must be a positive integer (≥1). Returns ------- @@ -81,7 +80,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax Notes ----- - Size of coefficients arrays depends on the length of the input array, the given hop_size and + Size of coefficients arrays depends on the length of the input array, the given hop size and the length of given scales. Examples @@ -91,7 +90,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax >>> import matplotlib.pyplot as plt >>> x = np.arange(512) >>> y = np.sin(2*np.pi*x/32) - >>> coef, freqs=pywt.cwt(y,np.arange(1,129),1,'gaus1') + >>> coef, freqs=pywt.cwt(y,np.arange(1,129),'gaus1',hop_size=1) >>> plt.matshow(coef) >>> plt.show() @@ -101,7 +100,7 @@ def cwt(data, scales, wavelet, hop_size=1, sampling_period=1., method='conv', ax >>> t = np.linspace(-1, 1, 200, endpoint=False) >>> sig = np.cos(2 * np.pi * 7 * t) + np.real(np.exp(-7*(t-0.4)**2)*np.exp(1j*2*np.pi*2*(t-0.4))) >>> widths = np.arange(1, 31) - >>> cwtmatr, freqs = pywt.cwt(sig, widths, 2, 'mexh') + >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh', hop_size=2) >>> plt.imshow(cwtmatr, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) >>> plt.show() From d9867336cdf14b148890a3fb6bac4f44b50f9e2d Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sat, 19 Jul 2025 10:08:36 +0200 Subject: [PATCH 18/41] Update test_cwt_wavelets.py Move hop_size to the end --- pywt/tests/test_cwt_wavelets.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 5e959e38..0e0d7f7b 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -379,10 +379,9 @@ def test_cwt_complex(dtype, tol, method): dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) - hop_size = 1 # real-valued tranfsorm as a reference - [cfs, f] = pywt.cwt(sst, scales, wavelet, hop_size, dt, method=method) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, hop_size=1) # verify same precision assert_equal(cfs.real.dtype, sst.dtype) @@ -390,8 +389,8 @@ def test_cwt_complex(dtype, tol, method): # complex-valued transform equals sum of the transforms of the real # and imaginary components sst_complex = sst + 1j*sst - [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, hop_size, dt, - method=method) + [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, + method=method, hop_size=1) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved assert_equal(cfs_complex.dtype, sst_complex.dtype) @@ -408,13 +407,12 @@ def test_cwt_batch(axis, method): dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) - hop_size = 1 # non-batch transform as reference - [cfs1, f] = pywt.cwt(sst1, scales, wavelet, hop_size, dt, method=method, axis=axis) + [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=1) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, hop_size, dt, method=method, axis=axis) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=1) # shape of input is not modified assert_equal(shape_in, sst.shape) From 5eed6aac6f2bc11c68feb080601d1db276a3e5e1 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sat, 19 Jul 2025 10:37:51 +0200 Subject: [PATCH 19/41] Update _cwt.py Add References to the research paper. --- pywt/_cwt.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 2fd7d779..e240cb6e 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -82,6 +82,12 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho ----- Size of coefficients arrays depends on the length of the input array, the given hop size and the length of given scales. + + References + ---------- + .. [1] Phan, D. T., Huynh, T. A., Pham, V. T., Tran, C. M., Mai, V. T., & Tran, N. Q. (2025). + Optimal Scalogram for Computational Complexity Reduction in Acoustic Recognition Using Deep Learning. + *arXiv preprint arXiv:2505.13017*. https://arxiv.org/abs/2505.13017 Examples -------- From 8189d35f90ea25c6f6168b594d89bd6d0f7d64de Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sat, 19 Jul 2025 10:39:50 +0200 Subject: [PATCH 20/41] Update _cwt.py Revise the notes --- pywt/_cwt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index e240cb6e..ed69504a 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -80,8 +80,8 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho Notes ----- - Size of coefficients arrays depends on the length of the input array, the given hop size and - the length of given scales. + Size of coefficients arrays depends on the length of the input array, + the length of given scales and the given hop size. References ---------- From 85482d40ab2141f00ad040a4b91a9e1364af75f8 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Fri, 1 Aug 2025 14:37:05 +0200 Subject: [PATCH 21/41] Fix trailing whitespace to pass pre-commit hook --- pywt/_cwt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index ed69504a..41804443 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -85,8 +85,8 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho References ---------- - .. [1] Phan, D. T., Huynh, T. A., Pham, V. T., Tran, C. M., Mai, V. T., & Tran, N. Q. (2025). - Optimal Scalogram for Computational Complexity Reduction in Acoustic Recognition Using Deep Learning. + .. [1] Phan, D. T., Huynh, T. A., Pham, V. T., Tran, C. M., Mai, V. T., & Tran, N. Q. (2025). + Optimal Scalogram for Computational Complexity Reduction in Acoustic Recognition Using Deep Learning. *arXiv preprint arXiv:2505.13017*. https://arxiv.org/abs/2505.13017 Examples From bd5134bf7996f7ce632884d905703e0174290683 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:17:40 +0200 Subject: [PATCH 22/41] Update _cwt.py Fix trailing whitespaces error --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 41804443..322b8dc8 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -82,7 +82,7 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho ----- Size of coefficients arrays depends on the length of the input array, the length of given scales and the given hop size. - + References ---------- .. [1] Phan, D. T., Huynh, T. A., Pham, V. T., Tran, C. M., Mai, V. T., & Tran, N. Q. (2025). From ce8786266ea35db256f233fa664587be9c4063a0 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:26:45 +0200 Subject: [PATCH 23/41] Update test_cwt_wavelets.py Test for hop_size greater than 1. --- pywt/tests/test_cwt_wavelets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 0e0d7f7b..85ed0859 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -390,7 +390,7 @@ def test_cwt_complex(dtype, tol, method): # and imaginary components sst_complex = sst + 1j*sst [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, - method=method, hop_size=1) + method=method, hop_size=128) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved assert_equal(cfs_complex.dtype, sst_complex.dtype) @@ -412,7 +412,7 @@ def test_cwt_batch(axis, method): [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=1) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=1) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=128) # shape of input is not modified assert_equal(shape_in, sst.shape) From fc90fecf7a6b5789f1601c47e4d0febfc28898d2 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 12:52:12 +0200 Subject: [PATCH 24/41] Update _cwt.py Resolve conflicts when merging to main --- pywt/_cwt.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 322b8dc8..d867e03b 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -24,7 +24,7 @@ def next_fast_len(n): return 2**ceil(np.log2(n)) -def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, hop_size=1): +def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, precision=12, *, hop_size=1): """ One dimensional Continuous Wavelet Transform. @@ -59,6 +59,12 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho axis: int, optional Axis over which to compute the CWT. If not given, the last axis is used. + precision: int, optional + Length of wavelet (``2 ** precision``) used to compute the CWT. Greater + will increase resolution, especially for higher scales, but will + compute a bit slower. Too low will distort coefficients and their + norms, with a zipper-like effect. The default is 12, it's recommended + to use >=12. hop_size : int Specifies the down-sampling factor applied on temporal axis during the transform. The output is sampled every hop size samples, rather than at every consecutive sample. @@ -94,9 +100,10 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho >>> import pywt >>> import numpy as np >>> import matplotlib.pyplot as plt - >>> x = np.arange(512) - >>> y = np.sin(2*np.pi*x/32) - >>> coef, freqs=pywt.cwt(y,np.arange(1,129),'gaus1',hop_size=1) + >>> x = np.exp(np.linspace(0, 2, 512)) + >>> y = np.cos(2*np.pi*x) # exponential chirp + >>> scales = np.logspace(np.log10(1), np.log10(128), 128) + >>> coef, freqs = pywt.cwt(y, scales, 'gaus1', hop_size=16) >>> plt.matshow(coef) >>> plt.show() @@ -105,8 +112,8 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho >>> import matplotlib.pyplot as plt >>> t = np.linspace(-1, 1, 200, endpoint=False) >>> sig = np.cos(2 * np.pi * 7 * t) + np.real(np.exp(-7*(t-0.4)**2)*np.exp(1j*2*np.pi*2*(t-0.4))) - >>> widths = np.arange(1, 31) - >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh', hop_size=2) + >>> widths = np.logspace(np.log10(1), np.log10(30), 30) + >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh', , hop_size=128) >>> plt.imshow(cwtmatr, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) >>> plt.show() @@ -129,7 +136,6 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, ho dt_out = dt_cplx if wavelet.complex_cwt else dt data_sampled = data[..., ::hop_size] out = np.empty((np.size(scales),) + data_sampled.shape, dtype=dt_out) - precision = 10 int_psi, x = integrate_wavelet(wavelet, precision=precision) int_psi = np.conj(int_psi) if wavelet.complex_cwt else int_psi From 84c3fcbb847cbf4964afe62f69086e568881eadf Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 18:59:22 +0200 Subject: [PATCH 25/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 85ed0859..0e0d7f7b 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -390,7 +390,7 @@ def test_cwt_complex(dtype, tol, method): # and imaginary components sst_complex = sst + 1j*sst [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, - method=method, hop_size=128) + method=method, hop_size=1) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved assert_equal(cfs_complex.dtype, sst_complex.dtype) @@ -412,7 +412,7 @@ def test_cwt_batch(axis, method): [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=1) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=128) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=1) # shape of input is not modified assert_equal(shape_in, sst.shape) From f4231a1cef50455237b837be21e9f4287dfe227e Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 19:36:29 +0200 Subject: [PATCH 26/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 0e0d7f7b..20165891 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -390,7 +390,7 @@ def test_cwt_complex(dtype, tol, method): # and imaginary components sst_complex = sst + 1j*sst [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, - method=method, hop_size=1) + method=method, hop_size=2) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved assert_equal(cfs_complex.dtype, sst_complex.dtype) @@ -412,7 +412,7 @@ def test_cwt_batch(axis, method): [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=1) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=1) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=2) # shape of input is not modified assert_equal(shape_in, sst.shape) From fe2a9f3079493094d809b1f8d4173400d8d77187 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 20:18:54 +0200 Subject: [PATCH 27/41] Update test_cwt_wavelets.py Test for hop size greater than 1 --- pywt/tests/test_cwt_wavelets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 20165891..f3a94d1f 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -381,7 +381,7 @@ def test_cwt_complex(dtype, tol, method): scales = np.arange(1, 32) # real-valued tranfsorm as a reference - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, hop_size=1) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, hop_size=128) # verify same precision assert_equal(cfs.real.dtype, sst.dtype) @@ -390,7 +390,7 @@ def test_cwt_complex(dtype, tol, method): # and imaginary components sst_complex = sst + 1j*sst [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, - method=method, hop_size=2) + method=method, hop_size=128) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved assert_equal(cfs_complex.dtype, sst_complex.dtype) @@ -409,10 +409,10 @@ def test_cwt_batch(axis, method): scales = np.arange(1, 32) # non-batch transform as reference - [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=1) + [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=128) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=2) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=128) # shape of input is not modified assert_equal(shape_in, sst.shape) From fef659381c8ccea01d30641a82b65900061adfda Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:07:32 +0200 Subject: [PATCH 28/41] Update test_cwt_wavelets.py Test for hop size greater than 1 --- pywt/tests/test_cwt_wavelets.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index f3a94d1f..550a27cb 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -379,9 +379,10 @@ def test_cwt_complex(dtype, tol, method): dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) + hop_size = 128 # real-valued tranfsorm as a reference - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, hop_size=128) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, hop_size=hop_size) # verify same precision assert_equal(cfs.real.dtype, sst.dtype) @@ -390,7 +391,7 @@ def test_cwt_complex(dtype, tol, method): # and imaginary components sst_complex = sst + 1j*sst [cfs_complex, f] = pywt.cwt(sst_complex, scales, wavelet, dt, - method=method, hop_size=128) + method=method, hop_size=hop_size) assert_allclose(cfs + 1j*cfs, cfs_complex, atol=tol, rtol=tol) # verify dtype is preserved assert_equal(cfs_complex.dtype, sst_complex.dtype) @@ -401,6 +402,7 @@ def test_cwt_batch(axis, method): dtype = np.float64 time, sst = pywt.data.nino() n_batch = 8 + hop_size = 128 batch_axis = 1 - axis sst1 = np.asarray(sst, dtype=dtype) sst = np.stack((sst1, ) * n_batch, axis=batch_axis) @@ -409,10 +411,10 @@ def test_cwt_batch(axis, method): scales = np.arange(1, 32) # non-batch transform as reference - [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=128) + [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=hop_size) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=128) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=hop_size) # shape of input is not modified assert_equal(shape_in, sst.shape) @@ -422,7 +424,7 @@ def test_cwt_batch(axis, method): # verify expected shape assert_equal(cfs.shape[0], len(scales)) - assert_equal(cfs.shape[1 + batch_axis], n_batch) + assert_equal(cfs.shape[1 + batch_axis], math.ceil(n_batch / hop_size)) assert_equal(cfs.shape[1 + axis], sst.shape[axis]) # batch result on stacked input is the same as stacked 1d result From f5640aed950ce10f410198920c11a22331ddfa7b Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:27:56 +0200 Subject: [PATCH 29/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 550a27cb..c7b56810 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -2,6 +2,7 @@ import os import pickle from itertools import product +import math import numpy as np import pytest From 368f272fe946a0cdd29437f7e41594b05f6775ce Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 21:33:08 +0200 Subject: [PATCH 30/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index c7b56810..a1435948 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -2,7 +2,6 @@ import os import pickle from itertools import product -import math import numpy as np import pytest @@ -425,7 +424,7 @@ def test_cwt_batch(axis, method): # verify expected shape assert_equal(cfs.shape[0], len(scales)) - assert_equal(cfs.shape[1 + batch_axis], math.ceil(n_batch / hop_size)) + assert_equal(cfs.shape[1 + batch_axis], np.ceil(n_batch / hop_size).astype(int)) assert_equal(cfs.shape[1 + axis], sst.shape[axis]) # batch result on stacked input is the same as stacked 1d result From 9c2b4147ce3d139c7a4b66da5d28bf199cdd9043 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:01:56 +0200 Subject: [PATCH 31/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index a1435948..e1f52a74 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -428,7 +428,7 @@ def test_cwt_batch(axis, method): assert_equal(cfs.shape[1 + axis], sst.shape[axis]) # batch result on stacked input is the same as stacked 1d result - assert_almost_equal(cfs, np.stack((cfs1,) * n_batch, axis=batch_axis + 1), + assert_almost_equal(cfs, np.stack((cfs1,) * np.ceil(n_batch / hop_size).astype(int), axis=batch_axis + 1), decimal=12) From 00fb8d49ed27d985ee6dc765b203ec148406a0a2 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:22:27 +0200 Subject: [PATCH 32/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index e1f52a74..99eec464 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -405,7 +405,7 @@ def test_cwt_batch(axis, method): hop_size = 128 batch_axis = 1 - axis sst1 = np.asarray(sst, dtype=dtype) - sst = np.stack((sst1, ) * n_batch, axis=batch_axis) + sst = np.stack((sst1, ) * np.ceil(n_batch / hop_size).astype(int), axis=batch_axis) dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) From c7da22799235a596f496d154c2832103a872a459 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:38:46 +0200 Subject: [PATCH 33/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 99eec464..7a238092 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -428,7 +428,7 @@ def test_cwt_batch(axis, method): assert_equal(cfs.shape[1 + axis], sst.shape[axis]) # batch result on stacked input is the same as stacked 1d result - assert_almost_equal(cfs, np.stack((cfs1,) * np.ceil(n_batch / hop_size).astype(int), axis=batch_axis + 1), + assert_almost_equal(cfs, np.stack((cfs1,) * np.ceil(n_batch / hop_size).astype(int), axis=np.ceil(batch_axis / hop_size).astype(int) + 1), decimal=12) From 79acb20226874f6b6b125a6d2a5c137cfed742fe Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 4 Aug 2025 22:54:39 +0200 Subject: [PATCH 34/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 7a238092..97788595 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -402,10 +402,10 @@ def test_cwt_batch(axis, method): dtype = np.float64 time, sst = pywt.data.nino() n_batch = 8 - hop_size = 128 + hop_size = 1 batch_axis = 1 - axis sst1 = np.asarray(sst, dtype=dtype) - sst = np.stack((sst1, ) * np.ceil(n_batch / hop_size).astype(int), axis=batch_axis) + sst = np.stack((sst1, ) * np.ceil(n_batch / hop_size).astype(int), axis=np.ceil(batch_axis / hop_size).astype(int)) dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) From 8996c80309b9c1d110dfaa7d2ef66c7138ba621f Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Tue, 5 Aug 2025 08:54:29 +0200 Subject: [PATCH 35/41] Update test_cwt_wavelets.py --- pywt/tests/test_cwt_wavelets.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 97788595..f0ad556e 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -405,7 +405,7 @@ def test_cwt_batch(axis, method): hop_size = 1 batch_axis = 1 - axis sst1 = np.asarray(sst, dtype=dtype) - sst = np.stack((sst1, ) * np.ceil(n_batch / hop_size).astype(int), axis=np.ceil(batch_axis / hop_size).astype(int)) + sst = np.stack((sst1, ) * n_batch, axis=batch_axis) dt = time[1] - time[0] wavelet = 'cmor1.5-1.0' scales = np.arange(1, 32) @@ -424,11 +424,11 @@ def test_cwt_batch(axis, method): # verify expected shape assert_equal(cfs.shape[0], len(scales)) - assert_equal(cfs.shape[1 + batch_axis], np.ceil(n_batch / hop_size).astype(int)) + assert_equal(cfs.shape[1 + batch_axis], n_batch) assert_equal(cfs.shape[1 + axis], sst.shape[axis]) # batch result on stacked input is the same as stacked 1d result - assert_almost_equal(cfs, np.stack((cfs1,) * np.ceil(n_batch / hop_size).astype(int), axis=np.ceil(batch_axis / hop_size).astype(int) + 1), + assert_almost_equal(cfs, np.stack((cfs1,) * n_batch, axis=batch_axis + 1), decimal=12) From 746d5690d5bc9fbbcf34dda2db12af3da17c5eca Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:45:22 +0200 Subject: [PATCH 36/41] Update _cwt.py Fix doctest error --- pywt/_cwt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 7facec1f..272c12ae 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -114,7 +114,7 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, preci >>> t = np.linspace(-1, 1, 200, endpoint=False) >>> sig = np.cos(2 * np.pi * 7 * t) + np.real(np.exp(-7*(t-0.4)**2)*np.exp(1j*2*np.pi*2*(t-0.4))) >>> widths = np.logspace(np.log10(1), np.log10(30), 30) - >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh', , hop_size=128) + >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh', hop_size=128) >>> plt.imshow(cwtmatr, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) >>> plt.show() From 0f0ffc6a2a496980af5005d83b071431d4f5bd5e Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Mon, 1 Sep 2025 12:03:10 +0200 Subject: [PATCH 37/41] Update _cwt.py --- pywt/_cwt.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 272c12ae..8e6cfe75 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -24,8 +24,7 @@ def next_fast_len(n): return 2**ceil(np.log2(n)) -def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, precision=12, - *, hop_size=1): +def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, precision=12, hop_size=1): """ One dimensional Continuous Wavelet Transform. @@ -66,7 +65,7 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, preci compute a bit slower. Too low will distort coefficients and their norms, with a zipper-like effect. The default is 12, it's recommended to use >=12. - hop_size : int + hop_size : int, optional Specifies the down-sampling factor applied on temporal axis during the transform. The output is sampled every hop size samples, rather than at every consecutive sample. For example: From e88ed4be079a8476d395e88615f42d5b4b0a8f44 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:43:28 +0200 Subject: [PATCH 38/41] Update _cwt.py Update examples --- pywt/_cwt.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pywt/_cwt.py b/pywt/_cwt.py index 8e6cfe75..efe5a2e4 100644 --- a/pywt/_cwt.py +++ b/pywt/_cwt.py @@ -117,6 +117,29 @@ def cwt(data, scales, wavelet, sampling_period=1., method='conv', axis=-1, *, pr >>> plt.imshow(cwtmatr, extent=[-1, 1, 1, 31], cmap='PRGn', aspect='auto', ... vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max()) >>> plt.show() + + >>> import pywt + >>> import numpy as np + >>> import matplotlib.pyplot as plt + >>> t = np.linspace(-1, 1, 200, endpoint=False) + >>> sig = np.cos(2 * np.pi * 7 * t) + np.real(np.exp(-7*(t-0.4)**2)*np.exp(1j*2*np.pi*2*(t-0.4))) + >>> widths = np.logspace(np.log10(1), np.log10(30), 30) + >>> cwtmatr, freqs = pywt.cwt(sig, widths, 'mexh') + >>> hop_size = 16 + >>> cwtmatr_h, freqs_h = pywt.cwt(sig, widths, 'mexh', hop_size=hop_size) + >>> fig, axes = plt.subplots(1, 2, figsize=(12, 5), sharey=True) + >>> im0 = axes[0].imshow( + cwtmatr, extent=[t[0], t[-1], widths[-1], widths[0]], + cmap='PRGn', aspect='auto', + vmax=abs(cwtmatr).max(), vmin=-abs(cwtmatr).max() + ) + >>> axes[0].set_title("Original scalogram") + >>> im1 = axes[1].imshow( + cwtmatr_h, extent=[t[0], t[-1], widths[-1], widths[0]], + cmap='PRGn', aspect='auto', + vmax=abs(cwtmatr_h).max(), vmin=-abs(cwtmatr_h).max() + ) + >>> axes[1].set_title("Scalogram with hop size") """ # accept array_like input; make a copy to ensure a contiguous array From 82bda67fc8947eadd5fc532417cfee0835597f3d Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:45:32 +0200 Subject: [PATCH 39/41] Update test_cwt_wavelets.py Revert back to default setting --- pywt/tests/test_cwt_wavelets.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index f0ad556e..4487b893 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -402,7 +402,6 @@ def test_cwt_batch(axis, method): dtype = np.float64 time, sst = pywt.data.nino() n_batch = 8 - hop_size = 1 batch_axis = 1 - axis sst1 = np.asarray(sst, dtype=dtype) sst = np.stack((sst1, ) * n_batch, axis=batch_axis) From 4a8d3c2084b989eb7125afc9f8e8d16d72d33a60 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 14 Sep 2025 12:50:07 +0200 Subject: [PATCH 40/41] Update test_cwt_wavelets.py default setting --- pywt/tests/test_cwt_wavelets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 4487b893..078047c1 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -410,10 +410,10 @@ def test_cwt_batch(axis, method): scales = np.arange(1, 32) # non-batch transform as reference - [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis, hop_size=hop_size) + [cfs1, f] = pywt.cwt(sst1, scales, wavelet, dt, method=method, axis=axis) shape_in = sst.shape - [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis, hop_size=hop_size) + [cfs, f] = pywt.cwt(sst, scales, wavelet, dt, method=method, axis=axis) # shape of input is not modified assert_equal(shape_in, sst.shape) From 78e87638a75c20ba832a8b24e17b47de31161288 Mon Sep 17 00:00:00 2001 From: Dang Thoai Phan <82931854+phandangthoai@users.noreply.github.com> Date: Sun, 14 Sep 2025 13:13:13 +0200 Subject: [PATCH 41/41] Update test_cwt_wavelets.py verify number of time steps reduced by hop_size --- pywt/tests/test_cwt_wavelets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pywt/tests/test_cwt_wavelets.py b/pywt/tests/test_cwt_wavelets.py index 078047c1..5e158048 100644 --- a/pywt/tests/test_cwt_wavelets.py +++ b/pywt/tests/test_cwt_wavelets.py @@ -387,6 +387,10 @@ def test_cwt_complex(dtype, tol, method): # verify same precision assert_equal(cfs.real.dtype, sst.dtype) + # verify number of time steps reduced by hop_size + expected_time_len = int(np.ceil(len(sst) / hop_size)) + assert_equal(cfs.shape[1], expected_time_len) + # complex-valued transform equals sum of the transforms of the real # and imaginary components sst_complex = sst + 1j*sst