Imported Upstream version 1.12.0
[platform/core/ml/nnfw.git] / runtime / onert / frontend / base_loader / include / base_loader.h
1 /*
2  * Copyright 2017 The TensorFlow Authors. All Rights Reserved.
3  * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #ifndef __BASE_LOADER_BASE_LOADER_H__
19 #define __BASE_LOADER_BASE_LOADER_H__
20
21 #include "ir/Graph.h"
22 #include "ir/Shape.h"
23 #include "ir/Operations.Include.h"
24
25 #include "flatbuffers/flexbuffers.h"
26
27 #include <map>
28 #include <memory>
29 #include <fstream>
30 #include <limits>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <sys/mman.h>
34 #include <unistd.h>
35 #include <util/logging.h>
36
37 namespace onert
38 {
39 namespace base_loader
40 {
41
42 template <typename LoaderDomain> class BaseLoader
43 {
44 protected:
45   using Verifier = typename LoaderDomain::Verifier;
46   using ActivationFunctionType = typename LoaderDomain::ActivationFunctionType;
47   using Buffer = typename LoaderDomain::Buffer;
48   using BuiltinOperator = typename LoaderDomain::BuiltinOperator;
49   using CustomOptionsFormat = typename LoaderDomain::CustomOptionsFormat;
50   using Model = typename LoaderDomain::Model;
51   using Operator = typename LoaderDomain::Operator;
52   using Padding = typename LoaderDomain::Padding;
53   using Pool2DOptions = typename LoaderDomain::Pool2DOptions;
54   using SubGraph = typename LoaderDomain::SubGraph;
55   using Tensor = typename LoaderDomain::Tensor;
56   using TensorType = typename LoaderDomain::TensorType;
57   using DimensionType = typename LoaderDomain::DimensionType;
58   using SparseIndexVector = typename LoaderDomain::SparseIndexVector;
59
60 protected:
61   bool isOptionalInputTensor(std::int32_t idx) { return idx == -1; }
62   virtual bool allowOptionalInputTensor(BuiltinOperator) = 0;
63
64 public:
65   /**
66    * @brief Construct a new Loader object
67    *
68    * @param graph reference on subgraphs
69    */
70   explicit BaseLoader(std::unique_ptr<ir::Subgraphs> &subgs)
71     : _base{nullptr}, _pagesize(getpagesize()), _fd(-1), _subgraphs(subgs), _model{nullptr}
72   {
73     _use_mmaped_data = util::getConfigBool(util::config::USE_MMAPED_DATA);
74   }
75
76   /**
77    * @brief Load a model from file
78    *
79    * @param file_path
80    */
81   void loadFromFile(const char *file_path);
82   /**
83    * @brief Load a model from a buffer
84    *
85    * @param buffer buffer pointer
86    * @param size buffer size
87    */
88   void loadFromBuffer(uint8_t *buffer, size_t size);
89
90 protected:
91   ~BaseLoader() = default;
92   void loadModel();
93
94   // Helper functions
95   ir::Activation convertActivation(ActivationFunctionType type);
96   ir::DataType tensorTypeToDataType(TensorType type);
97   ir::OperandIndex tensorIdxToOperandIdx(int32_t tensorIdx);
98
99   // Create operands form tflite::Tensor
100   ir::OperandIndex loadOperand(const Tensor *tensor, ir::Graph &subg);
101   void loadSparsity(const Tensor *tensor, const ir::Shape &shape, ir::TypeInfo &typeInfo);
102   void loadOperationIO(const Operator *op, ir::OperandIndexSequence &inputs,
103                        ir::OperandIndexSequence &outputs);
104   // Create operations from Operator
105   void loadOperation(const Operator *op, ir::Graph &subg);
106   // Load Strides and Paddings from options to param
107   template <typename Param, typename OptionsType>
108   void loadStridesAndPaddings(Param &param, const OptionsType *options);
109   // Load Pool2D param
110   template <typename Param> void loadPool2DOptions(Param &param, const Pool2DOptions *options);
111
112 private:
113   virtual std::unique_ptr<ir::Graph> loadSubgraph(const SubGraph *subg) = 0;
114   // Operations
115   template <typename OpIR, typename... Args>
116   const OpIR *loadOperationTo(const Operator *op, ir::Graph &subg, Args &&... args);
117
118   void loadAddV2(const Operator *op, ir::Graph &subg);
119   void loadArgMinMax(const Operator *op, ir::Graph &subg, bool is_argmax);
120   void loadBatchMatMul(const Operator *op, ir::Graph &subg);
121   void loadBinaryArithmetic(const Operator *op, ir::Graph &subg,
122                             ir::operation::BinaryArithmetic::ArithmeticType op_type);
123   void loadComparison(const Operator *op, ir::Graph &subg);
124   void loadConcatenation(const Operator *op, ir::Graph &subg);
125   void loadConv2D(const Operator *op, ir::Graph &subg);
126   void loadCustom(const Operator *op, ir::Graph &subg);
127   void loadDepthToSpace(const Operator *op, ir::Graph &subg);
128   void loadDepthwiseConv2D(const Operator *op, ir::Graph &subg);
129   void loadEinsum(const Operator *op, ir::Graph &subg);
130   void loadElementwiseActivation(const Operator *op, ir::Graph &subg,
131                                  ir::operation::ElementwiseActivation::Type op_type,
132                                  float alpha = 0.f, float beta = 0.f);
133   void loadElementwiseBinary(const Operator *op, ir::Graph &subg,
134                              ir::operation::ElementwiseBinary::ElementwiseBinaryType op_type);
135   void loadElementwiseUnary(const Operator *op, ir::Graph &subg,
136                             ir::operation::ElementwiseUnary::Type op_type);
137   void loadFC(const Operator *op, ir::Graph &subg);
138   void loadFusedBatchNorm(const Operator *op, ir::Graph &subg);
139   void loadGather(const Operator *op, ir::Graph &subg);
140   void loadIf(const Operator *op, ir::Graph &subg);
141   void loadLeakyRelu(const Operator *op, ir::Graph &subg);
142   void loadLogSoftmax(const Operator *op, ir::Graph &subg);
143   void loadOneHot(const Operator *op, ir::Graph &subg);
144   void loadPack(const Operator *op, ir::Graph &subg);
145   void loadPool2D(const Operator *op, ir::Graph &subg, ir::operation::Pool2D::PoolType op_type);
146   void loadReduce(const Operator *op, ir::Graph &subg,
147                   ir::operation::Reduce::ReduceType reduce_type);
148   void loadReduceAll(const Operator *op, ir::Graph &subg);
149   void loadReshape(const Operator *op, ir::Graph &subg);
150   void loadResizeBilinear(const Operator *op, ir::Graph &subg);
151   void loadResizeNearestNeighbor(const Operator *op, ir::Graph &subg);
152   void loadSoftmax(const Operator *op, ir::Graph &subg);
153   void loadSpaceToDepth(const Operator *op, ir::Graph &subg);
154   void loadSplit(const Operator *op, ir::Graph &subg);
155   void loadSplitV(const Operator *op, ir::Graph &subg);
156   void loadSqueeze(const Operator *op, ir::Graph &subg);
157   void loadStridedSlice(const Operator *op, ir::Graph &subg);
158   void loadTransposeConv(const Operator *op, ir::Graph &subg);
159   void loadUnidirectionalSequenceLSTM(const Operator *op, ir::Graph &subg);
160   void loadUnpack(const Operator *op, ir::Graph &subg);
161   void loadWhile(const Operator *op, ir::Graph &subg);
162
163   void verifySubgraphIndex(int subg_index)
164   {
165     const auto num_subgraphs = _model->subgraphs()->size();
166     if (subg_index < 0 || subg_index >= static_cast<int32_t>(num_subgraphs))
167       throw std::runtime_error{std::string{"Invalid subgraph index - "} +
168                                std::to_string(subg_index)};
169   }
170
171 protected:
172   // Base address for mapped region for loading (if needed)
173   uint8_t *_base;
174   // Memory page size
175   int32_t _pagesize;
176   // loaded file description
177   int _fd;
178   // Reference on loadable subgraphs
179   std::unique_ptr<ir::Subgraphs> &_subgraphs;
180   const Model *_model;
181   // Maps Tensor indices to onert Operands.
182   std::vector<ir::OperandIndex> _tensor_to_operand;
183   std::unordered_map<ir::OperandIndex, std::string> _tensor_names;
184   // Verifier
185   std::unique_ptr<Verifier> _verifier;
186   // Boolean flag to use MMAPED_DATA
187   bool _use_mmaped_data = false;
188 };
189
190 template <typename LoaderDomain>
191 void BaseLoader<LoaderDomain>::BaseLoader::loadFromFile(const char *file_path)
192 {
193   _fd = open(file_path, O_RDONLY);
194   if (_fd < 0)
195   {
196     throw std::runtime_error("Failed to open file " + std::string(file_path));
197   }
198
199   struct stat file_stat;
200   if (fstat(_fd, &file_stat) != 0)
201   {
202     throw std::runtime_error("Fstat failed or file " + std::string(file_path) +
203                              " is not a regular file");
204   }
205   int size = file_stat.st_size;
206
207   // Map model file into memory region
208   _base = static_cast<uint8_t *>(mmap(NULL, size, PROT_READ, MAP_PRIVATE, _fd, 0));
209   if (_base == MAP_FAILED)
210   {
211     close(_fd);
212     throw std::runtime_error("mmap failed - " + std::string(strerror(errno)));
213   }
214
215   _verifier = std::make_unique<Verifier>(reinterpret_cast<const std::uint8_t *>(_base), size);
216
217   loadModel();
218   munmap(_base, size);
219
220   close(_fd);
221 }
222
223 template <typename LoaderDomain>
224 void BaseLoader<LoaderDomain>::BaseLoader::loadFromBuffer(uint8_t *buffer, size_t size)
225 {
226   _base = buffer;
227   _verifier = std::make_unique<Verifier>(reinterpret_cast<const std::uint8_t *>(_base), size);
228   loadModel();
229 }
230
231 template <typename LoaderDomain>
232 ir::Activation
233 BaseLoader<LoaderDomain>::BaseLoader::convertActivation(const ActivationFunctionType type)
234 {
235   switch (type)
236   {
237     case ActivationFunctionType::ActivationFunctionType_NONE:
238       return ir::Activation::NONE;
239     case ActivationFunctionType::ActivationFunctionType_RELU:
240       return ir::Activation::RELU;
241     case ActivationFunctionType::ActivationFunctionType_RELU_N1_TO_1:
242       return ir::Activation::RELU1;
243     case ActivationFunctionType::ActivationFunctionType_RELU6:
244       return ir::Activation::RELU6;
245     case ActivationFunctionType::ActivationFunctionType_TANH:
246       return ir::Activation::TANH;
247     default:
248       throw std::runtime_error(std::string("Unsupported or invalid activation type: ") +
249                                std::to_string(static_cast<int>(type)));
250   }
251 }
252
253 template <typename LoaderDomain>
254 ir::DataType BaseLoader<LoaderDomain>::BaseLoader::tensorTypeToDataType(const TensorType type)
255 {
256   switch (type)
257   {
258     case TensorType::TensorType_FLOAT32:
259       return ir::DataType::FLOAT32;
260     case TensorType::TensorType_FLOAT16:
261       return ir::DataType::FLOAT16;
262     case TensorType::TensorType_INT32:
263       return ir::DataType::INT32;
264     case TensorType::TensorType_UINT8:
265       return ir::DataType::QUANT_UINT8_ASYMM;
266     case TensorType::TensorType_INT64:
267       return ir::DataType::INT64;
268     // case TensorType::TensorType_STRING:
269     case TensorType::TensorType_BOOL:
270       return ir::DataType::BOOL8;
271     case TensorType::TensorType_INT16:
272       return ir::DataType::QUANT_INT16_ASYMM;
273     // case TensorType::TensorType_COMPLEX64
274     case TensorType::TensorType_INT8:
275       return ir::DataType::QUANT_INT8_ASYMM;
276     // case TensorType::TensorType_FLOAT64
277     default:
278       throw std::runtime_error(
279         std::string("Unsupported tensor type: ").append(EnumNameTensorType(type)));
280   }
281 }
282
283 template <typename LoaderDomain>
284 ir::OperandIndex BaseLoader<LoaderDomain>::BaseLoader::tensorIdxToOperandIdx(int32_t tensorIdx)
285 {
286   return isOptionalInputTensor(tensorIdx) ? ir::OperandIndex() : _tensor_to_operand[tensorIdx];
287 }
288
289 /* Copy is copied from tensorflow lite */
290 template <typename T> bool Copy(const T *data_ptr, std::vector<uint16_t> &arr)
291 {
292   if (data_ptr->values() == nullptr)
293   {
294     return false;
295   }
296
297   int size = data_ptr->values()->size();
298   arr.reserve(size);
299   for (int i = 0; i < size; i++)
300   {
301     arr.emplace_back(static_cast<uint16_t>(data_ptr->values()->Get(i)));
302   }
303   return true;
304 }
305
306 template <typename LoaderDomain>
307 ir::OperandIndex BaseLoader<LoaderDomain>::loadOperand(const Tensor *tensor, ir::Graph &subg)
308 {
309   ir::Shape shape;
310   // Shape
311   const auto *tensor_shape = tensor->shape();
312   if (tensor_shape != nullptr)
313   {
314     for (const auto &dim : *tensor_shape)
315     {
316       shape.append(dim);
317     }
318   }
319
320   // Note for tensor->shape_signature()
321   // We don't handle shape signature
322   //    How we handle:
323   //       If shape_signature[k] == -1, we will use tensor->shape()[k] == 1
324   //       If app wants to change the input shape, call nnfw_apply_input_tensorinfo() can
325   //       be used.
326
327   // Type
328   ir::DataType data_type = tensorTypeToDataType(tensor->type());
329   // Quantization
330   auto q_params = tensor->quantization();
331   float scale = 0.0;
332   long zero_point = 0;
333   if (q_params != nullptr)
334   {
335     if (q_params->scale())
336     {
337       if (q_params->scale()->size() != 1)
338       {
339         throw std::runtime_error("Only 1 scale for a tensor is supported.");
340       }
341       scale = q_params->scale()->Get(0);
342     }
343
344     if (q_params->zero_point())
345     {
346       if (q_params->zero_point()->size() != 1)
347       {
348         throw std::runtime_error("Only 1 zero_point value for a tensor is supported.");
349       }
350       zero_point = q_params->zero_point()->Get(0);
351       // zero_point is long while TypeInfo.zero_point is defined as int32_t.
352       assert(zero_point >= std::numeric_limits<int32_t>::min());
353       assert(zero_point <= std::numeric_limits<int32_t>::max());
354     }
355     auto details = q_params->details_as_CustomQuantization();
356     if (details != nullptr)
357       throw std::runtime_error("Custom Quantization is not supported");
358   }
359   // Create TypeInfo
360   ir::TypeInfo type_info(data_type, scale, zero_point);
361   // Sparsity
362   loadSparsity(tensor, shape, type_info);
363
364   // Create operand
365   const auto operand_index = subg.addOperand(shape, type_info);
366
367   // Constant tensors are indicated by non-empty data.
368   const auto *data = _model->buffers()->Get(tensor->buffer())->data();
369   if (data != nullptr)
370   {
371     using std::ptrdiff_t;
372     std::unique_ptr<ir::Data> data_obj;
373     if (_fd == -1) // Model is from memory
374     {
375       data_obj = std::make_unique<ir::ExternalData>(data->data(), data->size());
376     }
377     else // Model is loaded(mmap'd) from a file
378     {
379       size_t data_size = data->size();
380       ptrdiff_t unaligned_offset_start = data->data() - _base;
381       ptrdiff_t offset_end = unaligned_offset_start + data_size;
382
383       // Calculated aligned offset from base address of mapped region
384       // munmap accepts memory address which is a multiple of the pagesize
385       ptrdiff_t aligned_offset_start = (unaligned_offset_start / _pagesize) * _pagesize;
386       size_t mmap_size = offset_end - aligned_offset_start;
387
388       if (_use_mmaped_data)
389       {
390         data_obj = std::make_unique<ir::MMapedData>(_fd, aligned_offset_start, mmap_size,
391                                                     unaligned_offset_start, data_size);
392       }
393       else
394       {
395         size_t offset = unaligned_offset_start - aligned_offset_start;
396         uint8_t *mmap_base = static_cast<uint8_t *>(
397           mmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, _fd, aligned_offset_start));
398         data_obj = std::make_unique<ir::CachedData>(mmap_base + offset, data_size);
399         munmap(mmap_base, mmap_size);
400       }
401     }
402     subg.setOperandValue(operand_index, std::move(data_obj));
403   }
404
405   _tensor_names.emplace(operand_index, tensor->name()->str());
406
407   // Variable
408   if (tensor->is_variable())
409   {
410     if (data != nullptr)
411       throw std::runtime_error("Variable tensor with buffer is not supported!");
412
413     subg.operands().at(operand_index).info().setAsVariable();
414   }
415
416   return operand_index;
417 }
418
419 template <typename LoaderDomain>
420 void BaseLoader<LoaderDomain>::loadSparsity(const Tensor *tensor, const ir::Shape &shape,
421                                             ir::TypeInfo &typeInfo)
422 {
423   auto src_sparsity = tensor->sparsity();
424   if (src_sparsity != nullptr)
425   {
426     std::vector<uint16_t> w1_segments;
427     std::vector<uint16_t> w1_indices;
428     // check traversal_order
429     if (src_sparsity->traversal_order())
430     {
431       const int traversal_order_size = src_sparsity->traversal_order()->size();
432       for (int i = 0; i < traversal_order_size; ++i)
433       {
434         if (i != src_sparsity->traversal_order()->Get(i))
435           throw std::runtime_error("traversal_order [0, 1, ..., n-1] is only supported.");
436       }
437     }
438     // check block_map
439     int block_rank = 0;
440     if (src_sparsity->block_map())
441     {
442       block_rank = src_sparsity->block_map()->size();
443       for (int i = 0; i < block_rank; ++i)
444       {
445         if (i != src_sparsity->block_map()->Get(i))
446           throw std::runtime_error("block_map [0, 1, ..., n-1] is only supported.");
447       }
448     }
449     // load metadata
450     const int dim_metadata_size = src_sparsity->dim_metadata()->size();
451     auto dense_rank = shape.rank();
452     if (dense_rank + block_rank != dim_metadata_size)
453       throw std::runtime_error("sparsity dim_metadata length is wrong.");
454     bool random_sparsity = dim_metadata_size == 2 && block_rank == 0;
455     bool block2D_sparsity = dim_metadata_size == 4 && block_rank == 2;
456     if (dim_metadata_size != !random_sparsity && !block2D_sparsity)
457       throw std::runtime_error(
458         "sparsity is supported only for 2D tensor with random or 16x1 block sparsity.");
459
460     const auto *src_metadata = src_sparsity->dim_metadata()->Get(0);
461     if (src_metadata->format() != DimensionType::DimensionType_DENSE)
462       throw std::runtime_error("sparse tensor dim[0] is not DENSE");
463     src_metadata = src_sparsity->dim_metadata()->Get(1);
464     if (src_metadata->format() != DimensionType::DimensionType_SPARSE_CSR)
465       throw std::runtime_error("sparse tensor dim[0] is not SPARSE_CSR");
466     auto ParseSparseIndexVector = [src_metadata, &w1_segments, &w1_indices]() {
467       if (src_metadata->array_segments() == nullptr || src_metadata->array_indices() == nullptr)
468         return false;
469       bool status = true;
470       switch (src_metadata->array_segments_type())
471       {
472         case SparseIndexVector::SparseIndexVector_Int32Vector:
473           status = Copy(src_metadata->array_segments_as_Int32Vector(), w1_segments);
474           break;
475         case SparseIndexVector::SparseIndexVector_Uint16Vector:
476           status = Copy(src_metadata->array_segments_as_Uint16Vector(), w1_segments);
477           break;
478         case SparseIndexVector::SparseIndexVector_Uint8Vector:
479           status = Copy(src_metadata->array_segments_as_Uint8Vector(), w1_segments);
480           break;
481         default:
482           return false;
483       }
484       if (status != true)
485         return false;
486       switch (src_metadata->array_indices_type())
487       {
488         case SparseIndexVector::SparseIndexVector_Int32Vector:
489           return Copy(src_metadata->array_indices_as_Int32Vector(), w1_indices);
490         case SparseIndexVector::SparseIndexVector_Uint16Vector:
491           return Copy(src_metadata->array_indices_as_Uint16Vector(), w1_indices);
492         case SparseIndexVector::SparseIndexVector_Uint8Vector:
493           return Copy(src_metadata->array_indices_as_Uint8Vector(), w1_indices);
494         default:
495           break;
496       }
497       return false;
498     };
499     if (ParseSparseIndexVector() == false)
500       throw std::runtime_error("Error during parsing sparsity index information");
501     // Get block size
502     std::vector<int32_t> block_size;
503     for (int i = 0; i < block_rank; ++i)
504     {
505       auto block_metadata = src_sparsity->dim_metadata()->Get(dense_rank + i);
506       if (block_metadata->format() != DimensionType::DimensionType_DENSE)
507         throw std::runtime_error("block dimension must be DENSE.");
508       block_size.push_back(block_metadata->dense_size());
509     }
510     typeInfo.sparsity(std::make_shared<ir::Sparsity>(std::move(w1_segments), std::move(w1_indices),
511                                                      std::move(block_size)));
512   }
513 }
514
515 template <typename LoaderDomain>
516 void BaseLoader<LoaderDomain>::loadOperationIO(const Operator *op, ir::OperandIndexSequence &inputs,
517                                                ir::OperandIndexSequence &outputs)
518 {
519   for (const std::int32_t idx : *op->inputs())
520   {
521     // Optional tensors are not supported yet except for FULLY_CONNECTED and BCQ_FULLY_CONNECTED
522     auto check_optional_input = [&]() {
523       auto builtin_code = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
524       if (isOptionalInputTensor(idx) && !allowOptionalInputTensor(builtin_code))
525         throw std::runtime_error(
526           std::string("loader doesn't support optional input tensor yet for ")
527             .append(EnumNameBuiltinOperator(builtin_code)));
528     };
529     check_optional_input();
530     inputs.append(tensorIdxToOperandIdx(idx));
531   }
532
533   for (const std::int32_t idx : *op->outputs())
534   {
535     outputs.append(tensorIdxToOperandIdx(idx));
536   }
537 }
538
539 template <typename LoaderDomain>
540 template <typename Param, typename OptionsType>
541 void BaseLoader<LoaderDomain>::loadStridesAndPaddings(Param &param, const OptionsType *options)
542 {
543   // Strides
544   param.stride.vertical = options->stride_h();
545   param.stride.horizontal = options->stride_w();
546   // Paddings
547   switch (options->padding())
548   {
549     case Padding::Padding_SAME:
550       param.padding.type = ir::PaddingType::SAME;
551       break;
552     case Padding::Padding_VALID:
553       param.padding.type = ir::PaddingType::VALID;
554       break;
555     default:
556       throw std::runtime_error{"Invalid padding type"};
557   }
558   // param paddings indexes unused
559 }
560
561 template <typename LoaderDomain>
562 template <typename Param>
563 void BaseLoader<LoaderDomain>::loadPool2DOptions(Param &param, const Pool2DOptions *options)
564 {
565   // Strides and Paddings
566   if (options->stride_h() <= 0 || options->stride_w() <= 0)
567     throw std::runtime_error{"Invalid stride vertical or horizontal - both must be bigger than 0"};
568   loadStridesAndPaddings(param, options);
569   // Filter width and height
570   // Strides
571   if (options->filter_width() <= 0 || options->filter_height() <= 0)
572     throw std::runtime_error{"Invalid filter width or height - both must be bigger than 0"};
573   param.kw = options->filter_width();
574   param.kh = options->filter_height();
575   // Activation
576   param.activation = convertActivation(options->fused_activation_function());
577 }
578
579 template <typename LoaderDomain>
580 template <typename OpIR, typename... Args>
581 const OpIR *BaseLoader<LoaderDomain>::loadOperationTo(const Operator *op, ir::Graph &subg,
582                                                       Args &&... args)
583 {
584   static_assert(sizeof...(args) <= 1, "You can't have more than 1 arguments!");
585   ir::OperandIndexSequence inputs;
586   ir::OperandIndexSequence outputs;
587
588   loadOperationIO(op, inputs, outputs);
589
590   std::unique_ptr<OpIR> new_op(new OpIR(inputs, outputs, std::forward<Args>(args)...));
591   auto ret = new_op.get();
592   subg.addOperation(std::move(new_op));
593
594   return ret;
595 }
596
597 template <typename LoaderDomain>
598 void BaseLoader<LoaderDomain>::loadConv2D(const Operator *op, ir::Graph &subg)
599 {
600   ir::operation::Conv2D::Param param;
601   const auto *options = op->builtin_options_as_Conv2DOptions();
602   param.activation = convertActivation(options->fused_activation_function());
603   loadStridesAndPaddings(param, options);
604   param.dilation.width_factor = options->dilation_w_factor();
605   param.dilation.height_factor = options->dilation_h_factor();
606
607   loadOperationTo<ir::operation::Conv2D>(op, subg, param);
608 }
609
610 template <typename LoaderDomain>
611 void BaseLoader<LoaderDomain>::loadDepthwiseConv2D(const Operator *op, ir::Graph &subg)
612 {
613   ir::operation::DepthwiseConv2D::Param param;
614   const auto *options = op->builtin_options_as_DepthwiseConv2DOptions();
615   param.activation = convertActivation(options->fused_activation_function());
616   loadStridesAndPaddings(param, options);
617   param.multiplier = options->depth_multiplier();
618   // Dilation h/w factor unused
619   param.dilation.width_factor = options->dilation_w_factor();
620   param.dilation.height_factor = options->dilation_h_factor();
621
622   loadOperationTo<ir::operation::DepthwiseConv2D>(op, subg, param);
623 }
624
625 template <typename LoaderDomain>
626 void BaseLoader<LoaderDomain>::loadTransposeConv(const Operator *op, ir::Graph &subg)
627 {
628   ir::operation::TransposeConv::Param param;
629   const auto *options = op->builtin_options_as_TransposeConvOptions();
630   loadStridesAndPaddings(param, options);
631
632   loadOperationTo<ir::operation::TransposeConv>(op, subg, param);
633 }
634
635 template <typename LoaderDomain>
636 void BaseLoader<LoaderDomain>::loadPool2D(const Operator *op, ir::Graph &subg,
637                                           ir::operation::Pool2D::PoolType op_type)
638 {
639   ir::operation::Pool2D::Param param;
640   param.op_type = op_type;
641   const auto *options = op->builtin_options_as_Pool2DOptions();
642
643   loadPool2DOptions(param, options);
644
645   loadOperationTo<ir::operation::Pool2D>(op, subg, param);
646 }
647
648 template <typename LoaderDomain>
649 void BaseLoader<LoaderDomain>::loadReshape(const Operator *op, ir::Graph &subg)
650 {
651   ir::operation::Reshape::Param param{};
652   const auto *options = op->builtin_options_as_ReshapeOptions();
653   if (options != nullptr)
654   {
655     const auto *new_shape = options->new_shape();
656     if (new_shape)
657     {
658       for (uint i = 0; i < new_shape->size(); ++i)
659       {
660         param.new_shape.push_back(new_shape->Get(i));
661       }
662     }
663   }
664
665   loadOperationTo<ir::operation::Reshape>(op, subg, param);
666 }
667
668 template <typename LoaderDomain>
669 void BaseLoader<LoaderDomain>::loadSoftmax(const Operator *op, ir::Graph &subg)
670 {
671   ir::operation::Softmax::Param param;
672   const auto *options = op->builtin_options_as_SoftmaxOptions();
673   // Beta
674   param.beta = options->beta();
675
676   loadOperationTo<ir::operation::Softmax>(op, subg, param);
677 }
678
679 template <typename LoaderDomain>
680 void BaseLoader<LoaderDomain>::loadConcatenation(const Operator *op, ir::Graph &subg)
681 {
682   ir::operation::Concat::Param param;
683   const auto *options = op->builtin_options_as_ConcatenationOptions();
684   // Axis
685   param.axis = options->axis();
686   // activation unused
687
688   loadOperationTo<ir::operation::Concat>(op, subg, param);
689 }
690
691 template <typename LoaderDomain>
692 void BaseLoader<LoaderDomain>::loadFC(const Operator *op, ir::Graph &subg)
693 {
694   ir::operation::FullyConnected::Param param;
695   const auto *options = op->builtin_options_as_FullyConnectedOptions();
696
697   param.activation = convertActivation(options->fused_activation_function());
698   param.weights_format = static_cast<ir::FullyConnectedWeightsFormat>(options->weights_format());
699
700   const auto fc = loadOperationTo<ir::operation::FullyConnected>(op, subg, param);
701
702   const auto &input_operand =
703     subg.operands().at(fc->getInputs().at(ir::operation::FullyConnected::INPUT));
704   auto &weights_operand =
705     subg.operands().at(fc->getInputs().at(ir::operation::FullyConnected::WEIGHT));
706   if (input_operand.typeInfo().type() == ir::DataType::FLOAT32 &&
707       ((weights_operand.typeInfo().type() == ir::DataType::QUANT_UINT8_ASYMM) ||
708        weights_operand.typeInfo().type() == ir::DataType::QUANT_INT8_ASYMM))
709   {
710     weights_operand.type(ir::DataType::QUANT_INT8_SYMM);
711   }
712 }
713
714 template <typename LoaderDomain>
715 void BaseLoader<LoaderDomain>::loadAddV2(const Operator *op, ir::Graph &subg)
716 {
717   ir::operation::BinaryArithmetic::Param param;
718   param.arithmetic_type = ir::operation::BinaryArithmetic::ArithmeticType::ADD;
719
720   if (op->custom_options() == nullptr)
721   {
722     param.activation = ir::Activation::NONE;
723   }
724   else
725   {
726     size_t custom_op_data_size = op->custom_options()->size();
727     auto custom_op_data = op->custom_options()->Data();
728     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
729     auto attr_map = data_root.AsMap();
730     const auto fused_activation_func = static_cast<typename LoaderDomain::ActivationFunctionType>(
731       attr_map["fused_activation_function"].AsInt8());
732     param.activation = convertActivation(fused_activation_func);
733   }
734
735   loadOperationTo<ir::operation::BinaryArithmetic>(op, subg, param);
736 }
737
738 template <typename LoaderDomain>
739 void BaseLoader<LoaderDomain>::loadDepthToSpace(const Operator *op, ir::Graph &subg)
740 {
741   ir::operation::DepthToSpace::Param param;
742   const auto *options = op->builtin_options_as_DepthToSpaceOptions();
743   param.block_size = options->block_size();
744
745   loadOperationTo<ir::operation::DepthToSpace>(op, subg, param);
746 }
747
748 template <typename LoaderDomain>
749 void BaseLoader<LoaderDomain>::loadBinaryArithmetic(
750   const Operator *op, ir::Graph &subg, ir::operation::BinaryArithmetic::ArithmeticType op_type)
751 {
752   ir::operation::BinaryArithmetic::Param param;
753   param.arithmetic_type = op_type;
754   switch (op_type)
755   {
756     case ir::operation::BinaryArithmetic::ArithmeticType::ADD:
757     {
758       const auto *add_options = op->builtin_options_as_AddOptions();
759       param.activation = convertActivation(add_options->fused_activation_function());
760       break;
761     }
762     case ir::operation::BinaryArithmetic::ArithmeticType::SUB:
763     {
764       const auto *sub_options = op->builtin_options_as_SubOptions();
765       param.activation = convertActivation(sub_options->fused_activation_function());
766       break;
767     }
768     case ir::operation::BinaryArithmetic::ArithmeticType::MUL:
769     {
770       const auto *mul_options = op->builtin_options_as_MulOptions();
771       param.activation = convertActivation(mul_options->fused_activation_function());
772       break;
773     }
774     case ir::operation::BinaryArithmetic::ArithmeticType::DIV:
775     {
776       const auto *div_options = op->builtin_options_as_DivOptions();
777       param.activation = convertActivation(div_options->fused_activation_function());
778       break;
779     }
780     default:
781       assert(false &&
782              "The function 'loadBinaryArithmetic' supports only BinaryArithmetic operations");
783       break;
784   }
785
786   loadOperationTo<ir::operation::BinaryArithmetic>(op, subg, param);
787 }
788
789 template <typename LoaderDomain>
790 void BaseLoader<LoaderDomain>::loadPack(const Operator *op, ir::Graph &subg)
791 {
792   ir::operation::Pack::Param param;
793   const auto *options = op->builtin_options_as_PackOptions();
794   param.num = options->values_count();
795   param.axis = options->axis();
796
797   loadOperationTo<ir::operation::Pack>(op, subg, param);
798 }
799
800 template <typename LoaderDomain>
801 void BaseLoader<LoaderDomain>::loadElementwiseActivation(
802   const Operator *op, ir::Graph &subg, ir::operation::ElementwiseActivation::Type op_type,
803   float alpha, float beta)
804 {
805   ir::operation::ElementwiseActivation::Param param;
806   param.op_type = op_type;
807   param.alpha = alpha;
808   param.beta = beta;
809
810   loadOperationTo<ir::operation::ElementwiseActivation>(op, subg, param);
811 }
812
813 template <typename LoaderDomain>
814 void BaseLoader<LoaderDomain>::loadResizeBilinear(const Operator *op, ir::Graph &subg)
815 {
816   ir::operation::ResizeBilinear::Param param;
817   param.align_corners = op->builtin_options_as_ResizeBilinearOptions()->align_corners();
818   param.half_pixel_centers = op->builtin_options_as_ResizeBilinearOptions()->half_pixel_centers();
819
820   loadOperationTo<ir::operation::ResizeBilinear>(op, subg, param);
821 }
822
823 template <typename LoaderDomain>
824 void BaseLoader<LoaderDomain>::loadResizeNearestNeighbor(const Operator *op, ir::Graph &subg)
825 {
826   ir::operation::ResizeNearestNeighbor::Param param;
827   param.align_corners = op->builtin_options_as_ResizeNearestNeighborOptions()->align_corners();
828
829   loadOperationTo<ir::operation::ResizeNearestNeighbor>(op, subg, param);
830 }
831
832 template <typename LoaderDomain>
833 void BaseLoader<LoaderDomain>::loadReduce(const Operator *op, ir::Graph &subg,
834                                           ir::operation::Reduce::ReduceType reduce_type)
835 {
836   ir::operation::Reduce::Param param;
837   param.reduce_type = reduce_type;
838   param.keep_dims = op->builtin_options_as_ReducerOptions()->keep_dims();
839
840   loadOperationTo<ir::operation::Reduce>(op, subg, param);
841 }
842
843 template <typename LoaderDomain>
844 void BaseLoader<LoaderDomain>::loadReduceAll(const Operator *op, ir::Graph &subg)
845 {
846   ir::operation::Reduce::Param param;
847   param.reduce_type = ir::operation::Reduce::ReduceType::ALL;
848   if (op->custom_options() == nullptr)
849   {
850     param.keep_dims = false;
851   }
852   else
853   {
854     size_t custom_op_data_size = op->custom_options()->size();
855     auto custom_op_data = op->custom_options()->Data();
856     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
857     auto attr_map = data_root.AsMap();
858     param.keep_dims = attr_map["keep_dims"].AsBool();
859   }
860
861   loadOperationTo<ir::operation::Reduce>(op, subg, param);
862 }
863
864 template <typename LoaderDomain>
865 void BaseLoader<LoaderDomain>::loadElementwiseBinary(
866   const Operator *op, ir::Graph &subg,
867   ir::operation::ElementwiseBinary::ElementwiseBinaryType op_type)
868 {
869   ir::operation::ElementwiseBinary::Param param;
870   param.op_type = op_type;
871
872   loadOperationTo<ir::operation::ElementwiseBinary>(op, subg, param);
873 }
874
875 template <typename LoaderDomain>
876 void BaseLoader<LoaderDomain>::loadElementwiseUnary(const Operator *op, ir::Graph &subg,
877                                                     ir::operation::ElementwiseUnary::Type op_type)
878 {
879   ir::operation::ElementwiseUnary::Param param;
880   param.op_type = op_type;
881
882   const auto eu = loadOperationTo<ir::operation::ElementwiseUnary>(op, subg, param);
883   if (op_type == ir::operation::ElementwiseUnary::Type::CAST)
884   {
885     auto qasymm8ToUint8 = [](ir::Operand &operand) {
886       if (operand.typeInfo().type() == ir::DataType::QUANT_UINT8_ASYMM)
887       {
888         operand.type(ir::DataType::UINT8);
889       }
890     };
891     qasymm8ToUint8(
892       subg.operands().at(eu->getInputs().at(ir::operation::ElementwiseUnary::Input::INPUT)));
893     qasymm8ToUint8(subg.operands().at(eu->getOutputs().at(0)));
894   }
895 }
896
897 template <typename LoaderDomain>
898 void BaseLoader<LoaderDomain>::loadGather(const Operator *op, ir::Graph &subg)
899 {
900   ir::operation::Gather::Param param;
901   param.axis = op->builtin_options_as_GatherOptions()->axis();
902
903   loadOperationTo<ir::operation::Gather>(op, subg, param);
904 }
905
906 template <typename LoaderDomain>
907 void BaseLoader<LoaderDomain>::loadBatchMatMul(const Operator *op, ir::Graph &subg)
908 {
909   ir::operation::BatchMatMul::Param param;
910
911   const auto builtin_op = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
912
913   switch (builtin_op)
914   {
915     case BuiltinOperator::BuiltinOperator_BATCH_MATMUL:
916       param.adj_x = op->builtin_options_as_BatchMatMulOptions()->adjoint_lhs();
917       param.adj_y = op->builtin_options_as_BatchMatMulOptions()->adjoint_rhs();
918       break;
919     case BuiltinOperator::BuiltinOperator_CUSTOM:
920       if (op->custom_options() == nullptr)
921       {
922         param.adj_x = false;
923         param.adj_y = false;
924       }
925       else
926       {
927         size_t custom_op_data_size = op->custom_options()->size();
928         auto custom_op_data = op->custom_options()->Data();
929         auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
930         auto attr_map = data_root.AsMap();
931         param.adj_x = attr_map["adj_x"].AsBool();
932         param.adj_y = attr_map["adj_y"].AsBool();
933       }
934       break;
935     default:
936       throw std::runtime_error(
937         std::string("Wrong loaded operation: ").append(EnumNameBuiltinOperator(builtin_op)) +
938         " as " + EnumNameBuiltinOperator(BuiltinOperator::BuiltinOperator_BATCH_MATMUL));
939   }
940
941   loadOperationTo<ir::operation::BatchMatMul>(op, subg, param);
942 }
943
944 template <typename LoaderDomain>
945 void BaseLoader<LoaderDomain>::loadSpaceToDepth(const Operator *op, ir::Graph &subg)
946 {
947   ir::operation::SpaceToDepth::Param param;
948   const auto *options = op->builtin_options_as_SpaceToDepthOptions();
949   param.block_size = options->block_size();
950
951   loadOperationTo<ir::operation::SpaceToDepth>(op, subg, param);
952 }
953
954 template <typename LoaderDomain>
955 void BaseLoader<LoaderDomain>::loadCustom(const Operator *op, ir::Graph &subg)
956 {
957   ir::OperandIndexSequence inputs;
958   ir::OperandIndexSequence outputs;
959
960   assert(op->custom_options_format() == CustomOptionsFormat::CustomOptionsFormat_FLEXBUFFERS &&
961          "Unsupported custom operation options format");
962
963   auto *op_code = _model->operator_codes()->Get(op->opcode_index());
964   auto custom_op_name = op_code->custom_code()->str();
965
966   enum class BuiltinOP
967   {
968     AddV2,
969     ReduceAll,
970     MatrixBandPart,
971     BatchMatMul,
972     Einsum,
973     BroadcastTo,
974     FusedBatchNorm,
975     StatelessRandomUniform,
976     Erf
977   };
978
979   // Mapping from custom op name string to BuiltinOP enum
980   std::map<std::string, BuiltinOP> builtin_map = {
981     {"AddV2", BuiltinOP::AddV2},
982     {"All", BuiltinOP::ReduceAll},
983     {"MatrixBandPart", BuiltinOP::MatrixBandPart},
984     {"BatchMatMulV2", BuiltinOP::BatchMatMul},
985     {"Einsum", BuiltinOP::Einsum},
986     {"FusedBatchNormV3", BuiltinOP::FusedBatchNorm},
987     {"BroadcastTo", BuiltinOP::BroadcastTo},
988     {"StatelessRandomUniform", BuiltinOP::StatelessRandomUniform},
989     {"Erf", BuiltinOP::Erf},
990   };
991
992   try
993   {
994     // Throw out_of_range if it is unknown custom op
995     auto custom_op_id = builtin_map.at(custom_op_name);
996     switch (custom_op_id)
997     {
998       case BuiltinOP::AddV2:
999         loadAddV2(op, subg);
1000         break;
1001       case BuiltinOP::ReduceAll:
1002         loadReduceAll(op, subg);
1003         break;
1004       case BuiltinOP::MatrixBandPart:
1005         loadOperationTo<ir::operation::MatrixBandPart>(op, subg);
1006         break;
1007       case BuiltinOP::BatchMatMul:
1008         loadBatchMatMul(op, subg);
1009         break;
1010       case BuiltinOP::Einsum:
1011         loadEinsum(op, subg);
1012         break;
1013       case BuiltinOP::BroadcastTo:
1014         loadOperationTo<ir::operation::BroadcastTo>(op, subg);
1015         break;
1016       case BuiltinOP::FusedBatchNorm:
1017         loadFusedBatchNorm(op, subg);
1018         break;
1019       case BuiltinOP::StatelessRandomUniform:
1020         loadOperationTo<ir::operation::StatelessRandomUniform>(op, subg);
1021         break;
1022       case BuiltinOP::Erf:
1023         loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::ERF);
1024         break;
1025       default:
1026         throw std::runtime_error{
1027           "Loader: Custom OP map is defined but operation loader function is not defined"};
1028     }
1029
1030     return;
1031   }
1032   catch (...)
1033   {
1034     loadOperationIO(op, inputs, outputs);
1035
1036     auto constraint = ir::OperandConstraint::createExact(inputs.size());
1037
1038     size_t custom_op_data_size = op->custom_options()->size();
1039     auto custom_op_data = new char[custom_op_data_size];
1040     std::copy(op->custom_options()->begin(), op->custom_options()->end(), custom_op_data);
1041
1042     ir::operation::Custom::Userdata userdata{};
1043     userdata.data = custom_op_data;
1044     userdata.size = custom_op_data_size;
1045
1046     auto new_op = std::make_unique<ir::operation::Custom>(constraint, inputs, outputs,
1047                                                           custom_op_name, userdata);
1048
1049     subg.addOperation(std::move(new_op));
1050   }
1051 }
1052
1053 template <typename LoaderDomain>
1054 void BaseLoader<LoaderDomain>::loadSqueeze(const Operator *op, ir::Graph &subg)
1055 {
1056   ir::operation::Squeeze::Param param;
1057   const auto *options = op->builtin_options_as_SqueezeOptions();
1058   const auto *dims = options->squeeze_dims();
1059   if (dims)
1060   {
1061     if (dims->size() > sizeof(param.dims) / sizeof(param.dims[0]))
1062       throw std::runtime_error("Squeeze: 'param.ndims' is out of range.");
1063     param.ndim = dims->size();
1064     for (int i = 0; i < param.ndim; ++i)
1065       param.dims[i] = dims->Get(i);
1066   }
1067
1068   loadOperationTo<ir::operation::Squeeze>(op, subg, param);
1069 }
1070
1071 template <typename LoaderDomain>
1072 void BaseLoader<LoaderDomain>::loadSplit(const Operator *op, ir::Graph &subg)
1073 {
1074   ir::operation::Split::Param param;
1075   const auto *options = op->builtin_options_as_SplitOptions();
1076   param.num_splits = options->num_splits();
1077
1078   loadOperationTo<ir::operation::Split>(op, subg, param);
1079 }
1080
1081 template <typename LoaderDomain>
1082 void BaseLoader<LoaderDomain>::loadSplitV(const Operator *op, ir::Graph &subg)
1083 {
1084   ir::operation::SplitV::Param param;
1085   const auto *options = op->builtin_options_as_SplitVOptions();
1086   param.num_splits = options->num_splits();
1087
1088   loadOperationTo<ir::operation::SplitV>(op, subg, param);
1089 }
1090
1091 template <typename LoaderDomain>
1092 void BaseLoader<LoaderDomain>::loadStridedSlice(const Operator *op, ir::Graph &subg)
1093 {
1094   ir::operation::StridedSlice::Param param;
1095   const auto *options = op->builtin_options_as_StridedSliceOptions();
1096   param.begin_mask = options->begin_mask();
1097   param.end_mask = options->end_mask();
1098   param.shrink_axis_mask = options->shrink_axis_mask();
1099
1100   loadOperationTo<ir::operation::StridedSlice>(op, subg, param);
1101 }
1102
1103 template <typename LoaderDomain>
1104 void BaseLoader<LoaderDomain>::loadUnpack(const Operator *op, ir::Graph &subg)
1105 {
1106   ir::operation::Unpack::Param param;
1107   const auto *options = op->builtin_options_as_UnpackOptions();
1108   param.num = options->num();
1109   param.axis = options->axis();
1110
1111   loadOperationTo<ir::operation::Unpack>(op, subg, param);
1112 }
1113
1114 template <typename LoaderDomain>
1115 void BaseLoader<LoaderDomain>::loadComparison(const Operator *op, ir::Graph &subg)
1116 {
1117   ir::operation::Comparison::Param param;
1118   const auto builtin_op = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
1119
1120   switch (builtin_op)
1121   {
1122     case BuiltinOperator::BuiltinOperator_EQUAL:
1123       param.comparison_type = ir::operation::Comparison::ComparisonType::Equal;
1124       break;
1125     case BuiltinOperator::BuiltinOperator_NOT_EQUAL:
1126       param.comparison_type = ir::operation::Comparison::ComparisonType::NotEqual;
1127       break;
1128     case BuiltinOperator::BuiltinOperator_GREATER_EQUAL:
1129       param.comparison_type = ir::operation::Comparison::ComparisonType::GreaterEqual;
1130       break;
1131     case BuiltinOperator::BuiltinOperator_GREATER:
1132       param.comparison_type = ir::operation::Comparison::ComparisonType::Greater;
1133       break;
1134     case BuiltinOperator::BuiltinOperator_LESS_EQUAL:
1135       param.comparison_type = ir::operation::Comparison::ComparisonType::LessEqual;
1136       break;
1137     case BuiltinOperator::BuiltinOperator_LESS:
1138       param.comparison_type = ir::operation::Comparison::ComparisonType::Less;
1139       break;
1140     default:
1141       throw std::runtime_error(
1142         std::string("Unsupported operation: ").append(EnumNameBuiltinOperator(builtin_op)));
1143   }
1144
1145   loadOperationTo<ir::operation::Comparison>(op, subg, param);
1146 }
1147
1148 template <typename LoaderDomain>
1149 void BaseLoader<LoaderDomain>::loadEinsum(const Operator *op, ir::Graph &subg)
1150 {
1151   ir::operation::Einsum::Param param;
1152   if (op->custom_options() == nullptr)
1153   {
1154     throw std::runtime_error{"Einsum: empty equation"};
1155   }
1156   else
1157   {
1158     size_t custom_op_data_size = op->custom_options()->size();
1159     auto custom_op_data = op->custom_options()->Data();
1160     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
1161     auto attr_map = data_root.AsMap();
1162     param.equation = attr_map["equation"].ToString();
1163   }
1164
1165   const auto es = loadOperationTo<ir::operation::Einsum>(op, subg, param);
1166   if (es->getInputs().size() != 2)
1167   {
1168     throw std::runtime_error{"Einsum: NYI input - only support two inputs"};
1169   }
1170 }
1171 template <typename LoaderDomain>
1172 void BaseLoader<LoaderDomain>::loadFusedBatchNorm(const Operator *op, ir::Graph &subg)
1173 {
1174   ir::operation::FusedBatchNorm::Param param;
1175   if (op->custom_options() == nullptr)
1176   {
1177     throw std::runtime_error{"FusedBatchNorm: empty option"};
1178   }
1179   else
1180   {
1181     size_t custom_op_data_size = op->custom_options()->size();
1182     auto custom_op_data = op->custom_options()->Data();
1183     auto data_root = flexbuffers::GetRoot(custom_op_data, custom_op_data_size);
1184     auto attr_map = data_root.AsMap();
1185     param.is_training = attr_map["is_training"].AsBool();
1186     param.epsilon = attr_map["epsilon"].AsFloat();
1187     param.data_format = attr_map["data_format"].ToString();
1188   }
1189
1190   const auto fbn = loadOperationTo<ir::operation::FusedBatchNorm>(op, subg, param);
1191
1192   if (fbn->getInputs().size() != 5)
1193   {
1194     throw std::runtime_error{"FusedBatchNorm: NYI input - only support five inputs"};
1195   }
1196 }
1197
1198 template <typename LoaderDomain>
1199 void BaseLoader<LoaderDomain>::loadOneHot(const Operator *op, ir::Graph &subg)
1200 {
1201   if (op->inputs()->size() != 4 || op->outputs()->size() != 1)
1202     throw std::runtime_error("OneHot Op has wrong number of input or output tensors.");
1203
1204   // Set parameter
1205   ir::operation::OneHot::Param param;
1206   param.axis = op->builtin_options_as_OneHotOptions()->axis();
1207
1208   loadOperationTo<ir::operation::OneHot>(op, subg, param);
1209 }
1210
1211 template <typename LoaderDomain>
1212 void BaseLoader<LoaderDomain>::loadIf(const Operator *op, ir::Graph &subg)
1213 {
1214   const auto *options = op->builtin_options_as_IfOptions();
1215   const int32_t then_index = options->then_subgraph_index();
1216   const int32_t else_index = options->else_subgraph_index();
1217
1218   verifySubgraphIndex(then_index);
1219   verifySubgraphIndex(else_index);
1220
1221   ir::operation::If::Param param;
1222   param.then_subg_index = ir::SubgraphIndex{static_cast<uint32_t>(then_index)};
1223   param.else_subg_index = ir::SubgraphIndex{static_cast<uint32_t>(else_index)};
1224
1225   loadOperationTo<ir::operation::If>(op, subg, param);
1226 }
1227
1228 template <typename LoaderDomain>
1229 void BaseLoader<LoaderDomain>::loadWhile(const Operator *op, ir::Graph &subg)
1230 {
1231   const auto *options = op->builtin_options_as_WhileOptions();
1232   const int32_t cond_index = options->cond_subgraph_index();
1233   const int32_t body_index = options->body_subgraph_index();
1234
1235   verifySubgraphIndex(cond_index);
1236   verifySubgraphIndex(body_index);
1237
1238   ir::operation::While::Param param;
1239   param.cond_subg_index = ir::SubgraphIndex{static_cast<uint32_t>(cond_index)};
1240   param.body_subg_index = ir::SubgraphIndex{static_cast<uint32_t>(body_index)};
1241
1242   loadOperationTo<ir::operation::While>(op, subg, param);
1243 }
1244
1245 template <typename LoaderDomain>
1246 void BaseLoader<LoaderDomain>::loadArgMinMax(const Operator *op, ir::Graph &subg, bool is_argmax)
1247 {
1248   ir::operation::ArgMinMax::Param param;
1249   const auto output_type = is_argmax ? op->builtin_options_as_ArgMaxOptions()->output_type()
1250                                      : op->builtin_options_as_ArgMinOptions()->output_type();
1251   param.output_type = tensorTypeToDataType(output_type);
1252   param.is_arg_max = is_argmax;
1253
1254   loadOperationTo<ir::operation::ArgMinMax>(op, subg, param);
1255 }
1256
1257 template <typename LoaderDomain>
1258 void BaseLoader<LoaderDomain>::loadLogSoftmax(const Operator *op, ir::Graph &subg)
1259 {
1260   ir::operation::LogSoftmax::Param param;
1261   // In tflite, beta is fixed to 1.0 and axis is fixed to -1.
1262   param.beta = 1.0f;
1263   param.axis = -1;
1264
1265   loadOperationTo<ir::operation::LogSoftmax>(op, subg, param);
1266 }
1267
1268 template <typename LoaderDomain>
1269 void BaseLoader<LoaderDomain>::loadLeakyRelu(const Operator *op, ir::Graph &subg)
1270 {
1271   float alpha = op->builtin_options_as_LeakyReluOptions()->alpha();
1272   loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::LEAKY_RELU, alpha,
1273                             1.f);
1274 }
1275
1276 template <typename LoaderDomain>
1277 void BaseLoader<LoaderDomain>::loadUnidirectionalSequenceLSTM(const Operator *op, ir::Graph &subg)
1278 {
1279   ir::operation::LSTM::Param param;
1280   const auto *options = op->builtin_options_as_UnidirectionalSequenceLSTMOptions();
1281   param.activation = convertActivation(options->fused_activation_function());
1282   param.cell_threshold = options->cell_clip();
1283   param.projection_threshold = options->proj_clip();
1284   param.time_major = options->time_major();
1285   // The asymmetric_quantize_inputs option is unused yet
1286
1287   ir::OperandIndexSequence inputs;
1288   for (const std::int32_t idx : *op->inputs())
1289   {
1290     inputs.append(tensorIdxToOperandIdx(idx));
1291   }
1292
1293   ir::OperandIndexSequence outputs;
1294   // loader doesn't support optional output tensor yet
1295   if (op->outputs()->size() != 1)
1296   {
1297     auto builtin_code = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
1298     throw std::runtime_error(std::string("loader doesn't support optional output tensor yet for ")
1299                                .append(EnumNameBuiltinOperator(builtin_code)));
1300   }
1301   for (size_t i = 0; i < ir::operation::LSTM::Output::OUTPUT; ++i)
1302   {
1303     // Add optional outputs
1304     outputs.append(ir::OperandIndex());
1305   }
1306   outputs.append(tensorIdxToOperandIdx(op->outputs()->Get(0)));
1307
1308   std::unique_ptr<ir::operation::LSTM> new_op(new ir::operation::LSTM(inputs, outputs, param));
1309   subg.addOperation(std::move(new_op));
1310 }
1311
1312 template <typename LoaderDomain>
1313 void BaseLoader<LoaderDomain>::loadOperation(const Operator *op, ir::Graph &subg)
1314 {
1315   const auto builtin_op = _model->operator_codes()->Get(op->opcode_index())->builtin_code();
1316
1317   switch (builtin_op)
1318   {
1319     case BuiltinOperator::BuiltinOperator_ADD_N:
1320       loadOperationTo<ir::operation::AddN>(op, subg);
1321       return;
1322     case BuiltinOperator::BuiltinOperator_CONV_2D:
1323       loadConv2D(op, subg);
1324       return;
1325     case BuiltinOperator::BuiltinOperator_AVERAGE_POOL_2D:
1326       loadPool2D(op, subg, ir::operation::Pool2D::PoolType::AVG);
1327       return;
1328     case BuiltinOperator::BuiltinOperator_DEPTHWISE_CONV_2D:
1329       loadDepthwiseConv2D(op, subg);
1330       return;
1331     case BuiltinOperator::BuiltinOperator_TRANSPOSE_CONV:
1332       loadTransposeConv(op, subg);
1333       return;
1334     case BuiltinOperator::BuiltinOperator_RESHAPE:
1335       loadReshape(op, subg);
1336       return;
1337     case BuiltinOperator::BuiltinOperator_SOFTMAX:
1338       loadSoftmax(op, subg);
1339       return;
1340     case BuiltinOperator::BuiltinOperator_MAX_POOL_2D:
1341       loadPool2D(op, subg, ir::operation::Pool2D::PoolType::MAX);
1342       return;
1343     case BuiltinOperator::BuiltinOperator_CONCATENATION:
1344       loadConcatenation(op, subg);
1345       return;
1346     case BuiltinOperator::BuiltinOperator_FLOOR:
1347       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::FLOOR);
1348       return;
1349     case BuiltinOperator::BuiltinOperator_FULLY_CONNECTED:
1350       loadFC(op, subg);
1351       return;
1352     case BuiltinOperator::BuiltinOperator_ADD:
1353       loadBinaryArithmetic(op, subg, ir::operation::BinaryArithmetic::ArithmeticType::ADD);
1354       return;
1355     case BuiltinOperator::BuiltinOperator_SUB:
1356       loadBinaryArithmetic(op, subg, ir::operation::BinaryArithmetic::ArithmeticType::SUB);
1357       return;
1358     case BuiltinOperator::BuiltinOperator_MUL:
1359       loadBinaryArithmetic(op, subg, ir::operation::BinaryArithmetic::ArithmeticType::MUL);
1360       return;
1361     case BuiltinOperator::BuiltinOperator_DIV:
1362       loadBinaryArithmetic(op, subg, ir::operation::BinaryArithmetic::ArithmeticType::DIV);
1363       return;
1364     case BuiltinOperator::BuiltinOperator_PACK:
1365       loadPack(op, subg);
1366       return;
1367     case BuiltinOperator::BuiltinOperator_ELU:
1368       loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::ELU);
1369       return;
1370     case BuiltinOperator::BuiltinOperator_RELU:
1371       loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::RELU,
1372                                 ir::operation::ElementwiseActivation::infinity, 0.f);
1373       return;
1374     case BuiltinOperator::BuiltinOperator_RELU_N1_TO_1:
1375       loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::RELU, 1.f,
1376                                 -1.f);
1377       return;
1378     case BuiltinOperator::BuiltinOperator_RELU6:
1379       loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::RELU, 6.f,
1380                                 0.f);
1381       return;
1382     case BuiltinOperator::BuiltinOperator_RESIZE_BILINEAR:
1383       loadResizeBilinear(op, subg);
1384       return;
1385     case BuiltinOperator::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR:
1386       loadResizeNearestNeighbor(op, subg);
1387       return;
1388     case BuiltinOperator::BuiltinOperator_RSQRT:
1389       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::RSQRT);
1390       return;
1391     case BuiltinOperator::BuiltinOperator_SELECT:
1392     case BuiltinOperator::BuiltinOperator_SELECT_V2:
1393       loadOperationTo<ir::operation::Select>(op, subg);
1394       return;
1395     case BuiltinOperator::BuiltinOperator_SQRT:
1396       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::SQRT);
1397       return;
1398     case BuiltinOperator::BuiltinOperator_SQUARE:
1399       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::SQUARE);
1400       return;
1401     case BuiltinOperator::BuiltinOperator_SQUARED_DIFFERENCE:
1402       loadOperationTo<ir::operation::SquaredDifference>(op, subg);
1403       return;
1404     case BuiltinOperator::BuiltinOperator_TANH:
1405       loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::TANH, 1.f,
1406                                 1.f);
1407       return;
1408     case BuiltinOperator::BuiltinOperator_TRANSPOSE:
1409       loadOperationTo<ir::operation::Transpose>(op, subg);
1410       return;
1411     case BuiltinOperator::BuiltinOperator_MEAN:
1412       loadReduce(op, subg, ir::operation::Reduce::ReduceType::MEAN);
1413       return;
1414     case BuiltinOperator::BuiltinOperator_REDUCE_ANY:
1415       loadReduce(op, subg, ir::operation::Reduce::ReduceType::ANY);
1416       return;
1417     case BuiltinOperator::BuiltinOperator_REDUCE_MAX:
1418       loadReduce(op, subg, ir::operation::Reduce::ReduceType::MAX);
1419       return;
1420     case BuiltinOperator::BuiltinOperator_REVERSE_V2:
1421       loadOperationTo<ir::operation::Reverse>(op, subg);
1422       return;
1423     case BuiltinOperator::BuiltinOperator_PAD:
1424     case BuiltinOperator::BuiltinOperator_PADV2:
1425       loadOperationTo<ir::operation::Pad>(op, subg);
1426       return;
1427     case BuiltinOperator::BuiltinOperator_LOGISTIC:
1428       loadElementwiseActivation(op, subg, ir::operation::ElementwiseActivation::Type::LOGISTIC);
1429       return;
1430     case BuiltinOperator::BuiltinOperator_EXP:
1431       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::EXP);
1432       return;
1433     case BuiltinOperator::BuiltinOperator_EXPAND_DIMS:
1434       loadOperationTo<ir::operation::ExpandDims>(op, subg);
1435       return;
1436     case BuiltinOperator::BuiltinOperator_GATHER:
1437       loadGather(op, subg);
1438       return;
1439     case BuiltinOperator::BuiltinOperator_SPACE_TO_BATCH_ND:
1440       loadOperationTo<ir::operation::SpaceToBatchND>(op, subg);
1441       return;
1442     case BuiltinOperator::BuiltinOperator_BATCH_TO_SPACE_ND:
1443       loadOperationTo<ir::operation::BatchToSpaceND>(op, subg);
1444       return;
1445     case BuiltinOperator::BuiltinOperator_SUM:
1446       loadReduce(op, subg, ir::operation::Reduce::ReduceType::SUM);
1447       return;
1448     case BuiltinOperator::BuiltinOperator_CUSTOM:
1449       loadCustom(op, subg);
1450       return;
1451     case BuiltinOperator::BuiltinOperator_SQUEEZE:
1452       loadSqueeze(op, subg);
1453       return;
1454     case BuiltinOperator::BuiltinOperator_PRELU:
1455       loadOperationTo<ir::operation::PReLU>(op, subg);
1456       return;
1457     case BuiltinOperator::BuiltinOperator_SPLIT:
1458       loadSplit(op, subg);
1459       return;
1460     case BuiltinOperator::BuiltinOperator_SPLIT_V:
1461       loadSplitV(op, subg);
1462       return;
1463     case BuiltinOperator::BuiltinOperator_SLICE:
1464       loadOperationTo<ir::operation::Slice>(op, subg);
1465       return;
1466     case BuiltinOperator::BuiltinOperator_STRIDED_SLICE:
1467       loadStridedSlice(op, subg);
1468       return;
1469     case BuiltinOperator::BuiltinOperator_UNPACK:
1470       loadUnpack(op, subg);
1471       return;
1472     case BuiltinOperator::BuiltinOperator_MINIMUM:
1473       loadElementwiseBinary(op, subg, ir::operation::ElementwiseBinary::ElementwiseBinaryType::MIN);
1474       return;
1475     case BuiltinOperator::BuiltinOperator_MAXIMUM:
1476       loadElementwiseBinary(op, subg, ir::operation::ElementwiseBinary::ElementwiseBinaryType::MAX);
1477       return;
1478     case BuiltinOperator::BuiltinOperator_CAST:
1479       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::CAST);
1480       return;
1481     case BuiltinOperator::BuiltinOperator_EQUAL:
1482     case BuiltinOperator::BuiltinOperator_NOT_EQUAL:
1483     case BuiltinOperator::BuiltinOperator_GREATER_EQUAL:
1484     case BuiltinOperator::BuiltinOperator_GREATER:
1485     case BuiltinOperator::BuiltinOperator_LESS_EQUAL:
1486     case BuiltinOperator::BuiltinOperator_LESS:
1487       loadComparison(op, subg);
1488       return;
1489     case BuiltinOperator::BuiltinOperator_ONE_HOT:
1490       loadOneHot(op, subg);
1491       return;
1492     case BuiltinOperator::BuiltinOperator_ABS:
1493       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::ABS);
1494       return;
1495     case BuiltinOperator::BuiltinOperator_COS:
1496       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::COS);
1497       return;
1498     case BuiltinOperator::BuiltinOperator_SIN:
1499       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::SIN);
1500       return;
1501     case BuiltinOperator::BuiltinOperator_SHAPE:
1502       loadOperationTo<ir::operation::Shape>(op, subg);
1503       return;
1504     case BuiltinOperator::BuiltinOperator_REDUCE_PROD:
1505       loadReduce(op, subg, ir::operation::Reduce::ReduceType::PROD);
1506       return;
1507     case BuiltinOperator::BuiltinOperator_IF:
1508       loadIf(op, subg);
1509       return;
1510     case BuiltinOperator::BuiltinOperator_WHILE:
1511       loadWhile(op, subg);
1512       return;
1513     case BuiltinOperator::BuiltinOperator_NEG:
1514       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::NEG);
1515       return;
1516     case BuiltinOperator::BuiltinOperator_ARG_MAX:
1517       loadArgMinMax(op, subg, true);
1518       return;
1519     case BuiltinOperator::BuiltinOperator_ARG_MIN:
1520       loadArgMinMax(op, subg, false);
1521       return;
1522     case BuiltinOperator::BuiltinOperator_LOG:
1523       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::LOG);
1524       return;
1525     case BuiltinOperator::BuiltinOperator_ROUND:
1526       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::ROUND);
1527       return;
1528     case BuiltinOperator::BuiltinOperator_POW:
1529       loadOperationTo<ir::operation::Pow>(op, subg);
1530       return;
1531     case BuiltinOperator::BuiltinOperator_LOGICAL_NOT:
1532       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::LOGICAL_NOT);
1533       return;
1534     case BuiltinOperator::BuiltinOperator_LOGICAL_AND:
1535       loadElementwiseBinary(op, subg,
1536                             ir::operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_AND);
1537       return;
1538     case BuiltinOperator::BuiltinOperator_LOGICAL_OR:
1539       loadElementwiseBinary(op, subg,
1540                             ir::operation::ElementwiseBinary::ElementwiseBinaryType::LOGICAL_OR);
1541       return;
1542     case BuiltinOperator::BuiltinOperator_FILL:
1543       loadOperationTo<ir::operation::Fill>(op, subg);
1544       return;
1545     case BuiltinOperator::BuiltinOperator_ZEROS_LIKE:
1546       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::ZEROS_LIKE);
1547       return;
1548     case BuiltinOperator::BuiltinOperator_TILE:
1549       loadOperationTo<ir::operation::Tile>(op, subg);
1550       return;
1551     case BuiltinOperator::BuiltinOperator_RANGE:
1552       loadOperationTo<ir::operation::Range>(op, subg);
1553       return;
1554     case BuiltinOperator::BuiltinOperator_BATCH_MATMUL:
1555       loadBatchMatMul(op, subg);
1556       return;
1557     case BuiltinOperator::BuiltinOperator_LOG_SOFTMAX:
1558       loadLogSoftmax(op, subg);
1559       return;
1560     case BuiltinOperator::BuiltinOperator_QUANTIZE:
1561       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::QUANTIZE);
1562       return;
1563     case BuiltinOperator::BuiltinOperator_DEQUANTIZE:
1564       loadElementwiseUnary(op, subg, ir::operation::ElementwiseUnary::Type::DEQUANTIZE);
1565       return;
1566     case BuiltinOperator::BuiltinOperator_SPACE_TO_DEPTH:
1567       loadSpaceToDepth(op, subg);
1568       return;
1569     case BuiltinOperator::BuiltinOperator_L2_NORMALIZATION:
1570       loadOperationTo<ir::operation::L2Normalization>(op, subg);
1571       break;
1572     case BuiltinOperator::BuiltinOperator_LEAKY_RELU:
1573       loadLeakyRelu(op, subg);
1574       return;
1575     case BuiltinOperator::BuiltinOperator_RANK:
1576       loadOperationTo<ir::operation::Rank>(op, subg);
1577       return;
1578     case BuiltinOperator::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM:
1579       loadUnidirectionalSequenceLSTM(op, subg);
1580       return;
1581     case BuiltinOperator::BuiltinOperator_DEPTH_TO_SPACE:
1582       loadDepthToSpace(op, subg);
1583       return;
1584     default:
1585       throw std::runtime_error(
1586         std::string("Unsupported operation: ").append(EnumNameBuiltinOperator(builtin_op)));
1587   }
1588 }
1589
1590 template <typename LoaderDomain> void BaseLoader<LoaderDomain>::loadModel()
1591 {
1592   LoaderDomain::VerifyModelBuffer(*_verifier.get());
1593   _model = LoaderDomain::GetModel(_base);
1594   // Version unused
1595   // const auto version = _model->version();
1596   // Description unused
1597   // const auto *description = _model->description();
1598   // Metabuffer unsued
1599   // const auto *metadata_buffer = _model->metadata_buffer();
1600   // Load subgraphs and map operations on subgraph
1601   const auto domain_subgraphs = _model->subgraphs();
1602   auto subgraphs = std::make_unique<ir::Subgraphs>();
1603   for (uint32_t subgraph_index = 0; subgraph_index < domain_subgraphs->size(); ++subgraph_index)
1604   {
1605     auto subg = loadSubgraph((*_model->subgraphs())[subgraph_index]);
1606     subgraphs->push(ir::SubgraphIndex{subgraph_index}, std::move(subg));
1607   }
1608   _subgraphs = std::move(subgraphs);
1609 }
1610
1611 } // namespace base_loader
1612 } // namespace onert
1613
1614 #endif //__BASE_LOADER_BASE_LOADER_H__