Skip to content

Commit 41c0e89

Browse files
JanWeldertJan Weldert
and
Jan Weldert
authored
Detector class bug fixes (#743)
* increase numba requirement * Fixes for test notebook * Update detectors class and add test notebook * Make it possible to weight events * adjust hypersurfaces for weighted events * Update hist.py * update multi det notebook * Add update_param_values function for Detector class * add test script for Detectors class * Add Detectors test and (somewhat) fix DistributionMaker test * only change bin uncertainties if they exist * make genie sys stage more generic * more flexibility when splitting interactions * Handle __builtins__ both as dict and as module * cythonize * move cython import * remove cython (seems like I do not know what I am doing) * NuFit 5.2 and small bug fixes * Detectors class bug fix * Add prior for deltacp * initialize params after changing selection * move param init into function --------- Co-authored-by: Jan Weldert <jweldert@cobalt01.icecube.wisc.edu>
1 parent 464b49c commit 41c0e89

File tree

3 files changed

+99
-43
lines changed

3 files changed

+99
-43
lines changed

pisa/analysis/analysis.py

+76-27
Original file line numberDiff line numberDiff line change
@@ -375,17 +375,21 @@ def update_param_values_detector(
375375
"""
376376
Modification of the update_param_values function to use with the Detectors class.
377377
"""
378+
assert hypo_maker.__class__.__name__ == "Detectors", "hypo_maker is not Detectors class"
379+
380+
if isinstance(params, Param): params = ParamSet(params)
381+
378382
for distribution_maker in hypo_maker:
379-
update_param_values(distribution_maker, params)
380-
381-
if isinstance(params, Param): params = ParamSet(params) # just for the following
382-
383-
for p in params.names: # now update params with det_names inside
384-
for i, det_name in enumerate(hypo_maker.det_names):
385-
if det_name in p:
386-
cp = deepcopy(params[p])
387-
cp.name = cp.name.replace('_'+det_name, "")
388-
update_param_values(hypo_maker._distribution_makers[i], cp)
383+
ps = deepcopy(params)
384+
for p in ps.names:
385+
if distribution_maker.detector_name in p:
386+
p_name = p.replace('_'+distribution_maker.detector_name, "")
387+
if p_name in ps.names:
388+
ps.remove(p_name)
389+
ps[p].name = p_name
390+
update_param_values(distribution_maker, ps,
391+
update_nominal_values, update_range, update_is_fixed)
392+
hypo_maker.init_params()
389393

390394
# TODO: move this to a central location prob. in utils
391395
class Counter(object):
@@ -1166,7 +1170,10 @@ def _fit_octants(self, data_dist, hypo_maker, metric, external_priors_penalty,
11661170
# Copy the fitted parameter values from the best fit case into the hypo maker's
11671171
# parameter values.
11681172
# Also reinstate the original parameter range for the angle
1169-
update_param_values(hypo_maker, best_fit_info.params.free, update_range=True)
1173+
if hypo_maker.__class__.__name__ == "Detectors":
1174+
update_param_values_detector(hypo_maker, best_fit_info.params.free, update_range=True)
1175+
else:
1176+
update_param_values(hypo_maker, best_fit_info.params.free, update_range=True)
11701177

11711178
return best_fit_info
11721179

@@ -1310,7 +1317,10 @@ def _fit_grid_scan(self, data_dist, hypo_maker, metric,
13101317
mod_param.is_fixed = True
13111318
# It is important not to use hypo_maker.update_params(mod_param) here
13121319
# because we don't want to overwrite the memory reference!
1313-
update_param_values(hypo_maker, mod_param, update_is_fixed=True)
1320+
if hypo_maker.__class__.__name__ == "Detectors":
1321+
update_param_values_detector(hypo_maker, mod_param, update_is_fixed=True)
1322+
else:
1323+
update_param_values(hypo_maker, mod_param, update_is_fixed=True)
13141324
new_fit_info = self.fit_recursively(
13151325
data_dist, hypo_maker, metric, external_priors_penalty,
13161326
local_fit_kwargs["method"], local_fit_kwargs["method_kwargs"],
@@ -1338,7 +1348,10 @@ def _fit_grid_scan(self, data_dist, hypo_maker, metric,
13381348
best_fit_result = all_fit_results[best_idx]
13391349

13401350
if do_refined_fit:
1341-
update_param_values(hypo_maker, best_fit_result.params.free)
1351+
if hypo_maker.__class__.__name__ == "Detectors":
1352+
update_param_values_detector(hypo_maker, best_fit_result.params.free)
1353+
else:
1354+
update_param_values(hypo_maker, best_fit_result.params.free)
13421355
# the params stored in the best fit may come from a grid point where
13431356
# parameters were fixed, so we free them up again
13441357
for param in originally_free:
@@ -1507,9 +1520,14 @@ def _fit_ranges(self, data_dist, hypo_maker, metric,
15071520
logging.info(f"parameter with modified range:\n{mod_param}")
15081521
# use update_param_values instead of hypo_maker.update_params so that we
15091522
# don't overwrite the internal memory reference
1510-
update_param_values(
1511-
hypo_maker, mod_param, update_range=True, update_nominal_values=True
1512-
)
1523+
if hypo_maker.__class__.__name__ == "Detectors":
1524+
update_param_values_detector(
1525+
hypo_maker, mod_param, update_range=True, update_nominal_values=True
1526+
)
1527+
else:
1528+
update_param_values(
1529+
hypo_maker, mod_param, update_range=True, update_nominal_values=True
1530+
)
15131531
fit_result = self.fit_recursively(
15141532
data_dist, hypo_maker, metric, external_priors_penalty,
15151533
local_fit_kwargs["method"], local_fit_kwargs["method_kwargs"],
@@ -1536,10 +1554,16 @@ def _fit_ranges(self, data_dist, hypo_maker, metric,
15361554
# set the values of all parameters in the hypo_maker to the best fit values
15371555
# without overwriting the memory reference.
15381556
# Also reset ranges and nominal values that we might have changed above!
1539-
update_param_values(
1540-
hypo_maker, best_fit_result.params.free,
1541-
update_range=True, update_nominal_values=True
1542-
)
1557+
if hypo_maker.__class__.__name__ == "Detectors":
1558+
update_param_values_detector(
1559+
hypo_maker, best_fit_result.params.free,
1560+
update_range=True, update_nominal_values=True
1561+
)
1562+
else:
1563+
update_param_values(
1564+
hypo_maker, best_fit_result.params.free,
1565+
update_range=True, update_nominal_values=True
1566+
)
15431567
return best_fit_result
15441568

15451569
def _fit_staged(self, data_dist, hypo_maker, metric,
@@ -1565,9 +1589,14 @@ def _fit_staged(self, data_dist, hypo_maker, metric,
15651589
for i, fit_kwargs in enumerate(local_fit_kwargs):
15661590
logging.info(f"Beginning fit {i+1} / {len(local_fit_kwargs)}")
15671591
if best_fit_params is not None:
1568-
update_param_values(
1569-
hypo_maker, best_fit_params.free, update_nominal_values=True
1570-
)
1592+
if hypo_maker.__class__.__name__ == "Detectors":
1593+
update_param_values_detector(
1594+
hypo_maker, best_fit_params.free, update_nominal_values=True
1595+
)
1596+
else:
1597+
update_param_values(
1598+
hypo_maker, best_fit_params.free, update_nominal_values=True
1599+
)
15711600
best_fit_info = self.fit_recursively(
15721601
data_dist, hypo_maker, metric, external_priors_penalty,
15731602
fit_kwargs["method"], fit_kwargs["method_kwargs"],
@@ -1588,9 +1617,14 @@ def _fit_staged(self, data_dist, hypo_maker, metric,
15881617
best_fit_info._rehash()
15891618
# Make sure that the hypo_maker has its params also at the best fit point
15901619
# with the original nominal parameter values.
1591-
update_param_values(
1592-
hypo_maker, best_fit_info.params.free, update_nominal_values=True
1593-
)
1620+
if hypo_maker.__class__.__name__ == "Detectors":
1621+
update_param_values_detector(
1622+
hypo_maker, best_fit_info.params.free, update_nominal_values=True
1623+
)
1624+
else:
1625+
update_param_values(
1626+
hypo_maker, best_fit_info.params.free, update_nominal_values=True
1627+
)
15941628
return best_fit_info
15951629

15961630
def _fit_scipy(self, data_dist, hypo_maker, metric,
@@ -1918,6 +1952,8 @@ def annealing_callback(x, f, context):
19181952
rescaled_pvals = optimize_result.pop('x')
19191953
rescaled_pvals = np.where(flip_x0, 1 - rescaled_pvals, rescaled_pvals)
19201954
hypo_maker._set_rescaled_free_params(rescaled_pvals) # pylint: disable=protected-access
1955+
if hypo_maker.__class__.__name__ == "Detectors":
1956+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
19211957

19221958
# Get the best-fit metric value
19231959
metric_val = sign * optimize_result.pop('fun')
@@ -1944,6 +1980,8 @@ def annealing_callback(x, f, context):
19441980
# Reset to starting value of the fit, rather than nominal values because
19451981
# the nominal value might be out of range if this is inside an octant check.
19461982
hypo_maker._set_rescaled_free_params(x0)
1983+
if hypo_maker.__class__.__name__ == "Detectors":
1984+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
19471985

19481986
# TODO: other metrics
19491987
fit_info = HypoFitResult(
@@ -2115,6 +2153,8 @@ def loss_func(x):
21152153
# values from [0,1] back to physical range)
21162154
rescaled_pvals = np.array(m.values)
21172155
hypo_maker._set_rescaled_free_params(rescaled_pvals) # pylint: disable=protected-access
2156+
if hypo_maker.__class__.__name__ == "Detectors":
2157+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
21182158

21192159
# Get the best-fit metric value
21202160
metric_val = sign * m.fval
@@ -2153,6 +2193,8 @@ def loss_func(x):
21532193
# Reset to starting value of the fit, rather than nominal values because
21542194
# the nominal value might be out of range if this is inside an octant check.
21552195
hypo_maker._set_rescaled_free_params(x0)
2196+
if hypo_maker.__class__.__name__ == "Detectors":
2197+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
21562198

21572199
# TODO: other metrics
21582200
fit_info = HypoFitResult(
@@ -2286,6 +2328,8 @@ def loss_func(x, grad):
22862328
# values from [0,1] back to physical range)
22872329
rescaled_pvals = xopt
22882330
hypo_maker._set_rescaled_free_params(rescaled_pvals) # pylint: disable=protected-access
2331+
if hypo_maker.__class__.__name__ == "Detectors":
2332+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
22892333

22902334
# Get the best-fit metric value
22912335
metric_val = sign * opt.last_optimum_value()
@@ -2322,6 +2366,8 @@ def loss_func(x, grad):
23222366

23232367
if self.blindness > 1: # only at stricter blindness level
23242368
hypo_maker._set_rescaled_free_params(x0)
2369+
if hypo_maker.__class__.__name__ == "Detectors":
2370+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
23252371

23262372
# TODO: other metrics
23272373
fit_info = HypoFitResult(
@@ -2403,6 +2449,8 @@ def ineq_func(x, grad):
24032449
if grad.size > 0:
24042450
raise RuntimeError("gradients not supported")
24052451
hypo_maker._set_rescaled_free_params(x)
2452+
if hypo_maker.__class__.__name__ == "Detectors":
2453+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
24062454
# In NLOPT, the inequality function must stay negative, while in
24072455
# scipy, the inequality function must stay positive. We keep with
24082456
# the scipy convention by flipping the sign.
@@ -2518,6 +2566,8 @@ def _minimizer_callable(self, scaled_param_vals, hypo_maker, data_dist,
25182566
scaled_param_vals = np.where(flip_x0, 1 - scaled_param_vals, scaled_param_vals)
25192567
# Set param values from the scaled versions the minimizer works with
25202568
hypo_maker._set_rescaled_free_params(scaled_param_vals) # pylint: disable=protected-access
2569+
if hypo_maker.__class__.__name__ == "Detectors":
2570+
update_param_values_detector(hypo_maker, hypo_maker.params.free) #updates values for ALL detectors
25212571

25222572
# Get the map set
25232573
try:
@@ -2548,7 +2598,6 @@ def _minimizer_callable(self, scaled_param_vals, hypo_maker, data_dist,
25482598
#
25492599
try:
25502600
if hypo_maker.__class__.__name__ == "Detectors":
2551-
update_param_values_detector(hypo_maker, hypo_maker.params) #updates values for ALL detectors
25522601
metric_val = 0
25532602
for i in range(len(hypo_maker.distribution_makers)):
25542603
data = data_dist[i].metric_total(expected_values=hypo_asimov_dist[i],

pisa/core/detectors.py

+20-15
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ def __init__(self, pipelines, label=None, set_livetime_from_data=True, profile=F
9191
n += 1
9292
if n < 2:
9393
raise NameError('Shared param %s only a free param in less than 2 detectors.' % sp)
94+
95+
self.init_params()
9496

9597
def __repr__(self):
9698
return self.tabulate(tablefmt="presto")
@@ -147,30 +149,33 @@ def get_outputs(self, **kwargs):
147149
return outputs
148150

149151
def update_params(self, params):
150-
for distribution_maker in self:
151-
distribution_maker.update_params(params)
152-
153-
#if None in self.det_names: return # No detector names
154-
155-
if isinstance(params,Param): params = ParamSet(params) # just for the following
156-
157-
for p in params.names: # now update params with det_names inside
158-
for i, det_name in enumerate(self.det_names):
159-
if det_name in p:
160-
cp = deepcopy(params[p])
161-
cp.name = cp.name.replace('_'+det_name, "")
162-
self._distribution_makers[i].update_params(cp)
152+
if isinstance(params,Param): params = ParamSet(params)
153+
154+
for distribution_maker in self.distribution_makers:
155+
ps = deepcopy(params)
156+
for p in ps.names:
157+
if distribution_maker.detector_name in p:
158+
p_name = p.replace('_'+distribution_maker.detector_name, "")
159+
if p_name in ps.names:
160+
ps.remove(p_name)
161+
ps[p].name = p_name
162+
distribution_maker.update_params(ps)
163+
self.init_params()
163164

164165
def select_params(self, selections, error_on_missing=True):
165166
for distribution_maker in self:
166167
distribution_maker.select_params(selections=selections, error_on_missing=error_on_missing)
168+
self.init_params()
167169

168170
@property
169171
def distribution_makers(self):
170172
return self._distribution_makers
171173

172174
@property
173175
def params(self):
176+
return self._params
177+
178+
def init_params(self):
174179
"""Returns a ParamSet including all params of all detectors. First the shared params
175180
(if there are some), then all the "single detector" params. If two detectors use a
176181
parameter with the same name (but not shared), the name of the detector is added to the
@@ -196,7 +201,7 @@ def params(self):
196201
params.extend(changed_param)
197202
else:
198203
params.extend(param)
199-
return params
204+
self._params = params
200205

201206
@property
202207
def shared_param_ind_list(self):
@@ -221,7 +226,7 @@ def param_selections(self):
221226
selections = None
222227
for distribution_maker in self:
223228
if selections != None and sorted(distribution_maker.param_selections) != selections:
224-
raise ('Different param_selections for different detectors.')
229+
raise AssertionError('Different param_selections for different detectors.')
225230
selections = sorted(distribution_maker.param_selections)
226231
return selections
227232

pisa_examples/resources/settings/osc/nufitv52.cfg

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ theta23_ih.3s_range = [39.9, 51.5] * units.deg
1919
theta23_ih.range = [30.0, 60.0] * units.deg
2020

2121
deltacp_nh = 232 units.deg
22+
deltacp_nh_gauss = 232 +/- 36 units.deg
2223
deltacp_nh.3s_range = [144,350] * units.deg
2324
deltacp_nh.range = [0,360] * units.deg
2425

2526
deltacp_ih = 276 units.deg
27+
deltacp_ih_gauss = 276 +/- 29 units.deg
2628
deltacp_ih.3s_range = [194,344] * units.deg
2729
deltacp_ih.range = [0,360] * units.deg
2830

@@ -39,4 +41,4 @@ deltam31_nh.3s_range = [0.002427, 0.002590] * units.eV**2
3941
deltam31_ih = -0.002412 units.eV**2
4042
deltam31_ih_gauss = -0.002412 +/- 0.000028 * units.eV**2
4143
deltam31_ih.3s_range = [-0.002496, -0.002332] * units.eV**2
42-
deltam31_ih.range = [-0.007, -0.001] * units.eV**2
44+
deltam31_ih.range = [-0.007, -0.001] * units.eV**2

0 commit comments

Comments
 (0)