Skip to content
Snippets Groups Projects
Commit 6c1e2ff3 authored by Mauro Donega's avatar Mauro Donega
Browse files

how to fit with minuit in python

parent 6b7e341e
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Maximum Likelihood method # iMinuit for LSQ
In this notebook we will be using iminuit to fit with LSQ. In this notebook we will be using iminuit to fit with LSQ.
iMinuit: iMinuit:
https://iminuit.readthedocs.io/en/latest/index.html# https://iminuit.readthedocs.io/en/latest/index.html#
Here below a quick summary of: Here below a quick summary of:
https://iminuit.readthedocs.io/en/latest/tutorials.html https://iminuit.readthedocs.io/en/latest/tutorials.html
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
import numpy as np import numpy as np
%matplotlib inline %matplotlib inline
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from iminuit import Minuit from iminuit import Minuit
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Generate random numbers on a straight line # Generate random numbers on a straight line
def line(x, a, b): def line(x, a, b):
return a + x * b return a + x * b
data_x = np.linspace(0, 1, 10) data_x = np.linspace(0, 1, 10)
# precomputed random numbers from a normal distribution # precomputed random numbers from a normal distribution
offsets = np.array([-0.49783783, -0.33041722, -1.71800806, 1.60229399, 1.36682387, offsets = np.array([-0.49783783, -0.33041722, -1.71800806, 1.60229399, 1.36682387,
-1.15424221, -0.91425267, -0.03395604, -1.27611719, -0.7004073 ]) -1.15424221, -0.91425267, -0.03395604, -1.27611719, -0.7004073 ])
data_y = line(data_x, 1, 2) + 0.1 * offsets # generate some data points with random offsets data_y = line(data_x, 1, 2) + 0.1 * offsets # generate some data points with random offsets
plt.plot(data_x, data_y, "o") plt.plot(data_x, data_y, "o")
plt.xlim(-0.1, 1.1); plt.xlim(-0.1, 1.1);
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Definition of the loss function you want to minimize (here the LSQ) # Definition of the loss function you want to minimize (here the LSQ)
def least_squares(a, b): def least_squares(a, b):
yvar = 0.01 yvar = 0.01
return sum((data_y - line(data_x, a, b)) ** 2 / yvar) return sum((data_y - line(data_x, a, b)) ** 2 / yvar)
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Minuit # Minuit
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Minuit uses introspection to detect the parameter names of your function - it means # Minuit uses introspection to detect the parameter names of your function - it means
# #
# A complete instance of Minuit contains the starting point of each parameter (<name>) and # A complete instance of Minuit contains the starting point of each parameter (<name>) and
# the step size (<err_name>) for the dimension corresponding to that parameter. # the step size (<err_name>) for the dimension corresponding to that parameter.
# #
# To correctly compute the uncertainties, it needs to know if you are doing a max likelihood # To correctly compute the uncertainties, it needs to know if you are doing a max likelihood
# or a least squares fit: # or a least squares fit:
# errordef = 0.5 for negative log-likelihood functions # errordef = 0.5 for negative log-likelihood functions
# errordef = 1 for least-squares functions # errordef = 1 for least-squares functions
# #
# You can specify the limits on the parameters to be fit ("limit_varname") # You can specify the limits on the parameters to be fit ("limit_varname")
# lower limit: use limit_<name> = (<value>, None) or (<value>, float("infinity")) # lower limit: use limit_<name> = (<value>, None) or (<value>, float("infinity"))
# upper limit: use limit_<name> = (None, <value>) or (-float("infinity"), <value>) # upper limit: use limit_<name> = (None, <value>) or (-float("infinity"), <value>)
# two-sided limit: use limit_<name> = (<min_value>, <max_value>) # two-sided limit: use limit_<name> = (<min_value>, <max_value>)
# #
# You can fix some of the parameters (ignore that dimension in the fit) setting <fix_name> = True/False # You can fix some of the parameters (ignore that dimension in the fit) setting <fix_name> = True/False
# fix_a=True, # fix_a=True,
m = Minuit(least_squares, m = Minuit(least_squares,
a=5, b=5, a=5, b=5,
fix_a = True, fix_a = True,
error_a=0.1, error_b=0.1, error_a=0.1, error_b=0.1,
limit_a=(0, None), limit_b=(0, 10), limit_a=(0, None), limit_b=(0, 10),
errordef=1) errordef=1)
m.get_param_states() m.get_param_states()
# Once Minuit is constructed you can still fix/release parameters as: # Once Minuit is constructed you can still fix/release parameters as:
m.fixed["a"] = False m.fixed["a"] = False
m.fixed["b"] = True m.fixed["b"] = True
# Trick to run over all parameters: # Trick to run over all parameters:
for key in m.fixed: for key in m.fixed:
m.fixed[key] = False m.fixed[key] = False
m.migrad() m.migrad()
# To change the value of a fixed parameter (or reset it to a different value) you can access it as: # To change the value of a fixed parameter (or reset it to a different value) you can access it as:
m.values["a"] = 0.5 m.values["a"] = 0.5
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# MIGRAD # MIGRAD
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Migrad performs Variable-Metric Minimization. It combines a steepest-descends algorithm along with # Migrad performs Variable-Metric Minimization. It combines a steepest-descends algorithm along with
# line search strategy. Migrad is very popular in high energy physics field because of its robustness. # line search strategy. Migrad is very popular in high energy physics field because of its robustness.
m.migrad() m.migrad()
``` ```
%% Output %% Output
------------------------------------------------------------------ ------------------------------------------------------------------
| FCN = 10.39 | Ncalls=42 (106 total) | | FCN = 10.39 | Ncalls=42 (106 total) |
| EDM = 4.32E-06 (Goal: 1E-05) | up = 1.0 | | EDM = 4.32E-06 (Goal: 1E-05) | up = 1.0 |
------------------------------------------------------------------ ------------------------------------------------------------------
| Valid Min. | Valid Param. | Above EDM | Reached call limit | | Valid Min. | Valid Param. | Above EDM | Reached call limit |
------------------------------------------------------------------ ------------------------------------------------------------------
| True | True | False | False | | True | True | False | False |
------------------------------------------------------------------ ------------------------------------------------------------------
| Hesse failed | Has cov. | Accurate | Pos. def. | Forced | | Hesse failed | Has cov. | Accurate | Pos. def. | Forced |
------------------------------------------------------------------ ------------------------------------------------------------------
| False | True | True | True | False | | False | True | True | True | False |
------------------------------------------------------------------ ------------------------------------------------------------------
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
| | Name | Value | Hesse Err | Minos Err- | Minos Err+ | Limit- | Limit+ | Fixed | | | Name | Value | Hesse Err | Minos Err- | Minos Err+ | Limit- | Limit+ | Fixed |
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
| 0 | a | 0.99 | 0.06 | | | 0 | | | | 0 | a | 0.99 | 0.06 | | | 0 | | |
| 1 | b | 1.95 | 0.10 | | | 0 | 10 | | | 1 | b | 1.95 | 0.10 | | | 0 | 10 | |
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from pprint import pprint from pprint import pprint
# To understand the results of the fit there are two dict-like objects: # To understand the results of the fit there are two dict-like objects:
# The first one is # The first one is
pprint (m.get_fmin()) pprint (m.get_fmin())
# The most important one here is is_valid. If this is false, the fit did not converge and the result is useless ! # The most important one here is is_valid. If this is false, the fit did not converge and the result is useless !
# #
# When is_valid = False it can be that the fit function is not analytical everywhere in the parameter space # When is_valid = False it can be that the fit function is not analytical everywhere in the parameter space
# or does not have a local minimum (the minimum may be at infinity, the extremum may be a saddle point # or does not have a local minimum (the minimum may be at infinity, the extremum may be a saddle point
# or maximum). Indicators for this are: # or maximum). Indicators for this are:
# is_above_max_edm=True # is_above_max_edm=True
# hesse_failed=True # hesse_failed=True
# has_posdef_covar=False # has_posdef_covar=False
# has_made_posdef_covar=True # has_made_posdef_covar=True
# #
# Migrad reached the call limit before the convergence so that has_reached_call_limit=True. # Migrad reached the call limit before the convergence so that has_reached_call_limit=True.
# The used number of function calls is nfcn, and the call limit can be changed with the keyword argument ncall # The used number of function calls is nfcn, and the call limit can be changed with the keyword argument ncall
# in the method Minuit.migrad # in the method Minuit.migrad
# #
# Migrad detects converge by a small edm value, the estimated distance to minimum. This is the difference between # Migrad detects converge by a small edm value, the estimated distance to minimum. This is the difference between
# the current minimum value of the minimized function and the next prediction based on a local quadratic # the current minimum value of the minimized function and the next prediction based on a local quadratic
# approximation of the function (something that Migrad computes as part of its algorithm). If the fit did not # approximation of the function (something that Migrad computes as part of its algorithm). If the fit did not
# converge, is_above_max_edm is true. # converge, is_above_max_edm is true.
# #
# To have a reliable uncertainty determination, you should make sure that: # To have a reliable uncertainty determination, you should make sure that:
# has_covariance = True # has_covariance = True
# has_accurate_covar = True # has_accurate_covar = True
# has_posdef_covar = True # has_posdef_covar = True
# has_made_posdef_covar = False # has_made_posdef_covar = False
# hesse_failed = False # hesse_failed = False
``` ```
%% Output %% Output
FMin(fval=10.387015571126001, edm=4.319932643302712e-06, tolerance=0.1, nfcn=42, ncalls=106, up=1.0, is_valid=True, has_valid_parameters=True, has_accurate_covar=True, has_posdef_covar=True, has_made_posdef_covar=False, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False) FMin(fval=10.387015571126001, edm=4.319932643302712e-06, tolerance=0.1, nfcn=42, ncalls=106, up=1.0, is_valid=True, has_valid_parameters=True, has_accurate_covar=True, has_posdef_covar=True, has_made_posdef_covar=False, hesse_failed=False, has_covariance=True, is_above_max_edm=False, has_reached_call_limit=False)
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# The second one is a list of dict-like objects which contain information about the fitted parameters: # The second one is a list of dict-like objects which contain information about the fitted parameters:
pprint(m.get_param_states()) pprint(m.get_param_states())
# Important fields are: # Important fields are:
# index: parameter index. # index: parameter index.
# name: parameter name. # name: parameter name.
# value: value of the parameter at the minimum. # value: value of the parameter at the minimum.
# error: uncertainty estimate for the parameter value. # error: uncertainty estimate for the parameter value.
``` ```
%% Output %% Output
[Param(number=0, name='a', value=0.9908538664000521, error=0.05876818861364508, is_const=False, is_fixed=False, has_limits=True, has_lower_limit=True, has_upper_limit=False, lower_limit=0.0, upper_limit=None), [Param(number=0, name='a', value=0.9908538664000521, error=0.05876818861364508, is_const=False, is_fixed=False, has_limits=True, has_lower_limit=True, has_upper_limit=False, lower_limit=0.0, upper_limit=None),
Param(number=1, name='b', value=1.945147699139382, error=0.09907833701567537, is_const=False, is_fixed=False, has_limits=True, has_lower_limit=True, has_upper_limit=True, lower_limit=0.0, upper_limit=10.0)] Param(number=1, name='b', value=1.945147699139382, error=0.09907833701567537, is_const=False, is_fixed=False, has_limits=True, has_lower_limit=True, has_upper_limit=True, lower_limit=0.0, upper_limit=10.0)]
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# if you need just the basic # if you need just the basic
print (m.migrad_ok()) print (m.migrad_ok())
print (m.matrix_accurate()) print (m.matrix_accurate())
``` ```
%% Output %% Output
True True
True True
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Hesse # Hesse
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# The Hesse algorithm numerically computes the matrix of second derivatives at the function minimum # The Hesse algorithm numerically computes the matrix of second derivatives at the function minimum
# (called the Hessian matrix) and inverts it. # (called the Hessian matrix) and inverts it.
# Pros: # Pros:
# (Comparably) fast computation. # (Comparably) fast computation.
# Provides covariance matrix for error propagation. # Provides covariance matrix for error propagation.
# Cons: # Cons:
# Wrong if function does not look like a hyperparabola around the minimum # Wrong if function does not look like a hyperparabola around the minimum
m.hesse() m.hesse()
``` ```
%% Output %% Output
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
| | Name | Value | Hesse Err | Minos Err- | Minos Err+ | Limit- | Limit+ | Fixed | | | Name | Value | Hesse Err | Minos Err- | Minos Err+ | Limit- | Limit+ | Fixed |
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
| 0 | a | 0.99 | 0.06 | | | 0 | | | | 0 | a | 0.99 | 0.06 | | | 0 | | |
| 1 | b | 1.95 | 0.10 | | | 0 | 10 | | | 1 | b | 1.95 | 0.10 | | | 0 | 10 | |
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Covariance - colored table # Covariance - colored table
m.matrix() m.matrix()
``` ```
%% Output %% Output
--------------------- ---------------------
| | a b | | | a b |
--------------------- ---------------------
| a | 0.003 -0.005 | | a | 0.003 -0.005 |
| b | -0.005 0.010 | | b | -0.005 0.010 |
--------------------- ---------------------
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# to access the matrix # to access the matrix
cov = m.matrix() cov = m.matrix()
print (cov) print (cov)
# or as a numpy array # or as a numpy array
npcov = m.np_matrix() npcov = m.np_matrix()
print (npcov) print (npcov)
``` ```
%% Output %% Output
--------------------- ---------------------
| | a b | | | a b |
--------------------- ---------------------
| a | 0.003 -0.005 | | a | 0.003 -0.005 |
| b | -0.005 0.010 | | b | -0.005 0.010 |
--------------------- ---------------------
[[ 0.00345475 -0.00490941] [[ 0.00345475 -0.00490941]
[-0.00490941 0.00981865]] [-0.00490941 0.00981865]]
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Correlation - colored table # Correlation - colored table
m.matrix(correlation=True) m.matrix(correlation=True)
``` ```
%% Output %% Output
----------------- -----------------
| | a b | | | a b |
----------------- -----------------
| a | 1.0 -0.8 | | a | 1.0 -0.8 |
| b | -0.8 1.0 | | b | -0.8 1.0 |
----------------- -----------------
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# to access the matrix # to access the matrix
corr = m.matrix(correlation=True) corr = m.matrix(correlation=True)
print (corr) print (corr)
# or as a numpy array # or as a numpy array
npcov = m.np_matrix(correlation=True) npcov = m.np_matrix(correlation=True)
print (npcov) print (npcov)
``` ```
%% Output %% Output
----------------- -----------------
| | a b | | | a b |
----------------- -----------------
| a | 1.0 -0.8 | | a | 1.0 -0.8 |
| b | -0.8 1.0 | | b | -0.8 1.0 |
----------------- -----------------
[[ 1. -0.84293683] [[ 1. -0.84293683]
[-0.84293683 1. ]] [-0.84293683 1. ]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Minos # Minos
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Minos implements the so-called profile likelihood method, where the neighborhood around the function minimum # Minos implements the so-called profile likelihood method, where the neighborhood around the function minimum
# is scanned until the contour is found where the function increase by the value of errordef. # is scanned until the contour is found where the function increase by the value of errordef.
# Pros: # Pros:
# Good for functions which are not very close to a hyper-parabola around the minimum # Good for functions which are not very close to a hyper-parabola around the minimum
# Produces pretty confidence regions for scientific plots # Produces pretty confidence regions for scientific plots
# Cons: # Cons:
# Takes really long time # Takes really long time
# Result is difficult to error-propagate, since it cannot be described by a covariance matrix # Result is difficult to error-propagate, since it cannot be described by a covariance matrix
# #
# The results contain information as: # The results contain information as:
# At Limit: Whether Minos hit a parameter limit before the finishing the contour. # At Limit: Whether Minos hit a parameter limit before the finishing the contour.
# Max FCN: Whether Minos reached the maximum number of allowed calls before finishing the contour. # Max FCN: Whether Minos reached the maximum number of allowed calls before finishing the contour.
# New Min: Whether Minos discovered a deeper local minimum in the neighborhood of the current one. # New Min: Whether Minos discovered a deeper local minimum in the neighborhood of the current one.
``` ```
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m.minos() m.minos()
``` ```
%% Output %% Output
------------------------------------------------- -------------------------------------------------
| a | Valid | | a | Valid |
------------------------------------------------- -------------------------------------------------
| Error | -0.06 | 0.06 | | Error | -0.06 | 0.06 |
| Valid | True | True | | Valid | True | True |
| At Limit | False | False | | At Limit | False | False |
| Max FCN | False | False | | Max FCN | False | False |
| New Min | False | False | | New Min | False | False |
------------------------------------------------- -------------------------------------------------
------------------------------------------------- -------------------------------------------------
| b | Valid | | b | Valid |
------------------------------------------------- -------------------------------------------------
| Error | -0.10 | 0.10 | | Error | -0.10 | 0.10 |
| Valid | True | True | | Valid | True | True |
| At Limit | False | False | | At Limit | False | False |
| Max FCN | False | False | | Max FCN | False | False |
| New Min | False | False | | New Min | False | False |
------------------------------------------------- -------------------------------------------------
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m.get_param_states() m.get_param_states()
``` ```
%% Output %% Output
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
| | Name | Value | Hesse Err | Minos Err- | Minos Err+ | Limit- | Limit+ | Fixed | | | Name | Value | Hesse Err | Minos Err- | Minos Err+ | Limit- | Limit+ | Fixed |
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
| 0 | a | 0.99 | 0.06 | -0.06 | 0.06 | 0 | | | | 0 | a | 0.99 | 0.06 | -0.06 | 0.06 | 0 | | |
| 1 | b | 1.95 | 0.10 | -0.10 | 0.10 | 0 | 10 | | | 1 | b | 1.95 | 0.10 | -0.10 | 0.10 | 0 | 10 | |
------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Other ways to access the fits results # Other ways to access the fits results
pprint(m.np_values()) pprint(m.np_values())
pprint(m.np_errors()) pprint(m.np_errors())
pprint(m.np_merrors()) # The layout returned by Minuit.np_merrors() follows the convention pprint(m.np_merrors()) # The layout returned by Minuit.np_merrors() follows the convention
# [abs(delta_down), delta_up] that is used by matplotlib.pyplot.errorbar. # [abs(delta_down), delta_up] that is used by matplotlib.pyplot.errorbar.
pprint(m.np_covariance()) pprint(m.np_covariance())
``` ```
%% Output %% Output
array([0.99085387, 1.9451477 ]) array([0.99085387, 1.9451477 ])
array([0.05876843, 0.09907874]) array([0.05876843, 0.09907874])
array([[0.05866313, 0.09929038], array([[0.05866313, 0.09929038],
[0.05888831, 0.09888435]]) [0.05888831, 0.09888435]])
array([[ 0.00345475, -0.00490941], array([[ 0.00345475, -0.00490941],
[-0.00490941, 0.00981865]]) [-0.00490941, 0.00981865]])
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Plot contours # Plot contours
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m.draw_mncontour('a','b', nsigma=4, numpoints=100) # nsigma=4 says: draw four contours from sigma=1 to 4 m.draw_mncontour('a','b', nsigma=4, numpoints=100) # nsigma=4 says: draw four contours from sigma=1 to 4
``` ```
%% Output %% Output
<matplotlib.contour.ContourSet at 0x11e23d690> <matplotlib.contour.ContourSet at 0x11e23d690>
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m.draw_profile('a'); m.draw_profile('a');
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
m.draw_mnprofile('a'); m.draw_mnprofile('a');
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
px, py = m.profile('a', subtract_min=True) px, py = m.profile('a', subtract_min=True)
plt.plot(px, py); plt.plot(px, py);
``` ```
%% Output %% Output
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
``` ```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment