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