ArmNN
 20.11
TfLiteParser.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "TfLiteParser.hpp"
7 
9 #include <armnn/Descriptors.hpp>
10 #include <armnn/Exceptions.hpp>
11 #include <armnn/Logging.hpp>
12 #include <armnn/Tensor.hpp>
13 #include <armnn/TypesUtils.hpp>
14 #include <armnn/utility/Assert.hpp>
17 
18 // armnnUtils:
19 #include <armnnUtils/Permute.hpp>
20 #include <Filesystem.hpp>
21 
22 #include <ParserHelper.hpp>
23 #include <VerificationHelpers.hpp>
24 
25 // The generated code based on the Tf Lite schema:
26 #include <schema_generated.h>
27 
28 #include <flatbuffers/flexbuffers.h>
29 
30 #include <fmt/format.h>
31 
32 #include <fstream>
33 #include <algorithm>
34 #include <limits>
35 #include <numeric>
36 #include <sstream>
37 
38 #define ARMNN_THROW_PARSE_EXCEPTION(msg) \
39  { \
40  throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
41  << ": " \
42  << CHECK_LOCATION().AsString()).str()); \
43  }
44 
45 using namespace armnn;
47 namespace armnnTfLiteParser
48 {
49 namespace
50 {
51 
52 const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
53 
54 void CheckSubgraph(const TfLiteParser::ModelPtr & model,
55  size_t subgraphIndex,
56  const CheckLocation & location)
57 {
58  if (model.get() == nullptr)
59  {
60  throw ParseException(
61  fmt::format("{} was called with invalid (null) model. "
62  "Possible reason is that the model is not yet loaded and Unpack(ed). "
63  "subgraph:{} at {}",
64  location.m_Function,
65  subgraphIndex,
66  location.FileLine()));
67  }
68  else if (subgraphIndex >= model->subgraphs.size())
69  {
70  throw ParseException(
71  fmt::format("{} was called with an invalid subgraph index. "
72  "subgraph:{} at {}",
73  location.m_Function,
74  subgraphIndex,
75  location.FileLine()));
76  }
77 }
78 
79 #define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
80  CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
81 
82 void CheckModel(const TfLiteParser::ModelPtr & model,
83  size_t subgraphIndex,
84  size_t operatorIndex,
85  const CheckLocation & location)
86 {
87  if (model.get() == nullptr)
88  {
89  throw ParseException(
90  fmt::format("{} was called with invalid (null) model. "
91  "Possible reason is that the model is not yet loaded and Unpack(ed). "
92  "subgraph:{} operator:{} at {}",
93  location.m_Function,
94  subgraphIndex,
95  operatorIndex,
96  location.FileLine()));
97  }
98  else if (subgraphIndex >= model->subgraphs.size())
99  {
100  throw ParseException(
101  fmt::format("{} was called with an invalid subgraph index. "
102  "subgraph:{} operator:{} at {}",
103  location.m_Function,
104  subgraphIndex,
105  operatorIndex,
106  location.FileLine()));
107  }
108  else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
109  operatorIndex != VIRTUAL_OPERATOR_ID)
110  {
111  throw ParseException(
112  fmt::format("{} was called with an invalid operator index. "
113  "subgraph:{} operator:{} at {}",
114  location.m_Function,
115  subgraphIndex,
116  operatorIndex,
117  location.FileLine()));
118  }
119 }
120 
121 #define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
122  CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
123 
124 void CheckTensor(const TfLiteParser::ModelPtr & model,
125  size_t subgraphIndex,
126  size_t tensorIndex,
127  const CheckLocation & location)
128 {
129  // not checking model, because I assume CHECK_MODEL already run
130  // and checked that. An assert would do.
131  ARMNN_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
132 
133  // also subgraph index should be checked by CHECK_MODEL so
134  // I only add an assert here
135  ARMNN_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
136 
137  // the tensor index is the only one to check here
138  if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
139  {
140  throw ParseException(
141  fmt::format("{} was called with an invalid tensor index. "
142  "subgraph:{} tensor:{} at {}",
143  location.m_Function,
144  subgraphIndex,
145  tensorIndex,
146  location.FileLine()));
147  }
148 }
149 
150 #define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
151  CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
152 
153 void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
154  const CheckLocation & location)
155 {
156  if (rawPtr == nullptr)
157  {
158  throw ParseException(
159  fmt::format("{} was called with a null tensor pointer at {}", location.m_Function, location.FileLine()));
160  }
161 }
162 
163 #define CHECK_TENSOR_PTR(TENSOR_PTR) \
164  CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
165 
166 void CheckBuffer(const TfLiteParser::ModelPtr & model,
167  size_t bufferIndex,
168  const CheckLocation & location)
169 {
170  if (model.get() == nullptr)
171  {
172  throw ParseException(
173  fmt::format("{} was called with invalid (null) model. "
174  "Possible reason is that the model is not yet loaded and Unpack(ed). "
175  "buffer:{} at {}",
176  location.m_Function,
177  bufferIndex,
178  location.FileLine()));
179  }
180  else if (bufferIndex >= model->buffers.size())
181  {
182  throw ParseException(
183  fmt::format("{} was called with an invalid buffer index. "
184  "buffer index:{} at {}",
185  location.m_Function,
186  bufferIndex,
187  location.FileLine()));
188  }
189  else if (model->buffers[bufferIndex].get() == nullptr)
190  {
191  throw ParseException(
192  fmt::format("The buffer #{} is null. {}",
193  bufferIndex,
194  location.AsString()));
195  }
196 }
197 
198 #define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
199  CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
200 
201 void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
202  const armnn::TensorInfo & tensorInfo,
203  uint32_t bufferId,
204  const CheckLocation & location)
205 {
206  if (bufferPtr == nullptr)
207  {
208  throw ParseException(
209  fmt::format("BufferPtr is null for buffer:{}. {}",
210  bufferId,
211  location.AsString()));
212  }
213  else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
214  tensorInfo.GetNumBytes() > bufferPtr->data.size())
215  {
216  std::stringstream ss;
217  ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
218  << "For tensor: " << tensorInfo.GetShape()
219  << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
220  << tensorInfo.GetNumElements() << " elements. " << location.AsString();
221  throw ParseException(ss.str());
222  }
223 }
224 
225 #define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
226  CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
227 
228 bool IsActivationSupported(tflite::ActivationFunctionType activationType)
229 {
230  switch(activationType)
231  {
232  case tflite::ActivationFunctionType_NONE:
233  case tflite::ActivationFunctionType_RELU:
234  case tflite::ActivationFunctionType_RELU6:
235  case tflite::ActivationFunctionType_TANH:
236  {
237  return true;
238  }
239  default:
240  {
241  return false;
242  }
243  }
244 }
245 
246 #define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
247  do { \
248  if (IsActivationSupported(OPTION->fused_activation_function) == false) \
249  { \
250  throw ParseException( \
251  fmt::format("TfLite parser doesn't suppport fused activation: " \
252  "{}/{} in {} subgraph:{} operator:{} at {}", \
253  OPTION->fused_activation_function, \
254  tflite::EnumNameActivationFunctionType(\
255  OPTION->fused_activation_function), \
256  __func__, \
257  SUBGRAPH_INDEX, \
258  OPERATOR_INDEX, \
259  CHECK_LOCATION().FileLine())); \
260  } \
261  } while(false)
262 
263 
264 std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
265 {
266  std::vector<unsigned int> result;
267  result.reserve(in.size());
268  for (auto & i : in)
269  {
270  result.push_back(CHECKED_NON_NEGATIVE(i));
271  }
272  return result;
273 }
274 
275 void CalcPadding(uint32_t inputSize,
276  uint32_t filterSize,
277  uint32_t stride,
278  uint32_t dilation,
279  uint32_t& paddingFront,
280  uint32_t& paddingBack,
281  tflite::Padding padding)
282 {
283  paddingFront = 0;
284  paddingBack = 0;
285  if (padding == tflite::Padding_SAME)
286  {
287  uint32_t outputSize = (inputSize + stride - 1) / stride;
288  uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
289  uint32_t temp = (outputSize - 1) * stride + dilatedSize;
290  if (temp > inputSize)
291  {
292  paddingFront = (temp - inputSize) / 2;
293  paddingBack = (temp - inputSize) - paddingFront;
294  }
295  }
296 }
297 
299  const std::vector<unsigned int>& shapes,
300  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3},
301  const bool outputTensor = false)
302 {
303  armnn::DataType type;
304  CHECK_TENSOR_PTR(tensorPtr);
305 
306  switch (tensorPtr->type)
307  {
308  case tflite::TensorType_UINT8:
310  break;
311  case tflite::TensorType_FLOAT32:
313  break;
314  case tflite::TensorType_INT8:
315  if (tensorPtr->quantization->zero_point.size() == 1)
316  {
317  // Per-tensor
319  }
320  else
321  {
322  // Per-channel
324  }
325  break;
326  case tflite::TensorType_INT16:
328  break;
329  case tflite::TensorType_INT32:
331  break;
332  case tflite::TensorType_INT64:
334  break;
335  default:
336  {
337  CheckLocation location = CHECK_LOCATION();
338  throw ParseException(
339  fmt::format("Unsupported data type {} = {} for tensor: {}. {}",
340  tensorPtr->type,
341  tflite::EnumNameTensorType(tensorPtr->type),
342  tensorPtr->name,
343  location.AsString()));
344  }
345  }
346  std::vector<unsigned int> safeShape = shapes;
347  bool isDynamic = false;
348  if (safeShape.size() == 0)
349  {
350  safeShape.push_back(1);
351  if (outputTensor)
352  {
353  isDynamic = true;
354  }
355  }
356 
357  float quantizationScale = 0.0f;
358  int32_t quantizationOffset = 0;
359 
360  if (tensorPtr->quantization.get())
361  {
362  if (tensorPtr->quantization->scale.size() <= 1)
363  {
364  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
365  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
366 
367  if (tensorPtr->quantization->scale.size() == 1)
368  {
369  quantizationScale = tensorPtr->quantization->scale[0];
370  }
371  if (tensorPtr->quantization->zero_point.size() == 1)
372  {
373  // NOTE: we lose precision here when converting from 64 bit to 32
374  // but this is what we support at the moment in ArmNN
375  quantizationOffset = armnn::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
376  }
377 
378  TensorShape tensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()),
379  safeShape.data());
380  if (isDynamic)
381  {
382  tensorShape = TensorShape(1, false);
383  }
384  armnn::TensorInfo result(tensorShape,
385  type,
386  quantizationScale,
387  quantizationOffset);
388  return result;
389  }
390  else
391  {
392  std::vector<float> quantizationScales;
393  std::vector<int32_t> quantizationOffsets;
394 
395  // Scale
396  std::copy(tensorPtr->quantization->scale.begin(),
397  tensorPtr->quantization->scale.end(),
398  std::back_inserter(quantizationScales));
399 
400  // QSymmS8 Per-axis
401  TensorShape tensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()),
402  safeShape.data());
403  if (isDynamic)
404  {
405  tensorShape = TensorShape(1, false);
406  }
407  armnn::TensorInfo result(tensorShape,
408  type,
409  quantizationScales,
410  dimensionMappings[armnn::numeric_cast<unsigned int>(
411  tensorPtr->quantization->quantized_dimension)]);
412  return result;
413  }
414  }
415  else
416  {
417  TensorShape tensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()),
418  safeShape.data());
419  if (isDynamic)
420  {
421  tensorShape = TensorShape(1, false);
422  }
423  armnn::TensorInfo result(tensorShape,
424  type,
425  quantizationScale,
426  quantizationOffset);
427  return result;
428  }
429 }
430 
432  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
433 {
434  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
435  return ToTensorInfo(tensorPtr, dimensions, dimensionMappings);
436 }
437 
439  const bool outputTensor)
440 {
441  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
442  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3};
443  return ToTensorInfo(tensorPtr, dimensions, dimensionMappings, outputTensor);
444 }
445 
446 template<typename T>
447 std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
448 CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
449  TfLiteParser::TensorRawPtr tensorPtr,
450  armnn::TensorInfo& tensorInfo,
452 {
453  IgnoreUnused(tensorPtr);
454  ARMNN_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
455  ARMNN_ASSERT_MSG(bufferPtr != nullptr,
456  fmt::format("Buffer for buffer:{} is null", tensorPtr->buffer).c_str());
457 
458  std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
459 
460  if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
461  {
462  tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
463  armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
464  reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
465  }
466  else
467  {
468  ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
469  }
470 
471  return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
472 }
473 
474 armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
475 {
476  // generate the binding id by shifting the tensor id by 8 bit
477  // and add the subgraph id, which allows 256 subgraphs
478  return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
479 }
480 
481 bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
482 {
483  const unsigned int actualSize = actual.GetNumDimensions();
484  if (actualSize != expected.size())
485  {
486  return false;
487  }
488 
489  for (unsigned int i = 0u; i < actualSize; i++)
490  {
491  if (expected[i] < 0 ||
492  actual[i] != static_cast<unsigned int>(expected[i]))
493  {
494  return false;
495  }
496  }
497 
498  return true;
499 }
500 
501 void CheckMatchingQuantization(const TensorInfo& first,
502  const TensorInfo& second,
503  const std::string& descName,
504  std::string const& firstName,
505  std::string const& secondName)
506 {
507  if (!first.IsQuantized() ||
508  !second.IsQuantized())
509  {
510  // Not a quantized type, ignore the validation
511  return;
512  }
513 
514  DataType firstDataType = first.GetDataType();
515  DataType secondDataType = second.GetDataType();
516 
517  if (firstDataType != secondDataType)
518  {
519  throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
520  " must be of the same quantized type, " +
521  firstName + " is " + GetDataTypeName(firstDataType) + ", " +
522  secondName + " is " + GetDataTypeName(secondDataType));
523  }
524 
525  if (!first.IsTypeSpaceMatch(second))
526  {
527  throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
528  " must have the same quantization space, " +
529  firstName + " has offset " + std::to_string(first.GetQuantizationOffset()) +
530  " and scale " + std::to_string(first.GetQuantizationScale()) + ", " +
531  secondName + " has offset " + std::to_string(second.GetQuantizationOffset()) +
532  " and scale " + std::to_string(second.GetQuantizationScale()));
533  }
534 }
535 
536 } // <anonymous>
537 
538 TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
539 : m_Options(options)
540 , m_Network(nullptr, nullptr)
541 , m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
542 {
543  // register supported operators
544  m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
545  m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
546  m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
547  m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
548  m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
549  m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
550  m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
551  m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
552  m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParser::ParseExp;
553  m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
554  m_ParserFunctions[tflite::BuiltinOperator_HARD_SWISH] = &TfLiteParser::ParseHardSwish;
555  m_ParserFunctions[tflite::BuiltinOperator_LEAKY_RELU] = &TfLiteParser::ParseLeakyRelu;
556  m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
557  m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
558  m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
559  m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
560  m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
561  m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
562  m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
563  m_ParserFunctions[tflite::BuiltinOperator_NEG] = &TfLiteParser::ParseNeg;
564  m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
565  m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
566  m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParser::ParseQuantize;
567  m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
568  m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
569  m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
570  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
571  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
572  m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
573  m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
574  m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
575  m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
576  m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParser::ParseSplitV;
577  m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
578  m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
579  m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
580  m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
581  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
582  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
583  m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
584  m_ParserFunctions[tflite::BuiltinOperator_DIV] = &TfLiteParser::ParseDiv;
585  m_ParserFunctions[tflite::BuiltinOperator_ARG_MAX] = &TfLiteParser::ParseArgMax;
586  // register supported custom operators
587  m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
588 }
589 
590 void TfLiteParser::ResetParser()
591 {
592  m_Network = armnn::INetworkPtr(nullptr, nullptr);
593  m_Model = nullptr;
594  m_SubgraphConnections.clear();
595 }
596 
598 {
599  ResetParser();
600  m_Model = LoadModelFromFile(graphFile);
601  return CreateNetworkFromModel();
602 }
603 
604 INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
605 {
606  ResetParser();
607  m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
608  return CreateNetworkFromModel();
609 }
610 
611 INetworkPtr TfLiteParser::CreateNetworkFromModel()
612 {
613 
614  using NetworkOptions = std::vector<BackendOptions>;
615  NetworkOptions networkOptions = {};
616  if (m_Options && m_Options.value().m_InferAndValidate)
617  {
618  BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod",
619  {
620  { "InferAndValidate", true }
621  });
622 
623  networkOptions.push_back(shapeInferenceMethodOption);
624  }
625 
626  m_Network = INetwork::Create(networkOptions);
627  ARMNN_ASSERT(m_Model.get() != nullptr);
628 
629  if (m_Model->subgraphs.size() != 1)
630  {
631  throw ParseException(
632  fmt::format("Current TfLite parser only supports 1 subgraph. Current one has: {} {}",
633  m_Model->subgraphs.size(),
634  CHECK_LOCATION().AsString()));
635  }
636 
637  size_t subgraphIndex = 0;
638  size_t operatorIndex = 0;
639  try
640  {
641  for (SubgraphPtr const& subgraph : m_Model->subgraphs)
642  {
643  m_SubgraphConnections.emplace_back(subgraph->tensors.size());
644  for (OperatorPtr const& op : subgraph->operators)
645  {
646  auto const& opCodePtr = m_Model->operator_codes[op->opcode_index];
647  auto builtinCode = opCodePtr->builtin_code;
648 
649  if (builtinCode > tflite::BuiltinOperator_MAX)
650  {
651  throw ParseException(fmt::format("Operator code {} is out of range 0-{}. "
652  "subgraph:{} operator idx:{}. {}",
653  builtinCode, tflite::BuiltinOperator_MAX, subgraphIndex,
654  operatorIndex, CHECK_LOCATION().AsString()));
655  }
656 
657  // lookup and call the parser function
658  auto& parserFunction = m_ParserFunctions[builtinCode];
659  (this->*parserFunction)(subgraphIndex, operatorIndex);
660  ++operatorIndex;
661  }
662 
663  SetupInputLayers(subgraphIndex);
664  SetupOutputLayers(subgraphIndex);
665  SetupConstantLayers(subgraphIndex);
666 
667  ++subgraphIndex;
668  operatorIndex = 0;
669  }
670  }
671  catch (const ParseException& e)
672  {
673  std::stringstream errorString;
674  errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
675  << subgraphIndex << " error: " << e.what();
676  ARMNN_LOG(error) << errorString.str();
677  std::stringstream errors;
678  errors << errorString.str() << "\n";
679  throw ParseException(errors.str());
680  }
681 
682  // establish the connections from the layer outputs to the inputs of the subsequent layers
683  for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
684  {
685  for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
686  {
687  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
688  {
689  for (size_t inputSlotIdx = 0;
690  inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
691  ++inputSlotIdx)
692  {
693  m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
694  *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
695  }
696  }
697  }
698  }
699 
700  return std::move(m_Network);
701 }
702 
703 void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
704  size_t tensorIndex,
705  armnn::IOutputSlot* slot)
706 {
707  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
708  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
709  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
710 
711  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
712 
713  // assuming there is only one producer for that tensor
714  if (tensorSlots.outputSlot != nullptr)
715  {
716  throw ParseException(fmt::format("Another layer has already registered itself as the producer of "
717  "subgraph:{} tensor:{} {}",
718  subgraphIndex,
719  tensorIndex,
720  CHECK_LOCATION().AsString()));
721  }
722 
723  tensorSlots.outputSlot = slot;
724 }
725 
726 void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
727  size_t tensorIndex,
728  armnn::IInputSlot* slot)
729 {
730  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
731  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
732  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
733 
734  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
735  tensorSlots.inputSlots.push_back(slot);
736 }
737 
738 void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
739 {
740  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
741 
742  // NOTE: By default we presume the custom operator is not supported
743  auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
744 
745  // Identify custom code defined for custom operator
746  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
747  const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
748 
749  // Find parser function that correspondes to custom code (if any)
750  auto iterator = m_CustomParserFunctions.find(customCode);
751  if (iterator != m_CustomParserFunctions.end())
752  {
753  customParserFunction = iterator->second;
754  }
755 
756  // Run parser function
757  (this->*customParserFunction)(subgraphIndex, operatorIndex);
758 }
759 
760 void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
761 {
762  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
763 
764  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
765 
766  auto opcodeIndex = operatorPtr->opcode_index;
767  auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
768 
769  if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
770  {
771  // Do not add StandInLayer, throw ParseException instead
772  throw ParseException(
773  fmt::format("Operator not supported. "
774  "subgraph:{} operator:{} "
775  "opcode_index:{} opcode:{} / {} {}",
776  subgraphIndex,
777  operatorIndex,
778  opcodeIndex,
779  opcode,
780  tflite::EnumNameBuiltinOperator(opcode),
781  CHECK_LOCATION().AsString()));
782  }
783 
784  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
785  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
786 
787  const unsigned int numInputs = armnn::numeric_cast<unsigned int>(inputs.size());
788  const unsigned int numOutputs = armnn::numeric_cast<unsigned int>(outputs.size());
789 
790  StandInDescriptor descriptor(numInputs, numOutputs);
791  auto layerName = fmt::format("StandIn:{}:{}:{}", subgraphIndex, operatorIndex, opcode);
792 
793  // Add a non-executable StandInLayer as a placeholder for any unsupported operator
794  IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
795  ARMNN_ASSERT(layer != nullptr);
796 
797  for (unsigned int i = 0u; i < numOutputs; ++i)
798  {
799  layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i], true));
800  }
801 
802  auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
803  auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
804 
805  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
806  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
807 }
808 
809 void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
810 {
811  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
812 
813  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
814  const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
815 
816  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
817 
819  desc.m_BiasEnabled = false;
820  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
821  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
823  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
824  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
825 
826  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
827  CHECK_VALID_SIZE(inputs.size(), 2, 3);
828 
829  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
830  CHECK_VALID_SIZE(outputs.size(), 1);
831 
832  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
833  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
834 
835  // assuming input is NHWC
836  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
837  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
838 
839  // assuming the filter is OHWI : Output, H, W, Input
840  // which is essentially the same as NHWC
841  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
842  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
843 
844  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
845  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
846  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
847  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
848 
849  auto filterTensorAndData = CreateConstTensor(inputs[1],
850  filterTensorInfo,
852  armnn::IConnectableLayer* layer = nullptr;
853 
854  auto layerName = fmt::format("Conv2D:{}:{}", subgraphIndex, operatorIndex);
855 
856  if (inputs.size() == 3)
857  {
858  desc.m_BiasEnabled = true;
859  armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
860  auto biasTensorAndData = CreateConstTensor(inputs[2],
861  biasTensorInfo,
863  layer = m_Network->AddConvolution2dLayer(desc,
864  filterTensorAndData.first,
865  Optional<ConstTensor>(biasTensorAndData.first),
866  layerName.c_str());
867  }
868  else
869  {
870  layer = m_Network->AddConvolution2dLayer(desc,
871  filterTensorAndData.first,
872  EmptyOptional(),
873  layerName.c_str());
874  }
875 
876  ARMNN_ASSERT(layer != nullptr);
877 
878  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
879  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
880 
881  // register the input connection slots for the layer, connections are made after all layers have been created
882  // only the tensors for the inputs are relevant, exclude the const tensors
883  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
884  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
885 
886  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
887  // register the output connection slots for the layer, connections are made after all layers have been created
888  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
889  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
890 }
891 
892 void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
893 {
894  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
895 
896  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
897  const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
898 
899  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
900 
902  desc.m_BiasEnabled = false;
903  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
904  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
906  CHECKED_NON_NEGATIVE(options->depth_multiplier);
907 
908  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
909  CHECK_VALID_SIZE(inputs.size(), 2, 3);
910  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
911  CHECK_VALID_SIZE(outputs.size(), 1);
912  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
913  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
914 
915  // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
916  PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
917 
918  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
919  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
920 
921  // Assuming input is NHWC
922  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
923  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
924 
925  // TensorflowLite weights come in the format [1, H, W, I * M]
926  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
927  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
928 
929  // Reshape weights as [ H, W, I, M ]
930  filterTensorInfo.SetShape({ filterHeight,
931  filterWidth,
932  inputTensorInfo.GetShape()[3],
933  filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
934 
935  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
936  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
937  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
938  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
939 
940  auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
941  armnn::IConnectableLayer* layer = nullptr;
942  auto layerName = fmt::format("DepthwiseConv2D:{}:{}", subgraphIndex, operatorIndex);
943 
944  if (inputs.size() == 3)
945  {
946  desc.m_BiasEnabled = true;
947  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
948  auto biasTensorAndData = CreateConstTensor(inputs[2],
949  biasTensorInfo,
951  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
952  filterTensorAndData.first,
953  Optional<ConstTensor>(biasTensorAndData.first),
954  layerName.c_str());
955  }
956  else
957  {
958  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
959  filterTensorAndData.first,
960  EmptyOptional(),
961  layerName.c_str());
962  }
963  ARMNN_ASSERT(layer != nullptr);
964 
965  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
966  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
967 
968  // register the input connection slots for the layer, connections are made after all layers have been created
969  // only the tensors for the inputs are relevant, exclude the const tensors
970  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
971  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
972 
973  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
974  // register the output connection slots for the layer, connections are made after all layers have been created
975  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
976  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
977 }
978 
979 void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
980 {
981  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
982 
983  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
984  CHECK_VALID_SIZE(inputs.size(), 1);
985 
986  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
987  CHECK_VALID_SIZE(outputs.size(), 1);
988 
989  auto layerName = fmt::format("Dequantize:{}:{}", subgraphIndex, operatorIndex);
990 
991  IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
992  ARMNN_ASSERT(layer != nullptr);
993 
994  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
995  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
996 
997  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
998  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
999 
1000  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1001  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1002 }
1003 
1004 void TfLiteParser::ParseExp(size_t subgraphIndex, size_t operatorIndex)
1005 {
1006  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1007 
1008  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1009  CHECK_VALID_SIZE(inputs.size(), 1);
1010 
1011  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1012  CHECK_VALID_SIZE(outputs.size(), 1);
1013 
1014  auto layerName = fmt::format("Exp:{}:{}", subgraphIndex, operatorIndex);
1015 
1017  desc.m_Operation = UnaryOperation::Exp;
1018  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerName.c_str());
1019  ARMNN_ASSERT(layer != nullptr);
1020 
1021  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1022  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1023 
1024  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1025  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1026 
1027  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1028  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1029 }
1030 
1031 void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1032 {
1033  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1034 
1035  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1036  CHECK_VALID_SIZE(inputs.size(), 1, 2);
1037 
1038  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1039  CHECK_VALID_SIZE(outputs.size(), 1);
1040 
1041  auto layerName = fmt::format("Transpose:{}:{}", subgraphIndex, operatorIndex);
1042  TransposeDescriptor desc;
1043 
1044  if (inputs.size() == 2)
1045  {
1046  armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1047  BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1048  auto numPermVecElements = permuteTensorInfo.GetNumElements();
1049  std::vector<unsigned int> permuteShape(numPermVecElements);
1050  ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
1051  PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
1052 
1053  desc = TransposeDescriptor(permutationVector);
1054  }
1055 
1056  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1057  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1058  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1059 
1060  IConnectableLayer* layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
1061  ARMNN_ASSERT(layer != nullptr);
1062  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1063 
1064  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1065  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1066 
1067  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1068  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1069 }
1070 
1071 void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1072 {
1073  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1074 
1075  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1076  const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1077 
1079  desc.m_BiasEnabled = false;
1080  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1081  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1083 
1084  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1085  CHECK_VALID_SIZE(inputs.size(), 3);
1086 
1087  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1088  CHECK_VALID_SIZE(outputs.size(), 1);
1089 
1090  if (inputs[0])
1091  {
1092  armnn::TensorInfo tensorInfo = ToTensorInfo(inputs[0]);
1093  std::vector<int> output_shape(tensorInfo.GetNumElements());
1094  if (tensorInfo.GetDataType() == DataType::Signed32)
1095  {
1096  ::memcpy(output_shape.data(), GetBuffer(m_Model, inputs[0]->buffer)->data.data(), tensorInfo.GetNumBytes());
1097  }
1098  if (tensorInfo.GetDataType() == DataType::QAsymmU8)
1099  {
1100  for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
1101  {
1102  output_shape[i] = GetBuffer(m_Model, inputs[0]->buffer)->data.data()[i];
1103  }
1104  }
1105  // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
1106  for (int dimension : output_shape)
1107  {
1108  desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
1109  }
1110  desc.m_OutputShapeEnabled = true;
1111  }
1112  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
1113  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1114 
1115  // TfLite uses NHWC tensors
1116  const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1117  const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1118 
1119  const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1120  const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1121 
1122  CalcPadding(inputHeight,
1123  filterHeight,
1124  desc.m_StrideY,
1125  1, // DilationY
1126  desc.m_PadTop,
1127  desc.m_PadBottom,
1128  options->padding);
1129 
1130  CalcPadding(inputWidth,
1131  filterWidth,
1132  desc.m_StrideX,
1133  1, // DilationX
1134  desc.m_PadLeft,
1135  desc.m_PadRight,
1136  options->padding);
1137 
1138  auto filterTensorAndData = CreateConstTensor(inputs[1],
1139  filterTensorInfo,
1141 
1142  armnn::IConnectableLayer* layer = nullptr;
1143  auto layerName = fmt::format("TransposeConv:{}:{}", subgraphIndex, operatorIndex);
1144 
1145  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1146  filterTensorAndData.first,
1147  EmptyOptional(),
1148  layerName.c_str());
1149 
1150  ARMNN_ASSERT(layer != nullptr);
1151 
1152  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1153  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1154 
1155  // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1156  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1157  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
1158 
1159  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1160  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1161 }
1162 
1163 void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1164 {
1165  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1166 }
1167 
1168 void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1169 {
1170  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1171 
1172  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1173  CHECK_VALID_SIZE(inputs.size(), 3);
1174 
1175  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1176  CHECK_VALID_SIZE(outputs.size(), 1);
1177 
1178  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1179  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1180 
1181  armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1182  BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1183 
1184  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1185  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1186 
1187  std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1188  ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1189 
1190  size_t step = 2;
1191  std::vector<std::pair<unsigned int, unsigned int>> crops;
1192  for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1193  {
1194  crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1195  }
1196 
1198  desc.m_BlockShape = blockShape;
1199  desc.m_Crops = crops;
1201 
1202  auto layerName = fmt::format("BatchToSpaceND:{}:{}", subgraphIndex, operatorIndex);
1203 
1204  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1205  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1206  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1207 
1208  IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1209  ARMNN_ASSERT(layer != nullptr);
1210  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1211 
1212  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1213  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1214 
1215  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1216  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1217 }
1218 
1219 void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1220 {
1221  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1222 
1223  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1224  CHECK_VALID_SIZE(inputs.size(), 1);
1225 
1226  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1227  CHECK_VALID_SIZE(outputs.size(), 1);
1228 
1231  auto layerName = fmt::format("L2Normalization:{}:{}", subgraphIndex, operatorIndex);
1232  IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1233 
1234  ARMNN_ASSERT(layer != nullptr);
1235 
1236  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1237  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1238 
1239  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1240  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1241 
1242  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1243  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1244 }
1245 
1246 void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1247 {
1248  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1249 }
1250 
1251 void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1252 {
1253  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1254 
1255  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1256  CHECK_VALID_SIZE(inputs.size(), 2);
1257 
1258  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1259  CHECK_VALID_SIZE(outputs.size(), 1);
1260 
1261  auto layerName = fmt::format("Maximum:{}:{}", subgraphIndex, operatorIndex);
1262 
1263  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1264  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1265  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1266 
1267  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1268  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1269 
1270  IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1271  ARMNN_ASSERT(layer != nullptr);
1272  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1273 
1274  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1275  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1276 
1277  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1278  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1279 }
1280 
1281 void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1282 {
1283  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1284 
1285  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1286  CHECK_VALID_SIZE(inputs.size(), 2);
1287 
1288  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1289  CHECK_VALID_SIZE(outputs.size(), 1);
1290 
1291  auto layerName = fmt::format("Minimum:{}:{}", subgraphIndex, operatorIndex);
1292 
1293  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1294  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1295  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1296 
1297  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1298  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1299 
1300  IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1301  ARMNN_ASSERT(layer != nullptr);
1302  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1303 
1304  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1305  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1306 
1307  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1308  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1309 }
1310 
1311 void TfLiteParser::ParsePool(size_t subgraphIndex,
1312  size_t operatorIndex,
1313  PoolingAlgorithm algorithm)
1314 {
1315  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1316 
1317  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1318  const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1319 
1320  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1321 
1322  std::string layerName;
1323 
1324  switch (algorithm)
1325  {
1326  case PoolingAlgorithm::Average:
1327  layerName =
1328  fmt::format("AveragePool2D:{}:{}", subgraphIndex, operatorIndex);
1329  break;
1330  case PoolingAlgorithm::Max:
1331  layerName =
1332  fmt::format("MaxPool2D:{}:{}", subgraphIndex, operatorIndex);
1333  break;
1334  default:
1335  ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1336  }
1337 
1338  Pooling2dDescriptor desc;
1339 
1340  desc.m_PoolType = algorithm;
1341  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1342  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1343  desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1344  desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1345  desc.m_PaddingMethod = PaddingMethod::Exclude;
1346  desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1348 
1349  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1350  CHECK_VALID_SIZE(inputs.size(), 1);
1351  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1352 
1353  // assuming input is NHWC
1354  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1355  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1356 
1357  CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1358  desc.m_PadTop, desc.m_PadBottom, options->padding);
1359  CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1360  desc.m_PadLeft, desc.m_PadRight, options->padding);
1361 
1362  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1363  CHECK_VALID_SIZE(outputs.size(), 1);
1364 
1365  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1366  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1367 
1368  IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1369  ARMNN_ASSERT(layer != nullptr);
1370  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1371 
1372  // register the input connection slots for the layer, connections are made after all layers have been created
1373  // only the tensors for the inputs are relevant, exclude the const tensors
1374  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1375  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1376 
1377  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1378  // register the output connection slots for the layer, connections are made after all layers have been created
1379  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1380  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1381 }
1382 
1383 void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1384 {
1385  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1386 
1387  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1388  CHECK_VALID_SIZE(inputs.size(), 3);
1389  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1390  CHECK_VALID_SIZE(outputs.size(), 1);
1391 
1392  SliceDescriptor desc;
1393 
1394  // set begin tensor info for slice descriptor
1395  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1396  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1397 
1398  std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1399  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1400 
1401  // set size tensor info for slice descriptor
1402  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1403  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1404 
1405  std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1406  ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1407  desc = SliceDescriptor(begin, size);
1408 
1409  auto layerName = fmt::format("Slice:{}:{}", subgraphIndex, operatorIndex);
1410 
1411  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1412  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1413  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1414 
1415  IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1416  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1417 
1418  // register the input connection slots for the layer, connections are made after all layers have been created
1419  // only the tensors for the inputs are relevant, exclude the const tensors
1420  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1421  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1422 
1423  // register the output connection slots for the layer, connections are made after all layers have been created
1424  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1425  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1426 }
1427 
1428 void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1429 {
1430  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1431  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1432  const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1433 
1434  SoftmaxDescriptor desc;
1435  desc.m_Beta = options->beta;
1436 
1437  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1438  CHECK_VALID_SIZE(inputs.size(), 1);
1439  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1440  CHECK_VALID_SIZE(outputs.size(), 1);
1441 
1442  auto layerName = fmt::format("Softmax:{}:{}", subgraphIndex, operatorIndex);
1443  IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1444 
1445  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1446  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1447 
1448  // register the input connection slots for the layer, connections are made after all layers have been created
1449  // only the tensors for the inputs are relevant, exclude the const tensors
1450  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1451  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1452 
1453  // register the output connection slots for the layer, connections are made after all layers have been created
1454  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1455  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1456 }
1457 
1458 void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1459 {
1460  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1461 
1462  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1463  CHECK_VALID_SIZE(inputs.size(), 3);
1464 
1465  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1466  CHECK_VALID_SIZE(outputs.size(), 1);
1467 
1468  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1469  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1470 
1471  armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1472  BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1473 
1474  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1475  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1476 
1477  std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1478  ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1479 
1480  size_t step = 2;
1481  std::vector<std::pair<unsigned int, unsigned int>> padList;
1482  for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1483  {
1484  padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1485  }
1486 
1488  desc.m_BlockShape = blockShape;
1489  desc.m_PadList = padList;
1491 
1492  auto layerName = fmt::format("SpaceToBatchND:{}:{}", subgraphIndex, operatorIndex);
1493 
1494  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1495  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1496  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1497 
1498  IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1499  ARMNN_ASSERT(layer != nullptr);
1500  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1501 
1502  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1503  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1504 
1505  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1506  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1507 }
1508 
1509 armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1510  const armnn::TensorInfo & inputTensorInfo)
1511 {
1512  CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1513  std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1514  static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1515 
1516  if (inputTensorInfo.GetNumDimensions() > 4)
1517  {
1518  std::stringstream ss;
1519  ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1520  << " shape:" << inputTensorInfo.GetShape() << " "
1521  << CHECK_LOCATION().AsString();
1522  throw ParseException(ss.str());
1523  }
1524 
1525  if (squeezeDims.empty())
1526  {
1527  squeezeDims.assign(dimensionSequence,
1528  dimensionSequence+inputTensorInfo.GetNumDimensions());
1529  }
1530 
1531  std::vector<uint32_t> outputDims;
1532  for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1533  {
1534  bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1535  auto currentDimension = inputTensorInfo.GetShape()[i];
1536  if (skipSqueeze || currentDimension != 1)
1537  {
1538  outputDims.push_back(currentDimension);
1539  }
1540  }
1541 
1542  if (outputDims.size() > 4)
1543  {
1544  std::stringstream ss;
1545  ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1546  << " shape:" << inputTensorInfo.GetShape() << " "
1547  << CHECK_LOCATION().AsString();
1548  throw ParseException(ss.str());
1549  }
1550 
1551  TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1552  outputDims.data());
1553 
1554  // we need to preserve the tensor type and the quantization data as well
1555  TensorInfo outTensorInfo = inputTensorInfo;
1556  outTensorInfo.SetShape(outShape);
1557 
1558  return outTensorInfo;
1559 }
1560 
1561 void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1562 {
1563  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1564 
1565  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1566  CHECK_VALID_SIZE(inputs.size(), 1);
1567 
1568  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1569  CHECK_VALID_SIZE(outputs.size(), 1);
1570 
1571  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1572  const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1573  auto layerName = fmt::format("Squeeze:{}:{}", subgraphIndex, operatorIndex);
1574 
1575  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1576  armnn::TensorInfo outputTensorInfo =
1577  TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1578  inputTensorInfo);
1579  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1580 
1581  ReshapeDescriptor reshapeDesc;
1582  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1583 
1584  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1585  ARMNN_ASSERT(layer != nullptr);
1586  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1587 
1588  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1589  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1590 
1591  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1592  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1593 }
1594 
1595 void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1596 {
1597  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1598 
1599  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1600  CHECK_VALID_SIZE(inputs.size(), 4);
1601 
1602  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1603  CHECK_VALID_SIZE(outputs.size(), 1);
1604 
1605  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1606  const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1607 
1609  desc.m_BeginMask = options->begin_mask;
1610  desc.m_EllipsisMask = options->ellipsis_mask;
1611  desc.m_EndMask = options->end_mask;
1612  desc.m_NewAxisMask = options->new_axis_mask;
1613  desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1615 
1616  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1617  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1618 
1619  std::vector<int> begin(beginTensorInfo.GetNumElements());
1620  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1621 
1622  armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1623  BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1624 
1625  std::vector<int> end(endTensorInfo.GetNumElements());
1626  ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1627 
1628  armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1629  BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1630 
1631  std::vector<int> stride(strideTensorInfo.GetNumElements());
1632  ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1633 
1634  desc.m_Begin = begin;
1635  desc.m_End = end;
1636  desc.m_Stride = stride;
1637 
1638  auto layerName = fmt::format("StridedSlice:{}:{}", subgraphIndex, operatorIndex);
1639  IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1640  ARMNN_ASSERT(layer != nullptr);
1641 
1642  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1643  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1644 
1645  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1646  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1647 
1648  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1649  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1650 }
1651 
1652 void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1653 {
1654  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1655 
1656  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1657  const auto * options = operatorPtr->builtin_options.AsSubOptions();
1658 
1659  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1660  CHECK_VALID_SIZE(inputs.size(), 2);
1661 
1662  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1663  CHECK_VALID_SIZE(outputs.size(), 1);
1664 
1665  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1666  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1667 
1668  auto layerName = fmt::format("Sub:{}:{}", subgraphIndex, operatorIndex);
1669  IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1670  ARMNN_ASSERT(layer != nullptr);
1671 
1672  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1673  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1674 
1675  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1676  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1677 
1678  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1679 
1680  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1681  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1682 }
1683 
1684 void TfLiteParser::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
1685 {
1686  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1687 
1688  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1689  const auto * options = operatorPtr->builtin_options.AsDivOptions();
1690 
1691  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1692  CHECK_VALID_SIZE(inputs.size(), 2);
1693 
1694  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1695  CHECK_VALID_SIZE(outputs.size(), 1);
1696 
1697  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1698  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1699 
1700  auto layerName = fmt::format("Div:{}:{}", subgraphIndex, operatorIndex);
1701  IConnectableLayer* layer = m_Network->AddDivisionLayer(layerName.c_str());
1702  ARMNN_ASSERT(layer != nullptr);
1703 
1704  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1705  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1706 
1707  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1708  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1709  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1710 
1711  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1712  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1713 }
1714 
1715 void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1716 {
1717  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1718 
1719  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1720  const auto * options = operatorPtr->builtin_options.AsAddOptions();
1721 
1722  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1723  CHECK_VALID_SIZE(inputs.size(), 2);
1724 
1725  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1726  CHECK_VALID_SIZE(outputs.size(), 1);
1727 
1728  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1729  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1730 
1731  auto layerName = fmt::format("Add:{}:{}", subgraphIndex, operatorIndex);
1732  IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1733  ARMNN_ASSERT(layer != nullptr);
1734 
1735  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1736  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1737 
1738  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1739  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1740  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1741 
1742  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1743  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1744 }
1745 
1746 void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1747 {
1748  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1749 
1750  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1751  const auto * options = operatorPtr->builtin_options.AsMulOptions();
1752 
1753  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1754  CHECK_VALID_SIZE(inputs.size(), 2);
1755 
1756  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1757  CHECK_VALID_SIZE(outputs.size(), 1);
1758 
1759  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1760  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1761 
1762  auto layerName = fmt::format("Mul:{}:{}", subgraphIndex, operatorIndex);
1763  IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1764  ARMNN_ASSERT(layer != nullptr);
1765 
1766  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1767  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1768 
1769  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1770  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1771  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1772 
1773  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1774  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1775 }
1776 
1777 void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1778 {
1779  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1780 
1781  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1782 
1783  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1784  CHECK_VALID_SIZE(outputs.size(), 1);
1785 
1786  armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1787  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1788 
1789  armnn::MeanDescriptor desc;
1790  std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1791  ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1792  desc.m_Axis = axis;
1793 
1794  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1795  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1796 
1797  desc.m_KeepDims =
1798  inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1799  true : false;
1800 
1801  auto layerName = fmt::format("Mean:{}:{}", subgraphIndex, operatorIndex);
1802  IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1803  ARMNN_ASSERT(layer != nullptr);
1804 
1805  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1806 
1807  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1808  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1809 
1810  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1811  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1812 }
1813 
1814 void TfLiteParser::ParseNeg(size_t subgraphIndex, size_t operatorIndex)
1815 {
1816  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1817 
1818  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1819  CHECK_VALID_SIZE(inputs.size(), 1);
1820 
1821  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1822  CHECK_VALID_SIZE(outputs.size(), 1);
1823 
1824  auto layerName = fmt::format("Neg:{}:{}", subgraphIndex, operatorIndex);
1826  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(descriptor, layerName.c_str());
1827  ARMNN_ASSERT(layer != nullptr);
1828 
1829  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1830  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1831 
1832  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1833  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1834 
1835  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1836  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1837 }
1838 
1839 void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1840 {
1841  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1842 
1843  TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1844 
1845  TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1846  CHECK_VALID_SIZE(outputs.size(), 1);
1847 
1848  armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1849  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1850 
1851  std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1852  ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1853 
1854  size_t step = 2;
1855  armnn::PadDescriptor desc;
1856  for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1857  {
1858  desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1859  }
1860 
1861  auto layerName = fmt::format("Pad:{}:{}", subgraphIndex, operatorIndex);
1862  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1863 
1864  IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1865  ARMNN_ASSERT(layer != nullptr);
1866  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1867 
1868  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1869  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1870 
1871  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1872  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1873 }
1874 
1875 void TfLiteParser::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
1876 {
1877  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1878 
1879  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1880  CHECK_VALID_SIZE(inputs.size(), 1);
1881 
1882  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1883  CHECK_VALID_SIZE(outputs.size(), 1);
1884 
1885  auto layerName = fmt::format("Quantize:{}:{}", subgraphIndex, operatorIndex);
1886 
1887  IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
1888  ARMNN_ASSERT(layer != nullptr);
1889 
1890  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1891  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1892 
1893  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1894  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1895 
1896  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1897  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1898 }
1899 
1900 void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1901 {
1902  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
1903 }
1904 
1905 void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1906 {
1907  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1908 }
1909 
1910 void TfLiteParser::ParseLeakyRelu(size_t subgraphIndex, size_t operatorIndex)
1911 {
1912  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::LeakyReLu);
1913 }
1914 
1915 void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1916 {
1917  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1918 }
1919 
1920 void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1921 {
1922  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1923 }
1924 
1925 void TfLiteParser::ParseHardSwish(size_t subgraphIndex, size_t operatorIndex)
1926 {
1927  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::HardSwish);
1928 }
1929 
1930 void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1931 {
1932  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1933  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1934  IgnoreUnused(operatorPtr);
1935 
1936  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1937  CHECK_VALID_SIZE(inputs.size(), 1);
1938 
1939  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1940  CHECK_VALID_SIZE(outputs.size(), 1);
1941 
1942  auto layerName = fmt::format("Activation:");
1943  ActivationDescriptor activationDesc;
1944  activationDesc.m_Function = activationType;
1945 
1946  switch (activationType)
1947  {
1948  case ActivationFunction::ReLu:
1949  {
1950  layerName += fmt::format("RELU:{}:{}", subgraphIndex, operatorIndex);
1951  break;
1952  }
1953  case ActivationFunction::BoundedReLu:
1954  {
1955  layerName += fmt::format("RELU6:{}:{}", subgraphIndex, operatorIndex);
1956  activationDesc.m_A = 6.0f;
1957  activationDesc.m_B = 0.0f;
1958  break;
1959  }
1960  case ActivationFunction::Sigmoid:
1961  {
1962  layerName += fmt::format("SIGMOID:{}:{}", subgraphIndex, operatorIndex);
1963  break;
1964  }
1965  case ActivationFunction::TanH:
1966  {
1967  layerName += fmt::format("TANH:{}:{}", subgraphIndex, operatorIndex);
1968  activationDesc.m_A = 1.0f;
1969  activationDesc.m_B = 1.0f;
1970  break;
1971  }
1972  case ActivationFunction::LeakyReLu:
1973  {
1974  layerName += fmt::format("LEAKYRELU:{}:{}", subgraphIndex, operatorIndex);
1975  const auto * options = operatorPtr->builtin_options.AsLeakyReluOptions();
1976  activationDesc.m_A = options->alpha;
1977  break;
1978  }
1979  case ActivationFunction::HardSwish:
1980  layerName += fmt::format("HARDSWISH:{}:{}", subgraphIndex, operatorIndex);
1981  break;
1982  default:
1983  {
1984  throw ParseException(
1985  fmt::format("Unexpected ActivationFunction[{}] when creating layerName {} ",
1986  static_cast<int>(activationType), CHECK_LOCATION().AsString()));
1987  }
1988  }
1989 
1990  IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1991 
1992  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1993  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1994 
1995  // register the input connection slots for the layer, connections are made after all layers have been created
1996  // only the tensors for the inputs are relevant, exclude the const tensors
1997  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1998  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1999 
2000  // register the output connection slots for the layer, connections are made after all layers have been created
2001  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2002  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2003 }
2005  const std::vector<int32_t> & targetDimsIn)
2006 {
2007  std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
2008  const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
2009 
2010  if (stretchDim != targetDimsIn.end())
2011  {
2012  if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
2013  {
2014  throw ParseException(
2015  fmt::format("At most one component of shape can be -1 {}", CHECK_LOCATION().AsString()));
2016  }
2017 
2018  auto targetNumElements =
2019  armnn::numeric_cast<unsigned int>(
2020  std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
2021 
2022  auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
2023  outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
2024  }
2025 
2026  TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
2027 
2028  TensorInfo reshapeInfo = inputTensorInfo;
2029  reshapeInfo.SetShape(outputShape);
2030 
2031  return reshapeInfo;
2032 }
2033 
2034 void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
2035 {
2036  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2037 
2038  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2039 
2040  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2041  CHECK_VALID_SIZE(outputs.size(), 1);
2042 
2043  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2044  const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
2045  auto layerName = fmt::format("Reshape:{}:{}", subgraphIndex, operatorIndex);
2046 
2047  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2048  armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
2049  CheckMatchingQuantization(inputTensorInfo, actualOutputTensorInfo, layerName, "Input 0", "Output 0");
2050 
2051  // Extracting new shape for the output
2052  // There are two ways it can be passed
2053  // * First is to define the target shape in the operator built-in options
2054  // * Second is to pass it as a second input tensor
2055  std::vector<int32_t> targetShape;
2056  bool targetShapeFound = false;
2057  // Check if built-in options were given
2058  if (options != nullptr)
2059  {
2060  // make sure the parameter is given
2061  if (options->new_shape.empty() == false)
2062  {
2063  targetShape = options->new_shape;
2064  targetShapeFound = true;
2065  }
2066  }
2067 
2068  // If there is no built-in option given or if the built-in new_shape parameter was empty
2069  if (!targetShapeFound)
2070  {
2071  // Check for a second input tensor
2072  if (inputs.size() > 1 && inputs[1] != nullptr)
2073  {
2074  if (inputs[1]->is_variable)
2075  {
2076  ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
2077  }
2078 
2079  if (inputs[1]->shape.size() != 1)
2080  {
2081  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
2082  }
2083 
2084  if (inputs[1]->type != tflite::TensorType_INT32)
2085  {
2086  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
2087  }
2088 
2089  // Extract target shape from input
2090  auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2091  auto values = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
2092  for (int i=0; i < inputs[1]->shape[0]; ++i)
2093  {
2094  targetShape.push_back(values[i]);
2095  }
2096  }
2097  else
2098  {
2099  ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2100  "At least one method required");
2101  }
2102  }
2103 
2104  armnn::TensorInfo reshapeOutputTensorInfo =
2105  TfLiteParser::OutputShapeOfReshape(inputTensorInfo, targetShape);
2106 
2107  // Check for valid input size and that reshape parameters equal output shape
2108  const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2109  if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
2110  {
2111  std::stringstream ss;
2112  ss << "New shape defined in reshape parameters "
2113  << reshapeOutputTensorShape
2114  << " does not equal output shape "
2115  << actualOutputTensorInfo.GetShape()
2116  << ": "
2117  << CHECK_LOCATION().AsString();
2118  throw ParseException(ss.str());
2119  }
2120 
2121  ReshapeDescriptor reshapeDesc;
2122  reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
2123 
2124  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
2125  ARMNN_ASSERT(layer != nullptr);
2126  layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
2127 
2128  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2129  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2130 
2131  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2132  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2133 }
2134 
2135 void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2136 {
2137  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2138 }
2139 
2140 void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2141 {
2142  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2143 }
2144 
2145 void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2146 {
2147  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2148 
2149  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2150  CHECK_VALID_SIZE(inputs.size(), 2);
2151 
2152  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2153  CHECK_VALID_SIZE(outputs.size(), 1);
2154 
2155  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2156 
2157  // Data for the parsed tensor args (size) must be stored locally.
2158  std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2159 
2160  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2161  ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2162 
2163  ResizeDescriptor desc;
2164  desc.m_Method = resizeMethod;
2165  desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
2166  desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2167  desc.m_DataLayout = armnn::DataLayout::NHWC;
2168 
2169  auto layerName = fmt::format("Resize:");
2170 
2171  switch (resizeMethod)
2172  {
2173  case ResizeMethod::Bilinear:
2174  {
2175  layerName += fmt::format("BILINEAR:{}:{}", subgraphIndex, operatorIndex);
2176 
2177  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2178  const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2179 
2180  desc.m_AlignCorners = options->align_corners;
2181  break;
2182  }
2183  case ResizeMethod::NearestNeighbor:
2184  {
2185  layerName += fmt::format("NEARESTNEIGHBOR:{}:{}", subgraphIndex, operatorIndex);
2186  break;
2187  }
2188  default:
2189  {
2190  throw ParseException(
2191  fmt::format("Unexpected ResizeMethod[{}] when creating layerName {} ",
2192  static_cast<int>(resizeMethod), CHECK_LOCATION().AsString()));
2193  }
2194  }
2195 
2196  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2197  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2198  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2199 
2200  IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
2201  ARMNN_ASSERT(layer != nullptr);
2202  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2203 
2204  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2205  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2206 
2207  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2208  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2209 }
2210 
2211 void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2212 {
2213  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2214 
2215  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2216  const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2217 
2218  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2219 
2220  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2221  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2222  CHECK_VALID_SIZE(outputs.size(), 1);
2223 
2224  unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2225  uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
2226 
2227  const unsigned int concatDimInput = static_cast<unsigned int>(
2228  (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
2229 
2230  OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2231  concatDescriptor.SetConcatAxis(concatDimInput);
2232 
2233  unsigned int mergeDimOrigin = 0;
2234 
2235  for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2236  {
2237  TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2238 
2239  // This set up concatDescriptor view origin
2241  inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
2242  }
2243 
2244  auto layerName = fmt::format("Concatenation:{}:{}", subgraphIndex, operatorIndex);
2245  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2246 
2247  IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
2248  ARMNN_ASSERT(layer != nullptr);
2249  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2250 
2251  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2252  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2253 
2254  // add fused activation layer
2255  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2256 
2257  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2258  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2259 }
2260 
2261 void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2262 {
2263  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2264 
2265  const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2266  const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2267 
2268  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2269 
2271  desc.m_BiasEnabled = false;
2272  desc.m_TransposeWeightMatrix = true;
2273 
2274  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2275  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2276  CHECK_VALID_SIZE(outputs.size(), 1);
2277 
2278  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2279 
2280  // Fully Connected Layer accepts two dimensional weights input
2281  int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2282  if (weightsDimension != 2)
2283  {
2284  throw ParseException(
2285  fmt::format("Dimension {} for Fully Connected weights is not supported by Armnn. "
2286  "Node {}",
2287  weightsDimension,
2288  CHECK_LOCATION().AsString()));
2289  }
2290 
2291  auto filterTensorAndData = CreateConstTensor(inputs[1],
2292  filterTensorInfo,
2294  armnn::IConnectableLayer* layer = nullptr;
2295  auto layerName = fmt::format("FullyConnected:{}:{}", subgraphIndex, operatorIndex);
2296 
2297  if (inputs.size() == 3)
2298  {
2299  desc.m_BiasEnabled = true;
2300  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
2301  auto biasTensorAndData = CreateConstTensor(inputs[2],
2302  biasTensorInfo,
2304  layer = m_Network->AddFullyConnectedLayer(desc,
2305  filterTensorAndData.first,
2306  Optional<ConstTensor>(biasTensorAndData.first),
2307  layerName.c_str());
2308  }
2309  else
2310  {
2311  layer = m_Network->AddFullyConnectedLayer(desc,
2312  filterTensorAndData.first,
2313  EmptyOptional(),
2314  layerName.c_str());
2315  }
2316  ARMNN_ASSERT(layer != nullptr);
2317 
2318  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2319 
2320  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2321 
2322  if (inputTensorInfo.GetNumDimensions() > 2)
2323  {
2324  // Add reshape to flatten to 2D [batch_size, input_size],
2325  // where "input_size" corresponds to the number of inputs to the layer,
2326  // matching the second dimension of weights,
2327  // and "batch_size" is calculated by dividing the number of elements by "input_size".
2328  std::vector<unsigned int> reshapedDimensions(2);
2329  reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2330  reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2331 
2332  if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2333  {
2334  throw ParseException(
2335  fmt::format("Failed to deduce input tensor shape from filter size {} {}",
2336  reshapedDimensions[1],
2337  CHECK_LOCATION().AsString()));
2338  }
2339 
2340  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2341  reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2342 
2343  std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
2345  desc.m_TargetShape = reshapedTensorInfo.GetShape();
2346  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2347 
2348  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2349  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2350 
2351  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2352  }
2353  else
2354  {
2355  // register the input connection slot for the layer
2356  // only the tensors for the inputs are relevant, exclude the const tensors
2357  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2358  }
2359 
2360  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2361  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2362 
2363  // we need to add the activation layer and fortunately we don't need to care about the data layout
2364  armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2365  options->fused_activation_function);
2366 
2367  // register the output connection slots for the layer, connections are made after all layers have been created
2368  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2369  RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2370 }
2371 
2372 void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2373 {
2374  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2375 
2376  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2377 
2378  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2379  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2380  CHECK_VALID_SIZE(outputs.size(), 4);
2381 
2382  // Obtain custom options from flexbuffers
2383  auto custom_options = operatorPtr->custom_options;
2384  const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2385 
2386  // Obtain descriptor information from tf lite
2388  desc.m_MaxDetections = m["max_detections"].AsUInt32();
2389  desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2390  desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2391  desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2392  desc.m_NumClasses = m["num_classes"].AsUInt32();
2393  desc.m_ScaleH = m["h_scale"].AsFloat();
2394  desc.m_ScaleW = m["w_scale"].AsFloat();
2395  desc.m_ScaleX = m["x_scale"].AsFloat();
2396  desc.m_ScaleY = m["y_scale"].AsFloat();
2397 
2398  if (!(m["use_regular_nms"].IsNull()))
2399  {
2400  desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
2401  }
2402  if (!(m["detections_per_class"].IsNull()))
2403  {
2404  desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2405  }
2406 
2407  if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2408  {
2409  throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2410  "must be positive and less than or equal to 1.");
2411  }
2412 
2413  armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2414  auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2416 
2417  auto layerName = fmt::format("DetectionPostProcess:{}:{}", subgraphIndex, operatorIndex);
2418  IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2419  layerName.c_str());
2420 
2421  ARMNN_ASSERT(layer != nullptr);
2422 
2423  // The model does not specify the output shapes.
2424  // The output shapes are calculated from the max_detection and max_classes_per_detection.
2425  unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2426  m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2427  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2428  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2429  m_OverridenOutputShapes.push_back({ 1 });
2430 
2431  for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2432  {
2433  armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
2434  layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2435  }
2436 
2437  // Register the input connection slots for the layer, connections are made after all layers have been created
2438  // only the tensors for the inputs are relevant, exclude the const tensors
2439  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2440  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2441 
2442  // Register the output connection slots for the layer, connections are made after all layers have been created
2443  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2444  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2445  outputTensorIndexes[1],
2446  outputTensorIndexes[2],
2447  outputTensorIndexes[3]});
2448 }
2449 
2450 /// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2451 void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2452 {
2453  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2454 
2455  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2456  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2457  CHECK_VALID_SIZE(outputs.size(), 1);
2458 
2459  if (inputs.size() < 1)
2460  {
2461  throw ParseException("Pack must have at least one input.");
2462  }
2463 
2464  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2465  const auto* options = operatorPtr->builtin_options.AsPackOptions();
2466 
2467  StackDescriptor desc;
2468  desc.m_Axis = static_cast<uint32_t>(options->axis);
2469  desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2470 
2471  // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2472  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2473  desc.m_InputShape = inputTensorInfo.GetShape();
2474 
2475  auto layerName = fmt::format("Pack:{}:{}", subgraphIndex, operatorIndex);
2476  IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2477 
2478  ARMNN_ASSERT(layer != nullptr);
2479 
2480  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2481  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2482 
2483  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2484  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2485 
2486  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2487  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2488 }
2489 
2490 void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2491 {
2492  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2493 
2494  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2495  const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2496 
2497  // This unpackAxis indicates the axis to unpack
2498  const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2499 
2500  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2501  CHECK_VALID_SIZE(inputs.size(), 1);
2502 
2503  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2504 
2505  if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2506  {
2507  throw ParseException(
2508  fmt::format("The unpack axis: {} cannot be greater than or equal to "
2509  "the number of input dimension {} {}",
2510  unpackAxis,
2511  inputTensorInfo.GetNumDimensions(),
2512  CHECK_LOCATION().AsString()));
2513  }
2514 
2515  unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2516  // If num is not defined, automatically infer from the length of the dimension axis.
2517  if(unpackNum == 0)
2518  {
2519  unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2520  }
2521 
2522  // If unpack number cannot be inferred and is still zero, throw ParseException.
2523  if(unpackNum == 0)
2524  {
2525  throw ParseException("Number to unpack must greater than zero.");
2526  }
2527 
2528  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2529  CHECK_VALID_SIZE(outputs.size(), unpackNum);
2530 
2531  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2532  std::vector<unsigned int> unpackDimSizes(inputDimSize);
2533 
2534  // Add current input shape to unpackDimSizes
2535  for (unsigned int i = 0; i < inputDimSize; ++i)
2536  {
2537  unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2538  }
2539 
2540  if (unpackDimSizes[unpackAxis] != unpackNum)
2541  {
2542  throw ParseException("Number to unpack must be the same as length of the dimension to "
2543  "unpack along.");
2544  }
2545 
2546  unpackDimSizes[unpackAxis] /= unpackNum;
2547 
2548  SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2549  for (unsigned int j = 0; j < unpackNum; ++j)
2550  {
2551  // Set the size of the views.
2552  for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2553  {
2554  splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2555  }
2556  splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2557  }
2558 
2559  auto layerName = fmt::format("Unpack:{}:{}", subgraphIndex, operatorIndex);
2560  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2561  ARMNN_ASSERT(layer != nullptr);
2562 
2563  TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2564  unpackDimSizes.data());
2565 
2566  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2567  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2568 
2569  // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2570  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2571  {
2572  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k], true);
2573  std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
2575  desc.m_TargetShape = outputTensorInfo.GetShape();
2576  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2577 
2578  layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2579  outputTensorInfo.GetDataType(),
2580  outputTensorInfo.GetQuantizationScale(),
2581  outputTensorInfo.GetQuantizationOffset()));
2582  layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2583 
2584  reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2585 
2586  uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2587  armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2588  RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2589  }
2590 }
2591 
2592 void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2593 {
2594  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2595 
2596  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2597  const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2598 
2599  const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2600 
2601  // If number of splits cannot be inferred and is zero, throw ParseException.
2602  if(numSplits == 0)
2603  {
2604  throw ParseException("Number to splits must greater than zero.");
2605  }
2606 
2607  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2608  CHECK_VALID_SIZE(inputs.size(), 2);
2609  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2610  CHECK_VALID_SIZE(outputs.size(), numSplits);
2611 
2612  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2613  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
2614 
2615  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2616  std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2617  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2618 
2619  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2620  const unsigned int splitDim = axisData[0];
2621 
2622  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2623  if (inputDimSize > MaxNumOfTensorDimensions)
2624  {
2625  throw ParseException(
2626  fmt::format("The number of dimensions: {} for input tensors of the split op cannot be greater than {} {}",
2627  inputTensorInfo.GetNumDimensions(),
2629  CHECK_LOCATION().AsString()));
2630  }
2631 
2632  std::vector<unsigned int> splitterDimSizes(inputDimSize);
2633 
2634  // Add current input shape to splitterDimSizes
2635  for (unsigned int i = 0; i < inputDimSize; ++i)
2636  {
2637  splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2638  }
2639 
2640  if (splitterDimSizes[splitDim] % numSplits != 0)
2641  {
2642  throw ParseException("Number of splits must evenly divide the dimension");
2643  }
2644  splitterDimSizes[splitDim] /= numSplits;
2645 
2646  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2647  for (unsigned int j = 0; j < numSplits; ++j)
2648  {
2649  // Set the size of the views.
2650  for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2651  {
2652  splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2653  }
2654  splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2655  }
2656 
2657  auto layerName = fmt::format("Split:{}:{}", subgraphIndex, operatorIndex);
2658  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2659  ARMNN_ASSERT(layer != nullptr);
2660 
2661  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2662  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
2663 
2664  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2665  {
2666  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
2667  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2668  }
2669 
2670  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2671  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2672 }
2673 
2674 unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
2675 {
2676  int numDims = armnn::numeric_cast<int>(numDimsIn);
2677  int v = idx < 0 ? numDims + idx : idx;
2678  ARMNN_ASSERT(v >= 0);
2679  ARMNN_ASSERT(v < numDims);
2680 
2681  return static_cast<unsigned int>(v);
2682 }
2683 
2684 void TfLiteParser::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
2685 {
2686  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2687 
2688  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2689  const auto * options = operatorPtr->builtin_options.AsSplitVOptions();
2690 
2691  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2692  CHECK_VALID_SIZE(inputs.size(), 3);
2693 
2694  auto& inputTensor = inputs[0];
2695  auto& splitsTensor = inputs[1];
2696  auto& axisTensor = inputs[2];
2697 
2698  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
2699  armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
2700  armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
2701  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2702 
2703  // Inputs
2704  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2705  if (inputDimSize > MaxNumOfTensorDimensions)
2706  {
2707  throw ParseException(
2708  fmt::format("The number of dimensions: {} for input tensors of the "
2709  "SplitV op cannot be greater than {} {}",
2710  inputTensorInfo.GetNumDimensions(),
2712  CHECK_LOCATION().AsString()));
2713  }
2714 
2715  // Get split axis
2716  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
2717  std::vector<int> axisData(axisTensorInfo.GetNumElements());
2718  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2719  const unsigned int splitDim = ComputeWrappedIndex(axisData[0], inputTensorInfo.GetNumDimensions());
2720 
2721  // Set split sizes
2722  CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
2723  unsigned int numSplits{0};
2724 
2725  if(options)
2726  {
2727  numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2728  }
2729  else
2730  {
2731  numSplits = splitsInfo.GetNumElements();
2732  }
2733 
2734  if (numSplits <=0)
2735  {
2736  throw ParseException("SplitV has invalid number of splits");
2737  }
2738 
2739  std::vector<int> splitsData(numSplits);
2740  BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
2741  ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
2742 
2743  unsigned int idx = 0;
2744  int numInferred{0};
2745  unsigned int inferIdx{0};
2746  int splitSum{0};
2747  for (auto split : splitsData)
2748  {
2749  if (split < 0)
2750  {
2751  numInferred++;
2752  inferIdx = idx;
2753  }
2754  else
2755  {
2756  splitSum += split;
2757  }
2758  idx++;
2759  }
2760  // Check for inferred Axis
2761  if (numInferred == 0)
2762  {
2763  if (splitSum != armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]))
2764  {
2765  throw ParseException("SplitV split_sizes does not sum to the dimension of value along split_dim.");
2766  }
2767  }
2768  else if (numInferred == 1)
2769  {
2770  splitsData[inferIdx] = armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - splitSum;
2771  }
2772  else
2773  {
2774  throw ParseException("Cannot infer split size for more than one split");
2775  }
2776 
2777  //Ouput size validation
2778  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2779  CHECK_VALID_SIZE(outputs.size(), numSplits);
2780 
2781  // Setup Armnn descriptor
2782  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2783  unsigned int accumSplit = 0;
2784  for (unsigned int j = 0; j < numSplits; ++j)
2785  {
2786  unsigned int splitSize = armnn::numeric_cast<unsigned int>(splitsData[j]);
2787 
2788  // Set the size of the views.
2789  for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
2790  {
2791  unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
2792  if (dimIdx == splitDim)
2793  {
2794  dimSize = splitSize;
2795  }
2796  splitDesc.SetViewSize(j, dimIdx, dimSize);
2797  }
2798 
2799  splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
2800  accumSplit += splitSize;
2801  }
2802 
2803  auto layerName = fmt::format("SplitV:{}:{}", subgraphIndex, operatorIndex);
2804  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2805  ARMNN_ASSERT(layer != nullptr);
2806 
2807  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2808  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2809 
2810  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2811  {
2812  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
2813  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2814  }
2815 
2816  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2817  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2818 }
2819 
2820 void TfLiteParser::ParseArgMax(size_t subgraphIndex, size_t operatorIndex)
2821 {
2822  const auto &operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2823  const auto *options = operatorPtr->builtin_options.AsArgMaxOptions();
2824 
2825  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2826  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2827  CHECK_VALID_SIZE(inputs.size(), 2);
2828 
2829  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2830  CHECK_VALID_SIZE(outputs.size(), 1);
2831 
2832  auto layerName = fmt::format("ArgMax:{}:{}", subgraphIndex, operatorIndex);
2833 
2834  armnn::TensorInfo sizeTensorInfo0 = ToTensorInfo(inputs[0]);
2835  armnn::TensorInfo sizeTensorInfo1 = ToTensorInfo(inputs[1]);
2836 
2837  // Get const axis value from model and set it to descriptor.
2838  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2839 
2840  ArgMinMaxDescriptor desc;
2841  desc.m_Axis = axisBufferPtr->data.data()[0];
2842  // If output_type is int32 then set Signed32 else Signed64. Default type is Signed64.
2843  desc.m_Output_Type = options->output_type == 3 ? armnn::DataType::Signed32 : armnn::DataType::Signed64;
2844  desc.m_Function = ArgMinMaxFunction::Max;
2845 
2846  // Register a ArgMax layer.
2847  IConnectableLayer *layer = m_Network->AddArgMinMaxLayer(desc, layerName.c_str());
2848 
2849  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2850  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2851 
2852  // Register input tensor to the layer.
2853  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2854  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2855 
2856  // Register output tensor to the layer.
2857  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2858  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2859 }
2860 
2861 armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2862  unsigned int outputSlot,
2863  tflite::ActivationFunctionType activationType)
2864 {
2865  ActivationDescriptor activationDesc;
2866  std::string layerName = prevLayer->GetName();
2867 
2868  switch(activationType)
2869  {
2870  case tflite::ActivationFunctionType_NONE:
2871  {
2872  // this is a no-op: return previous layer
2873  return prevLayer;
2874  }
2875  case tflite::ActivationFunctionType_RELU:
2876  {
2877  activationDesc.m_Function = ActivationFunction::ReLu;
2878  layerName += ":RELU";
2879  break;
2880  }
2881  case tflite::ActivationFunctionType_RELU6:
2882  {
2883  activationDesc.m_Function = ActivationFunction::BoundedReLu;
2884  activationDesc.m_A = 6.0f;
2885  activationDesc.m_B = 0.0f;
2886  layerName += ":RELU6";
2887  break;
2888  }
2889  case tflite::ActivationFunctionType_TANH:
2890  {
2891  activationDesc.m_Function = ActivationFunction::TanH;
2892  activationDesc.m_A = 1.0f;
2893  activationDesc.m_B = 1.0f;
2894  layerName += ":TANH";
2895  break;
2896  }
2897 
2898  // I only put these here as a reminder what others we could support
2899  case tflite::ActivationFunctionType_RELU_N1_TO_1:
2900  case tflite::ActivationFunctionType_SIGN_BIT:
2901  default:
2902  {
2903  throw ParseException(
2904  fmt::format("TfLite parser doesn't suppport fused activation: "
2905  "{}/{} {} ",
2906  activationType,
2907  tflite::EnumNameActivationFunctionType(activationType),
2908  CHECK_LOCATION().AsString()));
2909 
2910  }
2911  }
2912 
2913  IConnectableLayer* activationLayer =
2914  m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2915 
2916  auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2917  prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2918  activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2919  return activationLayer;
2920 }
2921 
2923 {
2924  if (fileName == nullptr)
2925  {
2926  throw InvalidArgumentException(fmt::format("Invalid (null) file name {}",
2927  CHECK_LOCATION().AsString()));
2928  }
2929  std::error_code errorCode;
2930  fs::path pathToFile(fileName);
2931  if (!fs::exists(pathToFile, errorCode))
2932  {
2933  //fmt::format() could not be used here (format error)
2934  std::stringstream msg;
2935  msg << "Cannot find the file (" << fileName << ") errorCode: " << errorCode
2936  << " " << CHECK_LOCATION().AsString();
2937 
2938  throw FileNotFoundException(msg.str());
2939  }
2940  std::ifstream file(fileName, std::ios::binary);
2941  std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2942  return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2943  fileContent.size());
2944 }
2945 
2946 TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2947 {
2948  if (binaryContent == nullptr)
2949  {
2950  throw InvalidArgumentException(fmt::format("Invalid (null) binary content {}",
2951  CHECK_LOCATION().AsString()));
2952  }
2953  flatbuffers::Verifier verifier(binaryContent, len);
2954  if (verifier.VerifyBuffer<tflite::Model>() == false)
2955  {
2956  throw ParseException(
2957  fmt::format("Buffer doesn't conform to the expected Tensorflow Lite "
2958  "flatbuffers format. size:{} {}",
2959  len,
2960  CHECK_LOCATION().AsString()));
2961  }
2962  return tflite::UnPackModel(binaryContent);
2963 }
2964 
2966  size_t subgraphIndex,
2967  size_t operatorIndex)
2968 {
2969  CHECK_MODEL(model, subgraphIndex, operatorIndex);
2970 
2971  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2972  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
2973 
2974  size_t inputCount = operatorPtr->inputs.size();
2975  TensorRawPtrVector result(inputCount);
2976  for (size_t i=0; i<inputCount; ++i)
2977  {
2978  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
2979  result[i] = subgraphPtr->tensors[inputId].get();
2980  }
2981  return result;
2982 }
2983 
2985  size_t subgraphIndex,
2986  size_t operatorIndex)
2987 {
2988  CHECK_MODEL(model, subgraphIndex, operatorIndex);
2989 
2990  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
2991  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
2992 
2993  size_t outputCount = operatorPtr->outputs.size();
2994  TensorRawPtrVector result(outputCount);
2995  for (size_t i=0; i<outputCount; ++i)
2996  {
2997  uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
2998  CHECK_TENSOR(model, subgraphIndex, outputId);
2999  result[i] = subgraphPtr->tensors[outputId].get();
3000  }
3001  return result;
3002 }
3003 
3005  size_t subgraphIndex)
3006 {
3007  CHECK_SUBGRAPH(model, subgraphIndex);
3008  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3009 
3010  size_t inputCount = subgraphPtr->inputs.size();
3011  TensorIdRawPtrVector result(inputCount);
3012  for (size_t i=0; i<inputCount; ++i)
3013  {
3014  uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
3015  CHECK_TENSOR(model, subgraphIndex, inputId);
3016  result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
3017  }
3018  return result;
3019 }
3020 
3022  size_t subgraphIndex)
3023 {
3024  CHECK_SUBGRAPH(model, subgraphIndex);
3025  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3026 
3027  size_t outputCount = subgraphPtr->outputs.size();
3028  TensorIdRawPtrVector result(outputCount);
3029  for (size_t i=0; i<outputCount; ++i)
3030  {
3031  uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
3032  result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
3033  }
3034  return result;
3035 }
3036 
3037 std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
3038  size_t subgraphIndex,
3039  size_t operatorIndex)
3040 {
3041  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3042  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3043  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3044  return operatorPtr->inputs;
3045 }
3046 
3047 std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
3048  size_t subgraphIndex,
3049  size_t operatorIndex)
3050 {
3051  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3052  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3053  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3054  return operatorPtr->outputs;
3055 }
3056 
3057 void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
3058  size_t operatorIndex,
3059  IConnectableLayer* layer,
3060  const std::vector<unsigned int>& tensorIndexes)
3061 {
3062  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3063  ARMNN_ASSERT(layer != nullptr);
3064  if (tensorIndexes.size() != layer->GetNumInputSlots())
3065  {
3066  throw ParseException(
3067  fmt::format("The number of tensor inputs ({}) does not match the number expected ({})"
3068  " for subgraph:{} operator index:{} {}",
3069  tensorIndexes.size(),
3070  layer->GetNumInputSlots(),
3071  subgraphIndex,
3072  operatorIndex,
3073  CHECK_LOCATION().AsString()));
3074  }
3075 
3076  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
3077  {
3078  unsigned int tensorIndex = tensorIndexes[slotIndex];
3079  armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
3080  RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
3081  }
3082 }
3083 
3084 void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
3085  size_t operatorIndex,
3086  IConnectableLayer* layer,
3087  const std::vector<unsigned int>& tensorIndexes)
3088 {
3089  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3090  ARMNN_ASSERT(layer != nullptr);
3091  if (tensorIndexes.size() != layer->GetNumOutputSlots())
3092  {
3093  throw ParseException(
3094  fmt::format("The number of tensor outputs ({}) does not match the number expected ({})"
3095  " for subgraph:{} operator index:{} {}",
3096  tensorIndexes.size(),
3097  layer->GetNumOutputSlots(),
3098  subgraphIndex,
3099  operatorIndex,
3100  CHECK_LOCATION().AsString()));
3101  }
3102 
3103  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
3104  {
3105  unsigned int tensorIndex = tensorIndexes[slotIndex];
3106  armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
3107  RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
3108  }
3109 }
3110 
3111 void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
3112 {
3113  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3114 
3115  auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
3116  for (auto const & tensorIdAndPtr : inputs)
3117  {
3118  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3119  IConnectableLayer* layer =
3120  m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3121 
3122  auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
3123  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3124 
3125  RegisterOutputSlots(subgraphIndex,
3126  VIRTUAL_OPERATOR_ID,
3127  layer,
3128  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3129  }
3130 }
3131 
3132 void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
3133 {
3134  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3135 
3136  auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
3137  for (auto const & tensorIdAndPtr : outputs)
3138  {
3139  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3140  IConnectableLayer* layer =
3141  m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3142 
3143  RegisterInputSlots(subgraphIndex,
3144  VIRTUAL_OPERATOR_ID,
3145  layer,
3146  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3147  }
3148 }
3149 
3150 void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
3151 {
3152  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3153 
3154  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
3155  for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
3156  {
3157  for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
3158  {
3159  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
3160  m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
3161  {
3162  TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
3163  armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
3164  auto tensorAndData = CreateConstTensor(tensorPtr,
3165  tensorInfo,
3167 
3168  std::string layerName = fmt::format("Constant:{}", tensorPtr->name);
3169  IConnectableLayer *layer =
3170  m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
3171 
3172  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3173  RegisterOutputSlots(subgraphIndex,
3174  VIRTUAL_OPERATOR_ID,
3175  layer,
3176  { tensorIndex });
3177 
3178  }
3179  }
3180  }
3181 }
3182 
3183 // example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3185 {
3186  CHECK_BUFFER(model, bufferIndex);
3187  return model->buffers[bufferIndex].get();
3188 }
3189 
3190 template<typename T>
3191 std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3192 TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
3193  TfLiteParser::TensorRawPtr tensorPtr,
3194  armnn::TensorInfo& tensorInfo,
3196 {
3197  auto constData = CreateConstTensorImpl<T>(bufferPtr,
3198  tensorPtr,
3199  tensorInfo,
3200  permutationVector);
3201  TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
3202  return std::make_pair(constData.first, std::move(storage));
3203 }
3204 
3205 std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3206 TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
3207  armnn::TensorInfo& tensorInfo,
3209 {
3210  CHECK_TENSOR_PTR(tensorPtr);
3211  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3212  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3213 
3214  switch (tensorInfo.GetDataType())
3215  {
3217  return CreateConstTensorAndStoreData<float>(bufferPtr,
3218  tensorPtr,
3219  tensorInfo,
3220  permutationVector);
3222  return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
3223  tensorPtr,
3224  tensorInfo,
3225  permutationVector);
3227  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3228  tensorPtr,
3229  tensorInfo,
3230  permutationVector);
3232  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3233  tensorPtr,
3234  tensorInfo,
3235  permutationVector);
3237  return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
3238  tensorPtr,
3239  tensorInfo,
3240  permutationVector);
3241  default:
3242  {
3243  std::stringstream errString;
3244  errString << "Unexpected datatype when creating const tensor: "
3245  << armnn::GetDataTypeName(tensorInfo.GetDataType())
3246  << " shape:" << tensorInfo.GetShape()
3247  << CHECK_LOCATION().AsString();
3248  throw ParseException(errString.str());
3249  }
3250  }
3251 }
3252 
3254  const std::string& name) const
3255 {
3256  CHECK_SUBGRAPH(m_Model, subgraphId);
3257  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3258  for (auto const & input : inputs)
3259  {
3260  if (input.second->name == name)
3261  {
3262  auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
3263  return std::make_pair(bindingId, ToTensorInfo(input.second));
3264  }
3265  }
3266 
3267  std::stringstream bindings;
3268  for (auto const & input : inputs)
3269  {
3270  bindings << "'" << input.second->name << "' ";
3271  }
3272 
3273  throw ParseException(
3274  fmt::format("No input binding found for subgraph:{} and name:{}. "
3275  "Possible inputs are: [{}] {}",
3276  subgraphId,
3277  name,
3278  bindings.str(),
3279  CHECK_LOCATION().AsString()));
3280 }
3281 
3283  const std::string& name) const
3284 {
3285  CHECK_SUBGRAPH(m_Model, subgraphId);
3286  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3287  for (unsigned int i = 0; i < outputs.size(); ++i)
3288  {
3289  auto const output = outputs[i];
3290  if (output.second->name == name)
3291  {
3292  auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
3293  std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
3294  m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
3295  return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
3296  }
3297  }
3298 
3299  std::stringstream bindings;
3300  for (auto const & output : outputs)
3301  {
3302  bindings << "'" << output.second->name << "' ";
3303  }
3304 
3305  throw ParseException(
3306  fmt::format("No output binding found for subgraph:{} and name:{}. "
3307  "Possible outputs are: [{}] {}",
3308  subgraphId,
3309  name,
3310  bindings.str(),
3311  CHECK_LOCATION().AsString()));
3312 }
3313 
3315 {
3316  return m_Model->subgraphs.size();
3317 }
3318 
3319 std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
3320 {
3321  CHECK_SUBGRAPH(m_Model, subgraphId);
3322  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3323  std::vector<std::string> result;
3324  result.reserve(inputs.size());
3325  for (auto const & input : inputs)
3326  {
3327  result.push_back(input.second->name);
3328  }
3329  return result;
3330 }
3331 
3332 std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
3333 {
3334  CHECK_SUBGRAPH(m_Model, subgraphId);
3335  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3336  std::vector<std::string> result;
3337  result.reserve(outputs.size());
3338  for (auto const & output : outputs)
3339  {
3340  result.push_back(output.second->name);
3341  }
3342  return result;
3343 }
3344 
3346 {
3347  return new TfLiteParser(options);
3348 }
3349 
3351 {
3353 }
3354 
3356 {
3357  delete parser;
3358 }
3359 
3360 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
3361 : m_FloatData(std::move(data))
3362 , m_Uint8Data(nullptr)
3363 , m_Int8Data(nullptr)
3364 , m_Int32Data(nullptr)
3365 {
3366 }
3367 
3368 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
3369 : m_FloatData(nullptr)
3370 , m_Uint8Data(std::move(data))
3371 , m_Int8Data(nullptr)
3372 , m_Int32Data(nullptr)
3373 {
3374 }
3375 
3376 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
3377 : m_FloatData(nullptr)
3378 , m_Uint8Data(nullptr)
3379 , m_Int8Data(std::move(data))
3380 , m_Int32Data(nullptr)
3381 {
3382 }
3383 
3384 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
3385 : m_FloatData(nullptr)
3386 , m_Uint8Data(nullptr)
3387 , m_Int8Data(nullptr)
3388 , m_Int32Data(std::move(data))
3389 {
3390 }
3391 
3392 } // armnnTfLiteParser
virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile) override
Create the network from a flatbuffers binary file on disk.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX)
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
virtual BindingPointInfo GetNetworkOutputBindingInfo(size_t subgraphId, const std::string &name) const override
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
UnaryOperation m_Operation
Specifies the elementwiseUnary operation to execute.
uint32_t m_Axis
0-based axis along which to stack the input tensors.
std::unique_ptr< tflite::SubGraphT > SubgraphPtr
static BufferRawPtr GetBuffer(const ModelPtr &model, size_t bufferIndex)
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
float m_ScaleW
Center size encoding scale weight.
bool IsTypeSpaceMatch(const TensorInfo &other) const
Check that the types are the same and, if quantize, that the quantization parameters are the same...
Definition: Tensor.cpp:423
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
static ModelPtr LoadModelFromBinary(const uint8_t *binaryContent, size_t len)
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
A TransposeConvolution2dDescriptor for the TransposeConvolution2dLayer.
#define ARMNN_THROW_PARSE_EXCEPTION(msg)
const TensorShape & GetShape() const
Definition: Tensor.hpp:187
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
virtual armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent) override
Create the network from a flatbuffers binary.
std::string AsString() const
Definition: Exceptions.hpp:29
int32_t m_ShrinkAxisMask
Shrink axis mask value. If set, the nth specification shrinks the dimensionality by 1...
A ReshapeDescriptor for the ReshapeLayer.
std::vector< int > m_Begin
Begin values for the input that will be sliced.
std::vector< TensorRawPtr > TensorRawPtrVector
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
float m_ScaleX
Center size encoding scale x.
TensorShape m_InputShape
Required shape of all input tensors.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
static ModelPtr LoadModelFromFile(const char *fileName)
bool m_BiasEnabled
Enable/disable bias.
std::vector< unsigned int > m_OutputShape
unsigned int GetNumBytes() const
Definition: Tensor.cpp:418
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
float m_Beta
Exponentiation value.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
ArgMinMaxFunction m_Function
Specify if the function is to find Min or Max.
Definition: Descriptors.hpp:65
uint32_t m_DetectionsPerClass
Detections per classes, used in Regular NMS.
bool m_OutputShapeEnabled
Output shape if it has been specified.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_BUFFER(MODEL, BUFFER_INDEX)
static TensorRawPtrVector GetInputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:163
uint32_t m_PadTop
Padding top value in the height dimension.
std::vector< BackendOptions > NetworkOptions
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t &outPadHead, uint32_t &outPadTail, bool samePadding)
Definition: TfParser.cpp:406
uint32_t m_PadRight
Padding right value in the width dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
std::unique_ptr< ITfLiteParser, void(*)(ITfLiteParser *parser)> ITfLiteParserPtr
virtual BindingPointInfo GetNetworkInputBindingInfo(size_t subgraphId, const std::string &name) const override
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
Copyright (c) 2020 ARM Limited.
void IgnoreUnused(Ts &&...)
uint32_t m_PadBottom
Padding bottom value in the height dimension.
int32_t m_BeginMask
Begin mask value.
virtual size_t GetSubgraphCount() const override
Return the number of subgraphs in the parsed model.
SizeType GetSize() const
Definition: Types.hpp:233
uint32_t m_DilationY
Dilation along y axis.
int32_t m_EndMask
End mask value.
PoolingAlgorithm
Definition: Types.hpp:104
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding values for the input dimension: heightPad{top, bottom} widthPad{left, right}.
uint32_t m_DilationY
Dilation factor value for height dimension.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
static armnn::TensorInfo OutputShapeOfReshape(const armnn::TensorInfo &inputTensorInfo, const std::vector< int32_t > &targetDimsIn)
static ITfLiteParserPtr Create(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:202
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX)
constexpr const char * GetDataTypeName(DataType dataType)
Definition: TypesUtils.hpp:180
void SetShape(const TensorShape &newShape)
Definition: Tensor.hpp:189
A ResizeDescriptor for the ResizeLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_MaxClassesPerDetection
Maximum numbers of classes per detection, used in Fast NMS.
std::vector< unsigned int > m_Axis
Values for the dimensions to reduce.
A StackDescriptor for the StackLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
TensorShape m_TargetShape
Target shape value.
static TensorIdRawPtrVector GetSubgraphInputs(const ModelPtr &model, size_t subgraphIndex)
static TensorRawPtrVector GetOutputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_MaxDetections
Maximum numbers of detections.
A PadDescriptor for the PadLayer.
armnn::TensorInfo ToTensorInfo(Deserializer::TensorRawPtr tensorPtr)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::unique_ptr< onnx::ModelProto > ModelPtr
Definition: OnnxParser.hpp:23
#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
void CheckTensor(const ConstTensor &t)
Definition: TensorTest.cpp:165
static TensorIdRawPtrVector GetSubgraphOutputs(const ModelPtr &model, size_t subgraphIndex)
bool CheckShape(const armnn::TensorShape &actual, const std::vector< uint32_t > &expected)
DataType
Definition: Types.hpp:32
float m_NmsIouThreshold
Intersection over union threshold.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationX
Dilation factor value for width dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
std::string FileLine() const
Definition: Exceptions.hpp:37
Status SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
Set the size of the views.
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
int32_t m_NewAxisMask
New axis mask value.
bool m_KeepDims
Enable/disable keep dimensions. If true, then the reduced dimensions that are of length 1 are kept...
armnnSerializer::TensorInfo * TensorRawPtr
std::vector< unsigned int > m_BlockShape
Block shape values.
static void Destroy(ITfLiteParser *parser)
An output connection slot for a layer.
Definition: INetwork.hpp:37
A L2NormalizationDescriptor for the L2NormalizationLayer.
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:469
An ArgMinMaxDescriptor for ArgMinMaxLayer.
Definition: Descriptors.hpp:51
float GetQuantizationScale() const
Definition: Tensor.cpp:452
static std::vector< int32_t > & GetOutputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
DataType GetDataType() const
Definition: Tensor.hpp:194
An OriginsDescriptor for the ConcatLayer.
bool has_value() const noexcept
Definition: Optional.hpp:53
A FullyConnectedDescriptor for the FullyConnectedLayer.
int32_t m_EllipsisMask
Ellipsis mask value.
bool m_BiasEnabled
Enable/disable bias.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:314
static ITfLiteParser * CreateRaw(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
#define CHECK_VALID_SIZE(ACTUAL,...)
uint32_t m_NumClasses
Number of classes.
#define CHECKED_NON_NEGATIVE(VALUE)
uint32_t m_PadTop
Padding top value in the height dimension.
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
A StandInDescriptor for the StandIn layer.
bool m_UseRegularNms
Use Regular NMS.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< unsigned int > m_BlockShape
Block shape value.
std::vector< int > m_Stride
Stride values for the input that will be sliced.
bool IsActivationSupported(const BackendId &backend, const TensorInfo &input, const TensorInfo &output, const ActivationDescriptor &descriptor, char *reasonIfUnsupported=nullptr, size_t reasonIfUnsupportedMaxLength=1024)
Deprecated in favor of IBackend and ILayerSupport interfaces.
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:20
#define CHECK_LOCATION()
Definition: Exceptions.hpp:197
uint32_t m_NumInputs
Number of input tensors.
A SliceDescriptor for the SliceLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
static std::vector< int32_t > & GetInputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_TENSOR_PTR(TENSOR_PTR)
float m_ScaleH
Center size encoding scale height.
std::vector< int > m_End
End values for the input that will be sliced.
A SpaceToBatchNdDescriptor for the SpaceToBatchNdLayer.
Struct for the users to pass backend specific options.
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
Definition: Descriptors.hpp:45
uint32_t m_DilationX
Dilation along x axis.
uint32_t m_PadLeft
Padding left value in the width dimension.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
static armnn::TensorInfo OutputShapeOfSqueeze(const std::vector< uint32_t > &squeezeDims, const armnn::TensorInfo &inputTensorInfo)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX)
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:93
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_Crops
The values to crop from the input dimension.
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
void SetConcatAxis(unsigned int concatAxis)
Set the concatenation axis value.
const tflite::BufferT * BufferRawPtr
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
ResizeMethod
Definition: Types.hpp:111
A MeanDescriptor for the MeanLayer.
virtual std::vector< std::string > GetSubgraphInputTensorNames(size_t subgraphId) const override
Return the input tensor names for a given subgraph.
armnn::BindingPointInfo BindingPointInfo
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
uint32_t m_PadRight
Padding right value in the width dimension.
A TransposeDescriptor for the TransposeLayer.
A StridedSliceDescriptor for the StridedSliceLayer.
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
int m_Axis
Axis to reduce across the input tensor.
Definition: Descriptors.hpp:67
virtual const char * GetName() const =0
Returns the name of the layer.
virtual std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const override
Return the output tensor names for a given subgraph.
float m_ScaleY
Center size encoding scale y.
float m_NmsScoreThreshold
NMS score threshold.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:101
virtual int Connect(IInputSlot &destination)=0
const char * m_Function
Definition: Exceptions.hpp:16
A Pooling2dDescriptor for the Pooling2dLayer.
std::unique_ptr< tflite::ModelT > ModelPtr
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:191
#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID)
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
Definition: Descriptors.hpp:47
bool IsQuantized() const
Definition: Tensor.cpp:495
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
Definition: Permute.cpp:98
A SoftmaxDescriptor for the SoftmaxLayer.
const tflite::TensorT * TensorRawPtr
DataLayout::NCHW DataLayout::NCHW DataLayout::NHWC DataLayout::NHWC true
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
Definition: Descriptors.hpp:43
armnn::DataType m_Output_Type
Definition: Descriptors.hpp:69
std::unique_ptr< tflite::OperatorT > OperatorPtr
An input connection slot for a layer.
Definition: INetwork.hpp:24
TfLiteParser(const armnn::Optional< ITfLiteParser::TfLiteParserOptions > &options=armnn::EmptyOptional())
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
constexpr unsigned int MaxNumOfTensorDimensions
Definition: Types.hpp:18
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const
Definition: Tensor.hpp:192
ActivationFunction
Definition: Types.hpp:56
std::vector< TensorIdRawPtr > TensorIdRawPtrVector
uint32_t m_PadRight
Padding right value in the width dimension.