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_proto3.pb.h"
4
5#include <stdexcept>
6#include <string>
7#include <cstring>
8#include <memory>
9#include <cassert>
10#include <iostream>
11#include <unordered_map>
12#include <functional>
13#include "TMVA/SOFIE_common.hxx"
14
15namespace TMVA {
16namespace Experimental {
17namespace SOFIE {
18
19// Declaration of operators
20// Unary operators
32// Binary operators
39// Nary operators
44//Comparision Operators
50//Is Operators
54// Reduce operators
59// Others
108// Declaration of fused operators
114
115// Definition of RModelParser_ONNX::OperatorsMap
117 // Registered operators
118 std::unordered_map<std::string, ParserFuncSignature> fOperatorsMap;
119};
120
121// helper function to get initialized tensor data
122template<typename T>
124};
125// trait function to extract data from TensorProto
126template<>
127struct ExtractDataFromTP<float> {
128 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
129 if (tensor->float_data_size() != length)
130 throw std::runtime_error("TMVA::SOFIE - Failed to read float initialized tensor - actual size is " + std::to_string(tensor->float_data_size()));
131 tensor->mutable_float_data()->ExtractSubrange(0, tensor->float_data_size(),
132 static_cast<float *>(data));
133 }
134};
135template<>
137 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
138 if (tensor->double_data_size() != length)
139 throw std::runtime_error("TMVA::SOFIE - Failed to read double initialized tensor - actual size is " + std::to_string(tensor->double_data_size()));
140 tensor->mutable_double_data()->ExtractSubrange(0, tensor->double_data_size(),
141 static_cast<double *>(data));
142 }
143};
144template<>
145struct ExtractDataFromTP<int32_t> {
146 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
147 if (tensor->int32_data_size() != length)
148 throw std::runtime_error("TMVA::SOFIE - Failed to read int32 initialized tensor - actual size is " + std::to_string(tensor->int32_data_size()));
149 tensor->mutable_int32_data()->ExtractSubrange(0, tensor->int32_data_size(),
150 static_cast<int32_t *>(data));
151 }
152};
153template<>
154struct ExtractDataFromTP<int64_t> {
155 static void Copy(onnx::TensorProto * tensor, void * data, int length) {
156 if (tensor->int64_data_size() != length)
157 throw std::runtime_error("TMVA::SOFIE - Failed to read int64 initialized tensor - actual size is " + std::to_string(tensor->int64_data_size()));
158 tensor->mutable_int64_data()->ExtractSubrange(0, tensor->int64_data_size(),
159 static_cast<int64_t *>(data));
160 }
161};
162
163#ifndef R__BYTESWAP
164namespace {
165
166// Copy nbytes from source to dest, byte-swapping each N-byte element. The
167// temporary avoids misaligned loads from the protobuf string buffer and makes
168// in-place swapping (dest == source) valid.
169template <std::size_t N>
170void CopyBswap(void *dest, const void *source, std::size_t nbytes)
171{
172 using value_type = typename RByteSwap<N>::value_type;
173 auto dst = static_cast<unsigned char *>(dest);
174 auto src = static_cast<const unsigned char *>(source);
175 for (std::size_t k = 0; k < nbytes; k += N) {
176 value_type v;
177 std::memcpy(&v, src + k, N);
179 std::memcpy(dst + k, &v, N);
180 }
181}
182
183// Copy a buffer of little-endian tensor elements to host (big-endian) byte order
184void CopyLEToHost(void *dest, const void *source, std::size_t nbytes, ETensorType tensor_type)
185{
186 switch (GetTypeSize(tensor_type)) {
187 case 1:
188 if (dest != source)
189 std::memcpy(dest, source, nbytes);
190 break;
191 case 2: CopyBswap<2>(dest, source, nbytes); break;
192 case 4: CopyBswap<4>(dest, source, nbytes); break;
193 case 8: CopyBswap<8>(dest, source, nbytes); break;
194 default:
195 throw std::runtime_error("Data type " + ConvertTypeToString(tensor_type) + " in tensor is not supported!\n");
196 }
197}
198
199} // anonymous namespace
200#endif
201
202std::shared_ptr<void> RModelParser_ONNX::GetInitializedTensorData(onnx::TensorProto *tensorproto, size_t tensor_size, ETensorType tensor_type)
203{
204
205 std::shared_ptr<void> data(malloc(tensor_size), free);
206
207 // check if initialized tensors are stored internally
208 if (tensorproto->data_location() != onnx::TensorProto::EXTERNAL) {
209 if (tensorproto->raw_data().size() > 0) {
210 if (tensorproto->raw_data().size() != tensor_size)
211 throw std::runtime_error("TMVA::SOFIE - Failed to read raw data of initialized tensor - actual raw size is " +
212 std::to_string(tensorproto->raw_data().size()));
213
214#ifdef R__BYTESWAP
215 // R__BYTESWAP is defined for little-endian architectures (most common ones)
216 std::memcpy(data.get(), tensorproto->raw_data().c_str(), tensor_size);
217#else
218 // big-endian architectures - need to swap bytes
219 CopyLEToHost(data.get(), tensorproto->raw_data().c_str(), tensor_size, tensor_type);
220#endif
221 } else {
222 // case tensor data are stored as specific types and not in raw_data
223 switch (tensor_type) {
224 case ETensorType::FLOAT: {
225 ExtractDataFromTP<float>::Copy(tensorproto, data.get(), tensor_size/ 4);
226 break;
227 }
228 case ETensorType::DOUBLE: {
229 ExtractDataFromTP<double>::Copy(tensorproto, data.get(), tensor_size/ 8);
230 break;
231 }
232 case ETensorType::INT32: {
233 ExtractDataFromTP<int32_t>::Copy(tensorproto, data.get(), tensor_size/ 4);
234 break;
235 }
236 case ETensorType::INT64: {
237 ExtractDataFromTP<int64_t>::Copy(tensorproto, data.get(), tensor_size/ 8);
238 break;
239 }
240 case ETensorType::BOOL: {
241 throw std::runtime_error("TMVA::SOFIE - ExtractData from TP in BOOL not supported");
242 break;
243 }
244 case ETensorType::UINT8: {
245 throw std::runtime_error("TMVA::SOFIE - ExtractData from TP in UINT8 not supported");
246 break;
247 }
248 default:
249 throw std::runtime_error("Data type " + ConvertTypeToString(tensor_type) + " in weight tensor is not supported!\n");
250 }
251 }
252
253 } else {
254 // case of external data
255 if (fVerbose)
256 std::cout << "Initialized data are stored externally in file " << fDataFileName;
257
258 // read now tensor from file
259 std::string location;
260 size_t offset = 0, buffer_size = 0;
261
262 for (const auto &kv : tensorproto->external_data()) {
263 if (kv.key() == "location") location = kv.value();
264 else if (kv.key() == "offset") offset = std::stoull(kv.value());
265 else if (kv.key() == "length") buffer_size = std::stoull(kv.value());
266 }
267 if (fVerbose)
268 std::cout << " at location " << location << " offset " << offset << " and with length " << buffer_size << std::endl;
269
270 if (buffer_size != tensor_size)
271 throw std::runtime_error("TMVA::SOFIE ONNX : invalid stored data size vs tensor size");
272
273 // open the data file if needed
274 if (!fDataFile.is_open()) {
275 fDataFile.open(fDataFileName, std::ios::binary);
276 if (!fDataFile.is_open())
277 throw std::runtime_error("TMVA::SOFIE ONNX: error reading external weight ONNX data file " + fDataFileName);
278 }
279
280 fDataFile.seekg(offset);
281 fDataFile.read(reinterpret_cast<char *>(data.get()), buffer_size);
282#ifndef R__BYTESWAP
283 // external data is stored little-endian like raw_data - swap in place
285#endif
286 }
287
288 return data;
289}
290
291
292// Constructor of the parser
293RModelParser_ONNX::RModelParser_ONNX() noexcept : fOperatorsMapImpl(std::make_unique<OperatorsMapImpl>()) {
294 // Register operators
295 // Unary operators
297 RegisterOperator("Reciprocal", ParseReciprocal);
304 RegisterOperator("Softplus", ParseSoftplus);
307 // Binary operators
314 // Nary operators
319 //Comparision Operators
320 RegisterOperator("Equal", ParseEq);
322 RegisterOperator("LessOrEqual", ParseLessEq);
323 RegisterOperator("Greater", ParseGreater);
324 RegisterOperator("GreaterOrEqual", ParseGreaterEq);
325 // Is If operators
329 // Reduce operators
330 RegisterOperator("ReduceMean", ParseReduceMean);
331 RegisterOperator("ReduceSum", ParseReduceSum);
332 RegisterOperator("ReduceSumSquare", ParseReduceSumSquare);
333 RegisterOperator("ReduceProd", ParseReduceProd);
334 // Others
335 RegisterOperator("BatchNormalization", ParseBatchNormalization);
336 RegisterOperator("Constant", ParseConstant);
337 RegisterOperator("ConstantOfShape", ParseConstant);
339 RegisterOperator("Concat", ParseConcat);
341 RegisterOperator("ConvTranspose", ParseConvTranspose);
344 RegisterOperator("Identity", ParseIdentity);
345 RegisterOperator("LeakyRelu", ParseLeakyRelu);
347 RegisterOperator("AveragePool", ParsePool);
348 RegisterOperator("GlobalAveragePool", ParsePool);
349 RegisterOperator("MaxPool", ParsePool);
351 RegisterOperator("Reshape", ParseReshape);
352 RegisterOperator("Flatten", ParseReshape);
353 RegisterOperator("Squeeze", ParseReshape);
354 RegisterOperator("Unsqueeze", ParseReshape);
359 RegisterOperator("Sigmoid", ParseSigmoid);
362 RegisterOperator("Softmax", ParseSoftmax);
363 RegisterOperator("LogSoftmax", ParseSoftmax);
365 RegisterOperator("Transpose", ParseTranspose);
366 RegisterOperator("MatMul", ParseMatMul);
367 RegisterOperator("LayerNormalization", ParseLayerNormalization);
368 RegisterOperator("Expand", ParseExpand);
369 RegisterOperator("Gather", ParseGather);
370 RegisterOperator("GatherND", ParseGatherND);
373 RegisterOperator("HardSigmoid", ParseHardSigmoid);
374 RegisterOperator("HardSwish", ParseHardSwish);
375 RegisterOperator("EyeLike", ParseEyeLike);
381 RegisterOperator("InstanceNormalization", ParseInstanceNormalization);
384 RegisterOperator("Einsum", ParseEinsum);
385 RegisterOperator("RandomNormal", ParseRandom);
386 RegisterOperator("RandomNormalLike", ParseRandom);
387 RegisterOperator("RandomUniform", ParseRandom);
388 RegisterOperator("RandomUniformLike", ParseRandom);
389 RegisterOperator("ScatterElements", ParseScatterElements);
390 RegisterOperator("ScatterND", ParseScatterND);
391 RegisterOperator("NonZero", ParseNonZero);
393}
394
395// Destructor of the parser
397
399{
400 fOperatorsMapImpl->fOperatorsMap[name] = func;
401}
402
404{
405 return fOperatorsMapImpl->fOperatorsMap.find(name) != fOperatorsMapImpl->fOperatorsMap.end();
406}
407
409{
410 std::vector<std::string> ops;
411 ops.reserve(fOperatorsMapImpl->fOperatorsMap.size());
412 for (auto &it : fOperatorsMapImpl->fOperatorsMap) {
413 ops.emplace_back(it.first);
414 }
415 // return sorted list in alphabetical order
416 std::sort(ops.begin(), ops.end());
417 return ops;
418}
419
424
426{
428}
429
434
435// Parse an operator
436std::unique_ptr<ROperator>
437RModelParser_ONNX::ParseOperator(const size_t i, const onnx::GraphProto &graphproto, const std::vector<size_t> &nodes, const std::vector<int> & children)
438{
439 if (i >= nodes.size())
440 throw std::runtime_error("TMVA::SOFIE - Error in parsing ordered operators " + std::to_string(i) + " is >= " + std::to_string(nodes.size()));
441 int idx = nodes[i];
442 const auto &nodeproto = graphproto.node(idx);
443 const std::string op_type = nodeproto.op_type();
444 if (fVerbose)
445 std::cout << "Parsing operator " << op_type << std::endl;
446
447 // perform the fusion of operators
448 if (fFusedOperators.count(idx) == 1) {
449 int idx1 = fFusedOperators[idx].second;
450 if (fVerbose) {
451 std::cout << "\tFusing operators " << graphproto.node(idx1).name()
452 << " with " << graphproto.node(idx1).name() << std::endl;
453 }
454 if (fFusedOperators[idx].first == EFusedOp::kMatMulAdd) {
455 return ParseFuseMatMulAdd(*this, graphproto.node(idx1), graphproto.node(idx));
456 } else if (fFusedOperators[idx].first == EFusedOp::kConvAdd) {
457 return ParseFuseConvAdd(*this, graphproto.node(idx1), graphproto.node(idx));
458 } else if (fFusedOperators[idx].first == EFusedOp::kConvTransAdd) {
459 return ParseFuseConvTransposeAdd(*this, graphproto.node(idx1), graphproto.node(idx));
460 } else if (fFusedOperators[idx].first == EFusedOp::kGemmRelu) {
461 return ParseFuseGemmRelu(*this, graphproto.node(idx1), graphproto.node(idx));
462 } else if (fFusedOperators[idx].first == EFusedOp::kBatchnormRelu) {
463 return ParseFuseBatchnormRelu(*this, graphproto.node(idx1), graphproto.node(idx));
464 }
465 }
466
467 // try to fuse with following operator in case it is not last one and having only a single child
468 if (children.size() == 1) {
469 int idx2 = children.front();
470 if (op_type == "MatMul") {
471 // Fuse MatMul and Add
472 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Add") {
474 return nullptr;
475 }
476 } else if (nodeproto.op_type() == "Conv" || nodeproto.op_type() == "ConvTranspose") {
477 // Fuse Conv or ConvTranspose without bias and Add
478 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Add") {
479 if (nodeproto.op_type() == "Conv") {
481 return nullptr;
482 } else {
484 return nullptr;
485 }
486 }
487 } else if (nodeproto.op_type() == "Gemm") {
488 // Fuse Gemm with activation operators
489 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Relu") {
491 return nullptr;
492 }
493 } else if (nodeproto.op_type() == "BatchNormalization") {
494 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Relu") {
496 return nullptr;
497 }
498 }
499 }
500
501 auto it = fOperatorsMapImpl->fOperatorsMap.find(op_type);
502 if (it == fOperatorsMapImpl->fOperatorsMap.end()) {
503 std::cout << "operator " << op_type << " is not supported" << std::endl;
504 throw std::runtime_error("TMVA::SOFIE Operator type " + op_type + " is not yet supported");
505 }
506 if (fVerbose) {
507 std::cout << "\tCreating operator " << op_type << std::endl;
508 }
509 return it->second(*this, nodeproto);
510}
511
512// Parse a model
513RModel RModelParser_ONNX::Parse(std::string const &filename, bool verbose)
514{
515 fVerbose = verbose;
516
517 fTensorTypeMap.clear();
518
519 auto model = LoadModel(filename);
520 if (!model)
521 throw std::runtime_error("TMVA::SOFIE - Failed to load onnx file " + filename);
522
523 const onnx::GraphProto &graph = model->graph(); // not a memory leak. model freed automatically at the end.
524
525
526 std::time_t ttime = std::time(0);
527 std::tm *gmt_time = std::gmtime(&ttime);
528 std::string parsetime(std::asctime(gmt_time));
529
530 // get name of model (filename without directory name)
531 char sep = '/';
532#ifdef _WIN32
533 sep = '\\';
534#endif
535 size_t isep = filename.rfind(sep, filename.length());
536 std::string filename_nodir = filename;
537 if (isep != std::string::npos) {
538 filename_nodir = (filename.substr(isep + 1, filename.length() - isep));
539 }
540
541 if (fDataFileName.empty() ) fDataFileName = filename + ".data";
542
545 return rmodel;
546}
547
548RModel RModelParser_ONNX::Parse(std::istream &input, std::string const &name, bool verbose)
549{
550 fVerbose = verbose;
551
552 fTensorTypeMap.clear();
553
554 auto model = LoadModel(input);
555 if (!model)
556 throw std::runtime_error("TMVA::SOFIE - Failed to parse ONNX model from input stream");
557
558 const onnx::GraphProto &graph = model->graph(); // not a memory leak. model freed automatically at the end.
559
560 std::time_t ttime = std::time(0);
561 std::tm *gmt_time = std::gmtime(&ttime);
562 std::string parsetime(std::asctime(gmt_time));
563
565 ParseONNXGraph(rmodel, graph, name);
566 return rmodel;
567}
568
569std::unique_ptr<onnx::ModelProto> RModelParser_ONNX::LoadModel(const std::string &filename) {
570 std::fstream input(filename, std::ios::in | std::ios::binary);
571 if (!input) {
572 std::cerr << "TMVA::SOFIE - Failed to open onnx file " << filename << std::endl;
573 return {};
574 }
575
576 return LoadModel(input);
577}
578
579std::unique_ptr<onnx::ModelProto> RModelParser_ONNX::LoadModel(std::istream &input) {
581 auto model = std::make_unique<onnx::ModelProto>();
582
583 if (!model->ParseFromIstream(&input)) {
584 std::cerr << "TMVA::SOFIE - Failed to parse ONNX model from input stream" << std::endl;
585 return {};
586 }
587
588 // ONNX version is ir_version() - model_version() returns 0
589 if (fVerbose) {
590 std::cout << "ONNX Version " << model->ir_version() << std::endl;
591 }
592 google::protobuf::ShutdownProtobufLibrary();
593 return model;
594
595}
596
597void RModelParser_ONNX::CheckGraph(const onnx::GraphProto & graph, int & level, std::map<std::string, int> & missingOperators) {
598 if (fVerbose)
599 std::cout << "\n" << graph.name() << " Graph operator list\n";
600 for (int i = 0; i < graph.node_size(); i++) {
601 const auto & node = graph.node(i);
602 const std::string opType = node.op_type();
603 if (fVerbose) {
604 std::cout << "\tOperator " << i << " : " << opType << " (" << node.name() << "), " << graph.node(i).input_size()
605 << " inputs : {";
606 for (int j = 0; j < graph.node(i).input_size(); j++) {
607 std::cout << graph.node(i).input(j);
608 if (j < graph.node(i).input_size() - 1)
609 std::cout << ", ";
610 }
611 std::cout << " }" << std::endl;
612 }
613 // check if operator exists
615 missingOperators[opType] = level;
616 // see if sub-graph exists as node attributes
617 for (int j = 0; j < node.attribute_size(); j++) {
618 const auto & attribute = node.attribute(j);
619 if (attribute.has_g()) {
620 const auto & subGraph = attribute.g();
621 level += 1;
623 }
624 }
625 }
626}
627
628bool RModelParser_ONNX::CheckModel(std::string filename, bool verbose) {
629
630 fVerbose = verbose;
631 auto model = LoadModel(filename);
632 if (!model) return false;
633
634 const onnx::GraphProto &graph = model->graph();
635 // Initial operator order
636 if (fVerbose)
637 std::cout << "\nModel operator list " << model->producer_name() << "\n";
638
639 std::map<std::string, int> missingOperators;
640 int level = 1;
641 CheckGraph(graph, level, missingOperators);
642
643 if (!missingOperators.empty()) {
644 std::cout << "List of missing operators for model loaded from file " << filename << std::endl;
645 for (auto & op : missingOperators) {
646 std::cout << op.first << " " << op.second << std::endl;
647 }
648 return false;
649 }
650 std::cout << "All operators in the loaded model are supported!\n";
651 return true;
652}
653
654void RModelParser_ONNX::ParseONNXGraph(RModel & rmodel, const onnx::GraphProto & graph, std::string graphName)
655{
656 bool verbose = fVerbose;
657
658 if (graphName.empty())
659 graphName = graph.name();
660
661 if (verbose)
662 std::cout << "\nParsing Graph - " << graphName << std::endl;
663
664 std::unordered_set<std::string> initializer_names;
665 for (int i = 0; i < graph.initializer_size(); i++) {
666 initializer_names.insert(graph.initializer(i).name());
667 }
668
669 if (verbose)
670 std::cout << "Parsing model inputs...." << std::endl;
671 /// Loop on model inputs
672 for (int i = 0; i < graph.input_size(); i++) {
673 RegisterTensorType(graph.input(i).name(),
674 static_cast<ETensorType>(graph.input(i).type().tensor_type().elem_type()));
675
676 if (verbose)
677 std::cout << "\tgraph input " << i << " name " << graph.input(i).name() << " type "
678 << graph.input(i).type().tensor_type().elem_type() << std::endl;
679
680 if (initializer_names.find(graph.input(i).name()) != initializer_names.end())
681 continue;
682
683 // input data node is not a weight node (has no initializer)
684 const onnx::ValueInfoProto &valueinfoproto = graph.input(i);
685 std::string input_name = valueinfoproto.name();
686
687 ETensorType type = static_cast<ETensorType>(valueinfoproto.type().tensor_type().elem_type());
688
689 std::vector<Dim> fShape;
690 bool existParam = false;
691 if (!valueinfoproto.type().tensor_type().has_shape())
692 throw std::runtime_error("TMVA::SOFIE data node with no shape restrictions is not supported yet");
693 for (int j = 0; j < valueinfoproto.type().tensor_type().shape().dim_size(); j++) {
694 Dim dim;
695 if (valueinfoproto.type().tensor_type().shape().dim(j).value_case() ==
696 onnx::TensorShapeProto_Dimension::ValueCase::kDimValue) {
697 int dim_value = valueinfoproto.type().tensor_type().shape().dim(j).dim_value();
698 dim.dim = dim_value;
699 // case input dim is -1 - set a parametric shape
700 if (dim_value < 0) {
701 dim.isParam = true;
702 existParam = true;
703 dim.param = UTILITY::Clean_name(input_name) + "_size";
704 }
705 } else if (valueinfoproto.type().tensor_type().shape().dim(j).value_case() ==
706 onnx::TensorShapeProto_Dimension::ValueCase::kDimParam) {
707 dim.isParam = true;
708 existParam = true;
709 dim.param = valueinfoproto.type().tensor_type().shape().dim(j).dim_param();
710 } else {
711 throw std::runtime_error("TMVA::SOFIE ONNX file error: Valueinfoproto " + input_name +
712 " has neither dim_value nor dim_param! \n");
713 }
714 fShape.push_back(dim);
715 }
716 if (valueinfoproto.type().tensor_type().shape().dim_size() == 0) {
717 Dim dim;
718 dim.dim = 1;
719 fShape.push_back(dim);
720 } // in case this TensorShapeProto has no dimension message: ONNX IR defines this to be a scalar
721
722 if (!existParam) {
723 std::vector<size_t> fShape_sizet;
724 for (auto &j : fShape) {
725 fShape_sizet.push_back(j.dim);
726 }
727
728 rmodel.AddInputTensorInfo(input_name, type, fShape_sizet);
729 } else {
730 rmodel.AddInputTensorInfo(input_name, type, fShape);
731 }
732 rmodel.AddInputTensorName(input_name); // store also names in given order
733 }
734
735 std::map<std::string, int> allInitializedTensors;
736
737 if (verbose)
738 std::cout << "\nParsing graph initializer list and fill model initialized tensors" << std::endl;
739
740 for (int i = 0; i < graph.initializer_size(); i++) {
741 onnx::TensorProto *tensorproto = const_cast<onnx::TensorProto *>(&graph.initializer(i));
742 std::vector<std::size_t> shape;
743 std::size_t tensor_length = 1;
744 for (int j = 0; j < tensorproto->dims_size(); j++) {
745 shape.push_back(tensorproto->dims(j));
746 tensor_length *= tensorproto->dims(j);
747 }
748 // in case of scalars keep an empty shape but with length =1
749
750 std::string tensor_name = graph.initializer(i).name();
751
752 if (verbose)
753 std::cout << "\t initializer " << i << " name " << tensor_name << " type " << graph.initializer(i).data_type()
754 << " and length " << tensor_length << std::endl;
755
756
757 // register also the initialized tensors
758 auto tensor_type = static_cast<ETensorType>(graph.initializer(i).data_type());
759 RegisterTensorType(tensor_name, tensor_type);
760
762 rmodel.AddInitializedTensor(tensor_name, tensor_type, shape, data);
763 allInitializedTensors[tensor_name] = i;
764
765 if (verbose) {
766 std::cout << "add initialized tensor " << tensor_name << "with shape " << ConvertShapeToString(shape) << "and ";
768 std::cout << " float data: ";
770 }
771 else if (tensor_type == ETensorType::INT64) {
772 std::cout << " int64 data: ";
774 }
775 else if (tensor_type == ETensorType::UINT8) {
776 std::cout << " uint8 data: ";
778 }
779 else if (tensor_type == ETensorType::BOOL) {
780 std::cout << " Boolean data: ";
782 }
783 std::cout << std::endl;
784 }
785 } // end initializer list
786
787 // Initial operator order
788 if (verbose) {
789 std::cout << "\nGraph operator list (ONNX order)\n";
790 for (int i = 0; i < graph.node_size(); i++) {
791 std::cout << "\tOperator " << i << " : " << graph.node(i).op_type() << " , " << graph.node(i).input_size()
792 << " inputs : {";
793 for (int j = 0; j < graph.node(i).input_size(); j++) {
794 std::cout << graph.node(i).input(j);
795 if (j < graph.node(i).input_size() - 1)
796 std::cout << ", ";
797 }
798 std::cout << " }" << std::endl;
799 }
800 }
801
802 // make order of nodes:
803 if (verbose)
804 std::cout << "\n***********************\nRe-Order graph operator list\n*************************\n";
805 std::vector<size_t> nodesOrder;
806 nodesOrder.reserve(graph.node_size());
807 std::vector<bool> foundNodes(graph.node_size());
808
809 // loop at graph inputs
810 std::map<std::string, int> allInputs;
811 for (int i = 0; i < graph.input_size(); i++) {
812 allInputs[graph.input(i).name()] = -1;
813 }
814 do {
815 auto psize = nodesOrder.size();
816 for (int i = 0; i < graph.node_size(); i++) {
817 if (foundNodes[i])
818 continue;
819 // check if all input exists add to list
820 bool existInputs = true;
821 int input_size = graph.node(i).input_size();
822 // special case for Reshape where shape is input and not a weight tensor
823 if (fVerbose)
824 std::cout << "Checking input of Node " << i << " : " << graph.node(i).name() << std::endl;
825 for (int j = 0; j < input_size; j++) {
826 std::string name = graph.node(i).input(j);
827 // skip empty names
828 if (!name.empty()) {
829 existInputs &= (allInputs.find(name) != allInputs.end() ||
831 if (fVerbose) {
832 std::cout << "\t\t input " << name << " "
833 << bool(allInputs.find(name) != allInputs.end()) << " " <<
835 existInputs << std::endl;
836 }
837 }
838 }
839 if (!existInputs) {
840 if (fVerbose) {
841 std::cout << "skip node " << graph.node(i).op_type() << " " << graph.node(i).name() << " inputs are not existing ";
842 for (int j = 0; j < input_size; j++) {
843 std::cout << graph.node(i).input(j) << " ";
844 }
845 std::cout << std::endl;
846 }
847 continue;
848 }
849
850 // adding node to the currectly ordered list
851 if (verbose)
852 std::cout << "===> New node " << graph.node(i).op_type() << " " << graph.node(i).name() << " order " << i << std::endl;
853
854 nodesOrder.push_back(i);
855 foundNodes[i] = true;
856 // register the outputs
857 for (int j = 0; j < graph.node(i).output_size(); j++) {
858 if (fVerbose) std::cout << "\toutput : " << graph.node(i).output(j) << std::endl;
859 allInputs[graph.node(i).output(j)] = i;
860 }
861 }
862 // no increment in nodes - something wrong
863 if (nodesOrder.size() == psize) {
864 int ilast = nodesOrder.back();
865 std::cout << "cannot find a new node after " << graph.node(ilast).op_type() << " " << graph.node(ilast).name() << std::endl;
866 throw std::runtime_error("TMVA::SOFIE - cannot find a new node ");
867 }
868 } while ((int)nodesOrder.size() < graph.node_size());
869
870
871 // find list of children for each operator (used for fusing oiperators)
872 std::vector<std::vector<int>> nodesChildren(graph.node_size());
873
874 for (int k = 0; k < graph.node_size(); k++) {
875 int i = nodesOrder[k];
876 // compute the number of output for the operators
877 if (graph.node(i).output_size() > 0) nodesChildren[i].reserve(graph.node(i).output_size());
878 for (const auto& output_name : graph.node(i).output()) {
879 // loop on all nodes
880 for (int l = k; l < graph.node_size(); l++) {
881 int j = nodesOrder[l];
882 for (const auto& input_name : graph.node(j).input()) {
883 if (input_name == output_name)
884 nodesChildren[i].push_back(j);
885 }
886 }
887 }
888 }
889
890 // print lit of order operators with list of inputs and list of children nodes
891 if (verbose) {
892 std::cout << "\nGraph operator list (re-ordered)\n";
893 for (int k = 0; k < graph.node_size(); k++) {
894 int i = nodesOrder[k];
895 std::cout << "\tOperator " << i << " : " << graph.node(i).op_type() << " , " << graph.node(i).name() << " input tensors : {";
896 for (int j = 0; j < graph.node(i).input_size(); j++) {
897 std::cout << graph.node(i).input(j);
898 if (j < graph.node(i).input_size() - 1)
899 std::cout << ", ";
900 }
901 std::cout << " } ";
902 std::cout << " children : {";
903 for ( const auto & ichild : nodesChildren[i]) {
904 std::cout << " [ " << ichild << " " << graph.node(ichild).op_type() << " , " << graph.node(ichild).name() << "]";
905 }
906 std::cout << "}" << std::endl;
907 }
908 }
909
910 // fill model with operators
911 if (verbose) {
912 std::cout << "Fill RModel with operators...\n";
913 }
914
915 // we have to record order of node execution separately to
916 // account for fused operators
917 size_t node_order_exec = 0;
918 for (int i = 0; i < graph.node_size(); i++) {
919 std::string op_type = graph.node(nodesOrder[i]).op_type();
920
921 if (verbose) {
922 std::cout << "\t" << i << " " << nodesOrder[i] << " parsing operator " << op_type << std::endl;
923 }
924
925 std::unique_ptr<ROperator> op = ParseOperator(i, graph, nodesOrder, nodesChildren[nodesOrder[i]]);
926 if (!op) {
927 if (verbose) {
928 std::cout << "\t\tskipping operator since it is fused with previous one" << std::endl;
929 }
930 // for skipping the fused nodes like Add after MatMul
931 continue;
932 }
933 rmodel.AddOperator(std::move(op), node_order_exec++);
934 }
935
936 std::vector<std::string> outputnames;
937 if (verbose)
938 std::cout << "\nParsing Graph output list\n";
939 for (int i = 0; i < graph.output_size(); i++) {
940 if (verbose)
941 std::cout << "\toutput " << i << " name " << graph.output(i).name() << std::endl;
942 outputnames.push_back(graph.output(i).name());
943 }
944 rmodel.AddOutputTensorNameList(outputnames);
945
946 return;
947}
948
949} // namespace SOFIE
950} // namespace Experimental
951} // 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:148
#define malloc
Definition civetweb.c:1575
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)
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
Definition ParseShape.cxx:9
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