Skip to content

Commit 9759cfa

Browse files
tomaskontrimasMatthias HuberMatthias HuberMatthias HuberMatthias Huber
authored
Stacking update (#85)
Introducing support for (weighted) stacking analyses. * Update to allow stacking analysis, hopefully didnt break the single source analysis * some updates * some little updates for the stacking part * some stacking updates * update the event selection to save memory consumption for large catalog stacking * fixed a bug * again, fixed a bug * Updated the signal injection for the stacking to require much less memory * added source weights to signal generation * Revert "added source weights to signal generation" This reverts commit 9bd551c. * Fix declination band range for the last batch of source injection * Use numerically stable method to calculate psi opening angle * Code cleanup * Add support for source weights * differentiating detector and hypothesis source weights - first pass * Add source weights and grads normalization * added weighted stacking support for traditional method Co-authored-by: Matthias Huber <[email protected]> Co-authored-by: Matthias Huber <[email protected]> Co-authored-by: Matthias Huber <[email protected]> Co-authored-by: Matthias Huber <[email protected]> Co-authored-by: ibsafa <[email protected]>
1 parent 43b9683 commit 9759cfa

16 files changed

+1777
-185
lines changed

skyllh/core/analysis.py

+233-17
Large diffs are not rendered by default.

skyllh/core/analysis_utils.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ def pointlikesource_to_data_field_array(
3737
The right-ascention of the point-like source.
3838
`dec`: float
3939
The declination of the point-like source.
40+
`src_w`: float
41+
The nomalized detector weight of the point-like source.
42+
`src_w_grad`: float
43+
The normalized weight gradient of the point-like source.
44+
`src_w_W`: float
45+
The nomalized hypothesis weight of the point-like source.
4046
4147
Parameters
4248
----------
@@ -58,12 +64,19 @@ def pointlikesource_to_data_field_array(
5864

5965
arr = np.empty(
6066
(len(sources),),
61-
dtype=[('ra', np.float), ('dec', np.float)],
62-
order='F')
67+
dtype=[('ra', np.float), ('dec', np.float),
68+
('src_w', np.float), ('src_w_grad', np.float), ('src_w_W', np.float)]
69+
, order='F')
6370

6471
for (i, src) in enumerate(sources):
65-
arr['ra'][i] = src.ra
66-
arr['dec'][i] = src.dec
72+
arr['ra'][i] = src.ra
73+
arr['dec'][i] = src.dec
74+
arr['src_w'][i] = src.weight.src_w
75+
arr['src_w_grad'][i] = src.weight.src_w_grad
76+
arr['src_w_W'][i] = src.weight.src_w_W
77+
norm = arr['src_w'].sum()
78+
#arr['src_w'] /= norm
79+
#arr['src_w_grad'] /= norm
6780

6881
return arr
6982

skyllh/core/config.py

+6
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@
7474
'analysis_required_mc_field_names': [
7575
'true_ra', 'true_dec', 'true_energy', 'mcweight'
7676
]
77+
},
78+
# Flag if specific calculations in the core module can be cached.
79+
'caching': {
80+
'pdf': {
81+
'MultiDimGridPDF': False
82+
}
7783
}
7884
}
7985

skyllh/core/llhratio.py

+409-42
Large diffs are not rendered by default.

skyllh/core/minimizer.py

+179
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import logging
77
import numpy as np
88
import scipy.optimize
9+
import iminuit
910
from typing import Optional, Dict, Any, List
1011

1112
from skyllh.core.parameters import FitParameterSet
@@ -663,6 +664,184 @@ def is_repeatable(self, status):
663664
return False
664665

665666

667+
class MinuitMinimizerImpl(MinimizerImpl):
668+
"""The MinuitMinimizerImpl class provides the minimizer implementation for
669+
minuit minimizer.
670+
"""
671+
def __init__(self, maxls=100):
672+
"""Creates a new minimizer instance.
673+
674+
Parameters
675+
----------
676+
maxls : int
677+
The maximum number of line search steps for an interation.
678+
"""
679+
super(MinuitMinimizerImpl, self).__init__()
680+
681+
self._maxls = maxls
682+
self._minuit = iminuit.Minuit
683+
684+
def minimize(self, initials, bounds, func, func_args=None, **kwargs):
685+
"""Minimizes the given function ``func`` with the given initial function
686+
argument values ``initials``.
687+
688+
Parameters
689+
----------
690+
initials : 1D numpy ndarray
691+
The ndarray holding the initial values of all the fit parameters.
692+
bounds : 2D (N_fitparams,2)-shaped numpy ndarray
693+
The ndarray holding the boundary values (vmin, vmax) of the fit
694+
parameters.
695+
func : callable
696+
The function that should get minimized.
697+
The call signature must be
698+
699+
``__call__(x, *args)``
700+
701+
The return value of ``func`` must be (f, grads), the function value
702+
at the function arguments ``x`` and the ndarray with the values of
703+
the function gradient for each fit parameter, if the
704+
``func_provides_grads`` keyword argument option is set to True.
705+
If set to False, ``func`` must return only the function value.
706+
func_args : sequence | None
707+
Optional sequence of arguments for ``func``.
708+
709+
Additional Keyword Arguments
710+
----------------------------
711+
Additional keyword arguments include options for this minimizer
712+
implementation.
713+
714+
Returns
715+
-------
716+
xmin : 1D ndarray
717+
The array containing the function arguments at the function's
718+
minimum.
719+
fmin : float
720+
The function value at its minimum.
721+
status : dict
722+
The status dictionary with information about the minimization
723+
process. The following information are provided:
724+
725+
niter : int
726+
The number of iterations needed to find the minimum.
727+
warnflag : int
728+
The warning flag indicating if the minimization did converge.
729+
The possible values are:
730+
731+
0: The minimization converged.
732+
"""
733+
if(func_args is None):
734+
func_args = tuple()
735+
if(kwargs is None):
736+
kwargs = {}
737+
738+
if('maxls' not in kwargs):
739+
kwargs['maxls'] = self._maxls
740+
741+
def func_val(*args):
742+
return func(args, *func_args)[0]
743+
744+
def func_grads(*args):
745+
return func(args, *func_args)[1]
746+
747+
# Set initial values for the fitting parameters.
748+
p_names = ['p{}'.format(_) for _ in range(len(initials))]
749+
fit_arg = dict(zip(p_names, initials))
750+
751+
p_bounds = ['limit_p{}'.format(_) for _ in range(len(initials))]
752+
pbounds = [bounds[i] for i in range(len(p_bounds))]
753+
fit_arg.update(dict(zip(p_bounds, pbounds)))
754+
755+
fit_min = self._minuit(
756+
fcn=func_val,
757+
grad=func_grads,
758+
forced_parameters=p_names,
759+
pedantic=False,
760+
print_level=0,
761+
**fit_arg)
762+
763+
fit_min.migrad(resume=False)
764+
ncalls = fit_min.ncalls
765+
for l in range(25):
766+
r = fit_min.migrad(resume=True)
767+
ncalls += fit_min.ncalls
768+
if r[0]['has_valid_parameters']:
769+
break
770+
771+
xmin = fit_min.args
772+
fmin = fit_min.fval
773+
774+
status = dict()
775+
status['warnflag'] = 0
776+
status['nit'] = ncalls
777+
778+
return (xmin, fmin, status)
779+
780+
def get_niter(self, status):
781+
"""Returns the number of iterations needed to find the minimum.
782+
783+
Parameters
784+
----------
785+
status : dict
786+
The dictionary with the status information about the minimization
787+
process.
788+
789+
Returns
790+
-------
791+
niter : int
792+
The number of iterations needed to find the minimum.
793+
"""
794+
return status['nit']
795+
796+
def has_converged(self, status):
797+
"""Analyzes the status information dictionary if the minimization
798+
process has converged. By definition the minimization process has
799+
converged if ``status['warnflag']`` equals 0.
800+
801+
Parameters
802+
----------
803+
status : dict
804+
The dictionary with the status information about the minimization
805+
process.
806+
807+
Returns
808+
-------
809+
converged : bool
810+
The flag if the minimization has converged (True), or not (False).
811+
"""
812+
if(status['warnflag'] == 0):
813+
return True
814+
return False
815+
816+
def is_repeatable(self, status):
817+
"""Checks if the minimization process can be repeated to get a better
818+
result. It's repeatable if
819+
820+
`status['warnflag'] == 2 and 'FACTR' in str(status['task'])`
821+
822+
Parameters
823+
----------
824+
status : dict
825+
The dictionary with the status information about the last
826+
minimization process.
827+
828+
Returns
829+
-------
830+
repeatable : bool
831+
The flag if the minimization process can be repeated to obtain a
832+
better minimum.
833+
"""
834+
if(status['warnflag'] == 2):
835+
task = str(status['task'])
836+
if('FACTR' in task):
837+
return True
838+
if('ABNORMAL_TERMINATION_IN_LNSRCH' in task):
839+
# This is causes most probably by starting the minimization at
840+
# a parameter boundary.
841+
return True
842+
return False
843+
844+
666845
class NRNsScan2dMinimizerImpl(NR1dNsMinimizerImpl):
667846
"""The NRNsScan2dMinimizerImpl class provides a minimizer implementation for
668847
the R2->R1 function where the first dimension is minimized using the

0 commit comments

Comments
 (0)