Fitting python functions in pyroot

Hi,

It seems like there is some kind of memory corruption going on when I fit a user defined
function in python. I am using root 4.01/02, python 2.3.3, and Linux 2.4.21. I appended
an example which is admittedly very convoluted. Simpler fits crash
also, but not as easily so it is harder to find an example for demonstration purposes.
The problem seems to occur as soon as I fit a user defined python function. When I define
TF1 with a TFormula that does the same thing as the python function, it works.
By the way, the example fit below will work if I move the line s=TF1(“s”,gax,-10.,10.,3)
two lines up (outside the function definition). This does not solve the problem though, it
just makes it more subtle.

Thanks,

Martin

PS: How can I use TMath::Erfc directly in python?

from ROOT import TH1D,TF1
from math import sqrt,exp

Just a Gaussian

def gax(y, q):
if q[1]!=0:
result= q[2]/sqrt(2.3.1415q[1]q[1])exp(-0.5(y[0]-q[0])(y[0]-q[0])/(q[1]*q[1]))
else:
result=0
return result

Function that integrates user defined Gaussian

def ga(x, p):
s=TF1(“s”,gax,-10.,10.,3)
s.SetParameters(p[0],p[1],p[2])
return s.Integral(x[0],10)

fill a histogram and fit

a=TH1D("","",100,-10,10)
er=TF1(“er”,“TMath::Erfc(x)”,0,10)
a.FillRandom(“er”,10000)

b=TF1(“b”,ga,-10,10,3)
b.SetParameters(0.,1.,1000.)
a.Fit(b,"","",-2,2)

Martin,

you are one cruel guy …

def ga(x, p):
   s=TF1("s",gax,-10.,10.,3)
   s.SetParameters(p[0],p[1],p[2])
   return s.Integral(x[0],10)

By making the TF1 “s” a local variable of “ga,” it will get cleaned up and
recreated every time that you call “ga” (which is a lot of times, given the
number of calls of a typical fit).

The code crashes because I haven’t dealt with all the details of memory
management of TF1’s that use python functions yet. The problem is that
in order to make CINT aware of a function, it needs to have a name. And
to please the user, the name better be the same as she gave it. So there
will be problems if you create one that has the same name as an existing
function (“s” in your case, over and over and over).

With python, you can instantiate TF1 with a callable object. Thus, simply
write a class, implement its call() member function with what you
did for “ga,” and keep “s” as a data member of that class.

For example:

class Ga:
   def __init__( self, s ):
      self.s = s

   def __call__( self, x, p ):
      self.s.SetParameters(p[0],p[1],p[2])
      return self.s.Integral(x[0],10)

ga = Ga( TF1( "s", gax, -10., 10., 3 ) )

I know of a few ways to improve things in the future, but fitting in python
is already slow enough without recreating TF1 every time, so better do it
with a class, like above …

As for TMath … it’s a namespace, not a class. Unfortunately, I do not know
of a workaround. I’ll have to fix it in the code. Thanks for pointing it out!

Best regards,
Wim