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,222 @@
1
+ import numpy as np
2
+ cimport numpy as np
3
+ from libcpp.string cimport string
4
+ from libcpp cimport bool
5
+ from libc.stdint cimport uintptr_t
6
+
7
+ from gpubackendtools import wrapper
8
+
9
+
10
+ cdef class pycppDetector:
11
+
12
+ def __cinit__(self,
13
+ *args,
14
+ **kwargs
15
+ ):
16
+ (
17
+ dt,
18
+ N,
19
+ n_arr,
20
+ L_arr,
21
+ x_arr,
22
+ links,
23
+ sc_r,
24
+ sc_e,
25
+ armlength
26
+ ), tkwargs = wrapper(*args, **kwargs)
27
+
28
+ self.dt = dt
29
+ self.N = N
30
+ self.n_arr = n_arr
31
+ self.L_arr = L_arr
32
+ self.x_arr = x_arr
33
+ self.links = links
34
+ self.sc_r = sc_r
35
+ self.sc_e = sc_e
36
+ self.armlength = armlength
37
+
38
+ cdef size_t n_arr_in = n_arr
39
+ cdef size_t L_arr_in = L_arr
40
+ cdef size_t x_arr_in = x_arr
41
+ cdef size_t links_in = links
42
+ cdef size_t sc_r_in = sc_r
43
+ cdef size_t sc_e_in = sc_e
44
+
45
+ self.g = new OrbitsWrap(
46
+ dt,
47
+ N,
48
+ <double*> n_arr_in,
49
+ <double*> L_arr_in,
50
+ <double*> x_arr_in,
51
+ <int*> links_in,
52
+ <int*> sc_r_in,
53
+ <int*> sc_e_in,
54
+ armlength
55
+ )
56
+
57
+ def __dealloc__(self):
58
+ self.g.dealloc()
59
+ if self.g:
60
+ del self.g
61
+
62
+ def __reduce__(self):
63
+ return (rebuild, (self.dt, self.N, self.n_arr, self.L_arr, self.x_arr, self.links, self.sc_r, self.sc_e, self.armlength))
64
+
65
+ def get_window(self, t: float) -> int:
66
+ return self.g.get_window(t)
67
+
68
+ def get_normal_unit_vec_single(self, t: float, link: int) -> np.ndarray:
69
+ cdef VecWrap *out = new VecWrap(0.0, 0.0, 0.0)
70
+ self.g.get_normal_unit_vec_ptr(out, t, link)
71
+
72
+ return np.array([out.x, out.y, out.z])
73
+
74
+ def get_normal_unit_vec_arr(self, t: np.ndarray, link: int) -> np.ndarray:
75
+ output = np.zeros((len(t), 3), dtype=float)
76
+ assert t.ndim == 1
77
+ for i in range(len(t)):
78
+ output[i] = self.get_normal_unit_vec_single(t[i], link)
79
+
80
+ return output
81
+
82
+ def get_normal_unit_vec(self, t: np.ndarray | float, link: int) -> np.ndarray:
83
+
84
+ if isinstance(t, float):
85
+ return self.get_normal_unit_vec_single(t, link)
86
+ elif isinstance(t, np.ndarray):
87
+ return self.get_normal_unit_vec_arr(t, link)
88
+
89
+ def get_link_ind(self, link: int) -> int:
90
+ return self.g.get_link_ind(link)
91
+
92
+ def get_sc_ind(self, sc: int) -> int:
93
+ return self.g.get_sc_ind(sc)
94
+
95
+ def get_light_travel_time_single(self, t: float, link: int) -> float:
96
+ return self.g.get_light_travel_time(t, link)
97
+
98
+ def get_light_travel_time_arr(self, t: np.ndarray, link: int) -> np.ndarray:
99
+ output = np.zeros((len(t),), dtype=float)
100
+ assert t.ndim == 1
101
+ for i in range(len(t)):
102
+ output[i] = self.get_light_travel_time_single(t[i], link)
103
+
104
+ return output
105
+
106
+ def get_light_travel_time(self, t: np.ndarray | float, link: int) -> np.ndarray:
107
+
108
+ if isinstance(t, float):
109
+ print("t", t)
110
+ return self.get_light_travel_time_single(t, link)
111
+ elif isinstance(t, np.ndarray):
112
+ return self.get_light_travel_time_arr(t, link)
113
+
114
+ def get_pos_single(self, t: float, sc: int) -> np.ndarray:
115
+
116
+ cdef VecWrap *out = new VecWrap(0.0, 0.0, 0.0)
117
+ self.g.get_pos_ptr(out, t, sc)
118
+
119
+ return np.array([out.x, out.y, out.z])
120
+
121
+ def get_pos_arr(self, t: np.ndarray, sc: int) -> np.ndarray:
122
+ output = np.zeros((len(t), 3), dtype=float)
123
+ assert t.ndim == 1
124
+ for i in range(len(t)):
125
+ output[i] = self.get_pos_single(t[i], sc)
126
+
127
+ return output
128
+
129
+ def get_pos(self, t: np.ndarray | float, sc: int) -> np.ndarray:
130
+
131
+ if isinstance(t, float):
132
+ return self.get_pos_single(t, sc)
133
+ elif isinstance(t, np.ndarray):
134
+ return self.get_pos_arr(t, sc)
135
+
136
+ def get_pos_arr_wrap(self, *args, **kwargs):
137
+ (pos_x, pos_y, pos_z, t, sc, num), tkwargs = wrapper(*args, **kwargs)
138
+
139
+ cdef size_t pos_x_in = pos_x
140
+ cdef size_t pos_y_in = pos_y
141
+ cdef size_t pos_z_in = pos_z
142
+ cdef size_t t_in = t
143
+ cdef size_t sc_in = sc
144
+
145
+ self.g.get_pos_arr(
146
+ <double *>pos_x_in,
147
+ <double *>pos_y_in,
148
+ <double *>pos_z_in,
149
+ <double *>t_in,
150
+ <int *>sc_in,
151
+ num
152
+ )
153
+
154
+ def get_normal_unit_vec_arr_wrap(self, *args, **kwargs):
155
+ (normal_unit_vec_x, normal_unit_vec_y, normal_unit_vec_z, t, sc, num), tkwargs = wrapper(*args, **kwargs)
156
+
157
+ cdef size_t normal_unit_vec_x_in = normal_unit_vec_x
158
+ cdef size_t normal_unit_vec_y_in = normal_unit_vec_y
159
+ cdef size_t normal_unit_vec_z_in = normal_unit_vec_z
160
+ cdef size_t t_in = t
161
+ cdef size_t sc_in = sc
162
+
163
+ self.g.get_normal_unit_vec_arr(
164
+ <double *>normal_unit_vec_x_in,
165
+ <double *>normal_unit_vec_y_in,
166
+ <double *>normal_unit_vec_z_in,
167
+ <double *>t_in,
168
+ <int *>sc_in,
169
+ num
170
+ )
171
+
172
+ def get_light_travel_time_arr_wrap(self, *args, **kwargs):
173
+ (ltt, t, link, num), tkwargs = wrapper(*args, **kwargs)
174
+
175
+ cdef size_t ltt_in = ltt
176
+ cdef size_t t_in = t
177
+ cdef size_t link_in = link
178
+
179
+ self.g.get_light_travel_time_arr(
180
+ <double *>ltt_in,
181
+ <double *>t_in,
182
+ <int *>link_in,
183
+ num
184
+ )
185
+
186
+ def get_sc_r_from_arr(self, i: int) -> int:
187
+ return self.g.get_sc_r_from_arr(i)
188
+
189
+ def get_sc_e_from_arr(self, i: int) -> int:
190
+ return self.g.get_sc_e_from_arr(i)
191
+
192
+ def get_link_from_arr(self, i: int) -> int:
193
+ return self.g.get_link_from_arr(i)
194
+
195
+
196
+ @property
197
+ def ptr(self) -> long:
198
+ return <uintptr_t>self.g
199
+
200
+
201
+ def rebuild(dt,
202
+ N,
203
+ n_arr,
204
+ L_arr,
205
+ x_arr,
206
+ links,
207
+ sc_r,
208
+ sc_e,
209
+ armlength
210
+ ):
211
+ c = pycppDetector(
212
+ dt,
213
+ N,
214
+ n_arr,
215
+ L_arr,
216
+ x_arr,
217
+ links,
218
+ sc_r,
219
+ sc_e,
220
+ armlength
221
+ )
222
+ return c
@@ -0,0 +1,312 @@
1
+ from __future__ import annotations
2
+ import warnings
3
+ from abc import ABC
4
+ from typing import Any, Tuple, Optional, List
5
+
6
+ import math
7
+ import numpy as np
8
+ from scipy import interpolate
9
+ import matplotlib.pyplot as plt
10
+
11
+ try:
12
+ import cupy as cp
13
+
14
+ except (ModuleNotFoundError, ImportError):
15
+ import numpy as cp
16
+
17
+ from . import detector as lisa_models
18
+ from .utils.utility import AET, get_array_module
19
+ from .utils.constants import *
20
+ from .stochastic import (
21
+ StochasticContribution,
22
+ FittedHyperbolicTangentGalacticForeground,
23
+ )
24
+ from .sensitivity import SensitivityMatrix
25
+
26
+
27
+ class DataResidualArray:
28
+ pass
29
+
30
+
31
+ class DataResidualArray:
32
+ """Container to hold Data, residual, or template information.
33
+
34
+ This class abstracts the connection with the sensitivity matrices to make this analysis
35
+ as generic as possible for the user frontend, while handling
36
+ special computations in the backend.
37
+
38
+ Args:
39
+ data_res_in: Data, residual, or template input information. Can be a list, numpy array
40
+ or another :class:`DataResidualArray`.
41
+ dt: Timestep in seconds.
42
+ f_arr: Frequency array.
43
+ df: Delta f in frequency domain.
44
+ **kwargs: For future compatibility.
45
+
46
+ """
47
+
48
+ def __init__(
49
+ self,
50
+ data_res_in: List[np.ndarray] | np.ndarray | DataResidualArray,
51
+ dt: Optional[float] = None,
52
+ f_arr: Optional[np.ndarray] = None,
53
+ df: Optional[float] = None,
54
+ **kwargs: dict,
55
+ ) -> None:
56
+ if isinstance(data_res_in, DataResidualArray):
57
+ for key, item in data_res_in.__dict__.items():
58
+ setattr(self, key, item)
59
+
60
+ else:
61
+ self._check_inputs(dt=dt, f_arr=f_arr, df=df)
62
+ self.data_res_arr = data_res_in
63
+ self._store_time_and_frequency_information(dt=dt, f_arr=f_arr, df=df)
64
+
65
+ @property
66
+ def init_kwargs(self) -> dict:
67
+ """Initial dt, df, f_arr"""
68
+ return self._init_kwargs
69
+
70
+ @init_kwargs.setter
71
+ def init_kwargs(self, init_kwargs: dict) -> None:
72
+ """Set initial kwargs."""
73
+ self._init_kwargs = init_kwargs
74
+
75
+ def _check_inputs(
76
+ self,
77
+ dt: Optional[float] = None,
78
+ f_arr: Optional[np.ndarray] = None,
79
+ df: Optional[float] = None,
80
+ ):
81
+ number_of_none = 0
82
+
83
+ number_of_none += 1 if dt is None else 0
84
+ number_of_none += 1 if f_arr is None else 0
85
+ number_of_none += 1 if df is None else 0
86
+
87
+ if number_of_none == 3:
88
+ raise ValueError("Must provide either df, dt, or f_arr.")
89
+
90
+ elif number_of_none == 1:
91
+ raise ValueError(
92
+ "Can only provide one of dt, f_arr, or df. Not more than one."
93
+ )
94
+ self.init_kwargs = dict(dt=dt, f_arr=f_arr, df=df)
95
+
96
+ def _store_time_and_frequency_information(
97
+ self,
98
+ dt: Optional[float] = None,
99
+ f_arr: Optional[np.ndarray] = None,
100
+ df: Optional[float] = None,
101
+ ):
102
+ if dt is not None:
103
+ self._dt = dt
104
+ self._Tobs = self.data_length * dt
105
+ self._df = 1 / self._Tobs
106
+ self._fmax = 1 / (2 * dt)
107
+ xp = get_array_module(self.data_res_arr)
108
+ self._f_arr = xp.asarray(np.fft.rfftfreq(self.data_length, dt))
109
+
110
+ # transform data
111
+ tmp = xp.fft.rfft(self.data_res_arr, axis=-1) * self._dt
112
+ del self._data_res_arr
113
+ self._data_res_arr = tmp
114
+ self.data_length = self._data_res_arr.shape[-1]
115
+
116
+ elif df is not None:
117
+ self._df = df
118
+ self._Tobs = 1 / self._df
119
+ self._fmax = (self.data_length - 1) * df
120
+ self._dt = 1 / (2 * self._fmax)
121
+ self._f_arr = np.arange(0.0, self._fmax, self._df)
122
+
123
+ elif f_arr is not None:
124
+ self._f_arr = f_arr
125
+ self._fmax = f_arr.max()
126
+ # constant spacing
127
+ if np.all(np.diff(f_arr) == np.diff(f_arr)[0]):
128
+ self._df = np.diff(f_arr)[0].item()
129
+
130
+ if f_arr[0] == 0.0:
131
+ # could be fft because of constant spacing and f_arr[0] == 0.0
132
+ self._Tobs = 1 / self._df
133
+ self._dt = 1 / (2 * self._fmax)
134
+
135
+ else:
136
+ # cannot be fft basis
137
+ self._Tobs = None
138
+ self._dt = None
139
+
140
+ else:
141
+ self._df = None
142
+ self._Tobs = None
143
+ self._dt = None
144
+
145
+ if len(self.f_arr) != self.data_length:
146
+ raise ValueError(
147
+ "Entered or determined f_arr does not have the same length as the data channel inputs."
148
+ )
149
+
150
+ @property
151
+ def fmax(self):
152
+ """Maximum frequency."""
153
+ return self._fmax
154
+
155
+ @property
156
+ def f_arr(self):
157
+ """Frequency array."""
158
+ return self._f_arr
159
+
160
+ @property
161
+ def dt(self):
162
+ """Time step in seconds."""
163
+ if self._dt is None:
164
+ raise ValueError("dt cannot be determined from this f_arr input.")
165
+
166
+ return self._dt
167
+
168
+ @property
169
+ def Tobs(self):
170
+ """Observation time in seconds"""
171
+ if self._Tobs is None:
172
+ raise ValueError("Tobs cannot be determined from this f_arr input.")
173
+
174
+ return self._Tobs
175
+
176
+ @property
177
+ def df(self):
178
+ """Delta f in the frequency domain."""
179
+ if self._df is None:
180
+ raise ValueError("df cannot be determined from this f_arr input.")
181
+
182
+ return self._df
183
+
184
+ @property
185
+ def frequency_arr(self) -> np.ndarray:
186
+ """Frequency array"""
187
+ return self._f_arr
188
+
189
+ @property
190
+ def data_res_arr(self) -> np.ndarray:
191
+ """Actual data residual array"""
192
+ return self._data_res_arr
193
+
194
+ @data_res_arr.setter
195
+ def data_res_arr(self, data_res_arr: List[np.ndarray] | np.ndarray) -> None:
196
+ """Set ``data_res_arr``."""
197
+ self._data_res_arr_input = data_res_arr
198
+
199
+ if (
200
+ isinstance(data_res_arr, np.ndarray) or isinstance(data_res_arr, cp.ndarray)
201
+ ) and data_res_arr.ndim == 1:
202
+ data_res_arr = [data_res_arr]
203
+
204
+ elif (
205
+ isinstance(data_res_arr, np.ndarray) or isinstance(data_res_arr, cp.ndarray)
206
+ ) and data_res_arr.ndim == 2:
207
+ data_res_arr = list(data_res_arr)
208
+
209
+ new_out = np.full(len(data_res_arr), None, dtype=object)
210
+ self.data_length = None
211
+ for i in range(len(data_res_arr)):
212
+ current_data = data_res_arr[i]
213
+ if isinstance(current_data, np.ndarray) or isinstance(
214
+ current_data, cp.ndarray
215
+ ):
216
+ if self.data_length is None:
217
+ self.data_length = len(current_data)
218
+ else:
219
+ assert len(current_data) == self.data_length
220
+
221
+ new_out[i] = current_data
222
+ else:
223
+ raise ValueError
224
+
225
+ self.nchannels = len(new_out)
226
+ xp = get_array_module(new_out[0])
227
+ self._data_res_arr = xp.asarray(list(new_out), dtype=new_out[0].dtype)
228
+
229
+ def __getitem__(self, index: tuple) -> np.ndarray:
230
+ """Index this class directly in ``self.data_res_arr``."""
231
+ return self.data_res_arr[index]
232
+
233
+ def __setitem__(self, index: tuple, value: float | np.ndarray) -> np.ndarray:
234
+ """Index this class directly in ``self.data_res_arr``."""
235
+ self.data_res_arr[index] = value
236
+
237
+ @property
238
+ def ndim(self) -> int:
239
+ """Number of dimensions in the `data_res_arr`."""
240
+ return self.data_res_arr.ndim
241
+
242
+ def flatten(self) -> np.ndarray:
243
+ """Flatten the ``data_res_arr``."""
244
+ return self.data_res_arr.flatten()
245
+
246
+ @property
247
+ def shape(self) -> tuple:
248
+ """Shape of ``data_res_arr``."""
249
+ return self.data_res_arr.shape
250
+
251
+ def loglog(
252
+ self,
253
+ ax: Optional[List[plt.Axes] | plt.Axes] = None,
254
+ fig: Optional[plt.Figure] = None,
255
+ inds: Optional[List[int] | int] = None,
256
+ char_strain: Optional[bool] = False,
257
+ **kwargs: dict,
258
+ ) -> Tuple[plt.Figure, plt.Axes]:
259
+ """Produce a log-log plot of the data.
260
+
261
+ Args:
262
+ ax: Matplotlib Axes objects to add plots. Either a list of Axes objects or a single Axes object.
263
+ fig: Matplotlib figure object.
264
+ inds: Integer index to select out which data to add to a single access.
265
+ A list can be provided if ax is a list. They must be the same length.
266
+ char_strain: If ``True`` return plot in characteristic strain representation.
267
+ **kwargs: Keyword arguments to be passed to ``loglog`` function in matplotlib.
268
+
269
+ Returns:
270
+ Matplotlib figure and axes objects in a 2-tuple.
271
+
272
+
273
+ """
274
+ if ax is None and fig is None:
275
+ nrows = 1
276
+ ncols = self.shape[0]
277
+
278
+ fig, ax = plt.subplots(nrows, ncols, sharex=True, sharey=True)
279
+ ax = ax.ravel()
280
+ inds_list = range(len(ax))
281
+
282
+ elif ax is not None:
283
+ if isinstance(ax, list):
284
+ assert len(ax) == np.prod(self.shape[:-1])
285
+ if inds is None:
286
+ inds_list = list(np.arange(np.prod(self.shape[:-1])))
287
+ else:
288
+ assert isinstance(inds, list) and len(inds) == len(ax)
289
+ inds_list = inds
290
+
291
+ elif isinstance(ax, plt.Axes):
292
+ assert inds is not None and (
293
+ isinstance(inds, tuple) or isinstance(inds, int)
294
+ )
295
+ ax = [ax]
296
+ inds_list = [inds]
297
+
298
+ elif fig is not None:
299
+ raise NotImplementedError
300
+
301
+ for i, ax_tmp in zip(inds_list, ax):
302
+ plot_in = np.abs(self.data_res_arr[i])
303
+ if char_strain:
304
+ plot_in *= self.frequency_arr
305
+ ax_tmp.loglog(self.frequency_arr, plot_in, **kwargs)
306
+
307
+ return (fig, ax)
308
+
309
+ @property
310
+ def char_strain(self) -> np.ndarray:
311
+ """Characteristic strain representation of the data."""
312
+ return np.sqrt(self.f_arr) * np.abs(self.data_res_arr)