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