JIT user defined functions in RDataFrame Filter/Define calls

Hi all,

Is there a (hopefully simple) recipe to follow for using a user defined C++ function in the JIT RDataFrame calls?

For example, assume I have some super-complicated function which I then later want to use in an RDataFrame call

float myComplicatedFunction(float arg1, float arg2) {
  // Implementation
}

int main() {
  ROOT::RDataFrame df;
  // set up the RDF etc
  // How do I make this work?
  df.Define("ComplicatedVariable", "myComplicatedFunction(Arg1, Arg2)"); 
}

At the moment trying this gives me a ‘cannot interpret the following expression ... make sure that it is valid C++.’ error. How do I make my own functions available to the JIT compiler?
If it’s relevant, I’m building my project in CMake.

Cheers,
Jon


ROOT Version: 6.16
Platform: SLC 6
Compiler: gcc 6.2


Hi,
you have two options:

You can make the function definition available to the interpreter, e.g. by putting it in a separate header, #include'ing that header in you C++ program and also including in from the interpreter: gInterpreter->Declare("#include <...>").

Or you can just not rely on jitting, which also results in slightly faster runtimes:

float myComplicatedFunction(float arg1, float arg2) {
  // Implementation
}

int main() {
  ROOT::RDataFrame df;
  df.Define("ComplicatedVariable", myComplicatedFunction, {"Arg1", "Arg2"}); 
}

Cheers,
Enrico

Hi,

OK - so I guess this gInterpreter line is the main thing I was missing. I was kind of wondering if it would be possible somehow if I generated a dictionary (but this is something I’m not so familiar with)?

I know I can not rely on jitting and that would be the right solution in the very simplified example I have here. However it’s not always quite this simple…

Cheers,
Jon

ROOT dictionaries are for I/O.
You can compile your functions in a library (a .so file) and then include the library headers and load the library’s shared obect with gInterpreter->Load instead of having the function body inlined in the header.

(Showing off my lack of knowledge clearly :slight_smile: )

So to make sure I’ve understood - there’s no way to do this without having some sort of gInterpreter line inside the script itself? Would it be evil to put the gInterpreter->Declare("#include ...") line inside the header where the functions are defined?

Cheers,
Jon

there’s no way to do this without having some sort of gInterpreter line inside the script itself?

Showing off my lack of knowledge: maybe with dictionaries? :sweat_smile:
What you want, in ROOT terms, is for the interpreter to autoload your functions, but I don’t know how to do it. @Danilo or @Axel might help with that.

Would it be evil to put the gInterpreter->Declare("#include ...") line inside the header where the functions are defined?

I think so, yes :smile: You would also have to jump through hoops to make a function call at global scope compile.

What you want, in ROOT terms, is for the interpreter to autoload your functions

Exactly what I should have said in the first place :slight_smile:

I’ll wait and see if others can chime in but this has already been very helpful.

As Enrico pointed out it’s either compile time or runtime-interpretation.

For the latter you need to inform the interpreter about the function declaration. gInterpreter->Declare("float myComplicatedFunction(float arg1, float arg2);"); is the most performant option. The interpreter will then resolve the symbol from the binary. That works best if linked with -shared despite the binary being an executable and not a shared library.

Another option is to use ACLiC: gROOT->ProcessLine(".L MyCode.C+"); will compile that code into a shared library and make the declarations available to the interpreter.

Cheers, Axel.

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