pywavelet 0.0.1b0__py3-none-any.whl → 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -0,0 +1,374 @@
|
|
1
|
+
import matplotlib.pyplot as plt
|
2
|
+
from typing import Optional, Tuple
|
3
|
+
|
4
|
+
import numpy as np
|
5
|
+
|
6
|
+
from .common import is_documented_by, xp, fmt_timerange
|
7
|
+
from .plotting import plot_wavelet_grid, plot_wavelet_trend
|
8
|
+
|
9
|
+
|
10
|
+
class Wavelet:
|
11
|
+
"""
|
12
|
+
A class to represent a wavelet transform result, with methods for plotting and accessing key properties like duration, sample rate, and grid size.
|
13
|
+
|
14
|
+
Attributes
|
15
|
+
----------
|
16
|
+
data : xp.ndarray
|
17
|
+
2D array representing the wavelet coefficients (frequency x time).
|
18
|
+
time : xp.ndarray
|
19
|
+
Array of time points.
|
20
|
+
freq : xp.ndarray
|
21
|
+
Array of corresponding frequency points.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(
|
25
|
+
self,
|
26
|
+
data: xp.ndarray,
|
27
|
+
time: xp.ndarray,
|
28
|
+
freq: xp.ndarray,
|
29
|
+
):
|
30
|
+
"""
|
31
|
+
Initialize the Wavelet object with data, time, and frequency arrays.
|
32
|
+
|
33
|
+
Parameters
|
34
|
+
----------
|
35
|
+
data : xp.ndarray
|
36
|
+
2D array representing the wavelet coefficients (frequency x time).
|
37
|
+
time : xp.ndarray
|
38
|
+
Array of time points.
|
39
|
+
freq : xp.ndarray
|
40
|
+
Array of corresponding frequency points.
|
41
|
+
|
42
|
+
Raises
|
43
|
+
------
|
44
|
+
AssertionError
|
45
|
+
If the length of the time array does not match the number of time bins in `data`.
|
46
|
+
If the length of the frequency array does not match the number of frequency bins in `data`.
|
47
|
+
"""
|
48
|
+
nf, nt = data.shape
|
49
|
+
assert len(time) == nt, f"len(time)={len(time)} != nt={nt}"
|
50
|
+
assert len(freq) == nf, f"len(freq)={len(freq)} != nf={nf}"
|
51
|
+
|
52
|
+
self.data = data
|
53
|
+
self.time = time
|
54
|
+
self.freq = freq
|
55
|
+
|
56
|
+
@is_documented_by(plot_wavelet_grid)
|
57
|
+
def plot(self, ax=None, *args, **kwargs) -> Tuple[plt.Figure, plt.Axes]:
|
58
|
+
kwargs["time_grid"] = kwargs.get("time_grid", self.time)
|
59
|
+
kwargs["freq_grid"] = kwargs.get("freq_grid", self.freq)
|
60
|
+
return plot_wavelet_grid(wavelet_data=self.data, ax=ax, *args, **kwargs)
|
61
|
+
|
62
|
+
@is_documented_by(plot_wavelet_trend)
|
63
|
+
def plot_trend(self, ax=None, *args, **kwargs) -> Tuple[plt.Figure, plt.Axes]:
|
64
|
+
kwargs["time_grid"] = kwargs.get("time_grid", self.time)
|
65
|
+
kwargs["freq_grid"] = kwargs.get("freq_grid", self.freq)
|
66
|
+
return plot_wavelet_trend(wavelet_data=self.data, ax=ax, *args, **kwargs)
|
67
|
+
|
68
|
+
@property
|
69
|
+
def Nt(self) -> int:
|
70
|
+
"""
|
71
|
+
Number of time bins.
|
72
|
+
|
73
|
+
Returns
|
74
|
+
-------
|
75
|
+
int
|
76
|
+
Length of the time array.
|
77
|
+
"""
|
78
|
+
return len(self.time)
|
79
|
+
|
80
|
+
@property
|
81
|
+
def Nf(self) -> int:
|
82
|
+
"""
|
83
|
+
Number of frequency bins.
|
84
|
+
|
85
|
+
Returns
|
86
|
+
-------
|
87
|
+
int
|
88
|
+
Length of the frequency array.
|
89
|
+
"""
|
90
|
+
return len(self.freq)
|
91
|
+
|
92
|
+
@property
|
93
|
+
def ND(self) -> int:
|
94
|
+
"""
|
95
|
+
Total number of data points in the wavelet grid.
|
96
|
+
|
97
|
+
Returns
|
98
|
+
-------
|
99
|
+
int
|
100
|
+
The product of `Nt` and `Nf`.
|
101
|
+
"""
|
102
|
+
return self.Nt * self.Nf
|
103
|
+
|
104
|
+
@property
|
105
|
+
def delta_T(self) -> float:
|
106
|
+
"""
|
107
|
+
Time resolution (ΔT) of the wavelet grid.
|
108
|
+
|
109
|
+
Returns
|
110
|
+
-------
|
111
|
+
float
|
112
|
+
Difference between consecutive time points.
|
113
|
+
"""
|
114
|
+
return self.time[1] - self.time[0]
|
115
|
+
|
116
|
+
@property
|
117
|
+
def delta_F(self) -> float:
|
118
|
+
"""
|
119
|
+
Frequency resolution (ΔF) of the wavelet grid.
|
120
|
+
|
121
|
+
Returns
|
122
|
+
-------
|
123
|
+
float
|
124
|
+
Inverse of twice the time resolution.
|
125
|
+
"""
|
126
|
+
return 1 / (2 * self.delta_T)
|
127
|
+
|
128
|
+
@property
|
129
|
+
def duration(self) -> float:
|
130
|
+
"""
|
131
|
+
Duration of the wavelet grid.
|
132
|
+
|
133
|
+
Returns
|
134
|
+
-------
|
135
|
+
float
|
136
|
+
Total duration in seconds.
|
137
|
+
"""
|
138
|
+
return float(self.Nt * self.delta_T)
|
139
|
+
|
140
|
+
@property
|
141
|
+
def delta_t(self) -> float:
|
142
|
+
"""
|
143
|
+
Time resolution of the wavelet grid, normalized by the total number of data points.
|
144
|
+
|
145
|
+
Returns
|
146
|
+
-------
|
147
|
+
float
|
148
|
+
Time resolution per data point.
|
149
|
+
"""
|
150
|
+
return float(self.duration / self.ND)
|
151
|
+
|
152
|
+
@property
|
153
|
+
def delta_f(self) -> float:
|
154
|
+
"""
|
155
|
+
Frequency resolution of the wavelet grid, normalized by the total number of data points.
|
156
|
+
|
157
|
+
Returns
|
158
|
+
-------
|
159
|
+
float
|
160
|
+
Frequency resolution per data point.
|
161
|
+
"""
|
162
|
+
return 1 / (2 * self.delta_t)
|
163
|
+
|
164
|
+
@property
|
165
|
+
def t0(self) -> float:
|
166
|
+
"""
|
167
|
+
Initial time point of the wavelet grid.
|
168
|
+
|
169
|
+
Returns
|
170
|
+
-------
|
171
|
+
float
|
172
|
+
First time point in the time array.
|
173
|
+
"""
|
174
|
+
return float(self.time[0])
|
175
|
+
|
176
|
+
@property
|
177
|
+
def tend(self) -> float:
|
178
|
+
"""
|
179
|
+
Final time point of the wavelet grid.
|
180
|
+
|
181
|
+
Returns
|
182
|
+
-------
|
183
|
+
float
|
184
|
+
Last time point in the time array.
|
185
|
+
"""
|
186
|
+
return float(self.time[-1])
|
187
|
+
|
188
|
+
@property
|
189
|
+
def shape(self) -> Tuple[int, int]:
|
190
|
+
"""
|
191
|
+
Shape of the wavelet grid.
|
192
|
+
|
193
|
+
Returns
|
194
|
+
-------
|
195
|
+
Tuple[int, int]
|
196
|
+
Tuple representing the shape of the data array (Nf, Nt).
|
197
|
+
"""
|
198
|
+
return self.data.shape
|
199
|
+
|
200
|
+
@property
|
201
|
+
def sample_rate(self) -> float:
|
202
|
+
"""
|
203
|
+
Sample rate of the wavelet grid.
|
204
|
+
|
205
|
+
Returns
|
206
|
+
-------
|
207
|
+
float
|
208
|
+
Sample rate calculated as the inverse of the time resolution.
|
209
|
+
"""
|
210
|
+
return 1 / self.delta_t
|
211
|
+
|
212
|
+
@property
|
213
|
+
def fs(self) -> float:
|
214
|
+
"""
|
215
|
+
Sample rate (fs) of the wavelet grid.
|
216
|
+
|
217
|
+
Returns
|
218
|
+
-------
|
219
|
+
float
|
220
|
+
The sample rate.
|
221
|
+
"""
|
222
|
+
return self.sample_rate
|
223
|
+
|
224
|
+
@property
|
225
|
+
def nyquist_frequency(self) -> float:
|
226
|
+
"""
|
227
|
+
Nyquist frequency of the wavelet grid.
|
228
|
+
|
229
|
+
Returns
|
230
|
+
-------
|
231
|
+
float
|
232
|
+
Nyquist frequency, which is half of the sample rate.
|
233
|
+
"""
|
234
|
+
return self.sample_rate / 2
|
235
|
+
|
236
|
+
def to_timeseries(self, nx: float = 4.0, mult: int = 32) -> "TimeSeries":
|
237
|
+
"""
|
238
|
+
Convert the wavelet grid to a time-domain signal.
|
239
|
+
|
240
|
+
Returns
|
241
|
+
-------
|
242
|
+
TimeSeries
|
243
|
+
A `TimeSeries` object representing the time-domain signal.
|
244
|
+
"""
|
245
|
+
from ..inverse import from_wavelet_to_time
|
246
|
+
return from_wavelet_to_time(self, dt=self.delta_t, nx=nx, mult=mult)
|
247
|
+
|
248
|
+
def to_frequencyseries(self, nx: float = 4.0) -> "FrequencySeries":
|
249
|
+
"""
|
250
|
+
Convert the wavelet grid to a frequency-domain signal.
|
251
|
+
|
252
|
+
Returns
|
253
|
+
-------
|
254
|
+
FrequencySeries
|
255
|
+
A `FrequencySeries` object representing the frequency-domain signal.
|
256
|
+
"""
|
257
|
+
from ..inverse import from_wavelet_to_freq
|
258
|
+
return from_wavelet_to_freq(self, dt=self.delta_t, nx=nx)
|
259
|
+
|
260
|
+
def __repr__(self) -> str:
|
261
|
+
"""
|
262
|
+
Return a string representation of the Wavelet object.
|
263
|
+
|
264
|
+
Returns
|
265
|
+
-------
|
266
|
+
str
|
267
|
+
String containing information about the shape of the wavelet grid.
|
268
|
+
"""
|
269
|
+
|
270
|
+
frange = ",".join([f"{f:.2e}" for f in (self.freq[0], self.freq[-1])])
|
271
|
+
trange = fmt_timerange((self.t0, self.tend))
|
272
|
+
Nfpow2 = int(np.log2(self.shape[0]))
|
273
|
+
Ntpow2 = int(np.log2(self.shape[1]))
|
274
|
+
shapef = f"NfxNf=[2^{Nfpow2}, 2^{Ntpow2}]"
|
275
|
+
return f"Wavelet({shapef}, [{frange}]Hz, {trange})"
|
276
|
+
|
277
|
+
def __add__(self, other):
|
278
|
+
"""Element-wise addition of two Wavelet objects."""
|
279
|
+
if isinstance(other, Wavelet):
|
280
|
+
return Wavelet(data=self.data + other.data, time=self.time, freq=self.freq)
|
281
|
+
elif isinstance(other, float):
|
282
|
+
return Wavelet(data=self.data + other, time=self.time, freq=self.freq)
|
283
|
+
|
284
|
+
def __sub__(self, other):
|
285
|
+
"""Element-wise subtraction of two Wavelet objects."""
|
286
|
+
if isinstance(other, Wavelet):
|
287
|
+
return Wavelet(data=self.data - other.data, time=self.time, freq=self.freq)
|
288
|
+
elif isinstance(other, float):
|
289
|
+
return Wavelet(data=self.data - other, time=self.time, freq=self.freq)
|
290
|
+
|
291
|
+
def __mul__(self, other):
|
292
|
+
"""Element-wise multiplication of two Wavelet objects."""
|
293
|
+
if isinstance(other, Wavelet):
|
294
|
+
return Wavelet(data=self.data * other.data, time=self.time, freq=self.freq)
|
295
|
+
elif isinstance(other, float):
|
296
|
+
return Wavelet(data=self.data * other, time=self.time, freq=self.freq)
|
297
|
+
|
298
|
+
def __truediv__(self, other):
|
299
|
+
"""Element-wise division of two Wavelet objects."""
|
300
|
+
if isinstance(other, Wavelet):
|
301
|
+
return Wavelet(data=self.data / other.data, time=self.time, freq=self.freq)
|
302
|
+
elif isinstance(other, float):
|
303
|
+
return Wavelet(data=self.data / other, time=self.time, freq=self.freq)
|
304
|
+
|
305
|
+
def __eq__(self, other:"Wavelet") -> bool:
|
306
|
+
"""Element-wise comparison of two Wavelet objects."""
|
307
|
+
data_all_same = xp.isclose(xp.nansum(self.data - other.data), 0)
|
308
|
+
time_same = (self.time == other.time).all()
|
309
|
+
freq_same = (self.freq == other.freq).all()
|
310
|
+
return data_all_same and time_same and freq_same
|
311
|
+
|
312
|
+
|
313
|
+
def noise_weighted_inner_product(self, other:"Wavelet", psd:"Wavelet") -> float:
|
314
|
+
"""
|
315
|
+
Compute the noise-weighted inner product of two wavelet grids given a PSD.
|
316
|
+
|
317
|
+
Parameters
|
318
|
+
----------
|
319
|
+
other : Wavelet
|
320
|
+
A `Wavelet` object representing the other wavelet grid.
|
321
|
+
psd : Wavelet
|
322
|
+
A `Wavelet` object representing the power spectral density.
|
323
|
+
|
324
|
+
Returns
|
325
|
+
-------
|
326
|
+
float
|
327
|
+
The noise-weighted inner product.
|
328
|
+
"""
|
329
|
+
from ...utils import noise_weighted_inner_product
|
330
|
+
return noise_weighted_inner_product(self, other, psd)
|
331
|
+
|
332
|
+
|
333
|
+
def matched_filter_snr(self, template:"Wavelet", psd:"Wavelet") -> float:
|
334
|
+
"""
|
335
|
+
Compute the matched filter SNR of the wavelet grid given a template.
|
336
|
+
|
337
|
+
Parameters
|
338
|
+
----------
|
339
|
+
template : Wavelet
|
340
|
+
A `Wavelet` object representing the template.
|
341
|
+
|
342
|
+
Returns
|
343
|
+
-------
|
344
|
+
float
|
345
|
+
The matched filter signal-to-noise ratio.
|
346
|
+
"""
|
347
|
+
mf = self.noise_weighted_inner_product(template, psd)
|
348
|
+
return mf / self.optimal_snr(psd)
|
349
|
+
|
350
|
+
def optimal_snr(self, psd:"Wavelet") -> float:
|
351
|
+
"""
|
352
|
+
Compute the optimal SNR of the wavelet grid given a PSD.
|
353
|
+
|
354
|
+
Parameters
|
355
|
+
----------
|
356
|
+
psd : Wavelet
|
357
|
+
A `Wavelet` object representing the power spectral density.
|
358
|
+
|
359
|
+
Returns
|
360
|
+
-------
|
361
|
+
float
|
362
|
+
The optimal signal-to-noise ratio.
|
363
|
+
"""
|
364
|
+
return xp.sqrt(self.noise_weighted_inner_product(self, psd))
|
365
|
+
|
366
|
+
def __copy__(self):
|
367
|
+
return Wavelet(
|
368
|
+
data=self.data.copy(),
|
369
|
+
time=self.time.copy(),
|
370
|
+
freq=self.freq.copy()
|
371
|
+
)
|
372
|
+
|
373
|
+
def copy(self):
|
374
|
+
return self.__copy__()
|
@@ -0,0 +1,34 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from .common import fmt_timerange, is_documented_by, xp
|
4
|
+
|
5
|
+
|
6
|
+
class WaveletMask:
|
7
|
+
def __init__(
|
8
|
+
self,
|
9
|
+
mask: xp.ndarray,
|
10
|
+
time: xp.ndarray,
|
11
|
+
freq: xp.ndarray,
|
12
|
+
):
|
13
|
+
self.mask = mask
|
14
|
+
self.time = time
|
15
|
+
self.freq = freq
|
16
|
+
|
17
|
+
def __repr__(self):
|
18
|
+
return f"WaveletMask({self.mask.shape}, {fmt_timerange(self.time)}, {self.freq})"
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def from_grid(cls, time_grid: xp.ndarray, freq_grid: xp.ndarray):
|
22
|
+
nt, nf = len(time_grid), len(freq_grid)
|
23
|
+
mask = xp.zeros((nf, nt), dtype=bool)
|
24
|
+
return cls(mask, time_grid, freq_grid)
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def from_frange(
|
28
|
+
cls, time_grid: xp.ndarray, freq_grid: xp.ndarray, frange: List[float]
|
29
|
+
):
|
30
|
+
self = cls.from_grid(time_grid, freq_grid)
|
31
|
+
self.mask[
|
32
|
+
(freq_grid >= frange[0]) & (freq_grid <= frange[1]), :
|
33
|
+
] = True
|
34
|
+
return self
|
pywavelet/utils.py
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
from typing import Union
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
from scipy.interpolate import interp1d
|
5
|
+
|
6
|
+
from .transforms.types import FrequencySeries, TimeSeries, Wavelet, WaveletMask
|
7
|
+
|
8
|
+
DATA_TYPE = Union[TimeSeries, FrequencySeries, Wavelet]
|
9
|
+
|
10
|
+
|
11
|
+
def evolutionary_psd_from_stationary_psd(
|
12
|
+
psd: np.ndarray,
|
13
|
+
psd_f: np.ndarray,
|
14
|
+
f_grid: np.ndarray,
|
15
|
+
t_grid: np.ndarray,
|
16
|
+
dt: float,
|
17
|
+
) -> Wavelet:
|
18
|
+
"""
|
19
|
+
PSD[ti,fi] = PSD[fi] / dt
|
20
|
+
"""
|
21
|
+
Nt = len(t_grid)
|
22
|
+
delta_f = f_grid[1] - f_grid[0]
|
23
|
+
nan_val = np.max(psd)
|
24
|
+
psd_grid = interp1d(
|
25
|
+
psd_f,
|
26
|
+
psd,
|
27
|
+
kind="nearest",
|
28
|
+
fill_value=nan_val,
|
29
|
+
bounds_error=False,
|
30
|
+
)(f_grid)
|
31
|
+
|
32
|
+
# repeat the PSD for each time bin
|
33
|
+
psd_grid = np.repeat(psd_grid[None, :], Nt, axis=0) / dt
|
34
|
+
return Wavelet(psd_grid.T, time=t_grid, freq=f_grid)
|
35
|
+
|
36
|
+
|
37
|
+
def noise_weighted_inner_product(
|
38
|
+
d: Wavelet, h: Wavelet, PSD: Wavelet
|
39
|
+
) -> float:
|
40
|
+
return np.nansum((d.data * h.data) / PSD.data)
|
41
|
+
|
42
|
+
|
43
|
+
def compute_snr(d: Wavelet, h: Wavelet, PSD: Wavelet) -> float:
|
44
|
+
"""Compute the SNR of a model h[ti,fi] given freqseries d[ti,fi] and PSD[ti,fi].
|
45
|
+
|
46
|
+
SNR(h) = Sum_{ti,fi} [ h_hat[ti,fi] d[ti,fi] / PSD[ti,fi]
|
47
|
+
|
48
|
+
Parameters
|
49
|
+
----------
|
50
|
+
h : np.ndarray
|
51
|
+
The model in the wavelet domain (binned in [ti,fi]).
|
52
|
+
d : np.ndarray
|
53
|
+
The freqseries in the wavelet domain (binned in [ti,fi]).
|
54
|
+
PSD : np.ndarray
|
55
|
+
The PSD in the wavelet domain (binned in [ti,fi]).
|
56
|
+
|
57
|
+
Returns
|
58
|
+
-------
|
59
|
+
float
|
60
|
+
The SNR of the model h given freqseries d and PSD.
|
61
|
+
|
62
|
+
"""
|
63
|
+
return np.sqrt(noise_weighted_inner_product(d, h, PSD))
|
64
|
+
|
65
|
+
|
66
|
+
def compute_likelihood(
|
67
|
+
data: Wavelet, template: Wavelet, psd: Wavelet, mask: WaveletMask = None
|
68
|
+
) -> float:
|
69
|
+
d = data.data
|
70
|
+
h = template.data
|
71
|
+
p = psd.data
|
72
|
+
if mask is not None:
|
73
|
+
m = mask.mask
|
74
|
+
d, h, p = d * m, h * m, p * m
|
75
|
+
|
76
|
+
return -0.5 * np.nansum((d - h) ** 2 / p)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: pywavelet
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: WDM wavelet transform your time/freq series!
|
5
|
+
Author-email: Pywavelet Team <avi.vajpeyi@gmail.com>
|
6
|
+
Project-URL: Homepage, https://pywavelet.github.io/pywavelet/
|
7
|
+
Project-URL: Bug Reports, https://pywavelet.com/pywavelet/pywavelet/issues
|
8
|
+
Project-URL: Source, https://github.com/pywavelet/pywavelet/
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
12
|
+
Classifier: Operating System :: OS Independent
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
14
|
+
Requires-Python: >=3.8
|
15
|
+
Description-Content-Type: text/markdown
|
16
|
+
Requires-Dist: numpy
|
17
|
+
Requires-Dist: numba
|
18
|
+
Requires-Dist: scipy
|
19
|
+
Requires-Dist: matplotlib
|
20
|
+
Requires-Dist: tqdm
|
21
|
+
Requires-Dist: rich
|
22
|
+
Requires-Dist: rocket-fft
|
23
|
+
Requires-Dist: astropy>=5.2.1
|
24
|
+
Provides-Extra: dev
|
25
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
26
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
27
|
+
Requires-Dist: pre-commit; extra == "dev"
|
28
|
+
Requires-Dist: flake8>=5.0.4; extra == "dev"
|
29
|
+
Requires-Dist: black>=22.12.0; extra == "dev"
|
30
|
+
Requires-Dist: black[jupyter]>=22.12.0; extra == "dev"
|
31
|
+
Requires-Dist: isort; extra == "dev"
|
32
|
+
Requires-Dist: mypy; extra == "dev"
|
33
|
+
Requires-Dist: jupyter-book; extra == "dev"
|
34
|
+
Requires-Dist: GitPython; extra == "dev"
|
35
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
pywavelet/__init__.py,sha256=zcK3Qj4wTrGZF1rU3aT6yA9LvliAOD4DVOY7gNfHhCI,53
|
2
|
+
pywavelet/_version.py,sha256=IMl2Pr_Sy4LVRKy_Sm4CdwUl1Gryous6ncL96EMYsnM,411
|
3
|
+
pywavelet/logger.py,sha256=u5Et6QLUU0IuTnPnxfev5-4GYmihTo7_yBCTf-rVUsA,377
|
4
|
+
pywavelet/utils.py,sha256=-fZULV8ze1cxs0SP03Di-naYPgNvHbADsFrJ6Vzi7aE,1918
|
5
|
+
pywavelet/transforms/__init__.py,sha256=FolK8WiVEJmGDC9xMupYVI_essXaXS4LYWKbqEqGx6o,289
|
6
|
+
pywavelet/transforms/phi_computer.py,sha256=vo1PK9Z70kKV-1lfyRoxWdhSYqwIgJK5CRCCJVei3xI,4545
|
7
|
+
pywavelet/transforms/forward/__init__.py,sha256=Yq4Tg3Ze98-17C9FIkOqMUdiLHe9x_YoyuRvxOxMOP0,176
|
8
|
+
pywavelet/transforms/forward/from_freq.py,sha256=HWADciv746P5zWNhnyRKDEDACTQnwDTR4usf2k0VIWk,2663
|
9
|
+
pywavelet/transforms/forward/from_time.py,sha256=zfpvmq7UHnynzAnsM_Pf4MXpO1GJ0TaCCERQKNsaBNU,2340
|
10
|
+
pywavelet/transforms/forward/main.py,sha256=FJSLUUsk3I91lUeB2YFiXQdX8gB9maFW60e4tgrd45A,4053
|
11
|
+
pywavelet/transforms/forward/wavelet_bins.py,sha256=43CHVSjNgC59Af1_h9t33VBBqGHJmIUaOUhJgekhMnc,1419
|
12
|
+
pywavelet/transforms/inverse/__init__.py,sha256=J4KIzPzbHNg_8fV_c1MpPq3slSqHQV0j3VFrjfd1Nog,121
|
13
|
+
pywavelet/transforms/inverse/main.py,sha256=l5yFvzmWObrO5Xt_8KYp62w829ab0pQLxcv0-QxkvG0,3015
|
14
|
+
pywavelet/transforms/inverse/to_freq.py,sha256=SExZMax-8A-tJpIA86pYY61X2qvlZ2MrZY27uzCQSV0,2778
|
15
|
+
pywavelet/transforms/inverse/to_time.py,sha256=BAYvrr41QHIbzwYMPyMnzv5mqSx40YigmBruWBwtZwc,5041
|
16
|
+
pywavelet/transforms/types/__init__.py,sha256=bNw6gb1S7aA1-wl38LEL7j0BhJBJveYDt2ERn-0kU4k,147
|
17
|
+
pywavelet/transforms/types/common.py,sha256=sLBn2d9cuL6NYeIv6NIogDjY6rYPZAPzCtWGwMlAwkI,1290
|
18
|
+
pywavelet/transforms/types/frequencyseries.py,sha256=Rtwt486UL0-TgMAdcMMVpyfi5PSLzxFcdE_RsYuyxQk,7463
|
19
|
+
pywavelet/transforms/types/plotting.py,sha256=aEIFoSuQRYwYc2639yLkbujXx_aav5A5tXG29rOSMOQ,10275
|
20
|
+
pywavelet/transforms/types/timeseries.py,sha256=Nl1tiKZ7kwu-EZ5JtubwgzgyjQZR86eGU3C3kElIPNg,9296
|
21
|
+
pywavelet/transforms/types/wavelet.py,sha256=raODhyegBd1_esuv2YP6tK_Nj9fYaLQ6O4pxpiFAVZU,10790
|
22
|
+
pywavelet/transforms/types/wavelet_mask.py,sha256=Qst9NQmvr6vx0Tg8TMABRnDdD3iQgiwgaQKqyf0lwfU,937
|
23
|
+
pywavelet-0.1.0.dist-info/METADATA,sha256=sCAmMS5b2zEqsetgkkFgb7DopcPjn_V1o1y3uzIAEEE,1307
|
24
|
+
pywavelet-0.1.0.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
25
|
+
pywavelet-0.1.0.dist-info/top_level.txt,sha256=g0Ezt0Rg0X-nrd-a0pAXKVRkuWNsF2M9Ynsjb9b2UYQ,10
|
26
|
+
pywavelet-0.1.0.dist-info/RECORD,,
|
pywavelet/fft_funcs.py
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
"""helper to make sure available fft functions are consistent across modules depending on install
|
2
|
-
mkl-fft is faster so it is the default, but numpy fft is probably more commonly installed to it is the fallback"""
|
3
|
-
try:
|
4
|
-
import mkl_fft
|
5
|
-
|
6
|
-
rfft = mkl_fft.rfft_numpy
|
7
|
-
irfft = mkl_fft.irfft_numpy
|
8
|
-
fft = mkl_fft.fft
|
9
|
-
ifft = mkl_fft.ifft
|
10
|
-
except ImportError:
|
11
|
-
import numpy
|
12
|
-
|
13
|
-
rfft = numpy.fft.rfft
|
14
|
-
irfft = numpy.fft.irfft
|
15
|
-
fft = numpy.fft.fft
|
16
|
-
ifft = numpy.fft.ifft
|
pywavelet/likelihood/__init__.py
DELETED
File without changes
|
pywavelet/likelihood/whittle.py
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
"""Whittle Likelihood (in the wavelet domain)
|
2
|
-
See eq 18, Cornish 2020 "Time-Frequency Analysis of Gravitational Wave Data"
|
3
|
-
|
4
|
-
LnL(d|h) = -1/2 * Sum_{ti,fi} [ ln(2pi) + ln(PSD[ti,fi]) + (d[ti,fi]-h[ti,fi])^2/PSD[ti,fi]]
|
5
|
-
|
6
|
-
where
|
7
|
-
- d[ti,fi] is the data in the wavelet domain,
|
8
|
-
- h[ti,fi] is the model in the wavelet domain,
|
9
|
-
- PSD[ti,fi] is the _evolutionary_ power spectral density in the wavelet domain.
|
10
|
-
|
11
|
-
For stationary noise:
|
12
|
-
PSD[ti,fi] = PSD[fi * Nt/2] Delta_f,
|
13
|
-
where
|
14
|
-
- fi * Nt/2 is the frequency index in the wavelet domain,
|
15
|
-
- Delta_f is the frequency bin width in the wavelet domain.
|
16
|
-
|
17
|
-
|
18
|
-
"""
|
19
|
-
|
20
|
-
from .likelihood_base import LikelihoodBase
|
21
|
-
|
22
|
-
|
23
|
-
class WhittleLikelihood(LikelihoodBase):
|
24
|
-
pass
|
pywavelet/transforms/common.py
DELETED
@@ -1,77 +0,0 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import scipy
|
3
|
-
|
4
|
-
from .. import fft_funcs as fft
|
5
|
-
|
6
|
-
PI = np.pi
|
7
|
-
|
8
|
-
|
9
|
-
def phitilde_vec(om: np.ndarray, Nf: int, nx=4.0) -> np.ndarray:
|
10
|
-
"""compute phitilde, om i array, nx is filter steepness, defaults to 4."""
|
11
|
-
# Note: Pi is Nyquist angular frequency
|
12
|
-
DOM = PI / Nf # 2 pi times DF
|
13
|
-
insDOM = 1.0 / np.sqrt(DOM)
|
14
|
-
B = PI / (2 * Nf)
|
15
|
-
A = (DOM - B) / 2
|
16
|
-
z = np.zeros(om.size)
|
17
|
-
|
18
|
-
mask = (np.abs(om) >= A) & (np.abs(om) < A + B)
|
19
|
-
|
20
|
-
x = (np.abs(om[mask]) - A) / B
|
21
|
-
y = scipy.special.betainc(nx, nx, x)
|
22
|
-
z[mask] = insDOM * np.cos(PI / 2.0 * y)
|
23
|
-
|
24
|
-
z[np.abs(om) < A] = insDOM
|
25
|
-
return z
|
26
|
-
|
27
|
-
|
28
|
-
def phitilde_vec_norm(Nf: int, Nt: int, nx: int) -> np.ndarray:
|
29
|
-
"""Normalize phitilde for inverse frequency domain transform."""
|
30
|
-
|
31
|
-
# Calculate the frequency values
|
32
|
-
ND = Nf * Nt
|
33
|
-
omegas = 2 * np.pi / ND * np.arange(0, Nt // 2 + 1)
|
34
|
-
|
35
|
-
# Calculate the unnormalized phitilde (u_phit)
|
36
|
-
u_phit = phitilde_vec(omegas, Nf, nx)
|
37
|
-
|
38
|
-
# Normalize the phitilde
|
39
|
-
nrm_fctor = np.sqrt(
|
40
|
-
(2 * np.sum(u_phit[1:] ** 2) + u_phit[0] ** 2) * 2 * PI / ND
|
41
|
-
)
|
42
|
-
nrm_fctor /= PI ** (3 / 2) / PI
|
43
|
-
|
44
|
-
return u_phit / nrm_fctor
|
45
|
-
|
46
|
-
|
47
|
-
def phi_vec(Nf: int, nx: int = 4.0, mult: int = 16) -> np.ndarray:
|
48
|
-
"""get time domain phi as fourier transform of phitilde_vec"""
|
49
|
-
insDOM = 1.0 / np.sqrt(PI / Nf)
|
50
|
-
K = mult * 2 * Nf
|
51
|
-
half_K = mult * Nf # np.int64(K/2)
|
52
|
-
|
53
|
-
dom = 2 * PI / K # max frequency is K/2*dom = pi/dt = OM
|
54
|
-
|
55
|
-
DX = np.zeros(K, dtype=np.complex128)
|
56
|
-
|
57
|
-
# zero frequency
|
58
|
-
DX[0] = insDOM
|
59
|
-
|
60
|
-
DX = DX.copy()
|
61
|
-
# postive frequencies
|
62
|
-
DX[1 : half_K + 1] = phitilde_vec(dom * np.arange(1, half_K + 1), Nf, nx)
|
63
|
-
# negative frequencies
|
64
|
-
DX[half_K + 1 :] = phitilde_vec(
|
65
|
-
-dom * np.arange(half_K - 1, 0, -1), Nf, nx
|
66
|
-
)
|
67
|
-
DX = K * fft.ifft(DX, K)
|
68
|
-
|
69
|
-
phi = np.zeros(K)
|
70
|
-
phi[0:half_K] = np.real(DX[half_K:K])
|
71
|
-
phi[half_K:] = np.real(DX[0:half_K])
|
72
|
-
|
73
|
-
nrm = np.sqrt(K / dom) # *np.linalg.norm(phi)
|
74
|
-
|
75
|
-
fac = np.sqrt(2.0) / nrm
|
76
|
-
phi *= fac
|
77
|
-
return phi
|