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