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.
Files changed (48) hide show
  1. lisaanalysistools/git_version.py +7 -0
  2. lisaanalysistools-1.1.20.dist-info/METADATA +281 -0
  3. lisaanalysistools-1.1.20.dist-info/RECORD +48 -0
  4. lisaanalysistools-1.1.20.dist-info/WHEEL +5 -0
  5. lisaanalysistools-1.1.20.dist-info/licenses/LICENSE +201 -0
  6. lisatools/.dylibs/libgcc_s.1.1.dylib +0 -0
  7. lisatools/.dylibs/libstdc++.6.dylib +0 -0
  8. lisatools/__init__.py +90 -0
  9. lisatools/_version.py +34 -0
  10. lisatools/analysiscontainer.py +474 -0
  11. lisatools/cutils/Detector.cu +307 -0
  12. lisatools/cutils/Detector.hpp +84 -0
  13. lisatools/cutils/__init__.py +129 -0
  14. lisatools/cutils/global.hpp +28 -0
  15. lisatools/cutils/pycppdetector.pyx +256 -0
  16. lisatools/datacontainer.py +312 -0
  17. lisatools/detector.py +867 -0
  18. lisatools/diagnostic.py +990 -0
  19. lisatools/git_version.py.in +7 -0
  20. lisatools/orbit_files/equalarmlength-orbits-best-fit-to-esa.h5 +0 -0
  21. lisatools/orbit_files/equalarmlength-orbits.h5 +0 -0
  22. lisatools/orbit_files/esa-trailing-orbits.h5 +0 -0
  23. lisatools/sampling/__init__.py +0 -0
  24. lisatools/sampling/likelihood.py +882 -0
  25. lisatools/sampling/moves/__init__.py +0 -0
  26. lisatools/sampling/moves/skymodehop.py +110 -0
  27. lisatools/sampling/prior.py +646 -0
  28. lisatools/sampling/stopping.py +320 -0
  29. lisatools/sampling/utility.py +411 -0
  30. lisatools/sensitivity.py +1554 -0
  31. lisatools/sources/__init__.py +6 -0
  32. lisatools/sources/bbh/__init__.py +1 -0
  33. lisatools/sources/bbh/waveform.py +106 -0
  34. lisatools/sources/defaultresponse.py +37 -0
  35. lisatools/sources/emri/__init__.py +1 -0
  36. lisatools/sources/emri/waveform.py +79 -0
  37. lisatools/sources/gb/__init__.py +1 -0
  38. lisatools/sources/gb/waveform.py +69 -0
  39. lisatools/sources/utils.py +459 -0
  40. lisatools/sources/waveformbase.py +41 -0
  41. lisatools/stochastic.py +327 -0
  42. lisatools/utils/__init__.py +0 -0
  43. lisatools/utils/constants.py +54 -0
  44. lisatools/utils/exceptions.py +95 -0
  45. lisatools/utils/parallelbase.py +11 -0
  46. lisatools/utils/utility.py +122 -0
  47. lisatools_backend_cpu/git_version.py +7 -0
  48. 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
+ """