lisaanalysistools 1.1.20__cp39-cp39-macosx_15_0_arm64.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.
- lisaanalysistools/git_version.py +7 -0
- lisaanalysistools-1.1.20.dist-info/METADATA +281 -0
- lisaanalysistools-1.1.20.dist-info/RECORD +48 -0
- lisaanalysistools-1.1.20.dist-info/WHEEL +5 -0
- lisaanalysistools-1.1.20.dist-info/licenses/LICENSE +201 -0
- lisatools/.dylibs/libgcc_s.1.1.dylib +0 -0
- lisatools/.dylibs/libstdc++.6.dylib +0 -0
- lisatools/__init__.py +90 -0
- lisatools/_version.py +34 -0
- lisatools/analysiscontainer.py +474 -0
- lisatools/cutils/Detector.cu +307 -0
- lisatools/cutils/Detector.hpp +84 -0
- lisatools/cutils/__init__.py +129 -0
- lisatools/cutils/global.hpp +28 -0
- lisatools/cutils/pycppdetector.pyx +256 -0
- lisatools/datacontainer.py +312 -0
- lisatools/detector.py +867 -0
- lisatools/diagnostic.py +990 -0
- lisatools/git_version.py.in +7 -0
- lisatools/orbit_files/equalarmlength-orbits-best-fit-to-esa.h5 +0 -0
- lisatools/orbit_files/equalarmlength-orbits.h5 +0 -0
- lisatools/orbit_files/esa-trailing-orbits.h5 +0 -0
- lisatools/sampling/__init__.py +0 -0
- lisatools/sampling/likelihood.py +882 -0
- lisatools/sampling/moves/__init__.py +0 -0
- lisatools/sampling/moves/skymodehop.py +110 -0
- lisatools/sampling/prior.py +646 -0
- lisatools/sampling/stopping.py +320 -0
- lisatools/sampling/utility.py +411 -0
- lisatools/sensitivity.py +1554 -0
- lisatools/sources/__init__.py +6 -0
- lisatools/sources/bbh/__init__.py +1 -0
- lisatools/sources/bbh/waveform.py +106 -0
- lisatools/sources/defaultresponse.py +37 -0
- lisatools/sources/emri/__init__.py +1 -0
- lisatools/sources/emri/waveform.py +79 -0
- lisatools/sources/gb/__init__.py +1 -0
- lisatools/sources/gb/waveform.py +69 -0
- lisatools/sources/utils.py +459 -0
- lisatools/sources/waveformbase.py +41 -0
- lisatools/stochastic.py +327 -0
- lisatools/utils/__init__.py +0 -0
- lisatools/utils/constants.py +54 -0
- lisatools/utils/exceptions.py +95 -0
- lisatools/utils/parallelbase.py +11 -0
- lisatools/utils/utility.py +122 -0
- lisatools_backend_cpu/git_version.py +7 -0
- lisatools_backend_cpu/pycppdetector.cpython-39-darwin.so +0 -0
|
@@ -0,0 +1,882 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from eryn.state import Branch, BranchSupplemental
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import cupy as cp
|
|
8
|
+
|
|
9
|
+
except (ModuleNotFoundError, ImportError):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Likelihood(object):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
template_model,
|
|
17
|
+
num_channels,
|
|
18
|
+
dt=None,
|
|
19
|
+
df=None,
|
|
20
|
+
f_arr=None,
|
|
21
|
+
parameter_transforms=None,
|
|
22
|
+
use_gpu=False,
|
|
23
|
+
vectorized=False,
|
|
24
|
+
separate_d_h=False,
|
|
25
|
+
return_cupy=False,
|
|
26
|
+
fill_data_noise=False,
|
|
27
|
+
transpose_params=False,
|
|
28
|
+
subset=None,
|
|
29
|
+
adjust_psd=False,
|
|
30
|
+
):
|
|
31
|
+
self.subset = subset
|
|
32
|
+
self.adjust_psd = adjust_psd
|
|
33
|
+
self.transpose_params = transpose_params
|
|
34
|
+
self.template_model = template_model
|
|
35
|
+
|
|
36
|
+
self.parameter_transforms = parameter_transforms
|
|
37
|
+
|
|
38
|
+
self.fill_data_noise = fill_data_noise
|
|
39
|
+
|
|
40
|
+
self.use_gpu = use_gpu
|
|
41
|
+
self.vectorized = vectorized
|
|
42
|
+
|
|
43
|
+
self.num_channels = num_channels
|
|
44
|
+
|
|
45
|
+
self.separate_d_h = separate_d_h
|
|
46
|
+
|
|
47
|
+
if dt is None and df is None and f_arr is None:
|
|
48
|
+
raise ValueError("Must provide dt, df or f_arr.")
|
|
49
|
+
|
|
50
|
+
self.dt, self.df, self.f_arr = dt, df, f_arr
|
|
51
|
+
|
|
52
|
+
if df is not None or f_arr is not None:
|
|
53
|
+
self.frequency_domain = True
|
|
54
|
+
else:
|
|
55
|
+
self.frequency_domain = False
|
|
56
|
+
|
|
57
|
+
self.return_cupy = return_cupy
|
|
58
|
+
|
|
59
|
+
self.noise_has_been_added = False
|
|
60
|
+
self._specific_likelihood_setup()
|
|
61
|
+
|
|
62
|
+
def _specific_likelihood_setup(self):
|
|
63
|
+
if isinstance(self.template_model, list):
|
|
64
|
+
raise ValueError("For single likelihood, template model cannot be a list.")
|
|
65
|
+
if hasattr(self.template_model, "get_ll"):
|
|
66
|
+
self.get_ll = self.template_model.get_ll
|
|
67
|
+
self.like_here = False
|
|
68
|
+
|
|
69
|
+
else:
|
|
70
|
+
self.fill_data_noise = False
|
|
71
|
+
self.like_here = True
|
|
72
|
+
|
|
73
|
+
# TODO: add previously injected signal from a file for example
|
|
74
|
+
# TODO: add SNR scaling, need to read out new distance
|
|
75
|
+
def inject_signal(
|
|
76
|
+
self,
|
|
77
|
+
data_stream=None,
|
|
78
|
+
params=None,
|
|
79
|
+
waveform_kwargs={},
|
|
80
|
+
noise_fn=None,
|
|
81
|
+
noise_args=[],
|
|
82
|
+
noise_kwargs={},
|
|
83
|
+
add_noise=False,
|
|
84
|
+
):
|
|
85
|
+
xp = cp if self.use_gpu else np
|
|
86
|
+
|
|
87
|
+
if params is not None:
|
|
88
|
+
if self.parameter_transforms is not None:
|
|
89
|
+
key = list(self.parameter_transforms.keys())[0]
|
|
90
|
+
params = self.parameter_transforms[key].both_transforms(params)
|
|
91
|
+
|
|
92
|
+
injection_channels = xp.asarray(
|
|
93
|
+
self.template_model(*params, **waveform_kwargs)
|
|
94
|
+
)
|
|
95
|
+
try:
|
|
96
|
+
injection_channels = injection_channels.get()
|
|
97
|
+
|
|
98
|
+
except AttributeError:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
elif data_stream is not None:
|
|
102
|
+
if isinstance(data_stream, list) is False:
|
|
103
|
+
raise ValueError("If data_stream is provided, it must be as a list.")
|
|
104
|
+
try:
|
|
105
|
+
injection_channels = xp.asarray(data_stream).get()
|
|
106
|
+
except AttributeError:
|
|
107
|
+
injection_channels = np.asarray(data_stream)
|
|
108
|
+
|
|
109
|
+
else:
|
|
110
|
+
raise ValueError(
|
|
111
|
+
"Must provide data_stream or params kwargs to inject signal."
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
self.injection_length = len(injection_channels[0])
|
|
115
|
+
|
|
116
|
+
for inj in injection_channels:
|
|
117
|
+
if len(inj) != self.injection_length:
|
|
118
|
+
raise ValueError("Length of all injection channels must match.")
|
|
119
|
+
|
|
120
|
+
if len(injection_channels) != self.num_channels:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
"Number of channels from template_model does not match number of channels declare by user."
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
if isinstance(noise_fn, list):
|
|
126
|
+
if len(noise_fn) != 1 and len(noise_fn) != self.num_channels:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
"Number of noise functions does not match number of channels declared by user."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
elif len(noise_fn) == 1:
|
|
132
|
+
noise_fn = [noise_fn[0] for _ in range(self.num_channels)]
|
|
133
|
+
|
|
134
|
+
else:
|
|
135
|
+
noise_fn = [noise_fn for _ in range(self.num_channels)]
|
|
136
|
+
|
|
137
|
+
if isinstance(noise_kwargs, list):
|
|
138
|
+
if len(noise_kwargs) != 1 and len(noise_kwargs) != self.num_channels:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"Number of noise kwargs does not match number of channels declared by user."
|
|
141
|
+
)
|
|
142
|
+
elif len(noise_kwargs) == 1:
|
|
143
|
+
noise_kwargs = [noise_kwargs[0] for _ in range(self.num_channels)]
|
|
144
|
+
|
|
145
|
+
else:
|
|
146
|
+
noise_kwargs = [noise_kwargs for _ in range(self.num_channels)]
|
|
147
|
+
|
|
148
|
+
if isinstance(noise_args, list):
|
|
149
|
+
if len(noise_args) != 1 and len(noise_args) != self.num_channels:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
"Number of noise args does not match number of channels declared by user."
|
|
152
|
+
)
|
|
153
|
+
elif len(noise_args) == 1:
|
|
154
|
+
noise_args = [noise_args[0] for _ in range(self.num_channels)]
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
noise_args = [noise_args for _ in range(self.num_channels)]
|
|
158
|
+
|
|
159
|
+
if self.frequency_domain:
|
|
160
|
+
if self.df is not None and self.f_arr is None:
|
|
161
|
+
df = self.df
|
|
162
|
+
can_add_noise = True
|
|
163
|
+
freqs = np.arange(self.injection_length) * df
|
|
164
|
+
injection_channels = [inj for inj in injection_channels]
|
|
165
|
+
|
|
166
|
+
else:
|
|
167
|
+
freqs = self.f_arr
|
|
168
|
+
can_add_noise = False
|
|
169
|
+
self.df = (freqs[1] - freqs[0]).item()
|
|
170
|
+
|
|
171
|
+
else:
|
|
172
|
+
dt = self.dt
|
|
173
|
+
freqs = np.fft.rfftfreq(self.injection_length, dt)
|
|
174
|
+
can_add_noise = True
|
|
175
|
+
self.df = df = 1.0 / (self.injection_length * dt)
|
|
176
|
+
|
|
177
|
+
if self.frequency_domain is False:
|
|
178
|
+
injection_channels = [np.fft.rfft(inj) * dt for inj in injection_channels]
|
|
179
|
+
|
|
180
|
+
if not self.adjust_psd:
|
|
181
|
+
psd = [
|
|
182
|
+
noise_fn_temp(freqs, *noise_args_temp, **noise_kwargs_temp)
|
|
183
|
+
for noise_fn_temp, noise_args_temp, noise_kwargs_temp in zip(
|
|
184
|
+
noise_fn, noise_args, noise_kwargs
|
|
185
|
+
)
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
if np.isnan(psd[0][0]):
|
|
189
|
+
for i in range(len(psd)):
|
|
190
|
+
psd[i][0] = 1e100
|
|
191
|
+
|
|
192
|
+
diff_freqs = np.zeros_like(freqs)
|
|
193
|
+
diff_freqs[1:] = np.diff(freqs)
|
|
194
|
+
diff_freqs[0] = diff_freqs[1]
|
|
195
|
+
|
|
196
|
+
self.base_injections = injection_channels
|
|
197
|
+
if add_noise and can_add_noise and self.noise_has_been_added is False:
|
|
198
|
+
raise NotImplementedError
|
|
199
|
+
norm = 0.5 * (1.0 / df) ** 0.5
|
|
200
|
+
noise_to_add = [
|
|
201
|
+
psd_temp ** (1 / 2)
|
|
202
|
+
* (
|
|
203
|
+
np.random.normal(0, norm, len(freqs))
|
|
204
|
+
+ 1j * np.random.normal(0, norm, len(freqs))
|
|
205
|
+
)
|
|
206
|
+
for psd_temp in psd
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
self.noise_to_add = noise_to_add
|
|
210
|
+
|
|
211
|
+
injection_channels = [
|
|
212
|
+
inj + noise for inj, noise in zip(injection_channels, noise_to_add)
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
# TODO: need to check this
|
|
216
|
+
self.noise_likelihood_factor = np.sum(
|
|
217
|
+
[
|
|
218
|
+
1.0 / 2.0 * (2 * np.pi) * np.sum(diff_freqs * psd_temp)
|
|
219
|
+
for psd_temp in psd
|
|
220
|
+
]
|
|
221
|
+
)
|
|
222
|
+
self.noise_has_been_added = True
|
|
223
|
+
|
|
224
|
+
self.noise_added_base_injections = injection_channels
|
|
225
|
+
|
|
226
|
+
# noise weighting
|
|
227
|
+
# injection_channels = [
|
|
228
|
+
# inj * (diff_freqs / psd_temp) ** (1 / 2)
|
|
229
|
+
# for inj, psd_temp in zip(injection_channels, psd)
|
|
230
|
+
# ]
|
|
231
|
+
|
|
232
|
+
# self.psd = xp.asarray(
|
|
233
|
+
# [(diff_freqs / psd_temp) ** (1 / 2) for psd_temp in psd]
|
|
234
|
+
# )
|
|
235
|
+
self.psd = xp.asarray(psd)
|
|
236
|
+
|
|
237
|
+
# if self.like_here is False:
|
|
238
|
+
# self.psd = [xp.asarray(nf.copy()) for nf in self.psd]
|
|
239
|
+
|
|
240
|
+
# if we need to evaluate the psd each time
|
|
241
|
+
else:
|
|
242
|
+
self.noise_fn, self.noise_args, self.noise_kwargs = (
|
|
243
|
+
noise_fn,
|
|
244
|
+
noise_args,
|
|
245
|
+
noise_kwargs,
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
self.freqs = xp.asarray(freqs)
|
|
249
|
+
|
|
250
|
+
if hasattr(self, "injection_channels") is False:
|
|
251
|
+
self.injection_channels = xp.asarray(injection_channels)
|
|
252
|
+
else:
|
|
253
|
+
self.injection_channels += xp.asarray(injection_channels)
|
|
254
|
+
|
|
255
|
+
if self.like_here is False:
|
|
256
|
+
self.injection_channels = [inj.copy() for inj in self.injection_channels]
|
|
257
|
+
|
|
258
|
+
self.data_length = len(self.injection_channels[0])
|
|
259
|
+
|
|
260
|
+
self.start_freq_ind = int(self.freqs[0] / self.df)
|
|
261
|
+
|
|
262
|
+
def get_ll(self, params, data, psd, *args, **kwargs):
|
|
263
|
+
xp = cp if self.use_gpu else np
|
|
264
|
+
|
|
265
|
+
if psd is None:
|
|
266
|
+
psd = self.psd
|
|
267
|
+
|
|
268
|
+
if data is None:
|
|
269
|
+
data = self.injection_channels
|
|
270
|
+
|
|
271
|
+
# TODO: make sure parameter transformations appear in posterior if possible
|
|
272
|
+
num_likes = params.shape[0]
|
|
273
|
+
if self.vectorized:
|
|
274
|
+
template_channels = xp.asarray(
|
|
275
|
+
self.template_model(*params, *args, **kwargs)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
else:
|
|
279
|
+
if isinstance(params[0], np.float64):
|
|
280
|
+
params = [params]
|
|
281
|
+
template_channels = [None for _ in range(len(params))]
|
|
282
|
+
for i, params_i in enumerate(params):
|
|
283
|
+
# np.save("params_last", params_i)
|
|
284
|
+
template_channels[i] = self.template_model(*params_i, *args, **kwargs)
|
|
285
|
+
|
|
286
|
+
template_channels = xp.asarray(template_channels)
|
|
287
|
+
|
|
288
|
+
# template_channels = xp.asarray(
|
|
289
|
+
# [self.template_model(*params_i, *args, **kwargs) for params_i in params]
|
|
290
|
+
# )
|
|
291
|
+
|
|
292
|
+
if self.frequency_domain is False:
|
|
293
|
+
template_channels = xp.fft.rfft(template_channels, axis=-1) * self.dt
|
|
294
|
+
|
|
295
|
+
if psd.ndim == 2:
|
|
296
|
+
psd = psd[xp.newaxis, :, :]
|
|
297
|
+
|
|
298
|
+
h = template_channels
|
|
299
|
+
if self.separate_d_h:
|
|
300
|
+
raise NotImplementedError
|
|
301
|
+
|
|
302
|
+
else:
|
|
303
|
+
if data.ndim == 2:
|
|
304
|
+
data = data[xp.newaxis, :, :]
|
|
305
|
+
|
|
306
|
+
psd[xp.isnan(psd) | xp.isinf(psd)] = 1e20
|
|
307
|
+
# combines all channels into 1D array per likelihood
|
|
308
|
+
|
|
309
|
+
d_minus_h = data - h
|
|
310
|
+
|
|
311
|
+
# TODO: add inds_slice to here from global
|
|
312
|
+
# start_ind = 1 if np.isnan(psd[0, 0, 0]) else 0
|
|
313
|
+
|
|
314
|
+
ll = -(
|
|
315
|
+
1.0
|
|
316
|
+
/ 2.0
|
|
317
|
+
* (
|
|
318
|
+
4.0
|
|
319
|
+
* self.df
|
|
320
|
+
* xp.sum(((d_minus_h.conj() * d_minus_h) / psd).real, axis=(1, 2))
|
|
321
|
+
)
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
if self.adjust_psd:
|
|
325
|
+
ll += xp.sum(xp.log(psd), axis=(1, 2))
|
|
326
|
+
|
|
327
|
+
if self.noise_has_been_added:
|
|
328
|
+
raise NotImplementedError
|
|
329
|
+
# TODO
|
|
330
|
+
ll += self.noise_likelihood_factor
|
|
331
|
+
|
|
332
|
+
out = xp.atleast_1d(ll.squeeze())
|
|
333
|
+
|
|
334
|
+
if self.use_gpu:
|
|
335
|
+
if self.return_cupy:
|
|
336
|
+
return out
|
|
337
|
+
else:
|
|
338
|
+
try:
|
|
339
|
+
return out.get()
|
|
340
|
+
except AttributeError:
|
|
341
|
+
return out
|
|
342
|
+
|
|
343
|
+
else:
|
|
344
|
+
return out
|
|
345
|
+
|
|
346
|
+
def evaluate_psd(
|
|
347
|
+
self,
|
|
348
|
+
noise_params,
|
|
349
|
+
f_arr=None,
|
|
350
|
+
noise_fn: list = None,
|
|
351
|
+
noise_kwargs: list = None,
|
|
352
|
+
noise_groups=None,
|
|
353
|
+
):
|
|
354
|
+
xp = cp if self.use_gpu else np
|
|
355
|
+
|
|
356
|
+
if noise_groups is None:
|
|
357
|
+
if len(np.unique(noise_groups)) != len(noise_groups):
|
|
358
|
+
raise ValueError(
|
|
359
|
+
"If providing noise_groups with adjustable leaf count, need to write custom evaluate_psd function."
|
|
360
|
+
)
|
|
361
|
+
if f_arr is None:
|
|
362
|
+
f_arr = self.freqs
|
|
363
|
+
|
|
364
|
+
assert isinstance(f_arr, xp.ndarray)
|
|
365
|
+
|
|
366
|
+
if noise_fn is None:
|
|
367
|
+
# must be a list
|
|
368
|
+
noise_fn = self.noise_fn
|
|
369
|
+
|
|
370
|
+
if noise_kwargs is None:
|
|
371
|
+
# must be a list
|
|
372
|
+
noise_kwargs = self.noise_kwargs
|
|
373
|
+
|
|
374
|
+
psd = xp.asarray(
|
|
375
|
+
[
|
|
376
|
+
noise_fn_temp(f_arr, *noise_params, **noise_kwargs_temp)
|
|
377
|
+
for noise_fn_temp, noise_kwargs_temp in zip(noise_fn, noise_kwargs)
|
|
378
|
+
]
|
|
379
|
+
).transpose((1, 0, 2))
|
|
380
|
+
return psd
|
|
381
|
+
|
|
382
|
+
def __call__(self, params, data=None, psd=None, *args, **kwargs):
|
|
383
|
+
xp = cp if self.use_gpu else np
|
|
384
|
+
if isinstance(params, list):
|
|
385
|
+
if len(params) != 2:
|
|
386
|
+
ValueError(
|
|
387
|
+
"If providing params for a single source Likelihood, must be an array if just parameters or a list of length 2 where the first entry in the parameter array and the second entry is the parameterization of the noise curve."
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
if not self.adjust_psd:
|
|
391
|
+
raise ValueError(
|
|
392
|
+
"If providing a list with noise parameters, adjust_psd kwarg in __init__ method must be true."
|
|
393
|
+
)
|
|
394
|
+
# must be transpose for noise
|
|
395
|
+
noise_params = params[1].T
|
|
396
|
+
|
|
397
|
+
params = params[0]
|
|
398
|
+
|
|
399
|
+
if psd is not None:
|
|
400
|
+
raise ValueError(
|
|
401
|
+
"If providing noise parameters to likelihood, cannot also provide psd kwarg."
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
else:
|
|
405
|
+
noise_params = None
|
|
406
|
+
|
|
407
|
+
assert isinstance(params, np.ndarray)
|
|
408
|
+
|
|
409
|
+
if self.parameter_transforms is not None:
|
|
410
|
+
keys = list(self.parameter_transforms.keys())
|
|
411
|
+
if len(keys) > 1:
|
|
412
|
+
if len(keys) > 2:
|
|
413
|
+
raise ValueError(
|
|
414
|
+
"parameter_transforms should only contain transforms for the parameters and the noise parameters."
|
|
415
|
+
)
|
|
416
|
+
if "noise_params" not in keys or "noise_params" != keys[0]:
|
|
417
|
+
raise ValueError(
|
|
418
|
+
"'noise_params' must be the model name given for noise information to maintain consistency. It must be provided in the second position in the parameter_transforms dictionary."
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
params = self.parameter_transforms[keys[0]].both_transforms(params)
|
|
422
|
+
|
|
423
|
+
if "noise_params" in keys:
|
|
424
|
+
noise_params = self.parameter_transforms[
|
|
425
|
+
"noise_params"
|
|
426
|
+
].both_transforms(noise_params)
|
|
427
|
+
|
|
428
|
+
# only has to do with params, not noise params
|
|
429
|
+
if self.transpose_params:
|
|
430
|
+
params = params.T
|
|
431
|
+
subset_axis = 1
|
|
432
|
+
else:
|
|
433
|
+
subset_axis = 0
|
|
434
|
+
|
|
435
|
+
num_likes = params.shape[subset_axis]
|
|
436
|
+
|
|
437
|
+
inds_likes = np.arange(num_likes)
|
|
438
|
+
if self.subset is not None:
|
|
439
|
+
if not isinstance(self.subset, int):
|
|
440
|
+
raise ValueError("Subset must be int.")
|
|
441
|
+
subset_sep = np.arange(self.subset, num_likes, self.subset)
|
|
442
|
+
inds_subset = np.split(inds_likes, subset_sep)
|
|
443
|
+
else:
|
|
444
|
+
inds_subset = [inds_likes]
|
|
445
|
+
|
|
446
|
+
out_ll = []
|
|
447
|
+
for inds in inds_subset:
|
|
448
|
+
if subset_axis == 0:
|
|
449
|
+
args_in = (params[inds],)
|
|
450
|
+
else:
|
|
451
|
+
args_in = (params[:, inds],)
|
|
452
|
+
|
|
453
|
+
args_in += args
|
|
454
|
+
|
|
455
|
+
if self.fill_data_noise or self.like_here or noise_params is not None:
|
|
456
|
+
if data is None:
|
|
457
|
+
data = self.injection_channels
|
|
458
|
+
|
|
459
|
+
if noise_params is not None:
|
|
460
|
+
# assumes that there is one set of noise parameters per regular parameters
|
|
461
|
+
psd = self.evaluate_psd(noise_params[:, inds])
|
|
462
|
+
else:
|
|
463
|
+
psd = self.psd
|
|
464
|
+
|
|
465
|
+
args_in += (data, psd)
|
|
466
|
+
|
|
467
|
+
out_ll.append(self.get_ll(*args_in, **kwargs))
|
|
468
|
+
return np.concatenate(out_ll, axis=0)
|
|
469
|
+
|
|
470
|
+
# TODO add Subset
|
|
471
|
+
"""
|
|
472
|
+
num_inds = len(inds_eval)
|
|
473
|
+
ind_skip = np.arange(self.subset, num_inds, self.subset)
|
|
474
|
+
inds_eval_temp = [inds for inds in np.split(inds_eval, ind_skip)]
|
|
475
|
+
|
|
476
|
+
temp = [None for j in range(len(inds_eval_temp))]
|
|
477
|
+
if self.get_d_h:
|
|
478
|
+
d_h = [None for j in range(len(inds_eval_temp))]
|
|
479
|
+
h_h = [None for j in range(len(inds_eval_temp))]
|
|
480
|
+
|
|
481
|
+
for j, inds in enumerate(inds_eval_temp):
|
|
482
|
+
if self.add_inds:
|
|
483
|
+
self.lnlike_kwargs["waveform_kwargs"]["inds"] = inds
|
|
484
|
+
|
|
485
|
+
temp[j] = self.lnlike.get_ll(x_in[inds], **self.lnlike_kwargs)
|
|
486
|
+
if self.get_d_h:
|
|
487
|
+
d_h[j] = self.lnlike.d_h
|
|
488
|
+
h_h[j] = self.lnlike.h_h
|
|
489
|
+
temp = np.concatenate(temp)
|
|
490
|
+
if self.get_d_h:
|
|
491
|
+
h_h = np.concatenate(h_h)
|
|
492
|
+
d_h = np.concatenate(d_h)
|
|
493
|
+
|
|
494
|
+
loglike_vals[inds_eval] = temp
|
|
495
|
+
loglike_vals[np.isnan(loglike_vals)] = np.inf
|
|
496
|
+
|
|
497
|
+
if self.get_d_h:
|
|
498
|
+
d_h_vals[inds_eval] = d_h
|
|
499
|
+
h_h_vals[inds_eval] = h_h
|
|
500
|
+
|
|
501
|
+
array_1 = (
|
|
502
|
+
-loglike_vals if self.add_prior is False else -loglike_vals + prior_vals
|
|
503
|
+
)
|
|
504
|
+
list_of_arrays = [array_1, prior_vals]
|
|
505
|
+
if self.get_d_h:
|
|
506
|
+
list_of_arrays = list_of_arrays + [d_h_vals, h_h_vals]
|
|
507
|
+
return np.asarray(list_of_arrays).T
|
|
508
|
+
@property
|
|
509
|
+
def d_h(self):
|
|
510
|
+
if self.separate_d_h is False:
|
|
511
|
+
raise ValueError("Cannot get dh term if self.separate_d_h if False.")
|
|
512
|
+
|
|
513
|
+
if hasattr(self.template_model, "d_h"):
|
|
514
|
+
return self.template_model.d_h.copy()
|
|
515
|
+
|
|
516
|
+
else:
|
|
517
|
+
raise ValueError("Template model does not have the d_h term available.")
|
|
518
|
+
|
|
519
|
+
@property
|
|
520
|
+
def h_h(self):
|
|
521
|
+
if self.separate_d_h is False:
|
|
522
|
+
raise ValueError("Cannot get dh term if self.separate_d_h if False.")
|
|
523
|
+
|
|
524
|
+
if hasattr(self.template_model, "h_h"):
|
|
525
|
+
return self.template_model.h_h.copy()
|
|
526
|
+
|
|
527
|
+
else:
|
|
528
|
+
raise ValueError("Template model does not have the d_h term available.")
|
|
529
|
+
|
|
530
|
+
"""
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
class GlobalLikelihood(Likelihood):
|
|
534
|
+
def __init__(
|
|
535
|
+
self,
|
|
536
|
+
*args,
|
|
537
|
+
fill_templates=False,
|
|
538
|
+
**kwargs,
|
|
539
|
+
):
|
|
540
|
+
super(GlobalLikelihood, self).__init__(*args, **kwargs)
|
|
541
|
+
self.fill_templates = fill_templates
|
|
542
|
+
|
|
543
|
+
def _specific_likelihood_setup(self):
|
|
544
|
+
if not isinstance(self.template_model, list):
|
|
545
|
+
self.template_model = [self.template_model]
|
|
546
|
+
|
|
547
|
+
if not isinstance(self.parameter_transforms, list):
|
|
548
|
+
self.parameter_transforms = [self.parameter_transforms]
|
|
549
|
+
|
|
550
|
+
if not isinstance(self.vectorized, list):
|
|
551
|
+
self.vectorized = [self.vectorized for _ in self.template_model]
|
|
552
|
+
|
|
553
|
+
assert (
|
|
554
|
+
len(self.template_model)
|
|
555
|
+
== len(self.parameter_transforms)
|
|
556
|
+
== len(self.vectorized)
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
self.like_here = True
|
|
560
|
+
|
|
561
|
+
def get_ll(
|
|
562
|
+
self,
|
|
563
|
+
params,
|
|
564
|
+
groups,
|
|
565
|
+
data,
|
|
566
|
+
psd,
|
|
567
|
+
data_length=None,
|
|
568
|
+
start_freq_ind=None,
|
|
569
|
+
args_list=None,
|
|
570
|
+
kwargs_list=None,
|
|
571
|
+
supps=None,
|
|
572
|
+
branch_supps=None,
|
|
573
|
+
):
|
|
574
|
+
# get supps
|
|
575
|
+
if not isinstance(params, list):
|
|
576
|
+
params = [params]
|
|
577
|
+
if not isinstance(groups, list):
|
|
578
|
+
groups = [groups]
|
|
579
|
+
|
|
580
|
+
assert len(groups) == len(params)
|
|
581
|
+
|
|
582
|
+
if branch_supps is not None:
|
|
583
|
+
if not isinstance(branch_supps, list):
|
|
584
|
+
branch_supps = [branch_supps]
|
|
585
|
+
|
|
586
|
+
else:
|
|
587
|
+
branch_supps = [None for _ in params]
|
|
588
|
+
|
|
589
|
+
assert len(groups) == len(branch_supps)
|
|
590
|
+
|
|
591
|
+
if args_list is None:
|
|
592
|
+
args_list = [[] for _ in params]
|
|
593
|
+
|
|
594
|
+
if kwargs_list is None:
|
|
595
|
+
kwargs_list = [{} for _ in params]
|
|
596
|
+
|
|
597
|
+
# TODO: make sure parameter transformations appear in posterior if possible
|
|
598
|
+
total_groups = np.max(np.concatenate(groups)) + 1
|
|
599
|
+
|
|
600
|
+
if data_length is not None or start_freq_ind is not None:
|
|
601
|
+
if data_length is None:
|
|
602
|
+
data_length = self.data_length
|
|
603
|
+
elif not isinstance(data_length, int):
|
|
604
|
+
raise ValueError("data_length must be int.")
|
|
605
|
+
|
|
606
|
+
if start_freq_ind is None:
|
|
607
|
+
start_freq_ind = self.start_freq_ind
|
|
608
|
+
elif not isinstance(start_freq_ind, int):
|
|
609
|
+
raise ValueError("start_freq_ind must be int.")
|
|
610
|
+
|
|
611
|
+
if (start_freq_ind - self.start_freq_ind) + data_length > self.data_length:
|
|
612
|
+
raise ValueError("start_freq_ind + data_length > full data length.")
|
|
613
|
+
|
|
614
|
+
if kwargs_list is None:
|
|
615
|
+
kwargs_list = [{}]
|
|
616
|
+
|
|
617
|
+
for kwargs in kwargs_list:
|
|
618
|
+
if isinstance(kwargs, dict):
|
|
619
|
+
kwargs["start_freq_ind"] = start_freq_ind
|
|
620
|
+
|
|
621
|
+
else:
|
|
622
|
+
start_freq_ind = self.start_freq_ind
|
|
623
|
+
data_length = self.data_length
|
|
624
|
+
|
|
625
|
+
if data is None:
|
|
626
|
+
data = self.injection_channels[xp.newaxis, :, inds_slice]
|
|
627
|
+
|
|
628
|
+
if psd is None:
|
|
629
|
+
psd = self.psd[xp.newaxis, :, inds_slice]
|
|
630
|
+
|
|
631
|
+
if supps is None or "data_minus_template" not in supps:
|
|
632
|
+
template_all = xp.zeros(
|
|
633
|
+
(total_groups, self.num_channels, data_length),
|
|
634
|
+
dtype=xp.complex128,
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
for i, (
|
|
638
|
+
params_i,
|
|
639
|
+
groups_i,
|
|
640
|
+
args_i,
|
|
641
|
+
kwargs_i,
|
|
642
|
+
tm_i,
|
|
643
|
+
vec_i,
|
|
644
|
+
branch_supp_i,
|
|
645
|
+
) in enumerate(
|
|
646
|
+
zip(
|
|
647
|
+
params,
|
|
648
|
+
groups,
|
|
649
|
+
args_list,
|
|
650
|
+
kwargs_list,
|
|
651
|
+
self.template_model,
|
|
652
|
+
self.vectorized,
|
|
653
|
+
branch_supps,
|
|
654
|
+
)
|
|
655
|
+
):
|
|
656
|
+
# TODO: make fill templates adjustable per model
|
|
657
|
+
if not self.fill_templates: # False
|
|
658
|
+
if vec_i:
|
|
659
|
+
template_channels = xp.asarray(
|
|
660
|
+
tm_i(params_i, *args_i, **kwargs_i)
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
else:
|
|
664
|
+
template_channels = xp.asarray(
|
|
665
|
+
[
|
|
666
|
+
tm_i(params_ij, *args_i, **kwargs_i)
|
|
667
|
+
for params_ij in params_i.T
|
|
668
|
+
]
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
if self.frequency_domain is False:
|
|
672
|
+
# TODO: vectorize this
|
|
673
|
+
# 2: is removal of DC component + right summation approximation
|
|
674
|
+
template_channels = (
|
|
675
|
+
xp.fft.rfft(template_channels, axis=-1) * self.dt
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
# TODO: could put this in c?
|
|
679
|
+
for group_ij in np.unique(groups_i):
|
|
680
|
+
inds1 = np.where(groups_i == group_ij)
|
|
681
|
+
template_all[group_ij] += template_channels[inds1].sum(axis=0)
|
|
682
|
+
|
|
683
|
+
else: # model will fill templates
|
|
684
|
+
kwargs_i_in = kwargs_i.copy()
|
|
685
|
+
if branch_supp_i is not None:
|
|
686
|
+
kwargs_i_in["branch_supps"] = branch_supp_i
|
|
687
|
+
if supps is not None:
|
|
688
|
+
kwargs_i_in["supps"] = supps
|
|
689
|
+
|
|
690
|
+
if vec_i:
|
|
691
|
+
tm_i.generate_global_template(
|
|
692
|
+
params_i, groups_i, template_all, *args_i, **kwargs_i_in
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
else:
|
|
696
|
+
for params_ij, groups_ij in zip(params_i.T, groups_i):
|
|
697
|
+
tm_i.generate_global_template(
|
|
698
|
+
params_ij,
|
|
699
|
+
groups_ij,
|
|
700
|
+
template_all,
|
|
701
|
+
*args_i,
|
|
702
|
+
**kwargs_i_in,
|
|
703
|
+
)
|
|
704
|
+
breakpoint()
|
|
705
|
+
# accelerate ?
|
|
706
|
+
d_minus_h = (data - template_all).reshape(
|
|
707
|
+
total_groups, len(self.injection_channels), -1
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
else:
|
|
711
|
+
d_minus_h = supps["data_minus_template"]
|
|
712
|
+
|
|
713
|
+
start_here = start_freq_ind - self.start_freq_ind
|
|
714
|
+
end_here = start_here + data_length
|
|
715
|
+
inds_slice = slice(start_here, end_here)
|
|
716
|
+
|
|
717
|
+
# avoid f = 0
|
|
718
|
+
start_ind = 1 if np.isnan(psd[0, 0, inds_slice][0]) else 0
|
|
719
|
+
|
|
720
|
+
self.signal_ll = -(
|
|
721
|
+
1.0
|
|
722
|
+
/ 2.0
|
|
723
|
+
* (
|
|
724
|
+
4.0
|
|
725
|
+
* self.df
|
|
726
|
+
* xp.sum(
|
|
727
|
+
(
|
|
728
|
+
d_minus_h[:, :, start_ind:].conj() * d_minus_h[:, :, start_ind:]
|
|
729
|
+
).real
|
|
730
|
+
/ psd[:, :, start_ind:],
|
|
731
|
+
axis=(1, 2),
|
|
732
|
+
)
|
|
733
|
+
)
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
ll = self.signal_ll.copy()
|
|
737
|
+
if self.adjust_psd:
|
|
738
|
+
self.noise_ll = -xp.sum(xp.log(psd), axis=(1, 2))
|
|
739
|
+
ll += self.noise_ll
|
|
740
|
+
|
|
741
|
+
out = xp.atleast_1d(ll.squeeze())
|
|
742
|
+
if xp.any(xp.isnan(out)):
|
|
743
|
+
breakpoint()
|
|
744
|
+
if self.use_gpu:
|
|
745
|
+
if self.return_cupy:
|
|
746
|
+
return out
|
|
747
|
+
else:
|
|
748
|
+
try:
|
|
749
|
+
return out.get()
|
|
750
|
+
except AttributeError:
|
|
751
|
+
return out
|
|
752
|
+
|
|
753
|
+
else:
|
|
754
|
+
return out
|
|
755
|
+
|
|
756
|
+
def __call__(self, params, groups, *args, data=None, psd=None, **kwargs):
|
|
757
|
+
if isinstance(params, np.ndarray):
|
|
758
|
+
params = [params]
|
|
759
|
+
elif not isinstance(params, list):
|
|
760
|
+
raise ValueError("params must be np.ndarray or list of np.ndarray.")
|
|
761
|
+
|
|
762
|
+
if isinstance(groups, np.ndarray):
|
|
763
|
+
groups = [groups]
|
|
764
|
+
elif not isinstance(groups, list):
|
|
765
|
+
raise ValueError("groups must be np.ndarray or list of np.ndarray.")
|
|
766
|
+
|
|
767
|
+
# if np.any(np.abs(params[0][:, 4]) > 1.0) or np.any(np.abs(params[0][:, 7]) > 1.0):
|
|
768
|
+
# breakpoint()
|
|
769
|
+
|
|
770
|
+
if self.parameter_transforms is not None:
|
|
771
|
+
for i, (params_i, transform_i) in enumerate(
|
|
772
|
+
zip(params, self.parameter_transforms)
|
|
773
|
+
):
|
|
774
|
+
params[i] = transform_i.both_transforms(params_i.copy())
|
|
775
|
+
|
|
776
|
+
else:
|
|
777
|
+
params = [params_i.T for params_i in params]
|
|
778
|
+
|
|
779
|
+
for par_i in params:
|
|
780
|
+
if np.any(np.isnan(par_i)):
|
|
781
|
+
breakpoint()
|
|
782
|
+
|
|
783
|
+
if self.adjust_psd:
|
|
784
|
+
assert len(params) > 1
|
|
785
|
+
assert len(groups) > 1
|
|
786
|
+
|
|
787
|
+
noise_params = params[-1]
|
|
788
|
+
noise_groups = groups[-1]
|
|
789
|
+
|
|
790
|
+
if "branch_supps" in kwargs:
|
|
791
|
+
assert len(kwargs["branch_supps"]) == len(groups)
|
|
792
|
+
noise_supps = kwargs["branch_supps"][-1]
|
|
793
|
+
kwargs["branch_supps"] = kwargs["branch_supps"][:-1]
|
|
794
|
+
|
|
795
|
+
params = params[:-1]
|
|
796
|
+
groups = groups[:-1]
|
|
797
|
+
|
|
798
|
+
else:
|
|
799
|
+
noise_params = None
|
|
800
|
+
|
|
801
|
+
args_in = [params] + [groups] + list(args)
|
|
802
|
+
|
|
803
|
+
if noise_params is not None:
|
|
804
|
+
psd = self.evaluate_psd(noise_params.T, noise_groups=noise_groups)
|
|
805
|
+
else:
|
|
806
|
+
if psd is None:
|
|
807
|
+
psd = self.psd
|
|
808
|
+
if self.like_here:
|
|
809
|
+
if isinstance(psd, list):
|
|
810
|
+
psd = xp.asarray(psd)[None, :, :] # .transpose(1, 0, 2)
|
|
811
|
+
|
|
812
|
+
args_in += args
|
|
813
|
+
|
|
814
|
+
if self.fill_data_noise or self.like_here or noise_params is not None:
|
|
815
|
+
if data is None:
|
|
816
|
+
data = self.injection_channels
|
|
817
|
+
|
|
818
|
+
args_in += [data, psd]
|
|
819
|
+
|
|
820
|
+
return self.get_ll(*args_in, **kwargs)
|
|
821
|
+
|
|
822
|
+
# TODO add Subset
|
|
823
|
+
"""
|
|
824
|
+
num_inds = len(inds_eval)
|
|
825
|
+
ind_skip = np.arange(self.subset, num_inds, self.subset)
|
|
826
|
+
inds_eval_temp = [inds for inds in np.split(inds_eval, ind_skip)]
|
|
827
|
+
|
|
828
|
+
temp = [None for j in range(len(inds_eval_temp))]
|
|
829
|
+
if self.get_d_h:
|
|
830
|
+
d_h = [None for j in range(len(inds_eval_temp))]
|
|
831
|
+
h_h = [None for j in range(len(inds_eval_temp))]
|
|
832
|
+
|
|
833
|
+
for j, inds in enumerate(inds_eval_temp):
|
|
834
|
+
if self.add_inds:
|
|
835
|
+
self.lnlike_kwargs["waveform_kwargs"]["inds"] = inds
|
|
836
|
+
|
|
837
|
+
temp[j] = self.lnlike.get_ll(x_in[inds], **self.lnlike_kwargs)
|
|
838
|
+
if self.get_d_h:
|
|
839
|
+
d_h[j] = self.lnlike.d_h
|
|
840
|
+
h_h[j] = self.lnlike.h_h
|
|
841
|
+
temp = np.concatenate(temp)
|
|
842
|
+
if self.get_d_h:
|
|
843
|
+
h_h = np.concatenate(h_h)
|
|
844
|
+
d_h = np.concatenate(d_h)
|
|
845
|
+
|
|
846
|
+
loglike_vals[inds_eval] = temp
|
|
847
|
+
loglike_vals[np.isnan(loglike_vals)] = np.inf
|
|
848
|
+
|
|
849
|
+
if self.get_d_h:
|
|
850
|
+
d_h_vals[inds_eval] = d_h
|
|
851
|
+
h_h_vals[inds_eval] = h_h
|
|
852
|
+
|
|
853
|
+
array_1 = (
|
|
854
|
+
-loglike_vals if self.add_prior is False else -loglike_vals + prior_vals
|
|
855
|
+
)
|
|
856
|
+
list_of_arrays = [array_1, prior_vals]
|
|
857
|
+
if self.get_d_h:
|
|
858
|
+
list_of_arrays = list_of_arrays + [d_h_vals, h_h_vals]
|
|
859
|
+
return np.asarray(list_of_arrays).T
|
|
860
|
+
@property
|
|
861
|
+
def d_h(self):
|
|
862
|
+
if self.separate_d_h is False:
|
|
863
|
+
raise ValueError("Cannot get dh term if self.separate_d_h if False.")
|
|
864
|
+
|
|
865
|
+
if hasattr(self.template_model, "d_h"):
|
|
866
|
+
return self.template_model.d_h.copy()
|
|
867
|
+
|
|
868
|
+
else:
|
|
869
|
+
raise ValueError("Template model does not have the d_h term available.")
|
|
870
|
+
|
|
871
|
+
@property
|
|
872
|
+
def h_h(self):
|
|
873
|
+
if self.separate_d_h is False:
|
|
874
|
+
raise ValueError("Cannot get dh term if self.separate_d_h if False.")
|
|
875
|
+
|
|
876
|
+
if hasattr(self.template_model, "h_h"):
|
|
877
|
+
return self.template_model.h_h.copy()
|
|
878
|
+
|
|
879
|
+
else:
|
|
880
|
+
raise ValueError("Template model does not have the d_h term available.")
|
|
881
|
+
|
|
882
|
+
"""
|