16#ifndef TMVA_RSOFIEREADER
17#define TMVA_RSOFIEREADER
34namespace Experimental {
53 RSofieReader(
const std::string &path, std::vector<std::vector<size_t>> inputShapes = {},
int verbose = 0)
55 Load(path, inputShapes, verbose);
58 void Load(
const std::string &path, std::vector<std::vector<size_t>> inputShapes = {},
int verbose = 0)
61 enum EModelType {kONNX, kKeras, kPt, kROOT, kNotDef};
62 EModelType
type = kNotDef;
64 auto pos1 = path.rfind(
"/");
65 auto pos2 = path.find(
".onnx");
66 if (pos2 != std::string::npos) {
69 pos2 = path.find(
".h5");
70 if (pos2 != std::string::npos) {
73 pos2 = path.find(
".pt");
74 if (pos2 != std::string::npos) {
78 pos2 = path.find(
".root");
79 if (pos2 != std::string::npos) {
85 if (
type == kNotDef) {
86 throw std::runtime_error(
"Input file is not an ONNX or Keras or PyTorch file");
88 if (pos1 == std::string::npos)
92 std::string modelName = path.substr(pos1,pos2-pos1);
93 std::string fileType = path.substr(pos2+1, path.length()-pos2-1);
94 if (verbose) std::cout <<
"Parsing SOFIE model " << modelName <<
" of type " << fileType << std::endl;
98 std::string parserCode;
102 throw std::runtime_error(
"RSofieReader: cannot use SOFIE with ONNX since libROOTTMVASofieParser is missing");
104 gInterpreter->Declare(
"#include \"TMVA/RModelParser_ONNX.hxx\"");
105 parserCode +=
"{\nTMVA::Experimental::SOFIE::RModelParser_ONNX parser ; \n";
107 parserCode +=
"TMVA::Experimental::SOFIE::RModel model = parser.Parse(\"" + path +
"\",true); \n";
109 parserCode +=
"TMVA::Experimental::SOFIE::RModel model = parser.Parse(\"" + path +
"\"); \n";
111 else if (
type == kKeras) {
114 throw std::runtime_error(
"RSofieReader: cannot use SOFIE with Keras since libPyMVA is missing");
118 if (!inputShapes.empty() && ! inputShapes[0].empty())
119 batch_size = std::to_string(inputShapes[0][0]);
120 parserCode +=
"{\nTMVA::Experimental::SOFIE::RModel model = TMVA::Experimental::SOFIE::PyKeras::Parse(\"" + path +
123 else if (
type == kPt) {
126 throw std::runtime_error(
"RSofieReader: cannot use SOFIE with PyTorch since libPyMVA is missing");
128 if (inputShapes.size() == 0) {
129 throw std::runtime_error(
"RSofieReader: cannot use SOFIE with PyTorch since the input tensor shape is missing and is needed by the PyTorch parser");
131 std::string inputShapesStr =
"{";
132 for (
unsigned int i = 0; i < inputShapes.size(); i++) {
133 inputShapesStr +=
"{ ";
134 for (
unsigned int j = 0; j < inputShapes[i].size(); j++) {
136 if (j < inputShapes[i].
size()-1) inputShapesStr +=
", ";
138 inputShapesStr +=
"}";
139 if (i < inputShapes.size()-1) inputShapesStr +=
", ";
141 inputShapesStr +=
"}";
142 parserCode +=
"{\nTMVA::Experimental::SOFIE::RModel model = TMVA::Experimental::SOFIE::PyTorch::Parse(\"" + path +
"\", "
143 + inputShapesStr +
"); \n";
145 else if (
type == kROOT) {
147 parserCode +=
"{\nauto fileRead = TFile::Open(\"" + path +
"\",\"READ\");\n";
148 parserCode +=
"TMVA::Experimental::SOFIE::RModel * modelPtr;\n";
149 parserCode +=
"auto keyList = fileRead->GetListOfKeys(); TString name;\n";
150 parserCode +=
"for (const auto&& k : *keyList) { \n";
151 parserCode +=
" TString cname = ((TKey*)k)->GetClassName(); if (cname==\"TMVA::Experimental::SOFIE::RModel\") name = k->GetName(); }\n";
152 parserCode +=
"fileRead->GetObject(name,modelPtr); fileRead->Close(); delete fileRead;\n";
153 parserCode +=
"TMVA::Experimental::SOFIE::RModel & model = *modelPtr;\n";
160 parserCode +=
"{ auto p = new TMVA::Experimental::SOFIE::ROperator_Custom<float>(\""
161 + op.fOpName +
"\"," + op.fInputNames +
"," + op.fOutputNames +
"," + op.fOutputShapes +
",\"" + op.fFileName +
"\");\n";
162 parserCode +=
"std::unique_ptr<TMVA::Experimental::SOFIE::ROperator> op(p);\n";
163 parserCode +=
"model.AddOperator(std::move(op));\n}\n";
168 if (inputShapes.size() > 0 && inputShapes[0].size() > 0) {
169 batchSize = inputShapes[0][0];
170 if (batchSize < 1) batchSize = 1;
172 if (verbose) std::cout <<
"generating the code with batch size = " << batchSize <<
" ...\n";
174 parserCode +=
"model.Generate(TMVA::Experimental::SOFIE::Options::kDefault,"
178 parserCode +=
"model.PrintRequiredInputTensors();\n";
179 parserCode +=
"model.PrintIntermediateTensors();\n";
180 parserCode +=
"model.PrintOutputTensors();\n";
187 parserCode +=
"model.PrintRequiredInputTensors();\n";
188 parserCode +=
"model.PrintIntermediateTensors();\n";
189 parserCode +=
"model.PrintOutputTensors();\n";
192 parserCode +=
"{ auto p = new TMVA::Experimental::SOFIE::ROperator_Custom<float>(\""
193 + op.fOpName +
"\"," + op.fInputNames +
"," + op.fOutputNames +
"," + op.fOutputShapes +
",\"" + op.fFileName +
"\");\n";
194 parserCode +=
"std::unique_ptr<TMVA::Experimental::SOFIE::ROperator> op(p);\n";
195 parserCode +=
"model.AddOperator(std::move(op));\n}\n";
197 parserCode +=
"model.Generate(TMVA::Experimental::SOFIE::Options::kDefault,"
202 parserCode +=
"model.PrintGenerated(); \n";
203 parserCode +=
"model.OutputGenerated();\n";
205 parserCode +=
"int nInputs = model.GetInputTensorNames().size();\n";
210 parserCode +=
"return nInputs;\n}\n";
212 if (verbose) std::cout <<
"//ParserCode being executed:\n" << parserCode << std::endl;
214 auto iret =
gROOT->ProcessLine(parserCode.c_str());
216 std::string msg =
"RSofieReader: error processing the parser code: \n" + parserCode;
217 throw std::runtime_error(msg);
221 throw std::runtime_error(
"RSofieReader does not yet support model with > 3 inputs");
225 std::string modelHeader = modelName +
".hxx";
226 if (verbose) std::cout <<
"compile generated code from file " <<modelHeader << std::endl;
228 std::string msg =
"RSofieReader: input header file " + modelHeader +
" is not existing";
229 throw std::runtime_error(msg);
231 if (verbose) std::cout <<
"Creating Inference function for model " << modelName << std::endl;
232 std::string declCode;
233 declCode +=
"#pragma cling optimize(2)\n";
234 declCode +=
"#include \"" + modelHeader +
"\"\n";
236 std::string sessionClassName =
"TMVA_SOFIE_" + modelName +
"::Session";
238 std::string uidName = uuid.
AsString();
239 uidName.erase(std::remove_if(uidName.begin(), uidName.end(),
240 [](
char const&
c ) ->
bool { return !std::isalnum(c); } ), uidName.end());
242 std::string sessionName =
"session_" + uidName;
243 declCode += sessionClassName +
" " + sessionName +
";";
245 if (verbose) std::cout <<
"//global session declaration\n" << declCode << std::endl;
249 std::string msg =
"RSofieReader: error compiling inference code and creating session class\n" + declCode;
250 throw std::runtime_error(msg);
256 std::stringstream ifuncCode;
257 std::string funcName =
"SofieInference_" + uidName;
258 ifuncCode <<
"std::vector<float> " + funcName +
"( void * ptr";
260 ifuncCode <<
", float * data" << i;
261 ifuncCode <<
") {\n";
262 ifuncCode <<
" " << sessionClassName <<
" * s = " <<
"(" << sessionClassName <<
"*) (ptr);\n";
263 ifuncCode <<
" return s->infer(";
264 for (
int i = 0; i <
fNInputs; i++) {
265 if (i>0) ifuncCode <<
",";
266 ifuncCode <<
"data" << i;
271 if (verbose) std::cout <<
"//Inference function code using global session instance\n"
272 << ifuncCode.str() << std::endl;
276 std::string msg =
"RSofieReader: error compiling inference function\n" + ifuncCode.str();
277 throw std::runtime_error(msg);
285 void AddCustomOperator(
const std::string &opName,
const std::string &inputNames,
const std::string & outputNames,
286 const std::string & outputShapes,
const std::string & fileName) {
287 if (
fInitialized) std::cout <<
"WARNING: Model is already loaded and initialised. It must be done after adding the custom operators" << std::endl;
288 fCustomOperators.push_back( {fileName, opName,inputNames, outputNames,outputShapes});
294 std::string msg =
"Wrong number of inputs - model requires " + std::to_string(
fNInputs);
295 throw std::runtime_error(msg);
297 auto fptr =
reinterpret_cast<std::vector<float> (*)(
void *,
const float *)
>(
fFuncPtr);
300 std::vector<float>
DoCompute(
const std::vector<float> &
x1,
const std::vector<float> &
x2) {
302 std::string msg =
"Wrong number of inputs - model requires " + std::to_string(
fNInputs);
303 throw std::runtime_error(msg);
305 auto fptr =
reinterpret_cast<std::vector<float> (*)(
void *,
const float *,
const float *)
>(
fFuncPtr);
308 std::vector<float>
DoCompute(
const std::vector<float> &
x1,
const std::vector<float> &
x2,
const std::vector<float> & x3) {
310 std::string msg =
"Wrong number of inputs - model requires " + std::to_string(
fNInputs);
311 throw std::runtime_error(msg);
313 auto fptr =
reinterpret_cast<std::vector<float> (*)(
void *,
const float *,
const float *,
const float *)
>(
fFuncPtr);
318 template<
typename... T>
322 return std::vector<float>();
332 std::vector<float>
Compute(
const std::vector<float> &
x) {
334 return std::vector<float>();
353 const auto rowsize =
x.GetStrides()[0];
354 auto fptr =
reinterpret_cast<std::vector<float> (*)(
void *,
const float *)
>(
fFuncPtr);
361 for (
size_t i = 1; i < nrows; i++) {
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char x2
Option_t Option_t TPoint TPoint const char x1
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
R__EXTERN TSystem * gSystem
#define R__WRITE_LOCKGUARD(mutex)
TMVA::RSofieReader class for reading external Machine Learning models in ONNX files,...
RSofieReader(const std::string &path, std::vector< std::vector< size_t > > inputShapes={}, int verbose=0)
Create TMVA model from ONNX file print level can be 0 (minimal) 1 with info , 2 with all ONNX parsing...
RTensor< float > Compute(RTensor< float > &x)
Compute model prediction on input RTensor The shape of the input tensor should be {nevents,...
std::vector< float > Compute(const std::vector< float > &x)
std::vector< float > Compute(T... x)
Compute model prediction on vector.
void Load(const std::string &path, std::vector< std::vector< size_t > > inputShapes={}, int verbose=0)
std::vector< float > DoCompute(const std::vector< float > &x1, const std::vector< float > &x2, const std::vector< float > &x3)
std::vector< CustomOperatorData > fCustomOperators
std::vector< float > DoCompute(const std::vector< float > &x1)
void AddCustomOperator(const std::string &opName, const std::string &inputNames, const std::string &outputNames, const std::string &outputShapes, const std::string &fileName)
std::vector< float > DoCompute(const std::vector< float > &x1, const std::vector< float > &x2)
RSofieReader()
Dummy constructor which needs model loading afterwards.
RTensor is a container with contiguous memory and shape information.
const Shape_t & GetShape() const
virtual int Load(const char *module, const char *entry="", Bool_t system=kFALSE)
Load a shared library.
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
std::string ToString(const T &val)
Utility function for conversion to strings.
R__EXTERN TVirtualRWMutex * gCoreMutex
create variable transformations
std::string fOutputShapes