Creating custom PDFs with RooFit in python

Hi Root and RooFit experts,
Is it possible to create a custom RooFit pdf from python? I am trying to make a pdf that uses a gaussian process with the scikit-learn library in python. I am running into problems trying to make a new python class that inherets from RooAbsPdf. What I am trying is this:

RooMyPdf.py

import ROOT

Class RooMyPdf(ROOT.RooAbsPdf):
    def __init__(self, name, title, x, alpha):
          super(RooMyPdf, self).__init__(name, title)

    def evaluate(self):
          #Do some calculations here
           return 1.0

Then trying create an instance of this class:

import ROOT
import RooMyPdf

x = ROOT.RooRealVar('x','x',-1.0,1.0)
alpha = ROOT.RooRealVar('alpha','alpha',0.0,1.0)
pdf = RooMyPdf.RooMyPdf("mypdf", "CustomPDF",x ,alpha)

And the output I get is:

Traceback (most recent call last):
  File "pdfTest.py", line 7, in <module>
    pdf = RooMyPdf.RooMyPdf("mypdf", "CustomPDF",x ,alpha)
  File "/Users/rob/Documents/Atlas/software/DiphotonAnalysis/GPBackgrounds/GPBackgrounds/RooMyPdf.py", line 5, in __init__
    super(RooMyPdf, self).__init__()
TypeError: none of the 3 overloaded methods succeeded. Full details:
  RooAbsPdf is abstract and can not be instantiated
  RooAbsPdf is abstract and can not be instantiated
  RooAbsPdf is abstract and can not be instantiated

Looking in the c++ implementation of the RooAbsPdf class, it doesnt look like the constructor is an abstract method so Im not really sure whats going on here. Am I doing something dumb or is this not possible to do in python? Any help would be greatly appreciated.

1 Like

It seems the argument type resolution doesn’t manage to pick up the correct constructor.
Try the default constructor and then call SetNameTitle(n, t).

By default constructor do you mean just init() with no arguments? Ive tried that too and it gives the same result. Or is there some other way to call the default constructor?

yes, that’s what I meant :slight_smile:
do you get the exact same traceback ?

Yes. Exactly the same. Actually, the printout of the traceback I posted originally above was from when I tried to use init() with no arguments. Grabbed the wrong one, but yes, the tracebacks are the same.

For reference, this is the one I get when I try to call init(name, title):

Traceback (most recent call last):
  File "pdfTest.py", line 7, in <module>
    pdf = RooMyPdf.RooMyPdf("mypdf", "CustomPDF",x ,alpha)
  File "/Users/rob/Documents/Atlas/software/DiphotonAnalysis/GPBackgrounds/GPBackgrounds/RooMyPdf.py", line 5, in __init__
    super(RooMyPdf, self).__init__(name, title)
TypeError: none of the 3 overloaded methods succeeded. Full details:
  RooAbsPdf is abstract and can not be instantiated
  RooAbsPdf is abstract and can not be instantiated
  RooAbsPdf is abstract and can not be instantiated

RooAbsPdf is abstract b/c it’s base class RooAbsReal has a pure virtual method evaluate(), which is not overridden in the former. Adding this method to the Python derived class does not make it available on the C++ side (and even in C++ you could not create the base class sec, but only as part of a derived class that provides the overridden function).

For the Python side to override the function, an intermediate class is needed that fills the slot in the C++ vtable and forwards calls to the Python method. See TPyFitFunction for some examples of how to do that.

Such a setup can be automated with Cling (I’ve proven before that it can be done even with ACLiC), but ROOT makes little use of abstract interfaces, so that never made it into the mainstream. For the fork, I may well add it, as it’s much more common in the RotW.

Aside, the opposite of deriving a C++ class from a Python class in Cling and then using the derived class back in Python works much easier and cleaner and I believe that feature is part of PyROOT.

Thanks Wim,
just to make sure I understand correctly; I can make a C++ class the inherits from RooAbsPdf and implement a dummy evaluate() function. Then I can make a python class that inherits from this class and just overwrite evaluate() now that it is no longer virtual.

Thanks for your help.

To instantiate it yes, but the C++ side will always call that dummy evaluate() and Python the Python one, hence the C++ side needs to forward to Python. Again, just look at TPyFitFunction.cxx (under root/bindings/pyroot/src). It’s a handfull of lines of C++, that you can even embed in your Python script by handing it to gInterpreter.LoadText() and have Cling JIT it.

Thank you Wim! I will take a look at that.

I actually ran across this, which may be of use:

http://nbviewer.jupyter.org/url/root.cern.ch/notebooks/HowTos/inherit-C++-classes-from-python.ipynb

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.