lisaanalysistools 1.0.14__cp312-cp312-macosx_11_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.

Potentially problematic release.


This version of lisaanalysistools might be problematic. Click here for more details.

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