# Minuit2Minimizer with ROOT.Math.IMultiGenFunction

Hello,
I have some old code written in python 2.7 using ROOT < 6.18 and I am working on converting it to python 3.10 with the newest ROOT 6.30.
I saw that there were some changes considering the TPyMultiGenFunction and I tried to replace it with the Math.IMultiGenFunction but I ran into a cppyy.ll.SegmentationViolation.

Here the old version:

``````minimizer = ROOT.Minuit2.Minuit2Minimizer(0)
errorfct = chi2fct3d_angles()
errorfct.SetVals(samplesize, points, errors)
minimizer.SetFunction(errorfct)

(...)

class chi2fct3d_angles( ROOT.TPyMultiGenFunction ):
def __init__( self ):
ROOT.TPyMultiGenFunction.__init__( self, self )
self.samplesize = 0
self.points = np.zeros(12)
self.errors = np.zeros(12)

def NDim( self ): # total number of variables
return 4.0

def SetVals(self, samplesizet, pointst, errorst):
self.samplesize = samplesizet
self.points = pointst
self.errors = errorst

def DoEval( self, pars):
chisq = []

for i in range(0,self.samplesize):
errsx = self.errors[i][0]**2
errsy = self.errors[i][1]**2
errsz = self.errors[i][2]**2

xvalue = self.points[i][0]
yvalue = self.points[i][1]
zvalue = self.points[i][2]
distx, disty, distz = dist_p_line_angles(xvalue, yvalue, zvalue, pars) # distance squared of line to point i

distx = distx**2
disty = disty**2
distz = distz**2
chisq.append((distx + disty + distz)/(errsx + errsy + errsz)) # add chisq of point i

chisq = np.sum(np.array(chisq))

return chisq

``````

Here is what I tried looking at the example at Python interface: PyROOT - ROOT

``````minimizer = ROOT.Minuit2.Minuit2Minimizer(0)
errorfct = chi2fct3d_angles()
errorfct.SetVals(samplesize, points, errors)
minimizer.SetFunction(errorfct)

(...)

class chi2fct3d_angles( ROOT.Math.IMultiGenFunction ):
def __init__( self ):
#ROOT.Math.IMultiGenFunction.__init__( self, self )
self.samplesize = 0
self.points = np.zeros(12)
self.errors = np.zeros(12)

def NDim( self ): # total number of variables
return 4.0

def SetVals(self, samplesizet, pointst, errorst):
self.samplesize = samplesizet
self.points = pointst
self.errors = errorst

def DoEval( self, pars):
chisq = []

for i in range(0,self.samplesize):
errsx = self.errors[i][0]**2
errsy = self.errors[i][1]**2
errsz = self.errors[i][2]**2

xvalue = self.points[i][0]
yvalue = self.points[i][1]
zvalue = self.points[i][2]
distx, disty, distz = dist_p_line_angles(xvalue, yvalue, zvalue, pars) # distance squared of line to point i
distx = distx**2
disty = disty**2
distz = distz**2
chisq.append((distx + disty + distz)/(errsx + errsy + errsz)) # add chisq of point i

chisq = np.sum(np.array(chisq))

return chisq

def Clone( self ):
x = chi2fct3d_angles()
ROOT.SetOwnership(x, False )
return x
``````

But I get the following error:

``````
minimizer.SetFunction(errorfct)
cppyy.ll.SegmentationViolation: void ROOT::Minuit2::Minuit2Minimizer::SetFunction(ROOT::Math::IBaseFunctionMultiDimTempl<double>& func) =>
SegmentationViolation: segfault in C++; program state was reset
``````

What change am I still missing?

Thank you!
Viktoria

ROOT Version: 6.30/06
Built for linuxx8664gcc on Apr 22 2024, 13:50:50
From tags/v6.30.06-0-g4f4e716372@v6.30.06-0-g4f4e716372
With c++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

Hi @viktoriak ,

I am adding PyROOT and math experts in the loop: @moneta @jonas @vpadulan

Best,
D

Hi @viktoriak!

I don’t know if this code is still supposed or work or not. PyROOT had big updates twice since ROOT 6.18, which a big cppyy upgrade in 6.22.

Here is a modified version of the example that works with the current ROOT but with a different (hopefully more robust) approach, using the `ROOT::Math::Functor`:

``````import ROOT

from array import array

class MyMultiGenFCN:

def DoEval( self, x ):
return (x[0] - 42) * (x[0] - 42)

def to_root_math_functor(self):
# keep the callable alive for the functor:
self._do_eval_callable = self.DoEval
functor = ROOT.Math.Functor(self._do_eval_callable, 1)
return functor

def main():
fitter = ROOT.Fit.Fitter()
myMultiGenFCN = MyMultiGenFCN()
functor = myMultiGenFCN.to_root_math_functor()

params = array('d', [1.])
fitter.FitFCN(functor, params)
fitter.Result().Print(ROOT.std.cout, True)

if __name__ == '__main__':
main()
``````

Cheers,
Jonas

Hi @jonas ,
thanks for the new example! I could adapt it to my case but now python is stuck on the `DoEval` `return` line (no errors, it just stays at this line without going further and doesn’t even respond to ctrl+C). I’m not sure if this is related or a different issue.

``````minimizer = ROOT.Minuit2.Minuit2Minimizer(0)
errorfct = chi2fct3d_angles() # define the function to minimise
errorfct.SetVals(samplesize, points, errors)
errorfunctor = errorfct.to_root_math_functor()
minimizer.SetFunction(errorfunctor)

(...)

class chi2fct3d_angles:
def __init__( self ):
self.samplesize = 0
self.points = np.zeros(12)
self.errors = np.zeros(12)

def NDim( self ): # total number of variables
return 4

def SetVals(self, samplesizet, pointst, errorst):
self.samplesize = samplesizet
self.points = pointst
self.errors = errorst

def DoEval( self, pars):
chisq = []

for i in range(0,self.samplesize):
errsx = self.errors[i][0]**2
errsy = self.errors[i][1]**2
errsz = self.errors[i][2]**2

xvalue = self.points[i][0]
yvalue = self.points[i][1]
zvalue = self.points[i][2]

distx, disty, distz = dist_p_line_angles(xvalue, yvalue, zvalue, pars) # distance squared of line to point i
distx = distx**2
disty = disty**2
distz = distz**2

chisq.append((distx + disty + distz)/(errsx + errsy + errsz)) # add chisq of point i

chisq = np.sum(np.array(chisq))

return chisq

def to_root_math_functor( self ):
self._do_eval_callable = self.DoEval
functor = ROOT.Math.Functor(self._do_eval_callable, self.NDim())
return functor
``````

I ran my script with `python -m trace --trace` and there it shows as very last line only “return chisq” before freezing.

Cheers,
Viktoria

I now went through it more thoroughly and the freezing seems to come from Minuit, as the return chisq does work a few times and then it gets stuck.

I added `minimizer.SetPrintLevel(3)` and `print(minimizer.Status())`:

``````minimizer = ROOT.Minuit2.Minuit2Minimizer(0)

# print errors etc during the minimisation
minimizer.SetPrintLevel(3)

errorfct = chi2fct3d_angles() # define the function to minimise
errorfct.SetVals(samplesize, points, errors)
errorfunctor = errorfct.to_root_math_functor()
minimizer.SetFunction(errorfunctor)

minimizer.SetMaxFunctionCalls(500)
minimizer.SetMaxIterations(500)
minimizer.SetTolerance(0.001)

minimizer.SetVariable(0,"px", pars[0], step[0])
minimizer.SetVariable(1,"pz", pars[1], step[1])
minimizer.SetVariable(2,"phi", pars[2], step[2])
minimizer.SetVariable(3,"theta", pars[3], step[3])

retval_min = minimizer.Minimize() # start the minimiser

minval = minimizer.MinValue() # minvalue of the function to minimise (chi2fct3d_angles() in this case)

stat = minimizer.Status()
print(stat)

covar_stat = minimizer.CovMatrixStatus()

temp_cov = []

ret_params = minimizer.X() # parameters of the fit
ret_errors = minimizer.Errors() # errors of the fit
final_params = [] # list to save all the parameters of the track

for i in range(0,pars.size):
final_params.append(ret_params[i])
for j in range(0,pars.size):
#print minimizer.CovMatrix(i,j)
temp_cov.append(minimizer.CovMatrix(i,j))

cov_matrices.append(temp_cov)
covar_mat_status.append(covar_stat)

final_lines_params.append(final_params)
chisq.append(minval)
conv_status.append(stat)
``````

where I get an output a few times:

``````Minuit2Minimizer: Minimize with max-calls 500 convergence for edm < 0.001 strategy 1
Info in <Minuit2>: MnSeedGenerator Computing seed using NumericalGradient calculator
Info in <Minuit2>: MnSeedGenerator 4 free parameters, FCN pointer 0x7fff1d714240
Info in <Minuit2>: InitialGradientCalculator Calculating initial gradient at point 	[                0                0     -1.334513594      2.143902497]
Info in <Minuit2>: InitialGradientCalculator Computed initial gradient for parameter px value 0 [ -0.001 , 0.001 ] dirin 0.001 grd 2000 g2 2e+06
Info in <Minuit2>: InitialGradientCalculator Computed initial gradient for parameter pz value 0 [ -0.001 , 0.001 ] dirin 0.001 grd 2000 g2 2e+06
Info in <Minuit2>: InitialGradientCalculator Computed initial gradient for parameter phi value -1.33451 [ -0.001 , 0.001 ] dirin 0.001 grd 2000 g2 2e+06
Info in <Minuit2>: InitialGradientCalculator Computed initial gradient for parameter theta value 2.1439 [ -0.001 , 0.001 ] dirin 0.001 grd 2000 g2 2e+06
at point 	[                0                0     -1.334513594      2.143902497]
``````

and then it freezes.

Cheers,
Viktoria

If the behavior is so random, then I would bet on object lifetime issues here.

Can you maybe share your full script so I can reproduce the crash? Then it should be very easy to figure it out.

Cheers,
Jonas

Hi @jonas,

I realised my ROOT was built with a Minuit multiprocessing flag that was added unintentionally.
I just ran it again after rebuilding properly and it works just fine!