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