Create and pass a C++ function in PyROOT

Hi,

Does anyone know how to create and pass a C++ function of the type

std:function<double(double, double, double)>

into PyROOT?

I have tried using ROOT.gInterpreter.ProcessLine() to do this, but I get a TypeError when I try to use the function that I created. From python I get that my function is of the type

<type 'ROOT.MethodProxy'>

Any ideas on how to correctly define a C++ function inside PyROOT?

Thanks in advance!
Ibles


ROOT Version: 6.16.00
Platform: Centos7
Compiler: GCC7


Hi,
it’s easier if you don’t use std::function.

Creating and passing a C++ function to PyROOT:

>>> ROOT.gInterpreter.Declare("int foo(int x) { return x*x; }")
>>> ROOT.foo(2)

Do you really need std::function? I’m not sure how PyROOT deals with it.
Cheers,
Enrico

Hi Enrico,

In some cases std::function is really needed in order to pass the function as a parameter to another function.

E.g. in C++ we have:

double reduce(const std::vector<double>& data, std::function<double(double, double, double)> func){
// process vector with func
// return scalar
}

import ROOT
func_def="""
double myOwnFitEF(double xPos_mm, double yPos_mm, double zPos_mm){
return 0.0;
};
"""
func = ROOT.gInterpreter.Declare(func_def);
func = ROOT.myOwnFitEF
print(type(func))

which returns <class 'ROOT.MethodProxy'>

But now if you want to pass this function to the C++ code:

ROOT.gSystem.Load(<path to my lib>);
ROOT.reduce(data, func)

you will get a unrecognised parameter 2 error.

1 Like

Hi @Luke_K,

Unfortunately, there is no support in PyROOT to pass a Python callable that represents a C++ callable (the MethodProxy) to a C++ function that expects an std::function.

If you want to do this dynamically, you need to do everything in C++, something like:

res = ROOT.gInterpreter.ProcessLine("reduce(genData(), myOwnFitEF)")
print(res)

All you seem to want to do and more is possible with cppyy (“experimental PyROOT”), so that’s coming.

That said, things are not impossible in PyROOT, but most of what you want has to go through C++ land (i.e. gInterpreter/Cling). Which is, of course, what cppyy does under the hood (just with much higher performance: a few quick measurements of a couple of different scenarios shows cppyy to outperform PyROOT in the range of 3.5x-6x!).

Anyway, the code below makes your example above work.

import ROOT
func_def="""
double myOwnFitEF(double xPos_mm, double yPos_mm, double zPos_mm){
   return xPos_mm + yPos_mm + zPos_mm;
};
"""
ROOT.gInterpreter.Declare(func_def);

cb_def="""
double reduce(const std::vector<double>& data, std::function<double(double, double, double)> func){
    return func(data[0], data[1], data[2]);
}
"""
ROOT.gInterpreter.Declare(cb_def);

stdfunc = ROOT.BindObject(
    ROOT.gInterpreter.ProcessLine('new std::function<double(double, double, double)>(myOwnFitEF);'),
    ROOT.std.function('double(double, double, double)'))
ROOT.SetOwnership(stdfunc, True)

v = ROOT.std.vector('double')()
v += [(x+1)**2 for x in range(3)]

print('From Python:', stdfunc(*v))
print('From C++:   ', ROOT.reduce(v, stdfunc))
3 Likes

Thanks, that works!

Looking forward to cppyy implementation.

When you say experimental PyROOT, do you mean all the nice things enabled with -Droot7=ON or is that belonging to a separate CMake setting?

Thanks! This is exactly what I was looking for.
I coincide with @Luke_K, I’m looking forward to seeing what cppyy can do.

Kind regards,
Ibles

@Luke_K: no, it’s a separate option, see this other post. I’ve never perused it myself, though.

@ibles: in the world outside HEP, the two most desired features are automatic template instantiations and callbacks. The latter both as in the example here, and cross-inheritance. Needless to say that those are the two corners with the most functional improvements. Below is an example with cppyy master, covering the same as above (where it needs to be said that callbacks into Python are (a lot) more expensive then callbacks into C++, so even as it looks more nice, for simple functions like this, JIT-ing a C++ function should still be preferred).

import cppyy

def myOwnFitEF(xPos_mm, yPos_mm, zPos_mm):
    return xPos_mm + yPos_mm + zPos_mm

cb_def="""
double reduce(const std::vector<double>& data, std::function<double(double, double, double)> func){
    return func(data[0], data[1], data[2]);
}
"""
cppyy.cppdef(cb_def);

v = cppyy.gbl.std.vector['double']([(x+1)**2 for x in range(3)])

print('From Python:', myOwnFitEF(*v))
print('From C++:   ', cppyy.gbl.reduce(v, myOwnFitEF))

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