Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RModelParser_ONNX.cxx
Go to the documentation of this file.
1#include "Byteswap.h"
3#include "onnx.hxx"
4
5#include <algorithm>
6#include <stdexcept>
7#include <string>
8#include <cstring>
9#include <memory>
10#include <cassert>
11#include <iostream>
12#include <unordered_map>
13#include <functional>
14#include "TMVA/SOFIE_common.hxx"
15
16namespace TMVA {
17namespace Experimental {
18namespace SOFIE {
19
20// Declaration of operators
21// Unary operators
33// Binary operators
40// Nary operators
45//Comparision Operators
51//Is Operators
55// Reduce operators
60// Others
109// Declaration of fused operators
115
116// Definition of RModelParser_ONNX::OperatorsMap
118 // Registered operators
119 std::unordered_map<std::string, ParserFuncSignature> fOperatorsMap;
120};
121
122// helper function to get initialized tensor data
123template<typename T>
125};
126// trait function to extract data from TensorProto
127template<>
128struct ExtractDataFromTP<float> {
129 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
130 if (tensor->float_data_size() != length)
131 throw std::runtime_error("TMVA::SOFIE - Failed to read float initialized tensor - actual size is " + std::to_string(tensor->float_data_size()));
132 const auto &src = tensor->float_data();
133 std::copy(src.begin(), src.end(), static_cast<float *>(data));
134 }
135};
136template<>
138 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
139 if (tensor->double_data_size() != length)
140 throw std::runtime_error("TMVA::SOFIE - Failed to read double initialized tensor - actual size is " + std::to_string(tensor->double_data_size()));
141 const auto &src = tensor->double_data();
142 std::copy(src.begin(), src.end(), static_cast<double *>(data));
143 }
144};
145template<>
146struct ExtractDataFromTP<int32_t> {
147 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
148 if (tensor->int32_data_size() != length)
149 throw std::runtime_error("TMVA::SOFIE - Failed to read int32 initialized tensor - actual size is " + std::to_string(tensor->int32_data_size()));
150 const auto &src = tensor->int32_data();
151 std::copy(src.begin(), src.end(), static_cast<int32_t *>(data));
152 }
153};
154template<>
155struct ExtractDataFromTP<int64_t> {
156 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
157 if (tensor->int64_data_size() != length)
158 throw std::runtime_error("TMVA::SOFIE - Failed to read int64 initialized tensor - actual size is " + std::to_string(tensor->int64_data_size()));
159 const auto &src = tensor->int64_data();
160 std::copy(src.begin(), src.end(), static_cast<int64_t *>(data));
161 }
162};
163
164#ifndef R__BYTESWAP
165namespace {
166
167// Copy nbytes from source to dest, byte-swapping each N-byte element. The
168// temporary avoids misaligned loads from the protobuf string buffer and makes
169// in-place swapping (dest == source) valid.
170template <std::size_t N>
171void CopyBswap(void *dest, const void *source, std::size_t nbytes)
172{
173 using value_type = typename RByteSwap<N>::value_type;
174 auto dst = static_cast<unsigned char *>(dest);
175 auto src = static_cast<const unsigned char *>(source);
176 for (std::size_t k = 0; k < nbytes; k += N) {
177 value_type v;
178 std::memcpy(&v, src + k, N);
180 std::memcpy(dst + k, &v, N);
181 }
182}
183
184// Copy a buffer of little-endian tensor elements to host (big-endian) byte order
185void CopyLEToHost(void *dest, const void *source, std::size_t nbytes, ETensorType tensor_type)
186{
187 switch (GetTypeSize(tensor_type)) {
188 case 1:
189 if (dest != source)
190 std::memcpy(dest, source, nbytes);
191 break;
192 case 2: CopyBswap<2>(dest, source, nbytes); break;
193 case 4: CopyBswap<4>(dest, source, nbytes); break;
194 case 8: CopyBswap<8>(dest, source, nbytes); break;
195 default:
196 throw std::runtime_error("Data type " + ConvertTypeToString(tensor_type) + " in tensor is not supported!\n");
197 }
198}
199
200} // anonymous namespace
201#endif
202
203std::shared_ptr<void> RModelParser_ONNX::GetInitializedTensorData(onnx::TensorProto *tensorproto, size_t tensor_size, ETensorType tensor_type)
204{
205
206 std::shared_ptr<void> data(malloc(tensor_size), free);
207
208 // check if initialized tensors are stored internally
209 if (tensorproto->data_location() != onnx::TensorProto::EXTERNAL) {
210 if (tensorproto->raw_data().size() > 0) {
211 if (tensorproto->raw_data().size() != tensor_size)
212 throw std::runtime_error("TMVA::SOFIE - Failed to read raw data of initialized tensor - actual raw size is " +
213 std::to_string(tensorproto->raw_data().size()));
214
215#ifdef R__BYTESWAP
216 // R__BYTESWAP is defined for little-endian architectures (most common ones)
217 std::memcpy(data.get(), tensorproto->raw_data().c_str(), tensor_size);
218#else
219 // big-endian architectures - need to swap bytes
220 CopyLEToHost(data.get(), tensorproto->raw_data().c_str(), tensor_size, tensor_type);
221#endif
222 } else {
223 // case tensor data are stored as specific types and not in raw_data
224 switch (tensor_type) {
225 case ETensorType::FLOAT: {
226 ExtractDataFromTP<float>::Copy(tensorproto, data.get(), tensor_size/ 4);
227 break;
228 }
229 case ETensorType::DOUBLE: {
230 ExtractDataFromTP<double>::Copy(tensorproto, data.get(), tensor_size/ 8);
231 break;
232 }
233 case ETensorType::INT32: {
234 ExtractDataFromTP<int32_t>::Copy(tensorproto, data.get(), tensor_size/ 4);
235 break;
236 }
237 case ETensorType::INT64: {
238 ExtractDataFromTP<int64_t>::Copy(tensorproto, data.get(), tensor_size/ 8);
239 break;
240 }
241 case ETensorType::BOOL: {
242 throw std::runtime_error("TMVA::SOFIE - ExtractData from TP in BOOL not supported");
243 break;
244 }
245 case ETensorType::UINT8: {
246 throw std::runtime_error("TMVA::SOFIE - ExtractData from TP in UINT8 not supported");
247 break;
248 }
249 default:
250 throw std::runtime_error("Data type " + ConvertTypeToString(tensor_type) + " in weight tensor is not supported!\n");
251 }
252 }
253
254 } else {
255 // case of external data
256
257 // read now tensor from file
258 std::string location;
259 size_t offset = 0, buffer_size = 0;
260
261 for (const auto &kv : tensorproto->external_data()) {
262 if (kv.key() == "location") location = kv.value();
263 else if (kv.key() == "offset") offset = std::stoull(kv.value());
264 else if (kv.key() == "length") buffer_size = std::stoull(kv.value());
265 }
266
267 // an explicitly set data file (SetExternalDataFile) takes precedence;
268 // otherwise use the location stored in the model, which is a path
269 // relative to the model directory, and as a last resort the
270 // conventional <model file>.data
271 std::string dataFileName = fDataFileName;
272 if (dataFileName.empty())
273 dataFileName = location.empty() ? fDefaultDataFileName : fModelDirectory + location;
274 if (dataFileName.empty())
275 throw std::runtime_error("TMVA::SOFIE ONNX : tensor " + tensorproto->name() +
276 " has external data but no data file location is available");
277
278 if (fVerbose)
279 std::cout << "Initialized data are stored externally in file " << dataFileName
280 << " at location " << location << " offset " << offset << " and with length " << buffer_size << std::endl;
281
282 if (buffer_size != tensor_size)
283 throw std::runtime_error("TMVA::SOFIE ONNX : invalid stored data size vs tensor size");
284
285 // open the data file if needed (a previous tensor may have opened a different one)
286 if (fDataFile.is_open() && fOpenedDataFileName != dataFileName)
287 fDataFile.close();
288 if (!fDataFile.is_open()) {
289 fDataFile.open(dataFileName, std::ios::binary);
290 if (!fDataFile.is_open())
291 throw std::runtime_error("TMVA::SOFIE ONNX: error reading external weight ONNX data file " + dataFileName);
293 }
294
295 fDataFile.seekg(offset);
296 fDataFile.read(reinterpret_cast<char *>(data.get()), buffer_size);
297#ifndef R__BYTESWAP
298 // external data is stored little-endian like raw_data - swap in place
299 CopyLEToHost(data.get(), data.get(), buffer_size, tensor_type);
300#endif
301 }
302
303 return data;
304}
305
306
307// Constructor of the parser
308RModelParser_ONNX::RModelParser_ONNX() noexcept : fOperatorsMapImpl(std::make_unique<OperatorsMapImpl>()) {
309 // Register operators
310 // Unary operators
312 RegisterOperator("Reciprocal", ParseReciprocal);
319 RegisterOperator("Softplus", ParseSoftplus);
322 // Binary operators
329 // Nary operators
334 //Comparision Operators
335 RegisterOperator("Equal", ParseEq);
337 RegisterOperator("LessOrEqual", ParseLessEq);
338 RegisterOperator("Greater", ParseGreater);
339 RegisterOperator("GreaterOrEqual", ParseGreaterEq);
340 // Is If operators
344 // Reduce operators
345 RegisterOperator("ReduceMean", ParseReduceMean);
346 RegisterOperator("ReduceSum", ParseReduceSum);
347 RegisterOperator("ReduceSumSquare", ParseReduceSumSquare);
348 RegisterOperator("ReduceProd", ParseReduceProd);
349 // Others
350 RegisterOperator("BatchNormalization", ParseBatchNormalization);
351 RegisterOperator("Constant", ParseConstant);
352 RegisterOperator("ConstantOfShape", ParseConstant);
354 RegisterOperator("Concat", ParseConcat);
356 RegisterOperator("ConvTranspose", ParseConvTranspose);
359 RegisterOperator("Identity", ParseIdentity);
360 RegisterOperator("LeakyRelu", ParseLeakyRelu);
362 RegisterOperator("AveragePool", ParsePool);
363 RegisterOperator("GlobalAveragePool", ParsePool);
364 RegisterOperator("MaxPool", ParsePool);
366 RegisterOperator("Reshape", ParseReshape);
367 RegisterOperator("Flatten", ParseReshape);
368 RegisterOperator("Squeeze", ParseReshape);
369 RegisterOperator("Unsqueeze", ParseReshape);
374 RegisterOperator("Sigmoid", ParseSigmoid);
377 RegisterOperator("Softmax", ParseSoftmax);
378 RegisterOperator("LogSoftmax", ParseSoftmax);
380 RegisterOperator("Transpose", ParseTranspose);
381 RegisterOperator("MatMul", ParseMatMul);
382 RegisterOperator("LayerNormalization", ParseLayerNormalization);
383 RegisterOperator("Expand", ParseExpand);
384 RegisterOperator("Gather", ParseGather);
385 RegisterOperator("GatherND", ParseGatherND);
388 RegisterOperator("HardSigmoid", ParseHardSigmoid);
389 RegisterOperator("HardSwish", ParseHardSwish);
390 RegisterOperator("EyeLike", ParseEyeLike);
396 RegisterOperator("InstanceNormalization", ParseInstanceNormalization);
399 RegisterOperator("Einsum", ParseEinsum);
400 RegisterOperator("RandomNormal", ParseRandom);
401 RegisterOperator("RandomNormalLike", ParseRandom);
402 RegisterOperator("RandomUniform", ParseRandom);
403 RegisterOperator("RandomUniformLike", ParseRandom);
404 RegisterOperator("ScatterElements", ParseScatterElements);
405 RegisterOperator("ScatterND", ParseScatterND);
406 RegisterOperator("NonZero", ParseNonZero);
408}
409
410// Destructor of the parser
412
414{
415 fOperatorsMapImpl->fOperatorsMap[name] = func;
416}
417
419{
420 return fOperatorsMapImpl->fOperatorsMap.find(name) != fOperatorsMapImpl->fOperatorsMap.end();
421}
422
424{
425 std::vector<std::string> ops;
426 ops.reserve(fOperatorsMapImpl->fOperatorsMap.size());
427 for (auto &it : fOperatorsMapImpl->fOperatorsMap) {
428 ops.emplace_back(it.first);
429 }
430 // return sorted list in alphabetical order
431 std::sort(ops.begin(), ops.end());
432 return ops;
433}
434
439
441{
443}
444
449
450// Parse an operator
451std::unique_ptr<ROperator>
452RModelParser_ONNX::ParseOperator(const size_t i, const onnx::GraphProto &graphproto, const std::vector<size_t> &nodes, const std::vector<int> & children)
453{
454 if (i >= nodes.size())
455 throw std::runtime_error("TMVA::SOFIE - Error in parsing ordered operators " + std::to_string(i) + " is >= " + std::to_string(nodes.size()));
456 int idx = nodes[i];
457 const auto &nodeproto = graphproto.node(idx);
458 const std::string op_type = nodeproto.op_type();
459 if (fVerbose)
460 std::cout << "Parsing operator " << op_type << std::endl;
461
462 // perform the fusion of operators
463 if (fFusedOperators.count(idx) == 1) {
464 int idx1 = fFusedOperators[idx].second;
465 if (fVerbose) {
466 std::cout << "\tFusing operators " << graphproto.node(idx1).name()
467 << " with " << graphproto.node(idx1).name() << std::endl;
468 }
469 if (fFusedOperators[idx].first == EFusedOp::kMatMulAdd) {
470 return ParseFuseMatMulAdd(*this, graphproto.node(idx1), graphproto.node(idx));
471 } else if (fFusedOperators[idx].first == EFusedOp::kConvAdd) {
472 return ParseFuseConvAdd(*this, graphproto.node(idx1), graphproto.node(idx));
473 } else if (fFusedOperators[idx].first == EFusedOp::kConvTransAdd) {
474 return ParseFuseConvTransposeAdd(*this, graphproto.node(idx1), graphproto.node(idx));
475 } else if (fFusedOperators[idx].first == EFusedOp::kGemmRelu) {
476 return ParseFuseGemmRelu(*this, graphproto.node(idx1), graphproto.node(idx));
477 } else if (fFusedOperators[idx].first == EFusedOp::kBatchnormRelu) {
478 return ParseFuseBatchnormRelu(*this, graphproto.node(idx1), graphproto.node(idx));
479 }
480 }
481
482 // try to fuse with following operator in case it is not last one and having only a single child
483 if (children.size() == 1) {
484 int idx2 = children.front();
485 if (op_type == "MatMul") {
486 // Fuse MatMul and Add
487 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Add") {
489 return nullptr;
490 }
491 } else if (nodeproto.op_type() == "Conv" || nodeproto.op_type() == "ConvTranspose") {
492 // Fuse Conv or ConvTranspose without bias and Add
493 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Add") {
494 if (nodeproto.op_type() == "Conv") {
496 return nullptr;
497 } else {
499 return nullptr;
500 }
501 }
502 } else if (nodeproto.op_type() == "Gemm") {
503 // Fuse Gemm with activation operators
504 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Relu") {
506 return nullptr;
507 }
508 } else if (nodeproto.op_type() == "BatchNormalization") {
509 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Relu") {
511 return nullptr;
512 }
513 }
514 }
515
516 auto it = fOperatorsMapImpl->fOperatorsMap.find(op_type);
517 if (it == fOperatorsMapImpl->fOperatorsMap.end()) {
518 std::cout << "operator " << op_type << " is not supported" << std::endl;
519 throw std::runtime_error("TMVA::SOFIE Operator type " + op_type + " is not yet supported");
520 }
521 if (fVerbose) {
522 std::cout << "\tCreating operator " << op_type << std::endl;
523 }
524 return it->second(*this, nodeproto);
525}
526
527// Parse a model
528RModel RModelParser_ONNX::Parse(std::string const &filename, bool verbose)
529{
530 fVerbose = verbose;
531
532 fTensorTypeMap.clear();
533
534 auto model = LoadModel(filename);
535 if (!model)
536 throw std::runtime_error("TMVA::SOFIE - Failed to load onnx file " + filename);
537
538 const onnx::GraphProto &graph = model->graph(); // not a memory leak. model freed automatically at the end.
539
540
541 std::time_t ttime = std::time(0);
542 std::tm *gmt_time = std::gmtime(&ttime);
543 std::string parsetime(std::asctime(gmt_time));
544
545 // get name of model (filename without directory name)
546 char sep = '/';
547#ifdef _WIN32
548 sep = '\\';
549#endif
550 size_t isep = filename.rfind(sep, filename.length());
551 std::string filename_nodir = filename;
552 if (isep != std::string::npos) {
553 filename_nodir = (filename.substr(isep + 1, filename.length() - isep));
554 }
555
556 fModelDirectory = (isep != std::string::npos) ? filename.substr(0, isep + 1) : "";
557 fDefaultDataFileName = filename + ".data";
558
562 return rmodel;
563}
564
565RModel RModelParser_ONNX::Parse(std::istream &input, std::string const &name, bool verbose)
566{
567 fVerbose = verbose;
568
569 fTensorTypeMap.clear();
570
571 auto model = LoadModel(input);
572 if (!model)
573 throw std::runtime_error("TMVA::SOFIE - Failed to parse ONNX model from input stream");
574
575 const onnx::GraphProto &graph = model->graph(); // not a memory leak. model freed automatically at the end.
576
577 std::time_t ttime = std::time(0);
578 std::tm *gmt_time = std::gmtime(&ttime);
579 std::string parsetime(std::asctime(gmt_time));
580
582 ParseONNXGraph(rmodel, graph, name);
584 return rmodel;
585}
586
587// Reset the state used to read external weight data, so that the next Parse
588// call does not pick up the data file of a previously parsed model. The
589// file name set with SetExternalDataFile is valid for a single Parse call.
591{
592 fDataFileName.clear();
593 fModelDirectory.clear();
594 fDefaultDataFileName.clear();
595 fOpenedDataFileName.clear();
596 if (fDataFile.is_open())
597 fDataFile.close();
598}
599
600std::unique_ptr<onnx::ModelProto> RModelParser_ONNX::LoadModel(const std::string &filename) {
601 std::fstream input(filename, std::ios::in | std::ios::binary);
602 if (!input) {
603 std::cerr << "TMVA::SOFIE - Failed to open onnx file " << filename << std::endl;
604 return {};
605 }
606
607 return LoadModel(input);
608}
609
610std::unique_ptr<onnx::ModelProto> RModelParser_ONNX::LoadModel(std::istream &input)
611{
612 auto model = std::make_unique<onnx::ModelProto>();
613
614 if (!model->ParseFromIstream(&input)) {
615 std::cerr << "TMVA::SOFIE - Failed to parse ONNX model from input stream" << std::endl;
616 return {};
617 }
618
619 // ONNX version is ir_version() - model_version() returns 0
620 if (fVerbose) {
621 std::cout << "ONNX Version " << model->ir_version() << std::endl;
622 }
623 return model;
624}
625
626void RModelParser_ONNX::CheckGraph(const onnx::GraphProto & graph, int & level, std::map<std::string, int> & missingOperators) {
627 if (fVerbose)
628 std::cout << "\n" << graph.name() << " Graph operator list\n";
629 for (int i = 0; i < graph.node_size(); i++) {
630 const auto & node = graph.node(i);
631 const std::string opType = node.op_type();
632 if (fVerbose) {
633 std::cout << "\tOperator " << i << " : " << opType << " (" << node.name() << "), " << graph.node(i).input_size()
634 << " inputs : {";
635 for (int j = 0; j < graph.node(i).input_size(); j++) {
636 std::cout << graph.node(i).input(j);
637 if (j < graph.node(i).input_size() - 1)
638 std::cout << ", ";
639 }
640 std::cout << " }" << std::endl;
641 }
642 // check if operator exists
644 missingOperators[opType] = level;
645 // see if sub-graph exists as node attributes
646 for (int j = 0; j < node.attribute_size(); j++) {
647 const auto & attribute = node.attribute(j);
648 if (attribute.has_g()) {
649 const auto & subGraph = attribute.g();
650 level += 1;
652 }
653 }
654 }
655}
656
657bool RModelParser_ONNX::CheckModel(std::string filename, bool verbose) {
658
659 fVerbose = verbose;
660 auto model = LoadModel(filename);
661 if (!model) return false;
662
663 const onnx::GraphProto &graph = model->graph();
664 // Initial operator order
665 if (fVerbose)
666 std::cout << "\nModel operator list " << model->producer_name() << "\n";
667
668 std::map<std::string, int> missingOperators;
669 int level = 1;
670 CheckGraph(graph, level, missingOperators);
671
672 if (!missingOperators.empty()) {
673 std::cout << "List of missing operators for model loaded from file " << filename << std::endl;
674 for (auto & op : missingOperators) {
675 std::cout << op.first << " " << op.second << std::endl;
676 }
677 return false;
678 }
679 std::cout << "All operators in the loaded model are supported!\n";
680 return true;
681}
682
684{
685 bool verbose = fVerbose;
686
687 if (graphName.empty())
688 graphName = graph.name();
689
690 if (verbose)
691 std::cout << "\nParsing Graph - " << graphName << std::endl;
692
693 // fFusedOperators is keyed by node index, so it is only valid for the graph
694 // being parsed: neither a second model parsed with the same parser nor a
695 // subgraph (e.g. of the If operator) may inherit it.
696 struct FusedOperatorsGuard {
697 std::map<int, std::pair<EFusedOp, int>> &fMap;
698 std::map<int, std::pair<EFusedOp, int>> fSaved;
699 FusedOperatorsGuard(std::map<int, std::pair<EFusedOp, int>> &map) : fMap(map) { fSaved.swap(fMap); }
700 ~FusedOperatorsGuard() { fMap.swap(fSaved); }
702
703 std::unordered_set<std::string> initializer_names;
704 for (int i = 0; i < graph.initializer_size(); i++) {
705 initializer_names.insert(graph.initializer(i).name());
706 }
707
708 if (verbose)
709 std::cout << "Parsing model inputs...." << std::endl;
710 /// Loop on model inputs
711 for (int i = 0; i < graph.input_size(); i++) {
712 RegisterTensorType(graph.input(i).name(),
713 static_cast<ETensorType>(graph.input(i).type().tensor_type().elem_type()));
714
715 if (verbose)
716 std::cout << "\tgraph input " << i << " name " << graph.input(i).name() << " type "
717 << graph.input(i).type().tensor_type().elem_type() << std::endl;
718
719 if (initializer_names.find(graph.input(i).name()) != initializer_names.end())
720 continue;
721
722 // input data node is not a weight node (has no initializer)
723 const onnx::ValueInfoProto &valueinfoproto = graph.input(i);
724 std::string input_name = valueinfoproto.name();
725
726 ETensorType type = static_cast<ETensorType>(valueinfoproto.type().tensor_type().elem_type());
727
728 std::vector<Dim> fShape;
729 bool existParam = false;
730 if (!valueinfoproto.type().tensor_type().has_shape())
731 throw std::runtime_error("TMVA::SOFIE data node with no shape restrictions is not supported yet");
732 for (int j = 0; j < valueinfoproto.type().tensor_type().shape().dim_size(); j++) {
733 Dim dim;
734 if (valueinfoproto.type().tensor_type().shape().dim(j).value_case() ==
736 int dim_value = valueinfoproto.type().tensor_type().shape().dim(j).dim_value();
737 dim.dim = dim_value;
738 // case input dim is -1 - set a parametric shape
739 if (dim_value < 0) {
740 dim.isParam = true;
741 existParam = true;
742 dim.param = UTILITY::Clean_name(input_name) + "_size";
743 }
744 } else if (valueinfoproto.type().tensor_type().shape().dim(j).value_case() ==
746 dim.isParam = true;
747 existParam = true;
748 dim.param = valueinfoproto.type().tensor_type().shape().dim(j).dim_param();
749 } else {
750 throw std::runtime_error("TMVA::SOFIE ONNX file error: Valueinfoproto " + input_name +
751 " has neither dim_value nor dim_param! \n");
752 }
753 fShape.push_back(dim);
754 }
755 if (valueinfoproto.type().tensor_type().shape().dim_size() == 0) {
756 Dim dim;
757 dim.dim = 1;
758 fShape.push_back(dim);
759 } // in case this TensorShapeProto has no dimension message: ONNX IR defines this to be a scalar
760
761 if (!existParam) {
762 std::vector<size_t> fShape_sizet;
763 for (auto &j : fShape) {
764 fShape_sizet.push_back(j.dim);
765 }
766
767 rmodel.AddInputTensorInfo(input_name, type, fShape_sizet);
768 } else {
769 rmodel.AddInputTensorInfo(input_name, type, fShape);
770 }
771 rmodel.AddInputTensorName(input_name); // store also names in given order
772 }
773
774 std::map<std::string, int> allInitializedTensors;
775
776 if (verbose)
777 std::cout << "\nParsing graph initializer list and fill model initialized tensors" << std::endl;
778
779 for (int i = 0; i < graph.initializer_size(); i++) {
781 std::vector<std::size_t> shape;
782 std::size_t tensor_length = 1;
783 for (int j = 0; j < tensorproto->dims_size(); j++) {
784 shape.push_back(tensorproto->dims(j));
785 tensor_length *= tensorproto->dims(j);
786 }
787 // in case of scalars keep an empty shape but with length =1
788
789 std::string tensor_name = graph.initializer(i).name();
790
791 if (verbose)
792 std::cout << "\t initializer " << i << " name " << tensor_name << " type " << graph.initializer(i).data_type()
793 << " and length " << tensor_length << std::endl;
794
795
796 // register also the initialized tensors
797 auto tensor_type = static_cast<ETensorType>(graph.initializer(i).data_type());
798 RegisterTensorType(tensor_name, tensor_type);
799
800 std::shared_ptr<void> data = GetInitializedTensorData(tensorproto, tensor_length * GetTypeSize(tensor_type), tensor_type);
801 rmodel.AddInitializedTensor(tensor_name, tensor_type, shape, data);
802 allInitializedTensors[tensor_name] = i;
803
804 if (verbose) {
805 std::cout << "add initialized tensor " << tensor_name << "with shape " << ConvertShapeToString(shape) << "and ";
806 if (tensor_type == ETensorType::FLOAT) {
807 std::cout << " float data: ";
809 }
810 else if (tensor_type == ETensorType::INT64) {
811 std::cout << " int64 data: ";
813 }
814 else if (tensor_type == ETensorType::UINT8) {
815 std::cout << " uint8 data: ";
817 }
818 else if (tensor_type == ETensorType::BOOL) {
819 std::cout << " Boolean data: ";
821 }
822 std::cout << std::endl;
823 }
824 } // end initializer list
825
826 // Initial operator order
827 if (verbose) {
828 std::cout << "\nGraph operator list (ONNX order)\n";
829 for (int i = 0; i < graph.node_size(); i++) {
830 std::cout << "\tOperator " << i << " : " << graph.node(i).op_type() << " , " << graph.node(i).input_size()
831 << " inputs : {";
832 for (int j = 0; j < graph.node(i).input_size(); j++) {
833 std::cout << graph.node(i).input(j);
834 if (j < graph.node(i).input_size() - 1)
835 std::cout << ", ";
836 }
837 std::cout << " }" << std::endl;
838 }
839 }
840
841 // make order of nodes:
842 if (verbose)
843 std::cout << "\n***********************\nRe-Order graph operator list\n*************************\n";
844 std::vector<size_t> nodesOrder;
845 nodesOrder.reserve(graph.node_size());
846 std::vector<bool> foundNodes(graph.node_size());
847
848 // loop at graph inputs
849 std::map<std::string, int> allInputs;
850 for (int i = 0; i < graph.input_size(); i++) {
851 allInputs[graph.input(i).name()] = -1;
852 }
853 do {
854 auto psize = nodesOrder.size();
855 for (int i = 0; i < graph.node_size(); i++) {
856 if (foundNodes[i])
857 continue;
858 // check if all input exists add to list
859 bool existInputs = true;
860 int input_size = graph.node(i).input_size();
861 // special case for Reshape where shape is input and not a weight tensor
862 if (fVerbose)
863 std::cout << "Checking input of Node " << i << " : " << graph.node(i).name() << std::endl;
864 for (int j = 0; j < input_size; j++) {
865 std::string name = graph.node(i).input(j);
866 // skip empty names
867 if (!name.empty()) {
868 existInputs &= (allInputs.find(name) != allInputs.end() ||
870 if (fVerbose) {
871 std::cout << "\t\t input " << name << " "
872 << bool(allInputs.find(name) != allInputs.end()) << " " <<
874 existInputs << std::endl;
875 }
876 }
877 }
878 if (!existInputs) {
879 if (fVerbose) {
880 std::cout << "skip node " << graph.node(i).op_type() << " " << graph.node(i).name() << " inputs are not existing ";
881 for (int j = 0; j < input_size; j++) {
882 std::cout << graph.node(i).input(j) << " ";
883 }
884 std::cout << std::endl;
885 }
886 continue;
887 }
888
889 // adding node to the currectly ordered list
890 if (verbose)
891 std::cout << "===> New node " << graph.node(i).op_type() << " " << graph.node(i).name() << " order " << i << std::endl;
892
893 nodesOrder.push_back(i);
894 foundNodes[i] = true;
895 // register the outputs
896 for (int j = 0; j < graph.node(i).output_size(); j++) {
897 if (fVerbose) std::cout << "\toutput : " << graph.node(i).output(j) << std::endl;
898 allInputs[graph.node(i).output(j)] = i;
899 }
900 }
901 // no increment in nodes - something wrong
902 if (nodesOrder.size() == psize) {
903 int ilast = nodesOrder.back();
904 std::cout << "cannot find a new node after " << graph.node(ilast).op_type() << " " << graph.node(ilast).name() << std::endl;
905 throw std::runtime_error("TMVA::SOFIE - cannot find a new node ");
906 }
907 } while ((int)nodesOrder.size() < graph.node_size());
908
909
910 // find list of children for each operator (used for fusing oiperators)
911 std::vector<std::vector<int>> nodesChildren(graph.node_size());
912
913 for (int k = 0; k < graph.node_size(); k++) {
914 int i = nodesOrder[k];
915 // compute the number of output for the operators
916 if (graph.node(i).output_size() > 0) nodesChildren[i].reserve(graph.node(i).output_size());
917 for (const auto& output_name : graph.node(i).output()) {
918 // loop on all nodes
919 for (int l = k; l < graph.node_size(); l++) {
920 int j = nodesOrder[l];
921 for (const auto& input_name : graph.node(j).input()) {
922 if (input_name == output_name)
923 nodesChildren[i].push_back(j);
924 }
925 }
926 }
927 }
928
929 // print lit of order operators with list of inputs and list of children nodes
930 if (verbose) {
931 std::cout << "\nGraph operator list (re-ordered)\n";
932 for (int k = 0; k < graph.node_size(); k++) {
933 int i = nodesOrder[k];
934 std::cout << "\tOperator " << i << " : " << graph.node(i).op_type() << " , " << graph.node(i).name() << " input tensors : {";
935 for (int j = 0; j < graph.node(i).input_size(); j++) {
936 std::cout << graph.node(i).input(j);
937 if (j < graph.node(i).input_size() - 1)
938 std::cout << ", ";
939 }
940 std::cout << " } ";
941 std::cout << " children : {";
942 for ( const auto & ichild : nodesChildren[i]) {
943 std::cout << " [ " << ichild << " " << graph.node(ichild).op_type() << " , " << graph.node(ichild).name() << "]";
944 }
945 std::cout << "}" << std::endl;
946 }
947 }
948
949 // fill model with operators
950 if (verbose) {
951 std::cout << "Fill RModel with operators...\n";
952 }
953
954 // we have to record order of node execution separately to
955 // account for fused operators
956 size_t node_order_exec = 0;
957 for (int i = 0; i < graph.node_size(); i++) {
958 std::string op_type = graph.node(nodesOrder[i]).op_type();
959
960 if (verbose) {
961 std::cout << "\t" << i << " " << nodesOrder[i] << " parsing operator " << op_type << std::endl;
962 }
963
964 std::unique_ptr<ROperator> op = ParseOperator(i, graph, nodesOrder, nodesChildren[nodesOrder[i]]);
965 if (!op) {
966 if (verbose) {
967 std::cout << "\t\tskipping operator since it is fused with previous one" << std::endl;
968 }
969 // for skipping the fused nodes like Add after MatMul
970 continue;
971 }
972 rmodel.AddOperator(std::move(op), node_order_exec++);
973 }
974
975 std::vector<std::string> outputnames;
976 if (verbose)
977 std::cout << "\nParsing Graph output list\n";
978 for (int i = 0; i < graph.output_size(); i++) {
979 if (verbose)
980 std::cout << "\toutput " << i << " name " << graph.output(i).name() << std::endl;
981 outputnames.push_back(graph.output(i).name());
982 }
983 rmodel.AddOutputTensorNameList(outputnames);
984
985 return;
986}
987
988} // namespace SOFIE
989} // namespace Experimental
990} // namespace TMVA
dims_t fShape
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define N
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void input
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
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 filename
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 offset
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 length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
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
char name[80]
Definition TGX11.cxx:142
const_iterator begin() const
const_iterator end() const
void RegisterOperator(const std::string &name, ParserFuncSignature func)
std::unique_ptr< ROperator > ParseOperator(const size_t, const onnx::GraphProto &, const std::vector< size_t > &, const std::vector< int > &)
bool IsRegisteredOperator(const std::string &name)
void CheckGraph(const onnx::GraphProto &g, int &level, std::map< std::string, int > &missingOperators)
void ParseONNXGraph(RModel &model, const onnx::GraphProto &g, std::string name="")
std::unordered_map< std::string, ETensorType > fTensorTypeMap
RModel Parse(std::string const &filename, bool verbose=false)
std::shared_ptr< void > GetInitializedTensorData(onnx::TensorProto *tensorproto, size_t tensor_length, ETensorType type)
std::map< int, std::pair< EFusedOp, int > > fFusedOperators
void RegisterTensorType(const std::string &, ETensorType)
ETensorType GetTensorType(const std::string &name)
std::vector< std::string > GetRegisteredOperators()
std::unique_ptr< onnx::ModelProto > LoadModel(const std::string &filename)
std::unique_ptr< OperatorsMapImpl > fOperatorsMapImpl
bool CheckModel(std::string filename, bool verbose=false)
const ValueInfoProto & input(int i) const
Definition onnx.hxx:549
const ValueInfoProto & output(int i) const
Definition onnx.hxx:551
const std::string & name() const
Definition onnx.hxx:545
const NodeProto & node(int i) const
Definition onnx.hxx:547
const TensorProto & initializer(int i) const
Definition onnx.hxx:553
std::string Clean_name(std::string input_tensor_name)
ParserFuncSignature ParseIsNaN
ParserFuncSignature ParseSqrt
ParserFuncSignature ParseBatchNormalization
ParserFuncSignature ParseGreater
std::function< std::unique_ptr< ROperator >(RModelParser_ONNX &, const onnx::NodeProto &, const onnx::NodeProto &)> ParserFuseFuncSignature
ParserFuncSignature ParseReshape
ParserFuseFuncSignature ParseFuseConvTransposeAdd
ParserFuncSignature ParseReduceMean
ParserFuseFuncSignature ParseFuseMatMulAdd
ParserFuncSignature ParseGather
ParserFuncSignature ParseNeg
ParserFuncSignature ParseWhere
Definition ParseWhere.cxx:9
ParserFuncSignature ParseCos
ParserFuncSignature ParseLog
ParserFuncSignature ParseLeakyRelu
ParserFuncSignature ParseExp
std::function< std::unique_ptr< ROperator >(RModelParser_ONNX &, const onnx::NodeProto &)> ParserFuncSignature
ParserFuncSignature ParseEinsum
ParserFuncSignature ParsePool
Definition ParsePool.cxx:9
ParserFuncSignature ParseDiv
ParserFuncSignature ParseLayerNormalization
ParserFuncSignature ParseConcat
ParserFuncSignature ParseTopK
Definition ParseTopK.cxx:9
ParserFuncSignature ParseMax
ParserFuncSignature ParseEq
ParserFuncSignature ParseIdentity
ParserFuncSignature ParseConvTranspose
ParserFuncSignature ParseReduceProd
ParserFuncSignature ParseNot
Definition ParseNot.cxx:9
ParserFuncSignature ParseSlice
Definition ParseSlice.cxx:9
ParserFuncSignature ParseRandom
ParserFuncSignature ParseTranspose
ParserFuncSignature ParseLess
ParserFuncSignature ParseShape
ParserFuncSignature ParseClip
Definition ParseClip.cxx:25
constexpr size_t GetTypeSize(ETensorType type)
ParserFuncSignature ParseScatterND
ParserFuncSignature ParseGRU
Definition ParseGRU.cxx:9
ParserFuncSignature ParseMatMul
ParserFuncSignature ParseErf
Definition ParseErf.cxx:9
ParserFuncSignature ParseSub
ParserFuncSignature ParseAdd
ParserFuncSignature ParseNonZero
ParserFuncSignature ParseIf
Definition ParseIf.cxx:9
ParserFuncSignature ParseRange
Definition ParseRange.cxx:9
ParserFuncSignature ParseSoftplus
ParserFuncSignature ParseExpand
ParserFuncSignature ParseRNN
Definition ParseRNN.cxx:9
ParserFuncSignature ParseHardSigmoid
ParserFuncSignature ParseLSTM
Definition ParseLSTM.cxx:9
ParserFuncSignature ParseCast
Definition ParseCast.cxx:9
ParserFuncSignature ParseReciprocal
ParserFuncSignature ParseSwish
Definition ParseSwish.cxx:9
ParserFuncSignature ParseSigmoid
ParserFuseFuncSignature ParseFuseConvAdd
ParserFuncSignature ParseAtan
ParserFuncSignature ParseFloor
ParserFuseFuncSignature ParseFuseBatchnormRelu
ParserFuncSignature ParseIsInf
ParserFuncSignature ParseSoftmax
ParserFuncSignature ParseGreaterEq
ParserFuncSignature ParseMod
std::string ConvertTypeToString(ETensorType type)
ParserFuncSignature ParseGelu
Definition ParseGelu.cxx:9
ParserFuncSignature ParseMean
ParserFuncSignature ParseSplit
Definition ParseSplit.cxx:9
ParserFuncSignature ParseConstant
ParserFuncSignature ParseSelu
Definition ParseSelu.cxx:9
ParserFuncSignature ParseLessEq
ParserFuncSignature ParseHardSwish
ParserFuncSignature ParseGatherND
ParserFuncSignature ParseSum
ParserFuncSignature ParseEyeLike
ParserFuncSignature ParsePad
Definition ParsePad.cxx:9
ParserFuncSignature ParseElu
Definition ParseElu.cxx:9
std::string ConvertShapeToString(const std::vector< size_t > &shape)
ParserFuncSignature ParseMin
ParserFuncSignature ParseRelu
Definition ParseRelu.cxx:9
ParserFuncSignature ParseReduceSum
ParserFuncSignature ParseConv
Definition ParseConv.cxx:9
ParserFuncSignature ParseInstanceNormalization
ParserFuncSignature ParseScatterElements
ParserFuncSignature ParseGemm
Definition ParseGemm.cxx:9
ParserFuncSignature ParseTile
Definition ParseTile.cxx:9
ParserFuncSignature ParseMul
ParserFuseFuncSignature ParseFuseGemmRelu
ParserFuncSignature ParsePow
ParserFuncSignature ParseAbs
ParserFuncSignature ParseSin
ParserFuncSignature ParseReduceSumSquare
ParserFuncSignature ParseTanh
Definition ParseTanh.cxx:9
create variable transformations
Helper templated class for swapping bytes; specializations for N={2,4,8} are provided below.
Definition Byteswap.h:124
static void Copy(onnx::TensorProto *tensor, void *data, int length)
static void Copy(onnx::TensorProto *tensor, void *data, int length)
static void Copy(onnx::TensorProto *tensor, void *data, int length)
static void Copy(onnx::TensorProto *tensor, void *data, int length)
std::unordered_map< std::string, ParserFuncSignature > fOperatorsMap
TLine l
Definition textangle.C:4