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.
Files changed (47) hide show
  1. pywavelet/__init__.py +1 -1
  2. pywavelet/_version.py +2 -2
  3. pywavelet/logger.py +6 -7
  4. pywavelet/transforms/__init__.py +10 -10
  5. pywavelet/transforms/forward/__init__.py +4 -0
  6. pywavelet/transforms/forward/from_freq.py +80 -0
  7. pywavelet/transforms/forward/from_time.py +66 -0
  8. pywavelet/transforms/forward/main.py +128 -0
  9. pywavelet/transforms/forward/wavelet_bins.py +58 -0
  10. pywavelet/transforms/inverse/__init__.py +3 -0
  11. pywavelet/transforms/inverse/main.py +96 -0
  12. pywavelet/transforms/{from_wavelets/inverse_wavelet_freq_funcs.py → inverse/to_freq.py} +43 -32
  13. pywavelet/transforms/{from_wavelets/inverse_wavelet_time_funcs.py → inverse/to_time.py} +49 -21
  14. pywavelet/transforms/phi_computer.py +152 -0
  15. pywavelet/transforms/types/__init__.py +4 -0
  16. pywavelet/transforms/types/common.py +53 -0
  17. pywavelet/transforms/types/frequencyseries.py +237 -0
  18. pywavelet/transforms/types/plotting.py +341 -0
  19. pywavelet/transforms/types/timeseries.py +280 -0
  20. pywavelet/transforms/types/wavelet.py +374 -0
  21. pywavelet/transforms/types/wavelet_mask.py +34 -0
  22. pywavelet/utils.py +76 -0
  23. pywavelet-0.1.0.dist-info/METADATA +35 -0
  24. pywavelet-0.1.0.dist-info/RECORD +26 -0
  25. {pywavelet-0.0.1b0.dist-info → pywavelet-0.1.0.dist-info}/WHEEL +1 -1
  26. pywavelet/fft_funcs.py +0 -16
  27. pywavelet/likelihood/__init__.py +0 -0
  28. pywavelet/likelihood/likelihood_base.py +0 -9
  29. pywavelet/likelihood/whittle.py +0 -24
  30. pywavelet/transforms/common.py +0 -77
  31. pywavelet/transforms/from_wavelets/__init__.py +0 -25
  32. pywavelet/transforms/to_wavelets/__init__.py +0 -52
  33. pywavelet/transforms/to_wavelets/transform_freq_funcs.py +0 -84
  34. pywavelet/transforms/to_wavelets/transform_time_funcs.py +0 -63
  35. pywavelet/utils/__init__.py +0 -0
  36. pywavelet/utils/fisher_matrix.py +0 -6
  37. pywavelet/utils/snr.py +0 -37
  38. pywavelet/waveform_generator/__init__.py +0 -0
  39. pywavelet/waveform_generator/build_lookup_table.py +0 -0
  40. pywavelet/waveform_generator/generators/__init__.py +0 -2
  41. pywavelet/waveform_generator/generators/functional_waveform_generator.py +0 -33
  42. pywavelet/waveform_generator/generators/lookuptable_waveform_generator.py +0 -15
  43. pywavelet/waveform_generator/generators/rom_waveform_generator.py +0 -0
  44. pywavelet/waveform_generator/waveform_generator.py +0 -14
  45. pywavelet-0.0.1b0.dist-info/METADATA +0 -35
  46. pywavelet-0.0.1b0.dist-info/RECORD +0 -29
  47. {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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: setuptools (75.3.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
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
File without changes
@@ -1,9 +0,0 @@
1
- from abc import ABC, abstractmethod
2
-
3
-
4
- class LikelihoodBase(ABC):
5
- def __init__(self):
6
- pass
7
-
8
- def log_likelihood(self, data, model):
9
- pass
@@ -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
@@ -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