pywavelet 0.0.1b0__py3-none-any.whl → 0.0.5__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) 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 +3 -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/utils.py +64 -0
  22. pywavelet-0.0.5.dist-info/METADATA +35 -0
  23. pywavelet-0.0.5.dist-info/RECORD +25 -0
  24. {pywavelet-0.0.1b0.dist-info → pywavelet-0.0.5.dist-info}/WHEEL +1 -1
  25. pywavelet/fft_funcs.py +0 -16
  26. pywavelet/likelihood/__init__.py +0 -0
  27. pywavelet/likelihood/likelihood_base.py +0 -9
  28. pywavelet/likelihood/whittle.py +0 -24
  29. pywavelet/transforms/common.py +0 -77
  30. pywavelet/transforms/from_wavelets/__init__.py +0 -25
  31. pywavelet/transforms/to_wavelets/__init__.py +0 -52
  32. pywavelet/transforms/to_wavelets/transform_freq_funcs.py +0 -84
  33. pywavelet/transforms/to_wavelets/transform_time_funcs.py +0 -63
  34. pywavelet/utils/__init__.py +0 -0
  35. pywavelet/utils/fisher_matrix.py +0 -6
  36. pywavelet/utils/snr.py +0 -37
  37. pywavelet/waveform_generator/__init__.py +0 -0
  38. pywavelet/waveform_generator/build_lookup_table.py +0 -0
  39. pywavelet/waveform_generator/generators/__init__.py +0 -2
  40. pywavelet/waveform_generator/generators/functional_waveform_generator.py +0 -33
  41. pywavelet/waveform_generator/generators/lookuptable_waveform_generator.py +0 -15
  42. pywavelet/waveform_generator/generators/rom_waveform_generator.py +0 -0
  43. pywavelet/waveform_generator/waveform_generator.py +0 -14
  44. pywavelet-0.0.1b0.dist-info/METADATA +0 -35
  45. pywavelet-0.0.1b0.dist-info/RECORD +0 -29
  46. {pywavelet-0.0.1b0.dist-info → pywavelet-0.0.5.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__()
pywavelet/utils.py ADDED
@@ -0,0 +1,64 @@
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
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(d: Wavelet, h: Wavelet, PSD: Wavelet) -> float:
38
+ return np.nansum((d.data * h.data) / PSD.data)
39
+
40
+ def compute_snr(d:Wavelet, h: Wavelet, PSD: Wavelet) -> float:
41
+ """Compute the SNR of a model h[ti,fi] given freqseries d[ti,fi] and PSD[ti,fi].
42
+
43
+ SNR(h) = Sum_{ti,fi} [ h_hat[ti,fi] d[ti,fi] / PSD[ti,fi]
44
+
45
+ Parameters
46
+ ----------
47
+ h : np.ndarray
48
+ The model in the wavelet domain (binned in [ti,fi]).
49
+ d : np.ndarray
50
+ The freqseries in the wavelet domain (binned in [ti,fi]).
51
+ PSD : np.ndarray
52
+ The PSD in the wavelet domain (binned in [ti,fi]).
53
+
54
+ Returns
55
+ -------
56
+ float
57
+ The SNR of the model h given freqseries d and PSD.
58
+
59
+ """
60
+ return np.sqrt(noise_weighted_inner_product(d, h, PSD))
61
+
62
+
63
+ def compute_likelihood(data:Wavelet, template:Wavelet, psd:Wavelet) -> float:
64
+ return -0.5 * np.nansum((data.data - template.data) ** 2 / psd.data)
@@ -0,0 +1,35 @@
1
+ Metadata-Version: 2.1
2
+ Name: pywavelet
3
+ Version: 0.0.5
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,25 @@
1
+ pywavelet/__init__.py,sha256=zcK3Qj4wTrGZF1rU3aT6yA9LvliAOD4DVOY7gNfHhCI,53
2
+ pywavelet/_version.py,sha256=EJB7__SNK9kQS_SWZB_U4DHJ3P8ftF6etZEihTYnuXE,411
3
+ pywavelet/logger.py,sha256=u5Et6QLUU0IuTnPnxfev5-4GYmihTo7_yBCTf-rVUsA,377
4
+ pywavelet/utils.py,sha256=rbmZ-PrnFstq05kh6xj8emuQ3-7oXqZinU03bpg3N7A,1746
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=4wUTVBk6A02xjZUY_w056eUZurYI9vVfa--I3Q6Udng,109
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-0.0.5.dist-info/METADATA,sha256=BaLK8jbUqss_8BeGWOlf_031PoCAlEV4rmuSaitQd0E,1307
23
+ pywavelet-0.0.5.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
24
+ pywavelet-0.0.5.dist-info/top_level.txt,sha256=g0Ezt0Rg0X-nrd-a0pAXKVRkuWNsF2M9Ynsjb9b2UYQ,10
25
+ pywavelet-0.0.5.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
@@ -1,25 +0,0 @@
1
- from pywavelet import fft_funcs as fft
2
- from pywavelet.transforms.common import phi_vec, phitilde_vec_norm
3
-
4
- from .inverse_wavelet_freq_funcs import inverse_wavelet_freq_helper_fast
5
- from .inverse_wavelet_time_funcs import inverse_wavelet_time_helper_fast
6
-
7
-
8
- def from_wavelet_to_time(wave_in, Nf, Nt, nx=4.0, mult=32):
9
- """fast inverse wavelet transform to time domain"""
10
- mult = min(mult, Nt // 2) # make sure K isn't bigger than ND
11
- phi = phi_vec(Nf, nx=nx, mult=mult) / 2
12
-
13
- return inverse_wavelet_time_helper_fast(wave_in, phi, Nf, Nt, mult)
14
-
15
-
16
- def from_wavelet_to_freq_to_time(wave_in, Nf, Nt, nx=4.0):
17
- """inverse wavlet transform to time domain via fourier transform of frequency domain"""
18
- res_f = from_wavelet_to_freq(wave_in, Nf, Nt, nx)
19
- return fft.irfft(res_f)
20
-
21
-
22
- def from_wavelet_to_freq(wave_in, Nf, Nt, nx=4.0):
23
- """inverse wavelet transform to freq domain signal"""
24
- phif = phitilde_vec_norm(Nf, Nt, nx)
25
- return inverse_wavelet_freq_helper_fast(wave_in, phif, Nf, Nt)