lisaanalysistools 1.1.19__cp310-cp310-macosx_14_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 (49) hide show
  1. lisaanalysistools/git_version.py +7 -0
  2. lisaanalysistools-1.1.19.dist-info/METADATA +281 -0
  3. lisaanalysistools-1.1.19.dist-info/RECORD +49 -0
  4. lisaanalysistools-1.1.19.dist-info/WHEEL +5 -0
  5. lisaanalysistools-1.1.19.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.pxd +44 -0
  16. lisatools/cutils/pycppdetector.pyx +222 -0
  17. lisatools/datacontainer.py +312 -0
  18. lisatools/detector.py +867 -0
  19. lisatools/diagnostic.py +990 -0
  20. lisatools/git_version.py.in +7 -0
  21. lisatools/orbit_files/equalarmlength-orbits-best-fit-to-esa.h5 +0 -0
  22. lisatools/orbit_files/equalarmlength-orbits.h5 +0 -0
  23. lisatools/orbit_files/esa-trailing-orbits.h5 +0 -0
  24. lisatools/sampling/__init__.py +0 -0
  25. lisatools/sampling/likelihood.py +882 -0
  26. lisatools/sampling/moves/__init__.py +0 -0
  27. lisatools/sampling/moves/skymodehop.py +110 -0
  28. lisatools/sampling/prior.py +646 -0
  29. lisatools/sampling/stopping.py +320 -0
  30. lisatools/sampling/utility.py +411 -0
  31. lisatools/sensitivity.py +1554 -0
  32. lisatools/sources/__init__.py +6 -0
  33. lisatools/sources/bbh/__init__.py +1 -0
  34. lisatools/sources/bbh/waveform.py +106 -0
  35. lisatools/sources/defaultresponse.py +37 -0
  36. lisatools/sources/emri/__init__.py +1 -0
  37. lisatools/sources/emri/waveform.py +79 -0
  38. lisatools/sources/gb/__init__.py +1 -0
  39. lisatools/sources/gb/waveform.py +69 -0
  40. lisatools/sources/utils.py +459 -0
  41. lisatools/sources/waveformbase.py +41 -0
  42. lisatools/stochastic.py +327 -0
  43. lisatools/utils/__init__.py +0 -0
  44. lisatools/utils/constants.py +54 -0
  45. lisatools/utils/exceptions.py +95 -0
  46. lisatools/utils/parallelbase.py +11 -0
  47. lisatools/utils/utility.py +122 -0
  48. lisatools_backend_cpu/git_version.py +7 -0
  49. lisatools_backend_cpu/pycppdetector.cpython-310-darwin.so +0 -0
@@ -0,0 +1,459 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ from typing import Any, Tuple, List, Optional
5
+
6
+ from ..diagnostic import snr as snr_func
7
+ from lisatools.diagnostic import (
8
+ covariance,
9
+ plot_covariance_corner,
10
+ plot_covariance_contour,
11
+ )
12
+ from ..sensitivity import A1TDISens, Sensitivity
13
+ from .waveformbase import SNRWaveform, AETTDIWaveform
14
+ from ..detector import LISAModel
15
+ from ..utils.constants import *
16
+ from eryn.utils import TransformContainer
17
+
18
+
19
+ class CalculationController:
20
+ """Wrapper class to controll investigative computations.
21
+
22
+ Args:
23
+ aet_template_gen: Template waveform generator.
24
+ model: Model for LISA.
25
+ psd_kwargs: psd_kwargs for :func:`lisatools.sensitivity.get_sensitivity`.
26
+ Tobs: Observation time in **years**.
27
+ dt: Timestep in seconds.
28
+ psd: Sensitivity curve type to use. Default is :class:`A1TDISens`
29
+ because we ignore ``T`` in these simplified calculations and
30
+ the ``A`` and ``E`` sensitivities are equivalent.
31
+
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ aet_template_gen: SNRWaveform | AETTDIWaveform,
37
+ model: LISAModel,
38
+ psd_kwargs: dict,
39
+ Tobs: float,
40
+ dt: float,
41
+ psd: Sensitivity = A1TDISens,
42
+ ) -> None:
43
+
44
+ # Store everything.
45
+ self.aet_template_gen = aet_template_gen
46
+ self.psd_kwargs = psd_kwargs
47
+ self.model = model
48
+ self.psd = psd
49
+ self.Tobs = Tobs
50
+ self.dt = dt
51
+
52
+ @property
53
+ def parameter_transforms(self) -> TransformContainer:
54
+ """Transform parameters from sampling basis to waveform basis."""
55
+ return self._parameter_transforms
56
+
57
+ @parameter_transforms.setter
58
+ def parameter_transforms(self, parameter_transforms: TransformContainer) -> None:
59
+ """Set parameter transforms."""
60
+ assert isinstance(parameter_transforms, TransformContainer)
61
+ self._parameter_transforms = parameter_transforms
62
+
63
+ def get_snr(self, *params: Any, **kwargs: Any) -> float:
64
+ """Compute the SNR.
65
+
66
+ Args:
67
+ *params: Parameters to go into waveform generator.
68
+ **kwargs: Kwargs for waveform generator.
69
+
70
+ Returns:
71
+ SNR.
72
+
73
+ """
74
+ # generate waveform
75
+ a_chan, e_chan, t_chan = self.aet_template_gen(*params, **kwargs)
76
+
77
+ # ignore t channel for snr computation
78
+ # compute SNR
79
+ opt_snr = snr_func(
80
+ [a_chan, e_chan],
81
+ psd=self.psd,
82
+ psd_kwargs={**self.psd_kwargs, "model": self.model},
83
+ dt=self.aet_template_gen.dt,
84
+ f_arr=self.aet_template_gen.f_arr,
85
+ df=self.aet_template_gen.df,
86
+ )
87
+
88
+ # prepare outputs
89
+ self.f_arr = self.aet_template_gen.f_arr
90
+ self.last_output = (a_chan, e_chan)
91
+
92
+ return opt_snr
93
+
94
+
95
+ def mT_q_to_m1_m2(mT: float, q: float) -> Tuple[float, float]:
96
+ """
97
+ q <= 1.0
98
+ """
99
+ return (mT / (1 + q), (q * mT) / (1 + q))
100
+
101
+
102
+ def dist_convert(x: float) -> float:
103
+ return x * 1e9 * PC_SI
104
+
105
+
106
+ def time_convert(x: float) -> float:
107
+ return x * YRSID_SI
108
+
109
+
110
+ class BBHCalculationController(CalculationController):
111
+ """Calculation controller for BBHs.
112
+
113
+ Args:
114
+ *args: Args for :class:`CalculationController`.
115
+ *kwargs: Kwargs for :class:`CalculationController`.
116
+
117
+ """
118
+
119
+ def __init__(self, *args: Any, **kwargs: Any):
120
+
121
+ # transforms from information matrix basis
122
+ parameter_transforms = {
123
+ 0: np.exp,
124
+ 4: dist_convert,
125
+ 7: np.arccos,
126
+ 9: np.arcsin,
127
+ 11: time_convert,
128
+ (0, 1): mT_q_to_m1_m2,
129
+ }
130
+ self.transform_fn = TransformContainer(
131
+ parameter_transforms=parameter_transforms, fill_dict=None # fill_dict
132
+ )
133
+
134
+ super(BBHCalculationController, self).__init__(*args, **kwargs)
135
+
136
+ def get_snr(self, *args: Any, **kwargs: Any) -> float:
137
+ """Compute the SNR.
138
+
139
+ Args:
140
+ *params: Parameters to go into waveform generator.
141
+ **kwargs: Kwargs for waveform generator.
142
+
143
+ Returns:
144
+ SNR.
145
+
146
+ """
147
+ # adjust kwargs to simplify calculation
148
+ if "t_obs_start" not in kwargs:
149
+ kwargs["shift_t_limits"] = True
150
+ kwargs["t_obs_start"] = 0.0
151
+ kwargs["t_obs_end"] = self.Tobs
152
+ # compute snr
153
+ return super(BBHCalculationController, self).get_snr(*args, **kwargs)
154
+
155
+ def get_cov(
156
+ self,
157
+ *params: Any,
158
+ more_accurate: bool = False,
159
+ eps: float = 1e-9,
160
+ deriv_inds: np.ndarray = None,
161
+ precision: bool = False,
162
+ **kwargs: Any
163
+ ) -> Tuple[np.ndarray, np.ndarray]:
164
+ """Get covariance matrix.
165
+
166
+ Args:
167
+ *params: Parameters for BBH. Must include ``f_ref``.
168
+ more_accurate: If ``True``, run a more accurate derivate requiring 2x more waveform generations.
169
+ eps: Absolute **derivative** step size. See :func:`lisatools.diagnostic.info_matrix`.
170
+ deriv_inds: Subset of parameters of interest for which to calculate the information matrix, by index.
171
+ If ``None``, it will be ``np.arange(len(params))``.
172
+ precision: If ``True``, uses 500-dps precision to compute the information matrix inverse (requires `mpmath <https://mpmath.org>`_).
173
+ This is typically a good idea as the information matrix can be highly ill-conditioned.
174
+ **kwargs: Kwargs for waveform generation.
175
+
176
+ Returns:
177
+ Parameters and covariance matrix.
178
+
179
+ """
180
+
181
+ # setup all bbh specific quantities.
182
+ assert len(params) == 12
183
+
184
+ if isinstance(params, tuple):
185
+ params = list(params)
186
+
187
+ params = np.asarray(params)
188
+
189
+ m1 = params[0]
190
+ m2 = params[1]
191
+ mT = m1 + m2
192
+
193
+ if m2 > m1:
194
+ tmp = m2
195
+ m2 = m1
196
+ m1 = tmp
197
+
198
+ q = m2 / m1
199
+
200
+ params[0] = mT
201
+ params[1] = q
202
+
203
+ params[0] = np.log(params[0])
204
+ params[4] = params[4] / 1e9 / PC_SI
205
+ params[7] = np.cos(params[7])
206
+ params[9] = np.sin(params[9])
207
+ params[11] = params[11] / YRSID_SI
208
+
209
+ # default deriv inds
210
+ if deriv_inds is None:
211
+ deriv_inds = np.delete(np.arange(12), 6)
212
+
213
+ # remove f_ref derivative
214
+ if 6 in deriv_inds:
215
+ deriv_inds = np.delete(deriv_inds, np.where(deriv_inds == 6)[0])
216
+
217
+ kwargs["return_array"] = True
218
+
219
+ if "t_obs_start" not in kwargs:
220
+ kwargs["shift_t_limits"] = True
221
+ kwargs["t_obs_start"] = 0.0
222
+ kwargs["t_obs_end"] = self.Tobs
223
+
224
+ # compute covariance
225
+ cov = covariance(
226
+ eps,
227
+ self.aet_template_gen,
228
+ params,
229
+ parameter_transforms=self.transform_fn,
230
+ inner_product_kwargs=dict(
231
+ psd=self.psd,
232
+ psd_kwargs={**self.psd_kwargs, "model": self.model},
233
+ dt=self.aet_template_gen.dt,
234
+ f_arr=self.aet_template_gen.f_arr,
235
+ df=self.aet_template_gen.df,
236
+ ),
237
+ waveform_kwargs=kwargs,
238
+ more_accurate=more_accurate,
239
+ deriv_inds=deriv_inds,
240
+ precision=precision,
241
+ )
242
+
243
+ # return parameters and their covariance
244
+ return params[deriv_inds], cov
245
+
246
+
247
+ class GBCalculationController(CalculationController):
248
+ """Calculation controller for GBs.
249
+
250
+ Args:
251
+ *args: Args for :class:`CalculationController`.
252
+ *kwargs: Kwargs for :class:`CalculationController`.
253
+
254
+ """
255
+
256
+ def __init__(self, *args: Any, **kwargs: Any):
257
+
258
+ # parameter transforms from sampling basis to waveform basis
259
+ parameter_transforms = {
260
+ 0: lambda x: x * 1e-23,
261
+ 1: lambda x: x / 1e3,
262
+ 2: lambda x: x * 1e-18,
263
+ 5: np.arccos,
264
+ 8: np.arcsin,
265
+ # (1, 2, 3): lambda x, y, z: (x, y, 11.0 / 3.0 * y**2 / x),
266
+ }
267
+ self.transform_fn = TransformContainer(
268
+ parameter_transforms=parameter_transforms, fill_dict=None # fill_dict
269
+ )
270
+
271
+ super(GBCalculationController, self).__init__(*args, **kwargs)
272
+ # convert back to seconds
273
+ self.Tobs *= YRSID_SI
274
+
275
+ def get_cov(
276
+ self,
277
+ *params: np.ndarray | list,
278
+ more_accurate: bool = False,
279
+ eps: float = 1e-9,
280
+ deriv_inds: np.ndarray = None,
281
+ precision: bool = False,
282
+ **kwargs: Any
283
+ ) -> Tuple[np.ndarray, np.ndarray]:
284
+ """Get covariance matrix.
285
+
286
+ Args:
287
+ *params: Parameters for GB. Must include ``fddot``.
288
+ more_accurate: If ``True``, run a more accurate derivate requiring 2x more waveform generations.
289
+ eps: Absolute **derivative** step size. See :func:`lisatools.diagnostic.info_matrix`.
290
+ deriv_inds: Subset of parameters of interest for which to calculate the information matrix, by index.
291
+ If ``None``, it will be ``np.arange(len(params))``.
292
+ precision: If ``True``, uses 500-dps precision to compute the information matrix inverse (requires `mpmath <https://mpmath.org>`_).
293
+ This is typically a good idea as the information matrix can be highly ill-conditioned.
294
+ **kwargs: Kwargs for waveform generation.
295
+
296
+ Returns:
297
+ Parameters and covariance matrix.
298
+
299
+ """
300
+ assert len(params) == 9
301
+
302
+ if isinstance(params, tuple):
303
+ params = list(params)
304
+
305
+ params = np.asarray(params)
306
+
307
+ # params[0] = np.log(params[0])
308
+ params[0] = params[0] / 1e-23
309
+ params[1] = params[1] * 1e3
310
+ params[2] = params[2] / 1e-18
311
+
312
+ if params[3] != 0.0:
313
+ raise NotImplementedError(
314
+ "This class has not been implemented for fddot != 0 yet."
315
+ )
316
+
317
+ params[5] = np.cos(params[5])
318
+ params[8] = np.sin(params[8])
319
+
320
+ if deriv_inds is None:
321
+ deriv_inds = np.delete(np.arange(9), 3)
322
+
323
+ # remove fddot for now
324
+ if 3 in deriv_inds:
325
+ deriv_inds = np.delete(deriv_inds, np.where(deriv_inds == 3)[0])
326
+
327
+ kwargs["return_array"] = True
328
+
329
+ kwargs["dt"] = self.dt
330
+ kwargs["T"] = self.Tobs
331
+
332
+ cov = covariance(
333
+ eps,
334
+ self.aet_template_gen,
335
+ params,
336
+ parameter_transforms=self.transform_fn,
337
+ inner_product_kwargs=dict(
338
+ psd=self.psd,
339
+ psd_kwargs={**self.psd_kwargs, "model": self.model},
340
+ dt=self.aet_template_gen.dt,
341
+ f_arr=self.aet_template_gen.f_arr,
342
+ df=self.aet_template_gen.df,
343
+ ),
344
+ waveform_kwargs=kwargs,
345
+ more_accurate=more_accurate,
346
+ deriv_inds=deriv_inds,
347
+ precision=precision,
348
+ )
349
+
350
+ return params[deriv_inds], cov
351
+
352
+ def get_snr(self, *args: Any, **kwargs: Any) -> float:
353
+ """Compute the SNR.
354
+
355
+ Args:
356
+ *params: Parameters to go into waveform generator.
357
+ **kwargs: Kwargs for waveform generator.
358
+
359
+ Returns:
360
+ SNR.
361
+
362
+ """
363
+ # make sure it is TDI 2
364
+ if "tdi2" not in kwargs:
365
+ kwargs["tdi2"] = True
366
+
367
+ kwargs["dt"] = self.dt
368
+ kwargs["T"] = self.Tobs
369
+
370
+ # ensures tdi2 is added correctly for GBGPU
371
+ return super(GBCalculationController, self).get_snr(*args, **kwargs)
372
+
373
+
374
+ class EMRICalculationController(CalculationController):
375
+ """Calculation controller for EMRIs.
376
+
377
+ Args:
378
+ *args: Args for :class:`CalculationController`.
379
+ *kwargs: Kwargs for :class:`CalculationController`.
380
+
381
+ """
382
+
383
+ def __init__(self, *args: Any, **kwargs: Any):
384
+
385
+ # parameter transforms for EMRIs
386
+ parameter_transforms = {
387
+ 0: np.exp,
388
+ 5: np.arccos,
389
+ 7: np.arccos,
390
+ 9: np.arccos,
391
+ # (1, 2, 3): lambda x, y, z: (x, y, 11.0 / 3.0 * y**2 / x),
392
+ }
393
+ self.transform_fn = TransformContainer(
394
+ parameter_transforms=parameter_transforms, fill_dict=None # fill_dict
395
+ )
396
+
397
+ super(EMRICalculationController, self).__init__(*args, **kwargs)
398
+
399
+ def get_cov(
400
+ self,
401
+ *params: np.ndarray | list,
402
+ more_accurate: bool = False,
403
+ eps: float = 1e-9,
404
+ deriv_inds: np.ndarray = None,
405
+ precision: bool = False,
406
+ **kwargs: Any
407
+ ) -> Tuple[np.ndarray, np.ndarray]:
408
+ """Get covariance matrix.
409
+
410
+ Args:
411
+ *params: Parameters for EMRIs.
412
+ more_accurate: If ``True``, run a more accurate derivate requiring 2x more waveform generations.
413
+ eps: Absolute **derivative** step size. See :func:`lisatools.diagnostic.info_matrix`.
414
+ deriv_inds: Subset of parameters of interest for which to calculate the information matrix, by index.
415
+ If ``None``, it will be ``np.arange(len(params))``.
416
+ precision: If ``True``, uses 500-dps precision to compute the information matrix inverse (requires `mpmath <https://mpmath.org>`_).
417
+ This is typically a good idea as the information matrix can be highly ill-conditioned.
418
+ **kwargs: Kwargs for waveform generation.
419
+
420
+ Returns:
421
+ Parameters and covariance matrix.
422
+
423
+ """
424
+ assert len(params) == 14
425
+
426
+ if isinstance(params, tuple):
427
+ params = list(params)
428
+
429
+ params = np.asarray(params)
430
+
431
+ params[0] = np.log(params[0])
432
+ params[5] = np.cos(params[5])
433
+ params[7] = np.cos(params[7])
434
+ params[9] = np.cos(params[9])
435
+
436
+ kwargs["return_array"] = True
437
+
438
+ assert self.aet_template_gen.response.dt == self.dt
439
+ assert self.aet_template_gen.response.T == self.Tobs
440
+
441
+ cov = covariance(
442
+ eps,
443
+ self.aet_template_gen,
444
+ params,
445
+ parameter_transforms=self.transform_fn,
446
+ inner_product_kwargs=dict(
447
+ psd=self.psd,
448
+ psd_kwargs={**self.psd_kwargs, "model": self.model},
449
+ dt=self.aet_template_gen.dt,
450
+ f_arr=self.aet_template_gen.f_arr,
451
+ df=self.aet_template_gen.df,
452
+ ),
453
+ waveform_kwargs=kwargs,
454
+ more_accurate=more_accurate,
455
+ deriv_inds=deriv_inds,
456
+ precision=precision,
457
+ )
458
+
459
+ return params[deriv_inds], cov
@@ -0,0 +1,41 @@
1
+ from abc import ABC
2
+ from typing import Union, Tuple
3
+ import numpy as np
4
+
5
+
6
+ class AETTDIWaveform(ABC):
7
+ """Base class for an AET TDI Waveform."""
8
+
9
+ @property
10
+ def dt(self) -> float:
11
+ """Timestep in seconds."""
12
+ return None
13
+
14
+ @property
15
+ def f_arr(self) -> np.ndarray:
16
+ """Frequency array."""
17
+ return None
18
+
19
+ @property
20
+ def df(self) -> float:
21
+ """Frequency bin size."""
22
+ return None
23
+
24
+
25
+ class SNRWaveform(ABC):
26
+ """Base class for a waveform built in a simpler fashion for SNR calculations."""
27
+
28
+ @property
29
+ def dt(self) -> float:
30
+ """Timestep in seconds."""
31
+ return None
32
+
33
+ @property
34
+ def f_arr(self) -> np.ndarray:
35
+ """Frequency array."""
36
+ return None
37
+
38
+ @property
39
+ def df(self) -> float:
40
+ """Frequency bin size."""
41
+ return None