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 if (fVerbose)
257 std::cout << "Initialized data are stored externally in file " << fDataFileName;
258
259 // read now tensor from file
260 std::string location;
261 size_t offset = 0, buffer_size = 0;
262
263 for (const auto &kv : tensorproto->external_data()) {
264 if (kv.key() == "location") location = kv.value();
265 else if (kv.key() == "offset") offset = std::stoull(kv.value());
266 else if (kv.key() == "length") buffer_size = std::stoull(kv.value());
267 }
268 if (fVerbose)
269 std::cout << " at location " << location << " offset " << offset << " and with length " << buffer_size << std::endl;
270
271 if (buffer_size != tensor_size)
272 throw std::runtime_error("TMVA::SOFIE ONNX : invalid stored data size vs tensor size");
273
274 // open the data file if needed
275 if (!fDataFile.is_open()) {
276 fDataFile.open(fDataFileName, std::ios::binary);
277 if (!fDataFile.is_open())
278 throw std::runtime_error("TMVA::SOFIE ONNX: error reading external weight ONNX data file " + fDataFileName);
279 }
280
281 fDataFile.seekg(offset);
282 fDataFile.read(reinterpret_cast<char *>(data.get()), buffer_size);
283#ifndef R__BYTESWAP
284 // external data is stored little-endian like raw_data - swap in place
285 CopyLEToHost(data.get(), data.get(), buffer_size, tensor_type);
286#endif
287 }
288
289 return data;
290}
291
292
293// Constructor of the parser
294RModelParser_ONNX::RModelParser_ONNX() noexcept : fOperatorsMapImpl(std::make_unique<OperatorsMapImpl>()) {
295 // Register operators
296 // Unary operators
298 RegisterOperator("Reciprocal", ParseReciprocal);
305 RegisterOperator("Softplus", ParseSoftplus);
308 // Binary operators
315 // Nary operators
320 //Comparision Operators
321 RegisterOperator("Equal", ParseEq);
323 RegisterOperator("LessOrEqual", ParseLessEq);
324 RegisterOperator("Greater", ParseGreater);
325 RegisterOperator("GreaterOrEqual", ParseGreaterEq);
326 // Is If operators
330 // Reduce operators
331 RegisterOperator("ReduceMean", ParseReduceMean);
332 RegisterOperator("ReduceSum", ParseReduceSum);
333 RegisterOperator("ReduceSumSquare", ParseReduceSumSquare);
334 RegisterOperator("ReduceProd", ParseReduceProd);
335 // Others
336 RegisterOperator("BatchNormalization", ParseBatchNormalization);
337 RegisterOperator("Constant", ParseConstant);
338 RegisterOperator("ConstantOfShape", ParseConstant);
340 RegisterOperator("Concat", ParseConcat);
342 RegisterOperator("ConvTranspose", ParseConvTranspose);
345 RegisterOperator("Identity", ParseIdentity);
346 RegisterOperator("LeakyRelu", ParseLeakyRelu);
348 RegisterOperator("AveragePool", ParsePool);
349 RegisterOperator("GlobalAveragePool", ParsePool);
350 RegisterOperator("MaxPool", ParsePool);
352 RegisterOperator("Reshape", ParseReshape);
353 RegisterOperator("Flatten", ParseReshape);
354 RegisterOperator("Squeeze", ParseReshape);
355 RegisterOperator("Unsqueeze", ParseReshape);
360 RegisterOperator("Sigmoid", ParseSigmoid);
363 RegisterOperator("Softmax", ParseSoftmax);
364 RegisterOperator("LogSoftmax", ParseSoftmax);
366 RegisterOperator("Transpose", ParseTranspose);
367 RegisterOperator("MatMul", ParseMatMul);
368 RegisterOperator("LayerNormalization", ParseLayerNormalization);
369 RegisterOperator("Expand", ParseExpand);
370 RegisterOperator("Gather", ParseGather);
371 RegisterOperator("GatherND", ParseGatherND);
374 RegisterOperator("HardSigmoid", ParseHardSigmoid);
375 RegisterOperator("HardSwish", ParseHardSwish);
376 RegisterOperator("EyeLike", ParseEyeLike);
382 RegisterOperator("InstanceNormalization", ParseInstanceNormalization);
385 RegisterOperator("Einsum", ParseEinsum);
386 RegisterOperator("RandomNormal", ParseRandom);
387 RegisterOperator("RandomNormalLike", ParseRandom);
388 RegisterOperator("RandomUniform", ParseRandom);
389 RegisterOperator("RandomUniformLike", ParseRandom);
390 RegisterOperator("ScatterElements", ParseScatterElements);
391 RegisterOperator("ScatterND", ParseScatterND);
392 RegisterOperator("NonZero", ParseNonZero);
394}
395
396// Destructor of the parser
398
400{
401 fOperatorsMapImpl->fOperatorsMap[name] = func;
402}
403
405{
406 return fOperatorsMapImpl->fOperatorsMap.find(name) != fOperatorsMapImpl->fOperatorsMap.end();
407}
408
410{
411 std::vector<std::string> ops;
412 ops.reserve(fOperatorsMapImpl->fOperatorsMap.size());
413 for (auto &it : fOperatorsMapImpl->fOperatorsMap) {
414 ops.emplace_back(it.first);
415 }
416 // return sorted list in alphabetical order
417 std::sort(ops.begin(), ops.end());
418 return ops;
419}
420
425
427{
429}
430
435
436// Parse an operator
437std::unique_ptr<ROperator>
438RModelParser_ONNX::ParseOperator(const size_t i, const onnx::GraphProto &graphproto, const std::vector<size_t> &nodes, const std::vector<int> & children)
439{
440 if (i >= nodes.size())
441 throw std::runtime_error("TMVA::SOFIE - Error in parsing ordered operators " + std::to_string(i) + " is >= " + std::to_string(nodes.size()));
442 int idx = nodes[i];
443 const auto &nodeproto = graphproto.node(idx);
444 const std::string op_type = nodeproto.op_type();
445 if (fVerbose)
446 std::cout << "Parsing operator " << op_type << std::endl;
447
448 // perform the fusion of operators
449 if (fFusedOperators.count(idx) == 1) {
450 int idx1 = fFusedOperators[idx].second;
451 if (fVerbose) {
452 std::cout << "\tFusing operators " << graphproto.node(idx1).name()
453 << " with " << graphproto.node(idx1).name() << std::endl;
454 }
455 if (fFusedOperators[idx].first == EFusedOp::kMatMulAdd) {
456 return ParseFuseMatMulAdd(*this, graphproto.node(idx1), graphproto.node(idx));
457 } else if (fFusedOperators[idx].first == EFusedOp::kConvAdd) {
458 return ParseFuseConvAdd(*this, graphproto.node(idx1), graphproto.node(idx));
459 } else if (fFusedOperators[idx].first == EFusedOp::kConvTransAdd) {
460 return ParseFuseConvTransposeAdd(*this, graphproto.node(idx1), graphproto.node(idx));
461 } else if (fFusedOperators[idx].first == EFusedOp::kGemmRelu) {
462 return ParseFuseGemmRelu(*this, graphproto.node(idx1), graphproto.node(idx));
463 } else if (fFusedOperators[idx].first == EFusedOp::kBatchnormRelu) {
464 return ParseFuseBatchnormRelu(*this, graphproto.node(idx1), graphproto.node(idx));
465 }
466 }
467
468 // try to fuse with following operator in case it is not last one and having only a single child
469 if (children.size() == 1) {
470 int idx2 = children.front();
471 if (op_type == "MatMul") {
472 // Fuse MatMul and Add
473 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Add") {
475 return nullptr;
476 }
477 } else if (nodeproto.op_type() == "Conv" || nodeproto.op_type() == "ConvTranspose") {
478 // Fuse Conv or ConvTranspose without bias and Add
479 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Add") {
480 if (nodeproto.op_type() == "Conv") {
482 return nullptr;
483 } else {
485 return nullptr;
486 }
487 }
488 } else if (nodeproto.op_type() == "Gemm") {
489 // Fuse Gemm with activation operators
490 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Relu") {
492 return nullptr;
493 }
494 } else if (nodeproto.op_type() == "BatchNormalization") {
495 if (idx2 < graphproto.node_size() && graphproto.node(idx2).op_type() == "Relu") {
497 return nullptr;
498 }
499 }
500 }
501
502 auto it = fOperatorsMapImpl->fOperatorsMap.find(op_type);
503 if (it == fOperatorsMapImpl->fOperatorsMap.end()) {
504 std::cout << "operator " << op_type << " is not supported" << std::endl;
505 throw std::runtime_error("TMVA::SOFIE Operator type " + op_type + " is not yet supported");
506 }
507 if (fVerbose) {
508 std::cout << "\tCreating operator " << op_type << std::endl;
509 }
510 return it->second(*this, nodeproto);
511}
512
513// Parse a model
514RModel RModelParser_ONNX::Parse(std::string const &filename, bool verbose)
515{
516 fVerbose = verbose;
517
518 fTensorTypeMap.clear();
519
520 auto model = LoadModel(filename);
521 if (!model)
522 throw std::runtime_error("TMVA::SOFIE - Failed to load onnx file " + filename);
523
524 const onnx::GraphProto &graph = model->graph(); // not a memory leak. model freed automatically at the end.
525
526
527 std::time_t ttime = std::time(0);
528 std::tm *gmt_time = std::gmtime(&ttime);
529 std::string parsetime(std::asctime(gmt_time));
530
531 // get name of model (filename without directory name)
532 char sep = '/';
533#ifdef _WIN32
534 sep = '\\';
535#endif
536 size_t isep = filename.rfind(sep, filename.length());
537 std::string filename_nodir = filename;
538 if (isep != std::string::npos) {
539 filename_nodir = (filename.substr(isep + 1, filename.length() - isep));
540 }
541
542 if (fDataFileName.empty() ) fDataFileName = filename + ".data";
543
546 return rmodel;
547}
548
549RModel RModelParser_ONNX::Parse(std::istream &input, std::string const &name, bool verbose)
550{
551 fVerbose = verbose;
552
553 fTensorTypeMap.clear();
554
555 auto model = LoadModel(input);
556 if (!model)
557 throw std::runtime_error("TMVA::SOFIE - Failed to parse ONNX model from input stream");
558
559 const onnx::GraphProto &graph = model->graph(); // not a memory leak. model freed automatically at the end.
560
561 std::time_t ttime = std::time(0);
562 std::tm *gmt_time = std::gmtime(&ttime);
563 std::string parsetime(std::asctime(gmt_time));
564
566 ParseONNXGraph(rmodel, graph, name);
567 return rmodel;
568}
569
570std::unique_ptr<onnx::ModelProto> RModelParser_ONNX::LoadModel(const std::string &filename) {
571 std::fstream input(filename, std::ios::in | std::ios::binary);
572 if (!input) {
573 std::cerr << "TMVA::SOFIE - Failed to open onnx file " << filename << std::endl;
574 return {};
575 }
576
577 return LoadModel(input);
578}
579
580std::unique_ptr<onnx::ModelProto> RModelParser_ONNX::LoadModel(std::istream &input)
581{
582 auto model = std::make_unique<onnx::ModelProto>();
583
584 if (!model->ParseFromIstream(&input)) {
585 std::cerr << "TMVA::SOFIE - Failed to parse ONNX model from input stream" << std::endl;
586 return {};
587 }
588
589 // ONNX version is ir_version() - model_version() returns 0
590 if (fVerbose) {
591 std::cout << "ONNX Version " << model->ir_version() << std::endl;
592 }
593 return model;
594}
595
596void RModelParser_ONNX::CheckGraph(const onnx::GraphProto & graph, int & level, std::map<std::string, int> & missingOperators) {
597 if (fVerbose)
598 std::cout << "\n" << graph.name() << " Graph operator list\n";
599 for (int i = 0; i < graph.node_size(); i++) {
600 const auto & node = graph.node(i);
601 const std::string opType = node.op_type();
602 if (fVerbose) {
603 std::cout << "\tOperator " << i << " : " << opType << " (" << node.name() << "), " << graph.node(i).input_size()
604 << " inputs : {";
605 for (int j = 0; j < graph.node(i).input_size(); j++) {
606 std::cout << graph.node(i).input(j);
607 if (j < graph.node(i).input_size() - 1)
608 std::cout << ", ";
609 }
610 std::cout << " }" << std::endl;
611 }
612 // check if operator exists
614 missingOperators[opType] = level;
615 // see if sub-graph exists as node attributes
616 for (int j = 0; j < node.attribute_size(); j++) {
617 const auto & attribute = node.attribute(j);
618 if (attribute.has_g()) {
619 const auto & subGraph = attribute.g();
620 level += 1;
622 }
623 }
624 }
625}
626
627bool RModelParser_ONNX::CheckModel(std::string filename, bool verbose) {
628
629 fVerbose = verbose;
630 auto model = LoadModel(filename);
631 if (!model) return false;
632
633 const onnx::GraphProto &graph = model->graph();
634 // Initial operator order
635 if (fVerbose)
636 std::cout << "\nModel operator list " << model->producer_name() << "\n";
637
638 std::map<std::string, int> missingOperators;
639 int level = 1;
640 CheckGraph(graph, level, missingOperators);
641
642 if (!missingOperators.empty()) {
643 std::cout << "List of missing operators for model loaded from file " << filename << std::endl;
644 for (auto & op : missingOperators) {
645 std::cout << op.first << " " << op.second << std::endl;
646 }
647 return false;
648 }
649 std::cout << "All operators in the loaded model are supported!\n";
650 return true;
651}
652
654{
655 bool verbose = fVerbose;
656
657 if (graphName.empty())
658 graphName = graph.name();
659
660 if (verbose)
661 std::cout << "\nParsing Graph - " << graphName << std::endl;
662
663 std::unordered_set<std::string> initializer_names;
664 for (int i = 0; i < graph.initializer_size(); i++) {
665 initializer_names.insert(graph.initializer(i).name());
666 }
667
668 if (verbose)
669 std::cout << "Parsing model inputs...." << std::endl;
670 /// Loop on model inputs
671 for (int i = 0; i < graph.input_size(); i++) {
672 RegisterTensorType(graph.input(i).name(),
673 static_cast<ETensorType>(graph.input(i).type().tensor_type().elem_type()));
674
675 if (verbose)
676 std::cout << "\tgraph input " << i << " name " << graph.input(i).name() << " type "
677 << graph.input(i).type().tensor_type().elem_type() << std::endl;
678
679 if (initializer_names.find(graph.input(i).name()) != initializer_names.end())
680 continue;
681
682 // input data node is not a weight node (has no initializer)
683 const onnx::ValueInfoProto &valueinfoproto = graph.input(i);
684 std::string input_name = valueinfoproto.name();
685
686 ETensorType type = static_cast<ETensorType>(valueinfoproto.type().tensor_type().elem_type());
687
688 std::vector<Dim> fShape;
689 bool existParam = false;
690 if (!valueinfoproto.type().tensor_type().has_shape())
691 throw std::runtime_error("TMVA::SOFIE data node with no shape restrictions is not supported yet");
692 for (int j = 0; j < valueinfoproto.type().tensor_type().shape().dim_size(); j++) {
693 Dim dim;
694 if (valueinfoproto.type().tensor_type().shape().dim(j).value_case() ==
696 int dim_value = valueinfoproto.type().tensor_type().shape().dim(j).dim_value();
697 dim.dim = dim_value;
698 // case input dim is -1 - set a parametric shape
699 if (dim_value < 0) {
700 dim.isParam = true;
701 existParam = true;
702 dim.param = UTILITY::Clean_name(input_name) + "_size";
703 }
704 } else if (valueinfoproto.type().tensor_type().shape().dim(j).value_case() ==
706 dim.isParam = true;
707 existParam = true;
708 dim.param = valueinfoproto.type().tensor_type().shape().dim(j).dim_param();
709 } else {
710 throw std::runtime_error("TMVA::SOFIE ONNX file error: Valueinfoproto " + input_name +
711 " has neither dim_value nor dim_param! \n");
712 }
713 fShape.push_back(dim);
714 }
715 if (valueinfoproto.type().tensor_type().shape().dim_size() == 0) {
716 Dim dim;
717 dim.dim = 1;
718 fShape.push_back(dim);
719 } // in case this TensorShapeProto has no dimension message: ONNX IR defines this to be a scalar
720
721 if (!existParam) {
722 std::vector<size_t> fShape_sizet;
723 for (auto &j : fShape) {
724 fShape_sizet.push_back(j.dim);
725 }
726
727 rmodel.AddInputTensorInfo(input_name, type, fShape_sizet);
728 } else {
729 rmodel.AddInputTensorInfo(input_name, type, fShape);
730 }
731 rmodel.AddInputTensorName(input_name); // store also names in given order
732 }
733
734 std::map<std::string, int> allInitializedTensors;
735
736 if (verbose)
737 std::cout << "\nParsing graph initializer list and fill model initialized tensors" << std::endl;
738
739 for (int i = 0; i < graph.initializer_size(); i++) {
741 std::vector<std::size_t> shape;
742 std::size_t tensor_length = 1;
743 for (int j = 0; j < tensorproto->dims_size(); j++) {
744 shape.push_back(tensorproto->dims(j));
745 tensor_length *= tensorproto->dims(j);
746 }
747 // in case of scalars keep an empty shape but with length =1
748
749 std::string tensor_name = graph.initializer(i).name();
750
751 if (verbose)
752 std::cout << "\t initializer " << i << " name " << tensor_name << " type " << graph.initializer(i).data_type()
753 << " and length " << tensor_length << std::endl;
754
755
756 // register also the initialized tensors
757 auto tensor_type = static_cast<ETensorType>(graph.initializer(i).data_type());
758 RegisterTensorType(tensor_name, tensor_type);
759
760 std::shared_ptr<void> data = GetInitializedTensorData(tensorproto, tensor_length * GetTypeSize(tensor_type), tensor_type);
761 rmodel.AddInitializedTensor(tensor_name, tensor_type, shape, data);
762 allInitializedTensors[tensor_name] = i;
763
764 if (verbose) {
765 std::cout << "add initialized tensor " << tensor_name << "with shape " << ConvertShapeToString(shape) << "and ";
766 if (tensor_type == ETensorType::FLOAT) {
767 std::cout << " float data: ";
769 }
770 else if (tensor_type == ETensorType::INT64) {
771 std::cout << " int64 data: ";
773 }
774 else if (tensor_type == ETensorType::UINT8) {
775 std::cout << " uint8 data: ";
777 }
778 else if (tensor_type == ETensorType::BOOL) {
779 std::cout << " Boolean data: ";
781 }
782 std::cout << std::endl;
783 }
784 } // end initializer list
785
786 // Initial operator order
787 if (verbose) {
788 std::cout << "\nGraph operator list (ONNX order)\n";
789 for (int i = 0; i < graph.node_size(); i++) {
790 std::cout << "\tOperator " << i << " : " << graph.node(i).op_type() << " , " << graph.node(i).input_size()
791 << " inputs : {";
792 for (int j = 0; j < graph.node(i).input_size(); j++) {
793 std::cout << graph.node(i).input(j);
794 if (j < graph.node(i).input_size() - 1)
795 std::cout << ", ";
796 }
797 std::cout << " }" << std::endl;
798 }
799 }
800
801 // make order of nodes:
802 if (verbose)
803 std::cout << "\n***********************\nRe-Order graph operator list\n*************************\n";
804 std::vector<size_t> nodesOrder;
805 nodesOrder.reserve(graph.node_size());
806 std::vector<bool> foundNodes(graph.node_size());
807
808 // loop at graph inputs
809 std::map<std::string, int> allInputs;
810 for (int i = 0; i < graph.input_size(); i++) {
811 allInputs[graph.input(i).name()] = -1;
812 }
813 do {
814 auto psize = nodesOrder.size();
815 for (int i = 0; i < graph.node_size(); i++) {
816 if (foundNodes[i])
817 continue;
818 // check if all input exists add to list
819 bool existInputs = true;
820 int input_size = graph.node(i).input_size();
821 // special case for Reshape where shape is input and not a weight tensor
822 if (fVerbose)
823 std::cout << "Checking input of Node " << i << " : " << graph.node(i).name() << std::endl;
824 for (int j = 0; j < input_size; j++) {
825 std::string name = graph.node(i).input(j);
826 // skip empty names
827 if (!name.empty()) {
828 existInputs &= (allInputs.find(name) != allInputs.end() ||
830 if (fVerbose) {
831 std::cout << "\t\t input " << name << " "
832 << bool(allInputs.find(name) != allInputs.end()) << " " <<
834 existInputs << std::endl;
835 }
836 }
837 }
838 if (!existInputs) {
839 if (fVerbose) {
840 std::cout << "skip node " << graph.node(i).op_type() << " " << graph.node(i).name() << " inputs are not existing ";
841 for (int j = 0; j < input_size; j++) {
842 std::cout << graph.node(i).input(j) << " ";
843 }
844 std::cout << std::endl;
845 }
846 continue;
847 }
848
849 // adding node to the currectly ordered list
850 if (verbose)
851 std::cout << "===> New node " << graph.node(i).op_type() << " " << graph.node(i).name() << " order " << i << std::endl;
852
853 nodesOrder.push_back(i);
854 foundNodes[i] = true;
855 // register the outputs
856 for (int j = 0; j < graph.node(i).output_size(); j++) {
857 if (fVerbose) std::cout << "\toutput : " << graph.node(i).output(j) << std::endl;
858 allInputs[graph.node(i).output(j)] = i;
859 }
860 }
861 // no increment in nodes - something wrong
862 if (nodesOrder.size() == psize) {
863 int ilast = nodesOrder.back();
864 std::cout << "cannot find a new node after " << graph.node(ilast).op_type() << " " << graph.node(ilast).name() << std::endl;
865 throw std::runtime_error("TMVA::SOFIE - cannot find a new node ");
866 }
867 } while ((int)nodesOrder.size() < graph.node_size());
868
869
870 // find list of children for each operator (used for fusing oiperators)
871 std::vector<std::vector<int>> nodesChildren(graph.node_size());
872
873 for (int k = 0; k < graph.node_size(); k++) {
874 int i = nodesOrder[k];
875 // compute the number of output for the operators
876 if (graph.node(i).output_size() > 0) nodesChildren[i].reserve(graph.node(i).output_size());
877 for (const auto& output_name : graph.node(i).output()) {
878 // loop on all nodes
879 for (int l = k; l < graph.node_size(); l++) {
880 int j = nodesOrder[l];
881 for (const auto& input_name : graph.node(j).input()) {
882 if (input_name == output_name)
883 nodesChildren[i].push_back(j);
884 }
885 }
886 }
887 }
888
889 // print lit of order operators with list of inputs and list of children nodes
890 if (verbose) {
891 std::cout << "\nGraph operator list (re-ordered)\n";
892 for (int k = 0; k < graph.node_size(); k++) {
893 int i = nodesOrder[k];
894 std::cout << "\tOperator " << i << " : " << graph.node(i).op_type() << " , " << graph.node(i).name() << " input tensors : {";
895 for (int j = 0; j < graph.node(i).input_size(); j++) {
896 std::cout << graph.node(i).input(j);
897 if (j < graph.node(i).input_size() - 1)
898 std::cout << ", ";
899 }
900 std::cout << " } ";
901 std::cout << " children : {";
902 for ( const auto & ichild : nodesChildren[i]) {
903 std::cout << " [ " << ichild << " " << graph.node(ichild).op_type() << " , " << graph.node(ichild).name() << "]";
904 }
905 std::cout << "}" << std::endl;
906 }
907 }
908
909 // fill model with operators
910 if (verbose) {
911 std::cout << "Fill RModel with operators...\n";
912 }
913
914 // we have to record order of node execution separately to
915 // account for fused operators
916 size_t node_order_exec = 0;
917 for (int i = 0; i < graph.node_size(); i++) {
918 std::string op_type = graph.node(nodesOrder[i]).op_type();
919
920 if (verbose) {
921 std::cout << "\t" << i << " " << nodesOrder[i] << " parsing operator " << op_type << std::endl;
922 }
923
924 std::unique_ptr<ROperator> op = ParseOperator(i, graph, nodesOrder, nodesChildren[nodesOrder[i]]);
925 if (!op) {
926 if (verbose) {
927 std::cout << "\t\tskipping operator since it is fused with previous one" << std::endl;
928 }
929 // for skipping the fused nodes like Add after MatMul
930 continue;
931 }
932 rmodel.AddOperator(std::move(op), node_order_exec++);
933 }
934
935 std::vector<std::string> outputnames;
936 if (verbose)
937 std::cout << "\nParsing Graph output list\n";
938 for (int i = 0; i < graph.output_size(); i++) {
939 if (verbose)
940 std::cout << "\toutput " << i << " name " << graph.output(i).name() << std::endl;
941 outputnames.push_back(graph.output(i).name());
942 }
943 rmodel.AddOutputTensorNameList(outputnames);
944
945 return;
946}
947
948} // namespace SOFIE
949} // namespace Experimental
950} // 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
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 std::string & name() const
Definition onnx.hxx:533
int output_size() const
Definition onnx.hxx:538
const ValueInfoProto & output(int i) const
Definition onnx.hxx:539
int input_size() const
Definition onnx.hxx:536
int initializer_size() const
Definition onnx.hxx:540
const ValueInfoProto & input(int i) const
Definition onnx.hxx:537
int node_size() const
Definition onnx.hxx:534
const NodeProto & node(int i) const
Definition onnx.hxx:535
const TensorProto & initializer(int i) const
Definition onnx.hxx:541
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