pywavelet 0.0.1b0__py3-none-any.whl → 0.1.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- pywavelet/__init__.py +1 -1
- pywavelet/_version.py +2 -2
- pywavelet/logger.py +6 -7
- pywavelet/transforms/__init__.py +10 -10
- pywavelet/transforms/forward/__init__.py +4 -0
- pywavelet/transforms/forward/from_freq.py +80 -0
- pywavelet/transforms/forward/from_time.py +66 -0
- pywavelet/transforms/forward/main.py +128 -0
- pywavelet/transforms/forward/wavelet_bins.py +58 -0
- pywavelet/transforms/inverse/__init__.py +3 -0
- pywavelet/transforms/inverse/main.py +96 -0
- pywavelet/transforms/{from_wavelets/inverse_wavelet_freq_funcs.py → inverse/to_freq.py} +43 -32
- pywavelet/transforms/{from_wavelets/inverse_wavelet_time_funcs.py → inverse/to_time.py} +49 -21
- pywavelet/transforms/phi_computer.py +152 -0
- pywavelet/transforms/types/__init__.py +4 -0
- pywavelet/transforms/types/common.py +53 -0
- pywavelet/transforms/types/frequencyseries.py +237 -0
- pywavelet/transforms/types/plotting.py +341 -0
- pywavelet/transforms/types/timeseries.py +280 -0
- pywavelet/transforms/types/wavelet.py +374 -0
- pywavelet/transforms/types/wavelet_mask.py +34 -0
- pywavelet/utils.py +76 -0
- pywavelet-0.1.0.dist-info/METADATA +35 -0
- pywavelet-0.1.0.dist-info/RECORD +26 -0
- {pywavelet-0.0.1b0.dist-info → pywavelet-0.1.0.dist-info}/WHEEL +1 -1
- pywavelet/fft_funcs.py +0 -16
- pywavelet/likelihood/__init__.py +0 -0
- pywavelet/likelihood/likelihood_base.py +0 -9
- pywavelet/likelihood/whittle.py +0 -24
- pywavelet/transforms/common.py +0 -77
- pywavelet/transforms/from_wavelets/__init__.py +0 -25
- pywavelet/transforms/to_wavelets/__init__.py +0 -52
- pywavelet/transforms/to_wavelets/transform_freq_funcs.py +0 -84
- pywavelet/transforms/to_wavelets/transform_time_funcs.py +0 -63
- pywavelet/utils/__init__.py +0 -0
- pywavelet/utils/fisher_matrix.py +0 -6
- pywavelet/utils/snr.py +0 -37
- pywavelet/waveform_generator/__init__.py +0 -0
- pywavelet/waveform_generator/build_lookup_table.py +0 -0
- pywavelet/waveform_generator/generators/__init__.py +0 -2
- pywavelet/waveform_generator/generators/functional_waveform_generator.py +0 -33
- pywavelet/waveform_generator/generators/lookuptable_waveform_generator.py +0 -15
- pywavelet/waveform_generator/generators/rom_waveform_generator.py +0 -0
- pywavelet/waveform_generator/waveform_generator.py +0 -14
- pywavelet-0.0.1b0.dist-info/METADATA +0 -35
- pywavelet-0.0.1b0.dist-info/RECORD +0 -29
- {pywavelet-0.0.1b0.dist-info → pywavelet-0.1.0.dist-info}/top_level.txt +0 -0
pywavelet/__init__.py
CHANGED
pywavelet/_version.py
CHANGED
pywavelet/logger.py
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
import sys
|
2
2
|
import warnings
|
3
|
+
from rich.logging import RichHandler
|
4
|
+
import logging
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
sys.stderr,
|
8
|
-
format="|<blue>pywavelet</>|{time:DD/MM HH:mm:ss}|{level}| {message} ",
|
9
|
-
colorize=True,
|
10
|
-
level="INFO",
|
6
|
+
FORMAT = "%(message)s"
|
7
|
+
logging.basicConfig(
|
8
|
+
level="INFO", format=FORMAT, datefmt="[%X]", handlers=[RichHandler(rich_tracebacks=True)]
|
11
9
|
)
|
12
10
|
|
11
|
+
logger = logging.getLogger("pywavelet")
|
13
12
|
|
14
13
|
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
15
14
|
warnings.filterwarnings("ignore", category=UserWarning)
|
pywavelet/transforms/__init__.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
from .
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
from .forward import from_freq_to_wavelet, from_time_to_wavelet, compute_bins
|
2
|
+
from .inverse import from_wavelet_to_freq, from_wavelet_to_time
|
3
|
+
|
4
|
+
__all__ = [
|
5
|
+
"from_wavelet_to_time",
|
6
|
+
"from_wavelet_to_freq",
|
7
|
+
"from_time_to_wavelet",
|
8
|
+
"from_freq_to_wavelet",
|
9
|
+
"compute_bins",
|
10
|
+
]
|
@@ -0,0 +1,80 @@
|
|
1
|
+
"""helper functions for transform_freq"""
|
2
|
+
import numpy as np
|
3
|
+
from numba import njit
|
4
|
+
from numpy import fft
|
5
|
+
|
6
|
+
def transform_wavelet_freq_helper(
|
7
|
+
data: np.ndarray, Nf: int, Nt: int, phif: np.ndarray
|
8
|
+
) -> np.ndarray:
|
9
|
+
"""helper to do the wavelet transform using the fast wavelet domain transform"""
|
10
|
+
wave = np.zeros((Nt, Nf)) # wavelet wavepacket transform of the signal
|
11
|
+
DX = np.zeros(Nt, dtype=np.complex128)
|
12
|
+
freq_strain = data.copy() # Convert
|
13
|
+
__core(Nf, Nt, DX, freq_strain, phif, wave)
|
14
|
+
return wave
|
15
|
+
|
16
|
+
# @njit()
|
17
|
+
def __core(Nf:int, Nt:int, DX:np.ndarray, freq_strain:np.ndarray, phif:np.ndarray, wave:np.ndarray):
|
18
|
+
for f_bin in range(0, Nf + 1):
|
19
|
+
__fill_wave_1(f_bin, Nt, Nf, DX, freq_strain, phif)
|
20
|
+
# Numba doesn't support np.ifft so we cant jit this
|
21
|
+
DX_trans = np.fft.ifft(DX, Nt)
|
22
|
+
__fill_wave_2(f_bin, DX_trans, wave, Nt, Nf)
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
@njit()
|
28
|
+
def __fill_wave_1(
|
29
|
+
f_bin: int,
|
30
|
+
Nt: int,
|
31
|
+
Nf: int,
|
32
|
+
DX: np.ndarray,
|
33
|
+
data: np.ndarray,
|
34
|
+
phif: np.ndarray,
|
35
|
+
) -> None:
|
36
|
+
"""helper for assigning DX in the main loop"""
|
37
|
+
i_base = Nt // 2
|
38
|
+
jj_base = f_bin * Nt // 2
|
39
|
+
|
40
|
+
if f_bin == 0 or f_bin == Nf:
|
41
|
+
# NOTE this term appears to be needed to recover correct constant (at least for m=0), but was previously missing
|
42
|
+
DX[Nt // 2] = phif[0] * data[f_bin * Nt // 2] / 2.0
|
43
|
+
else:
|
44
|
+
DX[Nt // 2] = phif[0] * data[f_bin * Nt // 2]
|
45
|
+
|
46
|
+
for jj in range(jj_base + 1 - Nt // 2, jj_base + Nt // 2):
|
47
|
+
j = np.abs(jj - jj_base)
|
48
|
+
i = i_base - jj_base + jj
|
49
|
+
if f_bin == Nf and jj > jj_base:
|
50
|
+
DX[i] = 0.0
|
51
|
+
elif f_bin == 0 and jj < jj_base:
|
52
|
+
DX[i] = 0.0
|
53
|
+
elif j == 0:
|
54
|
+
continue
|
55
|
+
else:
|
56
|
+
DX[i] = phif[j] * data[jj]
|
57
|
+
|
58
|
+
@njit()
|
59
|
+
def __fill_wave_2(
|
60
|
+
f_bin: int, DX_trans: np.ndarray, wave: np.ndarray, Nt: int, Nf: int
|
61
|
+
) -> None:
|
62
|
+
if f_bin == 0:
|
63
|
+
# half of lowest and highest frequency bin pixels are redundant, so store them in even and odd components of f_bin=0 respectively
|
64
|
+
for n in range(0, Nt, 2):
|
65
|
+
wave[n, 0] = DX_trans[n].real * np.sqrt(2)
|
66
|
+
elif f_bin == Nf:
|
67
|
+
for n in range(0, Nt, 2):
|
68
|
+
wave[n + 1, 0] = DX_trans[n].real * np.sqrt(2)
|
69
|
+
else:
|
70
|
+
for n in range(0, Nt):
|
71
|
+
if f_bin % 2:
|
72
|
+
if (n + f_bin) % 2:
|
73
|
+
wave[n, f_bin] = -DX_trans[n].imag
|
74
|
+
else:
|
75
|
+
wave[n, f_bin] = DX_trans[n].real
|
76
|
+
else:
|
77
|
+
if (n + f_bin) % 2:
|
78
|
+
wave[n, f_bin] = DX_trans[n].imag
|
79
|
+
else:
|
80
|
+
wave[n, f_bin] = DX_trans[n].real
|
@@ -0,0 +1,66 @@
|
|
1
|
+
"""helper functions for transform_time.py"""
|
2
|
+
import numpy as np
|
3
|
+
from numba import njit
|
4
|
+
from numpy import fft
|
5
|
+
|
6
|
+
|
7
|
+
def transform_wavelet_time_helper(
|
8
|
+
data: np.ndarray, Nf: int, Nt: int, phi: np.ndarray, mult: int
|
9
|
+
) -> np.ndarray:
|
10
|
+
"""helper function to do the wavelet transform in the time domain"""
|
11
|
+
# the time domain freqseries stream
|
12
|
+
ND = Nf * Nt
|
13
|
+
K = mult * 2 * Nf
|
14
|
+
assert len(data) == ND, f"len(data)={len(data)} != Nf*Nt={ND}"
|
15
|
+
|
16
|
+
# windowed freqseries packets
|
17
|
+
wdata = np.zeros(K)
|
18
|
+
wave = np.zeros((Nt, Nf)) # wavelet wavepacket transform of the signal
|
19
|
+
data_pad = np.concatenate((data, data[:K]))
|
20
|
+
__core(Nf, Nt, K, ND, wdata, data_pad, phi, wave, mult)
|
21
|
+
return wave
|
22
|
+
|
23
|
+
def __core(Nf: int, Nt: int, K: int, ND: int, wdata: np.ndarray, data_pad: np.ndarray, phi: np.ndarray, wave: np.ndarray, mult: int) -> None:
|
24
|
+
for time_bin_i in range(0, Nt):
|
25
|
+
__fill_wave_1(time_bin_i, K, ND, Nf, wdata, data_pad, phi)
|
26
|
+
wdata_trans = np.fft.rfft(wdata, K)
|
27
|
+
__fill_wave_2(time_bin_i, wave, wdata_trans, Nf, mult)
|
28
|
+
|
29
|
+
|
30
|
+
@njit()
|
31
|
+
def __fill_wave_1(
|
32
|
+
t_bin: int,
|
33
|
+
K: int,
|
34
|
+
ND: int,
|
35
|
+
Nf: int,
|
36
|
+
wdata: np.ndarray,
|
37
|
+
data_pad: np.ndarray,
|
38
|
+
phi: np.ndarray,
|
39
|
+
) -> None:
|
40
|
+
"""Assign wdata to be FFT'd in a loop with K extra values on the right to loop."""
|
41
|
+
# wrapping the freqseries is needed to make the sum in Eq 13 in Cornish paper from [-K/2, K/2]
|
42
|
+
jj = (t_bin * Nf - K // 2) % ND # Periodically wrap the freqseries
|
43
|
+
for j in range(K):
|
44
|
+
# Eq 13 from Cornish paper
|
45
|
+
wdata[j] = data_pad[jj] * phi[j] # Apply the window
|
46
|
+
jj = (jj + 1) % ND # Periodically wrap the freqseries
|
47
|
+
|
48
|
+
|
49
|
+
@njit()
|
50
|
+
def __fill_wave_2(
|
51
|
+
t_bin: int, wave: np.ndarray, wdata_trans: np.ndarray, Nf: int, mult: int
|
52
|
+
) -> None:
|
53
|
+
# wdata_trans = np.sum(wdata) * np.exp(1j * np.pi * np.arange(0, 1+K//2) / K)
|
54
|
+
|
55
|
+
# pack fft'd wdata into wave array
|
56
|
+
if t_bin % 2 == 0 and t_bin < wave.shape[0] - 1: # if EVEN t_bin
|
57
|
+
# m=0 value at even Nt and
|
58
|
+
wave[t_bin, 0] = wdata_trans[0].real / np.sqrt(2)
|
59
|
+
wave[t_bin + 1, 0] = wdata_trans[Nf * mult].real / np.sqrt(2)
|
60
|
+
|
61
|
+
# Cnm in eq 13
|
62
|
+
for j in range(1, Nf):
|
63
|
+
if (t_bin + j) % 2:
|
64
|
+
wave[t_bin, j] = -wdata_trans[j * mult].imag
|
65
|
+
else:
|
66
|
+
wave[t_bin, j] = wdata_trans[j * mult].real
|
@@ -0,0 +1,128 @@
|
|
1
|
+
from typing import Union
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from ...logger import logger
|
6
|
+
from ..phi_computer import phi_vec, phitilde_vec_norm
|
7
|
+
from ..types import FrequencySeries, TimeSeries, Wavelet
|
8
|
+
from .from_freq import transform_wavelet_freq_helper
|
9
|
+
from .from_time import transform_wavelet_time_helper
|
10
|
+
from .wavelet_bins import _get_bins, _preprocess_bins
|
11
|
+
|
12
|
+
|
13
|
+
__all__ = ["from_time_to_wavelet", "from_freq_to_wavelet"]
|
14
|
+
|
15
|
+
def from_time_to_wavelet(
|
16
|
+
timeseries: TimeSeries,
|
17
|
+
Nf: Union[int, None] = None,
|
18
|
+
Nt: Union[int, None] = None,
|
19
|
+
nx: float = 4.0,
|
20
|
+
mult: int = 32,
|
21
|
+
) -> Wavelet:
|
22
|
+
"""
|
23
|
+
Transform time-domain data to wavelet-domain data.
|
24
|
+
|
25
|
+
This function performs a forward wavelet transform, converting a
|
26
|
+
time-domain signal into a wavelet-domain representation.
|
27
|
+
|
28
|
+
Parameters
|
29
|
+
----------
|
30
|
+
timeseries : TimeSeries
|
31
|
+
Input time-domain data, represented as a `TimeSeries` object.
|
32
|
+
Nf : int, optional
|
33
|
+
Number of frequency bins for the wavelet transform. Default is None.
|
34
|
+
Nt : int, optional
|
35
|
+
Number of time bins for the wavelet transform. Default is None.
|
36
|
+
nx : float, optional
|
37
|
+
Number of standard deviations for the `phi_vec`, controlling the
|
38
|
+
width of the wavelets. Default is 4.0.
|
39
|
+
mult : int, optional
|
40
|
+
Number of time bins to use for the wavelet transform. Ensure `mult` is
|
41
|
+
not larger than half the number of time bins (`Nt`). Default is 32.
|
42
|
+
|
43
|
+
Returns
|
44
|
+
-------
|
45
|
+
Wavelet
|
46
|
+
A `Wavelet` object representing the transformed wavelet-domain data.
|
47
|
+
|
48
|
+
Warnings
|
49
|
+
--------
|
50
|
+
There can be significant leakage if `mult` is too small. The transform is
|
51
|
+
only approximately exact if `mult = Nt / 2`.
|
52
|
+
|
53
|
+
Notes
|
54
|
+
-----
|
55
|
+
The function warns when the `mult` value is too large, potentially leading
|
56
|
+
to inaccurate results.
|
57
|
+
"""
|
58
|
+
Nf, Nt = _preprocess_bins(timeseries, Nf, Nt)
|
59
|
+
dt = timeseries.dt
|
60
|
+
t_bins, f_bins = _get_bins(timeseries, Nf, Nt)
|
61
|
+
|
62
|
+
ND = Nf * Nt
|
63
|
+
|
64
|
+
if len(timeseries) != ND:
|
65
|
+
logger.warning(
|
66
|
+
f"len(freqseries)={len(timeseries)} != Nf*Nt={ND}. Truncating to freqseries[:{ND}]"
|
67
|
+
)
|
68
|
+
timeseries = timeseries[:ND]
|
69
|
+
if mult > Nt / 2:
|
70
|
+
logger.warning(
|
71
|
+
f"mult={mult} is too large for Nt={Nt}. This may lead to bogus results."
|
72
|
+
)
|
73
|
+
|
74
|
+
mult = min(mult, Nt // 2) # Ensure mult is not larger than ND/2
|
75
|
+
phi = phi_vec(Nf, dt=dt, d=nx, q=mult)
|
76
|
+
wave = transform_wavelet_time_helper(timeseries.data, Nf, Nt, phi, mult).T
|
77
|
+
return Wavelet(
|
78
|
+
wave * np.sqrt(2), time=t_bins, freq=f_bins
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
def from_freq_to_wavelet(
|
83
|
+
freqseries: FrequencySeries,
|
84
|
+
Nf: Union[int, None] = None,
|
85
|
+
Nt: Union[int, None] = None,
|
86
|
+
nx: float = 4.0,
|
87
|
+
) -> Wavelet:
|
88
|
+
"""
|
89
|
+
Transform frequency-domain data to wavelet-domain data.
|
90
|
+
|
91
|
+
This function performs a forward wavelet transform, converting a
|
92
|
+
frequency-domain signal into a wavelet-domain representation.
|
93
|
+
|
94
|
+
Parameters
|
95
|
+
----------
|
96
|
+
freqseries : FrequencySeries
|
97
|
+
Input frequency-domain data, represented as a `FrequencySeries` object.
|
98
|
+
Nf : int, optional
|
99
|
+
Number of frequency bins for the wavelet transform. Default is None.
|
100
|
+
Nt : int, optional
|
101
|
+
Number of time bins for the wavelet transform. Default is None.
|
102
|
+
nx : float, optional
|
103
|
+
Number of standard deviations for the `phi_vec`, controlling the
|
104
|
+
width of the wavelets. Default is 4.0.
|
105
|
+
|
106
|
+
Returns
|
107
|
+
-------
|
108
|
+
Wavelet
|
109
|
+
A `Wavelet` object representing the transformed wavelet-domain data.
|
110
|
+
|
111
|
+
Notes
|
112
|
+
-----
|
113
|
+
The function normalizes the wavelet-domain data to ensure consistency
|
114
|
+
during the transformation process.
|
115
|
+
"""
|
116
|
+
Nf, Nt = _preprocess_bins(freqseries, Nf, Nt)
|
117
|
+
t_bins, f_bins = _get_bins(freqseries, Nf, Nt)
|
118
|
+
dt = freqseries.dt
|
119
|
+
phif = phitilde_vec_norm(Nf, Nt, dt=dt, d=nx)
|
120
|
+
wave = transform_wavelet_freq_helper(
|
121
|
+
freqseries.data, Nf, Nt, phif
|
122
|
+
)
|
123
|
+
|
124
|
+
return Wavelet(
|
125
|
+
(2 / Nf) * wave.T * np.sqrt(2),
|
126
|
+
time=t_bins,
|
127
|
+
freq=f_bins
|
128
|
+
)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
from typing import Tuple, Union
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from ..types import FrequencySeries, TimeSeries
|
6
|
+
|
7
|
+
|
8
|
+
def _preprocess_bins(
|
9
|
+
data: Union[TimeSeries, FrequencySeries], Nf=None, Nt=None
|
10
|
+
) -> Tuple[int, int]:
|
11
|
+
"""preprocess the bins"""
|
12
|
+
|
13
|
+
|
14
|
+
if isinstance(data, TimeSeries):
|
15
|
+
N = len(data)
|
16
|
+
elif isinstance(data, FrequencySeries):
|
17
|
+
# len(d) = N // 2 + 1
|
18
|
+
N = 2 * (len(data) - 1)
|
19
|
+
|
20
|
+
if Nt is not None and Nf is None:
|
21
|
+
assert 1 <= Nt <= N, f"Nt={Nt} must be between 1 and N={N}"
|
22
|
+
Nf = N // Nt
|
23
|
+
|
24
|
+
elif Nf is not None and Nt is None:
|
25
|
+
assert 1 <= Nf <= N, f"Nf={Nf} must be between 1 and N={N}"
|
26
|
+
Nt = N // Nf
|
27
|
+
|
28
|
+
_N = Nf * Nt
|
29
|
+
return Nf, Nt
|
30
|
+
|
31
|
+
|
32
|
+
def _get_bins(
|
33
|
+
data: Union[TimeSeries, FrequencySeries],
|
34
|
+
Nf: Union[int, None] = None,
|
35
|
+
Nt: Union[int, None] = None,
|
36
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
37
|
+
|
38
|
+
T = data.duration
|
39
|
+
t_bins, f_bins = compute_bins(Nf, Nt, T)
|
40
|
+
|
41
|
+
# N = len(data)
|
42
|
+
# fs = N / T
|
43
|
+
# assert delta_f == fmax / Nf, f"delta_f={delta_f} != fmax/Nf={fmax/Nf}"
|
44
|
+
|
45
|
+
t_bins+= data.t0
|
46
|
+
|
47
|
+
return t_bins, f_bins
|
48
|
+
|
49
|
+
|
50
|
+
def compute_bins(Nf:int, Nt:int, T:float) -> Tuple[np.ndarray, np.ndarray]:
|
51
|
+
"""Get the bins for the wavelet transform
|
52
|
+
Eq 4-6 in Wavelets paper
|
53
|
+
"""
|
54
|
+
delta_T = T / Nt
|
55
|
+
delta_F = 1 / (2 * delta_T)
|
56
|
+
t_bins = np.arange(0, Nt) * delta_T
|
57
|
+
f_bins = np.arange(0, Nf) * delta_F
|
58
|
+
return t_bins, f_bins
|
@@ -0,0 +1,96 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
from ...transforms.phi_computer import phi_vec, phitilde_vec_norm
|
4
|
+
from ..types import FrequencySeries, TimeSeries, Wavelet
|
5
|
+
from .to_freq import inverse_wavelet_freq_helper_fast
|
6
|
+
from .to_time import inverse_wavelet_time_helper_fast
|
7
|
+
|
8
|
+
__all__ = ["from_wavelet_to_time", "from_wavelet_to_freq"]
|
9
|
+
|
10
|
+
INV_ROOT2 = 1.0 / np.sqrt(2)
|
11
|
+
|
12
|
+
def from_wavelet_to_time(
|
13
|
+
wave_in: Wavelet,
|
14
|
+
dt: float,
|
15
|
+
nx: float = 4.0,
|
16
|
+
mult: int = 32,
|
17
|
+
) -> TimeSeries:
|
18
|
+
"""
|
19
|
+
Perform an inverse wavelet transform to the time domain.
|
20
|
+
|
21
|
+
This function converts a wavelet-domain signal to a time-domain signal using
|
22
|
+
the inverse wavelet transform algorithm.
|
23
|
+
|
24
|
+
Parameters
|
25
|
+
----------
|
26
|
+
wave_in : Wavelet
|
27
|
+
Input wavelet, represented by a `Wavelet` object.
|
28
|
+
dt : float
|
29
|
+
Time step of the wavelet data.
|
30
|
+
nx : float, optional
|
31
|
+
Scaling parameter for the phi vector used in the transformation. Default is 4.0.
|
32
|
+
mult : int, optional
|
33
|
+
Multiplier parameter for the phi vector. Ensures that the `mult` value
|
34
|
+
is not larger than half the number of time bins (`wave_in.Nt`). Default is 32.
|
35
|
+
|
36
|
+
Returns
|
37
|
+
-------
|
38
|
+
TimeSeries
|
39
|
+
A `TimeSeries` object containing the signal transformed into the time domain.
|
40
|
+
|
41
|
+
Notes
|
42
|
+
-----
|
43
|
+
The transformation involves normalizing the output by the square root of 2
|
44
|
+
to ensure the proper backwards transformation.
|
45
|
+
"""
|
46
|
+
|
47
|
+
mult = min(mult, wave_in.Nt // 2) # Ensure mult is not larger than ND/2
|
48
|
+
phi = phi_vec(wave_in.Nf, d=nx, q=mult, dt=dt) / 2
|
49
|
+
h_t = inverse_wavelet_time_helper_fast(
|
50
|
+
wave_in.data.T, phi, wave_in.Nf, wave_in.Nt, mult
|
51
|
+
)
|
52
|
+
h_t *= INV_ROOT2 # Normalize to get proper backward transformation
|
53
|
+
ts = np.arange(0, wave_in.Nf * wave_in.Nt) * dt
|
54
|
+
return TimeSeries(data=h_t, time=ts)
|
55
|
+
|
56
|
+
|
57
|
+
def from_wavelet_to_freq(
|
58
|
+
wave_in: Wavelet,
|
59
|
+
dt: float,
|
60
|
+
nx:float=4.0
|
61
|
+
) -> FrequencySeries:
|
62
|
+
"""
|
63
|
+
Perform an inverse wavelet transform to the frequency domain.
|
64
|
+
|
65
|
+
This function converts a wavelet-domain signal into a frequency-domain
|
66
|
+
signal using the inverse wavelet transform algorithm.
|
67
|
+
|
68
|
+
Parameters
|
69
|
+
----------
|
70
|
+
wave_in : Wavelet
|
71
|
+
Input wavelet, represented by a `Wavelet` object.
|
72
|
+
dt : float
|
73
|
+
Time step of the wavelet data.
|
74
|
+
nx : float, optional
|
75
|
+
Scaling parameter for the phi vector used in the transformation. Default is 4.0.
|
76
|
+
|
77
|
+
Returns
|
78
|
+
-------
|
79
|
+
FrequencySeries
|
80
|
+
A `FrequencySeries` object containing the signal transformed into the frequency domain.
|
81
|
+
|
82
|
+
Notes
|
83
|
+
-----
|
84
|
+
The transformation involves normalizing the output by the square root of 2
|
85
|
+
to ensure the proper backwards transformation.
|
86
|
+
"""
|
87
|
+
|
88
|
+
phif = phitilde_vec_norm(wave_in.Nf, wave_in.Nt, dt=dt, d=nx)
|
89
|
+
freq_data = inverse_wavelet_freq_helper_fast(
|
90
|
+
wave_in.data, phif, wave_in.Nf, wave_in.Nt
|
91
|
+
)
|
92
|
+
|
93
|
+
freq_data *= INV_ROOT2
|
94
|
+
|
95
|
+
freqs = np.fft.rfftfreq(wave_in.ND, d=dt)
|
96
|
+
return FrequencySeries(data=freq_data, freq=freqs)
|
@@ -1,29 +1,61 @@
|
|
1
1
|
"""functions for computing the inverse wavelet transforms"""
|
2
2
|
import numpy as np
|
3
3
|
from numba import njit
|
4
|
+
from numpy import fft
|
4
5
|
|
5
|
-
from ... import fft_funcs as fft
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
def inverse_wavelet_freq_helper_fast(
|
8
|
+
wave_in: np.ndarray, phif: np.ndarray, Nf: int, Nt: int
|
9
|
+
) -> np.ndarray:
|
10
10
|
"""jit compatible loop for inverse_wavelet_freq"""
|
11
|
+
wave_in = wave_in.T
|
11
12
|
ND = Nf * Nt
|
12
13
|
|
13
14
|
prefactor2s = np.zeros(Nt, np.complex128)
|
14
|
-
res = np.zeros(ND
|
15
|
+
res = np.zeros(ND//2 +1, dtype=np.complex128)
|
16
|
+
__core(Nf, Nt, prefactor2s, wave_in, phif, res)
|
15
17
|
|
16
|
-
for m in range(0, Nf + 1):
|
17
|
-
pack_wave_inverse(m, Nt, Nf, prefactor2s, wave_in)
|
18
|
-
# with numba.objmode(fft_prefactor2s="complex128[:]"):
|
19
|
-
fft_prefactor2s = fft.fft(prefactor2s)
|
20
|
-
unpack_wave_inverse(m, Nt, Nf, phif, fft_prefactor2s, res)
|
21
18
|
|
22
19
|
return res
|
23
20
|
|
21
|
+
def __core(Nf: int, Nt: int, prefactor2s: np.ndarray, wave_in: np.ndarray, phif: np.ndarray, res: np.ndarray) -> None:
|
22
|
+
for m in range(0, Nf + 1):
|
23
|
+
__pack_wave_inverse(m, Nt, Nf, prefactor2s, wave_in)
|
24
|
+
fft_prefactor2s = np.fft.fft(prefactor2s)
|
25
|
+
__unpack_wave_inverse(m, Nt, Nf, phif, fft_prefactor2s, res)
|
26
|
+
|
24
27
|
|
25
28
|
@njit()
|
26
|
-
def
|
29
|
+
def __pack_wave_inverse(
|
30
|
+
m: int, Nt: int, Nf: int, prefactor2s: np.ndarray, wave_in: np.ndarray
|
31
|
+
) -> None:
|
32
|
+
"""helper for fast frequency domain inverse transform to prepare for fourier transform"""
|
33
|
+
if m == 0:
|
34
|
+
for n in range(0, Nt):
|
35
|
+
prefactor2s[n] = 2 ** (-1 / 2) * wave_in[(2 * n) % Nt, 0]
|
36
|
+
elif m == Nf:
|
37
|
+
for n in range(0, Nt):
|
38
|
+
prefactor2s[n] = 2 ** (-1 / 2) * wave_in[(2 * n) % Nt + 1, 0]
|
39
|
+
else:
|
40
|
+
for n in range(0, Nt):
|
41
|
+
val = wave_in[n, m] # bug is here
|
42
|
+
if (n + m) % 2:
|
43
|
+
mult2 = -1j
|
44
|
+
else:
|
45
|
+
mult2 = 1
|
46
|
+
|
47
|
+
prefactor2s[n] = mult2 * val
|
48
|
+
|
49
|
+
|
50
|
+
@njit()
|
51
|
+
def __unpack_wave_inverse(
|
52
|
+
m: int,
|
53
|
+
Nt: int,
|
54
|
+
Nf: int,
|
55
|
+
phif: np.ndarray,
|
56
|
+
fft_prefactor2s: np.ndarray,
|
57
|
+
res: np.ndarray,
|
58
|
+
) -> None:
|
27
59
|
"""helper for unpacking results of frequency domain inverse transform"""
|
28
60
|
|
29
61
|
if m == 0 or m == Nf:
|
@@ -52,25 +84,4 @@ def unpack_wave_inverse(m, Nt, Nf, phif, fft_prefactor2s, res):
|
|
52
84
|
ind31 = Nt - 1
|
53
85
|
if ind32 == Nt:
|
54
86
|
ind32 = 0
|
55
|
-
|
56
87
|
res[Nt // 2 * m] = fft_prefactor2s[(Nt // 2 * m) % Nt] * phif[0]
|
57
|
-
|
58
|
-
|
59
|
-
@njit()
|
60
|
-
def pack_wave_inverse(m, Nt, Nf, prefactor2s, wave_in):
|
61
|
-
"""helper for fast frequency domain inverse transform to prepare for fourier transform"""
|
62
|
-
if m == 0:
|
63
|
-
for n in range(0, Nt):
|
64
|
-
prefactor2s[n] = 1 / np.sqrt(2) * wave_in[(2 * n) % Nt, 0]
|
65
|
-
elif m == Nf:
|
66
|
-
for n in range(0, Nt):
|
67
|
-
prefactor2s[n] = 1 / np.sqrt(2) * wave_in[(2 * n) % Nt + 1, 0]
|
68
|
-
else:
|
69
|
-
for n in range(0, Nt):
|
70
|
-
val = wave_in[n, m]
|
71
|
-
if (n + m) % 2:
|
72
|
-
mult2 = -1j
|
73
|
-
else:
|
74
|
-
mult2 = 1
|
75
|
-
|
76
|
-
prefactor2s[n] = mult2 * val
|