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.

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