Callable with external data


ROOT Version: 6.22/06 (LCG_99/x86_64-centos7-gcc10-opt)


This is a continuation or extension of Add new column to RDataFrame

If I transcribe the code posted from swunsch to 6.22 and simplify

import ROOT

class AwesomeModel:
    def predict(self, x):
        return x[0] * x[1]

model = AwesomeModel()

@ROOT.Numba.Declare(["float"] * 2, "float")
def predictModel(var1, var2):
    return model.predict([var1, var2])

ROOT.gInterpreter.ProcessLine('cout << "2*3 = " << Numba::predictModel(2, 3) << endl;')

this fails with

Untyped global name 'model': cannot determine Numba type of <class '__main__.AwesomeModel'>

The goal is to define a callable that uses external variables/data which can be applied as a Define to a RDF. Something allong the lines:

import ROOT

data = [1,2,3]

@ROOT.Numba.Declare(["float"], "float")
def model(x, data):
    return x*data[0]

ROOT.gInterpreter.ProcessLine('cout << "apply data = " << Numba::model(2, data) << endl;')

and in the end

.Define("var3", "Numba::model(var1, data)")

Hi there,

Unfortunately you cannot simply take the code from the experimental ROOT.DeclareCppCallable in 6.22 and apply it to ROOT.Numba.Declare. We have decided to drop the feature to call in the C++ callable into the Python interpreter (as it happens with the model.predict), because it is blocked by the global interpreter lock in Python and results in a horrible runtime performance.

However, your second snippet can work with numba, see the following (runnable) example. However, only with static Python objects such as arrays.

import ROOT

data = (1, 2, 3)

@ROOT.Numba.Declare(['float', 'int'], 'float')
def model(x, i):
    return x * data[i]

ROOT.gInterpreter.ProcessLine('cout << "apply data = " << Numba::model(2, 1) << endl;')
apply data = 4

Another option is to push the external data into the C++ space, simply by using a function fully implemented in C++:

import ROOT

ROOT.gInterpreter.Declare('''
static const vector<float> data = {1, 2, 3};

float func(float x, int i) {
    return x * data[i];
}
''')

ROOT.gInterpreter.ProcessLine('cout << "apply data = " << func(2, 1) << endl;')

Best
Stefan

Thank you for the clarification and completely agree with the reasoning. However it might be beneficial, to still expose an interface ( e.g. ROOT.Numba.DeclareSlow ) that would be GIL blocking and let the user decided if they want to pay the price.

1 Like

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