Hello all.
This is either a bug in PyROOT or somewhere upstream, but I don’t think it is the expected functionality. The matter concerns the TMinuit class in Python, more specifically its method SetFCN. I would like to create several minimizers for different functions in PyROOT and set a different FCN for each. This can be done apparently by setting the FCN with SetFCN. However, if I run MIGRAD on any of these minimizers, they always use the FCN which was defined last, instead of the FCN which was assigned to them by SetFCN.
Consider the following code. I have defined two functions with appreciably different minima:
import ROOT
from array import array as arr
# two different functions to be minimized with Minuit
def FCN1(number_of_parameters, derivatives, f, parameters, internal_flag):
# has a minimum at (3.14, -2.71)
desired_minimum = (3.14, -2.71)
f[0] = (parameters[0] - desired_minimum[0]) ** 2 + (parameters[1] - desired_minimum[1]) ** 2 + 1.0
def FCN2(number_of_parameters, derivatives, f, parameters, internal_flag):
# has a minimum at (-9.91, 3.42)
desired_minimum = (-9.91, 3.42)
f[0] = (parameters[0] - desired_minimum[0]) ** 2 + (parameters[1] - desired_minimum[1]) ** 2 + 1.0
If I define a TMinuit instance for each and I assign each FCN to the minimizer via SetFCN then run MIGRAD on both, I get the minimum (-9.91, 3.42), which is true only for FCN2 (the one defined last). If, however, I post-pone the SetFCN for the second minimizer, I get a correct answer.
error_code = ROOT.Long(0)
par0, par1 = ROOT.Double(0), ROOT.Double(0)
par0e, par1e = ROOT.Double(0), ROOT.Double(0)
# create a TMinuit minimizer for each FCN
minuit1 = ROOT.TMinuit(2)
minuit2 = ROOT.TMinuit(2)
# Set the FCN for the minimizers
minuit1.SetFCN(FCN1)
minuit2.SetFCN(FCN2) # if this is moved below, everything works as expected
# minimize FCN1
minuit1.mnparm(0, 'a', 0.0, 0.01, 0, 0, error_code)
minuit1.mnparm(1, 'b', 0.0, 0.01, 0, 0, error_code)
minuit1.mnexcm("MIGRAD", arr('d', [6000, 0.1]), 2, error_code)
minuit1.mnexcm("HESSE", arr('d', [6000]), 1, error_code)
minuit1.GetParameter(0, par0, par0e)
minuit1.GetParameter(1, par1, par1e)
print par0, par1 # These final fit parameters are incorrect: (-9.91, 3.42), should be (3.14, -2.71)
#minuit2.SetFCN(FCN2) # if this were inserted here from above, everything would be OK
# minimize FCN2
minuit2.mnparm(0, 'a', 0.0, 0.01, 0, 0, error_code)
minuit2.mnparm(1, 'b', 0.0, 0.01, 0, 0, error_code)
minuit2.mnexcm("MIGRAD", arr('d', [6000, 0.1]), 2, error_code)
minuit2.mnexcm("HESSE", arr('d', [6000]), 1, error_code)
minuit2.GetParameter(0, par0, par0e)
minuit2.GetParameter(1, par1, par1e)
print par0, par1 # these are the correct parameters: (-9.91, 3.42)
Is there any way this is the expected behaviour? I mean, if I run: object.SetFCN(function)
I expect the FCN to be a property of that object, not somehow set globally, otherwise the syntax should be:
If not, is a fix possible or is this not a PyROOT problem, but more deeply rooted no pun intended ?