Skip to content

Commit

Permalink
[optimize] upgrade to iminuit version 2
Browse files Browse the repository at this point in the history
  • Loading branch information
peterstangl committed Jan 4, 2021
1 parent dadb2f8 commit 08d6b26
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 42 deletions.
57 changes: 17 additions & 40 deletions flavio/math/optimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,7 @@ def minimize_robust(fun, x0, args=(), methods=None, tries=3, disp=False,
if disp:
print("Skipping method MIGRAD: no iminuit installation found.")
continue
options = {'print_level': int(disp)} # 0 or 1
options.update(kwargs)
opt = minimize_migrad(fun, x0, args=args, **options)
opt = minimize_migrad(fun, x0, args=args, print_level=int(disp))
else:
options = {'disp': disp}
options.update(kwargs)
Expand Down Expand Up @@ -67,53 +65,32 @@ def mfun(*args):
return res


class AttributeDict(dict):
"""Dictionary subclass with attribute access"""
__getattr__ = dict.get
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__


class MinuitFunction(object):
"""Function wrapper for Minuit to allow supplying function with vector
"""Function wrapper for Minuit to allow supplying function with additional
arguments"""
def __init__(self, f, dim, args=()):
"""Initialize the instance. f: function, dim: number of dimensions"""
def __init__(self, f, args=()):
"""Initialize the instance. f: function"""
import iminuit
self.f = f
self.dim = dim
self.args = args
self.func_code = iminuit.util.make_func_code('x')

@property
def __code__(self):
"""Needed to fake the function signature for Minuit"""
d = AttributeDict()
d.co_varnames = ['x{}'.format(i) for i in range(self.dim)]
d.co_argcount = len(d.co_varnames)
return d

def __call__(self, *x):
def __call__(self, x):
return self.f(x, *self.args)


def minimize_migrad(fun, x0, args=(), dx0=None, **kwargs):
def minimize_migrad(fun, x0, args=(), print_level=0):
"""Minimization function using MINUIT's MIGRAD minimizer."""
import iminuit
mfun = MinuitFunction(f=fun, dim=len(x0), args=args)
# bring the parameters in a suitable form
par = iminuit.util.describe(mfun)
x0_dict = {par[i]: x0i for i, x0i in enumerate(x0)}
if dx0 is None:
dx0 = np.ones(len(x0))
dx0_dict = {'error_' + par[i]: dx0i for i, dx0i in enumerate(dx0)}
mfun = MinuitFunction(f=fun, args=args)
# run
minuit_args={'errordef': 1}
minuit_args.update(kwargs)
minuit = iminuit.Minuit(mfun, **x0_dict, **dx0_dict, **minuit_args)
fmin, param = minuit.migrad()
minuit = iminuit.Minuit(mfun, x0)
minuit.errordef = iminuit.Minuit.LEAST_SQUARES # == 1
minuit.print_level = print_level
mres = minuit.migrad()
# cast migrad result in terms of scipy-like result object
res = scipy.optimize.OptimizeResult()
res.success = fmin['is_valid']
res.fun = fmin['fval']
res.x = np.array([p['value'] for p in param])
res.nfev = fmin['nfcn']
res.success = mres.fmin.is_valid
res.fun = mres.fmin.fval
res.x = np.array(mres.values)
res.nfev = mres.fmin.nfcn
return res
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
'physics/data/wcsm/*',
]
},
install_requires=['numpy', 'scipy', 'setuptools>=3.3', 'pyyaml',
install_requires=['numpy>=1.16.5', 'scipy', 'setuptools>=3.3', 'pyyaml',
'ckmutil', 'wilson>=2.0', 'particle', ],
extras_require={
'testing': ['nose2'],
'plotting': ['matplotlib>=2.0'],
'sampling': ['iminuit'],
'sampling': ['iminuit>=2.0'],
},
)

0 comments on commit 08d6b26

Please sign in to comment.