redback 1.0.1__py3-none-any.whl → 1.0.2__py3-none-any.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.
- redback/__init__.py +4 -0
- redback/constraints.py +31 -25
- redback/get_data/lasair.py +3 -4
- redback/get_data/swift.py +7 -7
- redback/interaction_processes.py +1 -4
- redback/likelihoods.py +207 -21
- redback/plotting.py +10 -10
- redback/priors/csm_interaction.prior +6 -7
- redback/priors/csm_nickel.prior +3 -3
- redback/priors/one_comp_kne_rosswog_heatingrate.prior +5 -0
- redback/priors/tde_analytical.prior +5 -5
- redback/priors/tde_analytical_bolometric.prior +6 -4
- redback/priors/tophat_from_emulator.prior +9 -0
- redback/priors/two_comp_kne_rosswog_heatingrate.prior +9 -0
- redback/priors/two_layer_stratified_kilonova.prior +1 -1
- redback/priors.py +11 -0
- redback/sed.py +194 -2
- redback/simulate_transients.py +61 -32
- redback/tables/filters.csv +14 -0
- redback/tables/ztf.tar.gz +0 -0
- redback/transient/afterglow.py +3 -2
- redback/transient/kilonova.py +1 -1
- redback/transient/supernova.py +1 -1
- redback/transient/tde.py +1 -1
- redback/transient/transient.py +2 -2
- redback/transient_models/afterglow_models.py +42 -0
- redback/transient_models/combined_models.py +47 -32
- redback/transient_models/extinction_models.py +5 -4
- redback/transient_models/kilonova_models.py +241 -8
- redback/transient_models/magnetar_driven_ejecta_models.py +2 -2
- redback/transient_models/phenomenological_models.py +13 -0
- redback/transient_models/supernova_models.py +50 -36
- redback/transient_models/tde_models.py +126 -1
- redback/utils.py +283 -6
- {redback-1.0.1.dist-info → redback-1.0.2.dist-info}/METADATA +6 -3
- {redback-1.0.1.dist-info → redback-1.0.2.dist-info}/RECORD +39 -37
- {redback-1.0.1.dist-info → redback-1.0.2.dist-info}/WHEEL +1 -1
- redback/tables/ztf_obslog.csv +0 -106649
- {redback-1.0.1.dist-info → redback-1.0.2.dist-info}/LICENCE.md +0 -0
- {redback-1.0.1.dist-info → redback-1.0.2.dist-info}/top_level.txt +0 -0
redback/__init__.py
CHANGED
|
@@ -2,3 +2,7 @@ from redback import analysis, constants, get_data, redback_errors, priors, resul
|
|
|
2
2
|
transient_models, utils, photosphere, sed, interaction_processes, constraints, plotting, model_library, simulate_transients
|
|
3
3
|
from redback.transient import afterglow, kilonova, prompt, supernova, tde
|
|
4
4
|
from redback.sampler import fit_model
|
|
5
|
+
from redback.utils import setup_logger
|
|
6
|
+
|
|
7
|
+
__version__ = "1.0.2"
|
|
8
|
+
setup_logger(log_level='info')
|
redback/constraints.py
CHANGED
|
@@ -2,6 +2,7 @@ import numpy as np
|
|
|
2
2
|
import redback.eos as eos
|
|
3
3
|
from redback.constants import *
|
|
4
4
|
from redback.utils import calc_tfb
|
|
5
|
+
from scipy.interpolate import interp1d
|
|
5
6
|
|
|
6
7
|
def slsn_constraint(parameters):
|
|
7
8
|
"""
|
|
@@ -23,7 +24,7 @@ def slsn_constraint(parameters):
|
|
|
23
24
|
neutrino_energy = 1e51
|
|
24
25
|
total_energy = kinetic_energy + neutrino_energy
|
|
25
26
|
# ensure rotational energy is greater than total output energy
|
|
26
|
-
converted_parameters['erot_constraint'] = rotational_energy
|
|
27
|
+
converted_parameters['erot_constraint'] = total_energy/rotational_energy
|
|
27
28
|
# ensure t_nebula is greater than 100 days
|
|
28
29
|
converted_parameters['t_nebula_min'] = tnebula - 100
|
|
29
30
|
return converted_parameters
|
|
@@ -43,7 +44,7 @@ def basic_magnetar_powered_sn_constraints(parameters):
|
|
|
43
44
|
kinetic_energy = 0.5 * mej * vej**2
|
|
44
45
|
rotational_energy = 2.6e52 * (mass_ns/1.4)**(3./2.) * p0**(-2)
|
|
45
46
|
# ensure rotational energy is greater than total output energy
|
|
46
|
-
converted_parameters['erot_constraint'] = rotational_energy
|
|
47
|
+
converted_parameters['erot_constraint'] = kinetic_energy/rotational_energy
|
|
47
48
|
return converted_parameters
|
|
48
49
|
|
|
49
50
|
def general_magnetar_powered_sn_constraints(parameters):
|
|
@@ -61,7 +62,7 @@ def general_magnetar_powered_sn_constraints(parameters):
|
|
|
61
62
|
tau = parameters['tsd']
|
|
62
63
|
rotational_energy = 2*l0*tau
|
|
63
64
|
# ensure rotational energy is greater than total output energy
|
|
64
|
-
converted_parameters['erot_constraint'] = rotational_energy
|
|
65
|
+
converted_parameters['erot_constraint'] = kinetic_energy/rotational_energy
|
|
65
66
|
return converted_parameters
|
|
66
67
|
|
|
67
68
|
def general_magnetar_powered_supernova_constraints(parameters):
|
|
@@ -77,7 +78,7 @@ def general_magnetar_powered_supernova_constraints(parameters):
|
|
|
77
78
|
nn = parameters['nn']
|
|
78
79
|
rotational_energy = (nn-1)*l0*tau/2.0
|
|
79
80
|
# ensure rotational energy is less than the maximum spin down energy
|
|
80
|
-
converted_parameters['erot_constraint'] = 1e53
|
|
81
|
+
converted_parameters['erot_constraint'] = rotational_energy/1e53
|
|
81
82
|
return converted_parameters
|
|
82
83
|
|
|
83
84
|
def tde_constraints(parameters):
|
|
@@ -91,7 +92,7 @@ def tde_constraints(parameters):
|
|
|
91
92
|
rp = parameters['pericenter_radius']
|
|
92
93
|
mass_bh = parameters['mass_bh']
|
|
93
94
|
schwarzchild_radius = (2 * graviational_constant * mass_bh * solar_mass /(speed_of_light**2))/au_cgs
|
|
94
|
-
converted_parameters['disruption_radius'] = rp
|
|
95
|
+
converted_parameters['disruption_radius'] = schwarzchild_radius/rp
|
|
95
96
|
return converted_parameters
|
|
96
97
|
|
|
97
98
|
def gaussianrise_tde_constraints(parameters):
|
|
@@ -103,13 +104,11 @@ def gaussianrise_tde_constraints(parameters):
|
|
|
103
104
|
converted_parameters = parameters.copy()
|
|
104
105
|
ms = parameters['stellar_mass']
|
|
105
106
|
mbh6 = parameters['mbh_6']
|
|
106
|
-
etamin = 0.01*(ms**(-7./15.))*(mbh6**(2./3.))
|
|
107
107
|
betamax = 12.*(ms**(7./15.))*(mbh6**(-2./3.))
|
|
108
108
|
tfb = calc_tfb(binding_energy_const=0.8, mbh_6=mbh6,stellar_mass=ms)/86400
|
|
109
109
|
tfb_obs = tfb * (1 + parameters['redshift'])
|
|
110
|
-
converted_parameters['
|
|
111
|
-
converted_parameters['
|
|
112
|
-
converted_parameters['tfb_max'] = tfb_obs - converted_parameters['peak_time']
|
|
110
|
+
converted_parameters['beta_high'] = converted_parameters['beta']/betamax
|
|
111
|
+
converted_parameters['tfb_max'] = converted_parameters['peak_time']/tfb_obs
|
|
113
112
|
return converted_parameters
|
|
114
113
|
|
|
115
114
|
def nuclear_burning_constraints(parameters):
|
|
@@ -126,7 +125,7 @@ def nuclear_burning_constraints(parameters):
|
|
|
126
125
|
kinetic_energy = 0.5 * mej * (vej / 2.0) ** 2
|
|
127
126
|
excess_constant = -(56.0 / 4.0 * 2.4249 - 53.9037) / proton_mass * mev_cgs
|
|
128
127
|
emax = excess_constant * mej * fnickel
|
|
129
|
-
converted_parameters['emax_constraint'] = emax
|
|
128
|
+
converted_parameters['emax_constraint'] = kinetic_energy/emax
|
|
130
129
|
return converted_parameters
|
|
131
130
|
|
|
132
131
|
def simple_fallback_constraints(parameters):
|
|
@@ -149,7 +148,7 @@ def simple_fallback_constraints(parameters):
|
|
|
149
148
|
neutrino_energy = 1e51
|
|
150
149
|
total_energy = e_fallback + neutrino_energy
|
|
151
150
|
# ensure total energy is greater than kinetic energy
|
|
152
|
-
converted_parameters['en_constraint'] = total_energy
|
|
151
|
+
converted_parameters['en_constraint'] = kinetic_energy/total_energy
|
|
153
152
|
# ensure t_nebula is greater than 100 days
|
|
154
153
|
converted_parameters['t_nebula_min'] = tnebula - 100
|
|
155
154
|
return converted_parameters
|
|
@@ -162,15 +161,18 @@ def csm_constraints(parameters):
|
|
|
162
161
|
:param parameters: dictionary of parameters
|
|
163
162
|
:return: converted_parameters dictionary where the violated samples are thrown out
|
|
164
163
|
"""
|
|
165
|
-
from redback.utils import get_csm_properties
|
|
166
164
|
converted_parameters = parameters.copy()
|
|
167
165
|
mej = parameters['mej']
|
|
168
166
|
csm_mass = parameters['csm_mass']
|
|
169
167
|
kappa = parameters['kappa']
|
|
170
168
|
r0 = parameters['r0']
|
|
171
169
|
vej = parameters['vej']
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
if hasattr(parameters['mej'], "__len__"):
|
|
171
|
+
nn = parameters.get('nn', np.ones(len(mej)) * 8.)
|
|
172
|
+
delta = parameters.get('delta', np.ones(len(mej)))
|
|
173
|
+
else:
|
|
174
|
+
nn = parameters.get('nn', 12.)
|
|
175
|
+
delta = parameters.get('delta', 0.)
|
|
174
176
|
eta = parameters['eta']
|
|
175
177
|
rho = parameters['rho']
|
|
176
178
|
|
|
@@ -180,12 +182,16 @@ def csm_constraints(parameters):
|
|
|
180
182
|
vej = vej * km_cgs
|
|
181
183
|
Esn = 3. * vej ** 2 * mej / 10.
|
|
182
184
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
ns = [6, 7, 8, 9, 10, 12, 14]
|
|
186
|
+
Bfs = [1.377, 1.299, 1.267, 1.250, 1.239, 1.226, 1.218]
|
|
187
|
+
As = [0.62, 0.27, 0.15, 0.096, 0.067, 0.038, 0.025]
|
|
188
|
+
|
|
189
|
+
Bf_func = interp1d(ns, Bfs)
|
|
190
|
+
A_func = interp1d(ns, As)
|
|
191
|
+
|
|
192
|
+
Bf = Bf_func(nn)
|
|
193
|
+
AA = A_func(nn)
|
|
194
|
+
|
|
189
195
|
qq = rho * r0 ** eta
|
|
190
196
|
# outer CSM shell radius
|
|
191
197
|
radius_csm = ((3.0 - eta) / (4.0 * np.pi * qq) * csm_mass + r0 ** (3.0 - eta)) ** (
|
|
@@ -203,14 +209,14 @@ def csm_constraints(parameters):
|
|
|
203
209
|
(3.0 - delta) * (nn - 3.0) * mej) ** ((nn - 5.0) / 2.0))
|
|
204
210
|
|
|
205
211
|
tshock = ((radius_csm - r0) / Bf / (AA * g_n / qq) ** (
|
|
206
|
-
1. / (nn - eta))) ** ((nn - eta) /(nn - 3))
|
|
212
|
+
1. / (nn - eta))) ** ((nn - eta) / (nn - 3))
|
|
207
213
|
|
|
208
|
-
diffusion_time = np.sqrt(2. * kappa * mass_csm_threshold /(vej * 13.7 * 3.e10))
|
|
214
|
+
diffusion_time = np.sqrt(2. * kappa * mass_csm_threshold / (vej * 13.7 * 3.e10))
|
|
209
215
|
# ensure shock crossing time is greater than diffusion time
|
|
210
|
-
|
|
216
|
+
converted_parameters['shock_time'] = diffusion_time/tshock
|
|
211
217
|
# ensure photospheric radius is within the csm i.e., r_photo < radius_csm and r_photo > r0
|
|
212
|
-
converted_parameters['photosphere_constraint_1'] = radius_csm
|
|
213
|
-
converted_parameters['photosphere_constraint_2'] = r_photosphere
|
|
218
|
+
converted_parameters['photosphere_constraint_1'] = r_photosphere/radius_csm
|
|
219
|
+
converted_parameters['photosphere_constraint_2'] = r0/r_photosphere
|
|
214
220
|
return converted_parameters
|
|
215
221
|
|
|
216
222
|
def piecewise_polytrope_eos_constraints(parameters):
|
redback/get_data/lasair.py
CHANGED
|
@@ -58,8 +58,8 @@ class LasairDataGetter(DataGetter):
|
|
|
58
58
|
f"Are you sure you are using the right alias?")
|
|
59
59
|
data = pd.read_html(self.url)
|
|
60
60
|
data = data[1]
|
|
61
|
-
data['diff_magnitude'] = [data['
|
|
62
|
-
data['diff_magnitude_error'] = [data['
|
|
61
|
+
data['diff_magnitude'] = [data['unforced mag'].iloc[x].split(" ")[0] for x in range(len(data))]
|
|
62
|
+
data['diff_magnitude_error'] = [data['unforced mag'].iloc[x].split(" ")[-1] for x in range(len(data))]
|
|
63
63
|
|
|
64
64
|
logger.warning('Using the difference magnitude to calculate quantities. '
|
|
65
65
|
'Reduce the data yourself if you would like to use a reference magnitude')
|
|
@@ -67,7 +67,6 @@ class LasairDataGetter(DataGetter):
|
|
|
67
67
|
# Change the dataframe to the correct raw dataframe format
|
|
68
68
|
del data['UTC']
|
|
69
69
|
del data['images']
|
|
70
|
-
del data['magpsf']
|
|
71
70
|
data.to_csv(self.raw_file_path, index=False)
|
|
72
71
|
logger.info(f"Retrieved data for {self.transient}.")
|
|
73
72
|
|
|
@@ -83,7 +82,7 @@ class LasairDataGetter(DataGetter):
|
|
|
83
82
|
return pd.read_csv(self.processed_file_path)
|
|
84
83
|
|
|
85
84
|
raw_data = pd.read_csv(self.raw_file_path)
|
|
86
|
-
raw_data = raw_data[raw_data['
|
|
85
|
+
raw_data = raw_data[raw_data['unforced mag status'] != 'limit']
|
|
87
86
|
lasair_to_general_bands = {"g": "ztfg", "r": "ztfr", "i":'ztfi'}
|
|
88
87
|
processed_data = pd.DataFrame()
|
|
89
88
|
|
redback/get_data/swift.py
CHANGED
|
@@ -199,9 +199,9 @@ class SwiftDataGetter(GRBDataGetter):
|
|
|
199
199
|
driver = fetch_driver()
|
|
200
200
|
try:
|
|
201
201
|
driver.get(self.grb_website)
|
|
202
|
-
driver.
|
|
202
|
+
driver.find_element("xpath", "//select[@name='xrtsub']/option[text()='no']").click()
|
|
203
203
|
time.sleep(20)
|
|
204
|
-
driver.
|
|
204
|
+
driver.find_element("id","xrt_DENSITY_makeDownload").click()
|
|
205
205
|
time.sleep(20)
|
|
206
206
|
grb_url = driver.current_url
|
|
207
207
|
# scrape the data
|
|
@@ -227,19 +227,19 @@ class SwiftDataGetter(GRBDataGetter):
|
|
|
227
227
|
# select option for BAT bin_size
|
|
228
228
|
bat_binning = 'batxrtbin'
|
|
229
229
|
if check_element(driver, bat_binning):
|
|
230
|
-
driver.
|
|
230
|
+
driver.find_element("xpath", "//select[@name='batxrtbin']/option[text()='SNR 4']").click()
|
|
231
231
|
# select option for subplot
|
|
232
232
|
subplot = "batxrtsub"
|
|
233
233
|
if check_element(driver, subplot):
|
|
234
|
-
driver.
|
|
234
|
+
driver.find_element("xpath","//select[@name='batxrtsub']/option[text()='no']").click()
|
|
235
235
|
# Select option for flux density
|
|
236
236
|
flux_density1 = "batxrtband1"
|
|
237
237
|
flux_density0 = "batxrtband0"
|
|
238
238
|
if (check_element(driver, flux_density1)) and (check_element(driver, flux_density0)):
|
|
239
|
-
driver.
|
|
240
|
-
driver.
|
|
239
|
+
driver.find_element("xpath",".//*[@id='batxrtband1']").click()
|
|
240
|
+
driver.find_element("xpath",".//*[@id='batxrtband0']").click()
|
|
241
241
|
# Generate data file
|
|
242
|
-
driver.
|
|
242
|
+
driver.find_element("xpath",".//*[@id='batxrt_XRTBAND_makeDownload']").click()
|
|
243
243
|
time.sleep(20)
|
|
244
244
|
grb_url = driver.current_url
|
|
245
245
|
driver.quit()
|
redback/interaction_processes.py
CHANGED
|
@@ -165,8 +165,6 @@ class CSMDiffusion(object):
|
|
|
165
165
|
# photosphere radius
|
|
166
166
|
r_photosphere = self.r_photosphere
|
|
167
167
|
|
|
168
|
-
tau_diff = (self.kappa * self.csm_mass) / (13.8 * speed_of_light * r_photosphere) / day_to_s
|
|
169
|
-
|
|
170
168
|
# mass of the optically thick CSM (tau > 2/3).
|
|
171
169
|
mass_csm_threshold = self.mass_csm_threshold
|
|
172
170
|
|
|
@@ -180,7 +178,7 @@ class CSMDiffusion(object):
|
|
|
180
178
|
lu = len(uniq_times)
|
|
181
179
|
|
|
182
180
|
num = int(round(timesteps / 2.0))
|
|
183
|
-
lsp = np.logspace(np.log10(
|
|
181
|
+
lsp = np.logspace(np.log10(t0 /self.dense_times[-1]) + minimum_log_spacing, 0, num)
|
|
184
182
|
xm = np.unique(np.concatenate((lsp, 1 - lsp)))
|
|
185
183
|
|
|
186
184
|
int_times = tb + (uniq_times.reshape(lu, 1) - tb) * xm
|
|
@@ -192,7 +190,6 @@ class CSMDiffusion(object):
|
|
|
192
190
|
|
|
193
191
|
uniq_lums = np.trapz(int_args, int_times, axis=1)
|
|
194
192
|
uniq_lums *= np.exp(-int_tes/t0)/t0
|
|
195
|
-
|
|
196
193
|
new_lums = uniq_lums[np.searchsorted(uniq_times, self.time)]
|
|
197
194
|
return new_lums
|
|
198
195
|
|
redback/likelihoods.py
CHANGED
|
@@ -3,11 +3,13 @@ from typing import Any, Union
|
|
|
3
3
|
|
|
4
4
|
import bilby
|
|
5
5
|
from scipy.special import gammaln
|
|
6
|
-
|
|
6
|
+
from redback.utils import logger
|
|
7
|
+
from bilby.core.prior import DeltaFunction, Constraint
|
|
7
8
|
|
|
8
9
|
class _RedbackLikelihood(bilby.Likelihood):
|
|
9
10
|
|
|
10
|
-
def __init__(self, x: np.ndarray, y: np.ndarray, function: callable, kwargs: dict = None
|
|
11
|
+
def __init__(self, x: np.ndarray, y: np.ndarray, function: callable, kwargs: dict = None, priors=None,
|
|
12
|
+
fiducial_parameters=None) -> None:
|
|
11
13
|
"""
|
|
12
14
|
|
|
13
15
|
:param x: The x values.
|
|
@@ -18,11 +20,19 @@ class _RedbackLikelihood(bilby.Likelihood):
|
|
|
18
20
|
:type function: callable
|
|
19
21
|
:param kwargs: Any additional keywords for 'function'.
|
|
20
22
|
:type kwargs: Union[dict, None]
|
|
23
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
24
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
25
|
+
:type priors: Union[dict, None]
|
|
26
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
27
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
28
|
+
:type fiducial_parameters: Union[dict, None]
|
|
21
29
|
"""
|
|
22
30
|
self.x = x
|
|
23
31
|
self.y = y
|
|
24
32
|
self.function = function
|
|
25
33
|
self.kwargs = kwargs
|
|
34
|
+
self.priors = priors
|
|
35
|
+
self.fiducial_parameters = fiducial_parameters
|
|
26
36
|
|
|
27
37
|
parameters = bilby.core.utils.introspection.infer_parameters_from_function(func=function)
|
|
28
38
|
super().__init__(parameters=dict.fromkeys(parameters))
|
|
@@ -46,11 +56,81 @@ class _RedbackLikelihood(bilby.Likelihood):
|
|
|
46
56
|
"""
|
|
47
57
|
return len(self.x)
|
|
48
58
|
|
|
59
|
+
@property
|
|
60
|
+
def parameters_to_be_updated(self):
|
|
61
|
+
if self.priors is None:
|
|
62
|
+
return None
|
|
63
|
+
else:
|
|
64
|
+
parameters_to_be_updated = [key for key in self.priors if not isinstance(
|
|
65
|
+
self.priors[key], (DeltaFunction, Constraint, float, int))]
|
|
66
|
+
return parameters_to_be_updated
|
|
67
|
+
|
|
68
|
+
def get_parameter_dictionary_from_list(self, parameter_list):
|
|
69
|
+
parameter_dictionary = dict(zip(self.parameters_to_be_updated, parameter_list))
|
|
70
|
+
excluded_parameter_keys = set(self.fiducial_parameters) - set(self.parameters_to_be_updated)
|
|
71
|
+
for key in excluded_parameter_keys:
|
|
72
|
+
parameter_dictionary[key] = self.fiducial_parameters[key]
|
|
73
|
+
return parameter_dictionary
|
|
74
|
+
|
|
75
|
+
def get_parameter_list_from_dictionary(self, parameter_dict):
|
|
76
|
+
return [parameter_dict[k] for k in self.parameters_to_be_updated]
|
|
77
|
+
|
|
78
|
+
def get_bounds_from_priors(self, priors):
|
|
79
|
+
bounds = []
|
|
80
|
+
for key in self.parameters_to_be_updated:
|
|
81
|
+
bounds.append([priors[key].minimum, priors[key].maximum])
|
|
82
|
+
return bounds
|
|
83
|
+
|
|
84
|
+
def lnlike_scipy_maximize(self, parameter_list):
|
|
85
|
+
self.parameters.update(self.get_parameter_dictionary_from_list(parameter_list))
|
|
86
|
+
return -self.log_likelihood()
|
|
87
|
+
|
|
88
|
+
def find_maximum_likelihood_parameters(self, iterations=5, maximization_kwargs=None, method='Nelder-Mead',
|
|
89
|
+
break_threshold=1e-3):
|
|
90
|
+
"""
|
|
91
|
+
Estimate the maximum likelihood
|
|
92
|
+
|
|
93
|
+
:param iterations: Iterations to run the minimizer for before stopping. Default is 5.
|
|
94
|
+
:param maximization_kwargs: Any extra keyword arguments passed to the scipy minimize function
|
|
95
|
+
:param method: Minimize method to use. Default is 'Nelder-Mead'
|
|
96
|
+
:param break_threshold: The threshold for the difference in log likelihood to break the loop. Default is 1e-3.
|
|
97
|
+
:return: Dictionary of maximum likelihood parameters
|
|
98
|
+
"""
|
|
99
|
+
from scipy.optimize import minimize
|
|
100
|
+
parameter_bounds = self.get_bounds_from_priors(self.priors)
|
|
101
|
+
if self.priors is None:
|
|
102
|
+
raise ValueError("Priors must be provided to use this functionality")
|
|
103
|
+
if maximization_kwargs is None:
|
|
104
|
+
maximization_kwargs = dict()
|
|
105
|
+
self.parameters.update(self.fiducial_parameters)
|
|
106
|
+
self.parameters["fiducial"] = 0
|
|
107
|
+
updated_parameters_list = self.get_parameter_list_from_dictionary(self.fiducial_parameters)
|
|
108
|
+
old_fiducial_ln_likelihood = self.log_likelihood()
|
|
109
|
+
for it in range(iterations):
|
|
110
|
+
logger.info(f"Optimizing fiducial parameters. Iteration : {it + 1}")
|
|
111
|
+
output = minimize(
|
|
112
|
+
self.lnlike_scipy_maximize,
|
|
113
|
+
x0=updated_parameters_list,
|
|
114
|
+
bounds=parameter_bounds,
|
|
115
|
+
method=method,
|
|
116
|
+
**maximization_kwargs,)
|
|
117
|
+
updated_parameters_list = output['x']
|
|
118
|
+
updated_parameters = self.get_parameter_dictionary_from_list(updated_parameters_list)
|
|
119
|
+
self.parameters.update(updated_parameters)
|
|
120
|
+
new_fiducial_ln_likelihood = self.log_likelihood_ratio()
|
|
121
|
+
logger.info(f"Current lnlikelihood: {new_fiducial_ln_likelihood:.2f}")
|
|
122
|
+
logger.info(f"Updated parameters: {updated_parameters}")
|
|
123
|
+
if new_fiducial_ln_likelihood - old_fiducial_ln_likelihood < break_threshold:
|
|
124
|
+
break
|
|
125
|
+
old_fiducial_ln_likelihood = new_fiducial_ln_likelihood
|
|
126
|
+
return updated_parameters
|
|
127
|
+
|
|
49
128
|
|
|
50
129
|
class GaussianLikelihood(_RedbackLikelihood):
|
|
51
130
|
def __init__(
|
|
52
131
|
self, x: np.ndarray, y: np.ndarray, sigma: Union[float, None, np.ndarray],
|
|
53
|
-
function: callable, kwargs: dict = None
|
|
132
|
+
function: callable, kwargs: dict = None, priors=None,
|
|
133
|
+
fiducial_parameters=None) -> None:
|
|
54
134
|
"""A general Gaussian likelihood - the parameters are inferred from the arguments of function.
|
|
55
135
|
|
|
56
136
|
:param x: The x values.
|
|
@@ -67,10 +147,17 @@ class GaussianLikelihood(_RedbackLikelihood):
|
|
|
67
147
|
:type function: callable
|
|
68
148
|
:param kwargs: Any additional keywords for 'function'.
|
|
69
149
|
:type kwargs: dict
|
|
150
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
151
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
152
|
+
:type priors: Union[dict, None]
|
|
153
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
154
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
155
|
+
:type fiducial_parameters: Union[dict, None]
|
|
70
156
|
"""
|
|
71
157
|
|
|
72
158
|
self._noise_log_likelihood = None
|
|
73
|
-
super().__init__(x=x, y=y, function=function, kwargs=kwargs
|
|
159
|
+
super().__init__(x=x, y=y, function=function, kwargs=kwargs, priors=priors,
|
|
160
|
+
fiducial_parameters=fiducial_parameters)
|
|
74
161
|
self.sigma = sigma
|
|
75
162
|
if self.sigma is None:
|
|
76
163
|
self.parameters['sigma'] = None
|
|
@@ -121,7 +208,8 @@ class GaussianLikelihood(_RedbackLikelihood):
|
|
|
121
208
|
class GaussianLikelihoodUniformXErrors(GaussianLikelihood):
|
|
122
209
|
def __init__(
|
|
123
210
|
self, x: np.ndarray, y: np.ndarray, sigma: Union[float, None, np.ndarray],
|
|
124
|
-
bin_size: Union[float, None, np.ndarray], function: callable, kwargs: dict = None
|
|
211
|
+
bin_size: Union[float, None, np.ndarray], function: callable, kwargs: dict = None, priors=None,
|
|
212
|
+
fiducial_parameters=None) -> None:
|
|
125
213
|
"""A general Gaussian likelihood with uniform errors in x- the parameters are inferred from the
|
|
126
214
|
arguments of function. Takes into account the X errors with a Uniform likelihood between the
|
|
127
215
|
bin high and bin low values. Note that the prior for the true x values must be uniform in this range!
|
|
@@ -142,9 +230,16 @@ class GaussianLikelihoodUniformXErrors(GaussianLikelihood):
|
|
|
142
230
|
:type function: callable
|
|
143
231
|
:param kwargs: Any additional keywords for 'function'.
|
|
144
232
|
:type kwargs: dict
|
|
233
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
234
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
235
|
+
:type priors: Union[dict, None]
|
|
236
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
237
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
238
|
+
:type fiducial_parameters: Union[dict, None]
|
|
145
239
|
"""
|
|
146
240
|
|
|
147
|
-
super().__init__(x=x, y=y, sigma=sigma, function=function, kwargs=kwargs
|
|
241
|
+
super().__init__(x=x, y=y, sigma=sigma, function=function, kwargs=kwargs, priors=priors,
|
|
242
|
+
fiducial_parameters=fiducial_parameters)
|
|
148
243
|
self.xerr = bin_size * np.ones(self.n)
|
|
149
244
|
|
|
150
245
|
def noise_log_likelihood(self) -> float:
|
|
@@ -183,7 +278,7 @@ class GaussianLikelihoodUniformXErrors(GaussianLikelihood):
|
|
|
183
278
|
class GaussianLikelihoodQuadratureNoise(GaussianLikelihood):
|
|
184
279
|
def __init__(
|
|
185
280
|
self, x: np.ndarray, y: np.ndarray, sigma_i: Union[float, None, np.ndarray],
|
|
186
|
-
function: callable, kwargs: dict = None) -> None:
|
|
281
|
+
function: callable, kwargs: dict = None, priors=None, fiducial_parameters=None) -> None:
|
|
187
282
|
"""
|
|
188
283
|
A general Gaussian likelihood - the parameters are inferred from the
|
|
189
284
|
arguments of function
|
|
@@ -205,7 +300,8 @@ class GaussianLikelihoodQuadratureNoise(GaussianLikelihood):
|
|
|
205
300
|
"""
|
|
206
301
|
self.sigma_i = sigma_i
|
|
207
302
|
# These lines of code infer the parameters from the provided function
|
|
208
|
-
super().__init__(x=x, y=y, sigma=sigma_i, function=function, kwargs=kwargs
|
|
303
|
+
super().__init__(x=x, y=y, sigma=sigma_i, function=function, kwargs=kwargs, priors=priors,
|
|
304
|
+
fiducial_parameters=fiducial_parameters)
|
|
209
305
|
|
|
210
306
|
@property
|
|
211
307
|
def full_sigma(self) -> Union[float, np.ndarray]:
|
|
@@ -231,19 +327,80 @@ class GaussianLikelihoodQuadratureNoise(GaussianLikelihood):
|
|
|
231
327
|
"""
|
|
232
328
|
return np.nan_to_num(self._gaussian_log_likelihood(res=self.residual, sigma=self.full_sigma))
|
|
233
329
|
|
|
330
|
+
class GaussianLikelihoodWithFractionalNoise(GaussianLikelihood):
|
|
331
|
+
def __init__(
|
|
332
|
+
self, x: np.ndarray, y: np.ndarray, sigma_i: Union[float, None, np.ndarray],
|
|
333
|
+
function: callable, kwargs: dict = None, priors=None, fiducial_parameters=None) -> None:
|
|
334
|
+
"""
|
|
335
|
+
A Gaussian likelihood with noise that is proportional to the model.
|
|
336
|
+
The parameters are inferred from the arguments of function
|
|
337
|
+
|
|
338
|
+
:param x: The x values.
|
|
339
|
+
:type x: np.ndarray
|
|
340
|
+
:param y: The y values.
|
|
341
|
+
:type y: np.ndarray
|
|
342
|
+
:param sigma_i: The standard deviation of the noise. This is part of the full noise.
|
|
343
|
+
The sigma used in the likelihood is sigma = sqrt(sigma_i^2*model_y**2)
|
|
344
|
+
:type sigma_i: Union[float, None, np.ndarray]
|
|
345
|
+
:param function:
|
|
346
|
+
The python function to fit to the data. Note, this must take the
|
|
347
|
+
dependent variable as its first argument. The other arguments
|
|
348
|
+
will require a prior and will be sampled over (unless a fixed
|
|
349
|
+
value is given).
|
|
350
|
+
:type function: callable
|
|
351
|
+
:param kwargs: Any additional keywords for 'function'.
|
|
352
|
+
:type kwargs: dict
|
|
353
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
354
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
355
|
+
:type priors: Union[dict, None]
|
|
356
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
357
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
358
|
+
:type fiducial_parameters: Union[dict, None]
|
|
359
|
+
"""
|
|
360
|
+
self.sigma_i = sigma_i
|
|
361
|
+
# These lines of code infer the parameters from the provided function
|
|
362
|
+
super().__init__(x=x, y=y, sigma=sigma_i, function=function, kwargs=kwargs, priors=priors,
|
|
363
|
+
fiducial_parameters=fiducial_parameters)
|
|
364
|
+
|
|
365
|
+
@property
|
|
366
|
+
def full_sigma(self) -> Union[float, np.ndarray]:
|
|
367
|
+
"""
|
|
368
|
+
:return: The standard deviation of the full noise
|
|
369
|
+
:rtype: Union[float, np.ndarray]
|
|
370
|
+
"""
|
|
371
|
+
model_y = self.function(self.x, **self.parameters, **self.kwargs)
|
|
372
|
+
return np.sqrt(self.sigma_i**2.*model_y**2)
|
|
373
|
+
|
|
374
|
+
def noise_log_likelihood(self) -> float:
|
|
375
|
+
"""
|
|
376
|
+
:return: The noise log-likelihood, i.e. the log-likelihood assuming the signal is just noise.
|
|
377
|
+
:rtype: float
|
|
378
|
+
"""
|
|
379
|
+
if self._noise_log_likelihood is None:
|
|
380
|
+
self._noise_log_likelihood = self._gaussian_log_likelihood(res=self.y, sigma=self.sigma_i)
|
|
381
|
+
return self._noise_log_likelihood
|
|
382
|
+
|
|
383
|
+
def log_likelihood(self) -> float:
|
|
384
|
+
"""
|
|
385
|
+
:return: The log-likelihood.
|
|
386
|
+
:rtype: float
|
|
387
|
+
"""
|
|
388
|
+
return np.nan_to_num(self._gaussian_log_likelihood(res=self.residual, sigma=self.full_sigma))
|
|
389
|
+
|
|
234
390
|
class GaussianLikelihoodWithSystematicNoise(GaussianLikelihood):
|
|
235
391
|
def __init__(
|
|
236
392
|
self, x: np.ndarray, y: np.ndarray, sigma_i: Union[float, None, np.ndarray],
|
|
237
|
-
function: callable, kwargs: dict = None) -> None:
|
|
393
|
+
function: callable, kwargs: dict = None, priors=None, fiducial_parameters=None) -> None:
|
|
238
394
|
"""
|
|
239
|
-
A
|
|
240
|
-
arguments of function
|
|
395
|
+
A Gaussian likelihood with a systematic noise term that is proportional to the model + some additive noise.
|
|
396
|
+
The parameters are inferred from the arguments of function
|
|
241
397
|
|
|
398
|
+
:param x: The x values.
|
|
242
399
|
:type x: np.ndarray
|
|
243
400
|
:param y: The y values.
|
|
244
401
|
:type y: np.ndarray
|
|
245
402
|
:param sigma_i: The standard deviation of the noise. This is part of the full noise.
|
|
246
|
-
The sigma used in the likelihood is sigma = sqrt(sigma_i^2 + sigma^2)
|
|
403
|
+
The sigma used in the likelihood is sigma = sqrt(sigma_i^2 + model_y**2*sigma^2)
|
|
247
404
|
:type sigma_i: Union[float, None, np.ndarray]
|
|
248
405
|
:param function:
|
|
249
406
|
The python function to fit to the data. Note, this must take the
|
|
@@ -253,10 +410,17 @@ class GaussianLikelihoodWithSystematicNoise(GaussianLikelihood):
|
|
|
253
410
|
:type function: callable
|
|
254
411
|
:param kwargs: Any additional keywords for 'function'.
|
|
255
412
|
:type kwargs: dict
|
|
413
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
414
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
415
|
+
:type priors: Union[dict, None]
|
|
416
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
417
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
418
|
+
:type fiducial_parameters: Union[dict, None]
|
|
256
419
|
"""
|
|
257
420
|
self.sigma_i = sigma_i
|
|
258
421
|
# These lines of code infer the parameters from the provided function
|
|
259
|
-
super().__init__(x=x, y=y, sigma=sigma_i, function=function, kwargs=kwargs
|
|
422
|
+
super().__init__(x=x, y=y, sigma=sigma_i, function=function, kwargs=kwargs, priors=priors,
|
|
423
|
+
fiducial_parameters=fiducial_parameters)
|
|
260
424
|
|
|
261
425
|
@property
|
|
262
426
|
def full_sigma(self) -> Union[float, np.ndarray]:
|
|
@@ -286,10 +450,11 @@ class GaussianLikelihoodWithSystematicNoise(GaussianLikelihood):
|
|
|
286
450
|
class GaussianLikelihoodQuadratureNoiseNonDetections(GaussianLikelihoodQuadratureNoise):
|
|
287
451
|
def __init__(
|
|
288
452
|
self, x: np.ndarray, y: np.ndarray, sigma_i: Union[float, np.ndarray], function: callable,
|
|
289
|
-
kwargs: dict = None, upperlimit_kwargs: dict = None) -> None:
|
|
453
|
+
kwargs: dict = None, upperlimit_kwargs: dict = None, priors=None, fiducial_parameters=None) -> None:
|
|
290
454
|
"""A general Gaussian likelihood - the parameters are inferred from the
|
|
291
455
|
arguments of function. Takes into account non-detections with a Uniform likelihood for those points
|
|
292
456
|
|
|
457
|
+
:param x: The x values.
|
|
293
458
|
:type x: np.ndarray
|
|
294
459
|
:param y: The y values.
|
|
295
460
|
:type y: np.ndarray
|
|
@@ -304,8 +469,15 @@ class GaussianLikelihoodQuadratureNoiseNonDetections(GaussianLikelihoodQuadratur
|
|
|
304
469
|
:type function: callable
|
|
305
470
|
:param kwargs: Any additional keywords for 'function'.
|
|
306
471
|
:type kwargs: dict
|
|
307
|
-
|
|
308
|
-
|
|
472
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
473
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
474
|
+
:type priors: Union[dict, None]
|
|
475
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
476
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
477
|
+
:type fiducial_parameters: Union[dict, None]
|
|
478
|
+
"""
|
|
479
|
+
super().__init__(x=x, y=y, sigma_i=sigma_i, function=function, kwargs=kwargs, priors=priors,
|
|
480
|
+
fiducial_parameters=fiducial_parameters)
|
|
309
481
|
self.upperlimit_kwargs = upperlimit_kwargs
|
|
310
482
|
|
|
311
483
|
@property
|
|
@@ -345,7 +517,7 @@ class GRBGaussianLikelihood(GaussianLikelihood):
|
|
|
345
517
|
|
|
346
518
|
def __init__(
|
|
347
519
|
self, x: np.ndarray, y: np.ndarray, sigma: Union[float, np.ndarray],
|
|
348
|
-
function: callable, kwargs: dict = None) -> None:
|
|
520
|
+
function: callable, kwargs: dict = None, priors=None, fiducial_parameters=None) -> None:
|
|
349
521
|
"""A general Gaussian likelihood - the parameters are inferred from the
|
|
350
522
|
arguments of function.
|
|
351
523
|
|
|
@@ -363,14 +535,21 @@ class GRBGaussianLikelihood(GaussianLikelihood):
|
|
|
363
535
|
:type function: callable
|
|
364
536
|
:param kwargs: Any additional keywords for 'function'.
|
|
365
537
|
:type kwargs: dict
|
|
538
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
539
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
540
|
+
:type priors: Union[dict, None]
|
|
541
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
542
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
543
|
+
:type fiducial_parameters: Union[dict, None]
|
|
366
544
|
"""
|
|
367
|
-
super().__init__(x=x, y=y, sigma=sigma, function=function, kwargs=kwargs
|
|
545
|
+
super().__init__(x=x, y=y, sigma=sigma, function=function, kwargs=kwargs, priors=priors,
|
|
546
|
+
fiducial_parameters=fiducial_parameters)
|
|
368
547
|
|
|
369
548
|
|
|
370
549
|
class PoissonLikelihood(_RedbackLikelihood):
|
|
371
550
|
def __init__(
|
|
372
551
|
self, time: np.ndarray, counts: np.ndarray, function: callable, integrated_rate_function: bool = True,
|
|
373
|
-
dt: Union[float, np.ndarray] = None, kwargs: dict = None) -> None:
|
|
552
|
+
dt: Union[float, np.ndarray] = None, kwargs: dict = None, priors=None, fiducial_parameters=None) -> None:
|
|
374
553
|
"""
|
|
375
554
|
:param time: The time values.
|
|
376
555
|
:type time: np.ndarray
|
|
@@ -389,8 +568,15 @@ class PoissonLikelihood(_RedbackLikelihood):
|
|
|
389
568
|
:type dt: Union[float, None, np.ndarray]
|
|
390
569
|
:param kwargs: Any additional keywords for 'function'.
|
|
391
570
|
:type kwargs: dict
|
|
392
|
-
|
|
393
|
-
|
|
571
|
+
:param priors: The priors for the parameters. Default to None if not provided.
|
|
572
|
+
Only necessary if using maximum likelihood estimation functionality.
|
|
573
|
+
:type priors: Union[dict, None]
|
|
574
|
+
:param fiducial_parameters: The starting guesses for model parameters to
|
|
575
|
+
use in the optimization for maximum likelihood estimation. Default to None if not provided.
|
|
576
|
+
:type fiducial_parameters: Union[dict, None]
|
|
577
|
+
"""
|
|
578
|
+
super(PoissonLikelihood, self).__init__(x=time, y=counts, function=function, kwargs=kwargs, priors=priors,
|
|
579
|
+
fiducial_parameters=fiducial_parameters)
|
|
394
580
|
self.integrated_rate_function = integrated_rate_function
|
|
395
581
|
self.dt = dt
|
|
396
582
|
self.parameters['background_rate'] = 0
|