ArmNN
 21.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 
10 #include <armnn/BackendOptions.hpp>
11 #include <armnn/Descriptors.hpp>
12 #include <armnn/Exceptions.hpp>
13 #include <armnn/Logging.hpp>
14 #include <armnn/Tensor.hpp>
16 #include <armnn/TypesUtils.hpp>
17 #include <armnn/utility/Assert.hpp>
20 
21 // armnnUtils:
22 #include <armnnUtils/Permute.hpp>
24 
25 #include <ParserHelper.hpp>
26 #include <VerificationHelpers.hpp>
27 
28 // The generated code based on the Tf Lite schema:
29 #include <schema_generated.h>
30 
31 #include <flatbuffers/flexbuffers.h>
32 
33 #include <fmt/format.h>
34 
35 #include <algorithm>
36 #include <fstream>
37 #include <iostream>
38 #include <limits>
39 #include <numeric>
40 #include <sstream>
41 
42 #define ARMNN_THROW_PARSE_EXCEPTION(msg) \
43  { \
44  throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
45  << ": " \
46  << CHECK_LOCATION().AsString()).str()); \
47  }
48 
49 using namespace armnn;
51 namespace armnnTfLiteParser
52 {
53 
54 ITfLiteParser::ITfLiteParser(const armnn::Optional<TfLiteParserOptions>& options) :
55  pTfLiteParserImpl(new TfLiteParserImpl(options)) {}
56 
57 ITfLiteParser::~ITfLiteParser() = default;
58 
59 ITfLiteParser* ITfLiteParser::CreateRaw(const armnn::Optional<TfLiteParserOptions>& options)
60 {
61  return new ITfLiteParser(options);
62 }
63 
64 ITfLiteParserPtr ITfLiteParser::Create(const armnn::Optional<TfLiteParserOptions>& options)
65 {
66  return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
67 }
68 
69 void ITfLiteParser::Destroy(ITfLiteParser* parser)
70 {
71  delete parser;
72 }
73 
74 armnn::INetworkPtr ITfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
75 {
76  return pTfLiteParserImpl->CreateNetworkFromBinaryFile(graphFile);
77 }
78 
79 armnn::INetworkPtr ITfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
80 {
81  return pTfLiteParserImpl->CreateNetworkFromBinary(binaryContent);
82 }
83 
84 BindingPointInfo ITfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
85  const std::string& name) const
86 {
87  return pTfLiteParserImpl->GetNetworkInputBindingInfo(subgraphId, name);
88 }
89 
90 BindingPointInfo ITfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
91  const std::string& name) const
92 {
93  return pTfLiteParserImpl->GetNetworkOutputBindingInfo(subgraphId, name);
94 }
95 
96 size_t ITfLiteParser::GetSubgraphCount() const
97 {
98  return pTfLiteParserImpl->GetSubgraphCount();
99 }
100 
101 std::vector<std::string> ITfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
102 {
103  return pTfLiteParserImpl->GetSubgraphInputTensorNames(subgraphId);
104 }
105 
106 std::vector<std::string> ITfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
107 {
108  return pTfLiteParserImpl->GetSubgraphOutputTensorNames(subgraphId);
109 }
110 
111 namespace
112 {
113 
114 const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
115 
116 void CheckSubgraph(const TfLiteParserImpl::ModelPtr & model,
117  size_t subgraphIndex,
118  const CheckLocation & location)
119 {
120  if (model.get() == nullptr)
121  {
122  throw ParseException(
123  fmt::format("{} was called with invalid (null) model. "
124  "Possible reason is that the model is not yet loaded and Unpack(ed). "
125  "subgraph:{} at {}",
126  location.m_Function,
127  subgraphIndex,
128  location.FileLine()));
129  }
130  else if (subgraphIndex >= model->subgraphs.size())
131  {
132  throw ParseException(
133  fmt::format("{} was called with an invalid subgraph index. "
134  "subgraph:{} at {}",
135  location.m_Function,
136  subgraphIndex,
137  location.FileLine()));
138  }
139 }
140 
141 #define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
142  CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
143 
144 void CheckModel(const TfLiteParserImpl::ModelPtr & model,
145  size_t subgraphIndex,
146  size_t operatorIndex,
147  const CheckLocation & location)
148 {
149  if (model.get() == nullptr)
150  {
151  throw ParseException(
152  fmt::format("{} was called with invalid (null) model. "
153  "Possible reason is that the model is not yet loaded and Unpack(ed). "
154  "subgraph:{} operator:{} at {}",
155  location.m_Function,
156  subgraphIndex,
157  operatorIndex,
158  location.FileLine()));
159  }
160  else if (subgraphIndex >= model->subgraphs.size())
161  {
162  throw ParseException(
163  fmt::format("{} was called with an invalid subgraph index. "
164  "subgraph:{} operator:{} at {}",
165  location.m_Function,
166  subgraphIndex,
167  operatorIndex,
168  location.FileLine()));
169  }
170  else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
171  operatorIndex != VIRTUAL_OPERATOR_ID)
172  {
173  throw ParseException(
174  fmt::format("{} was called with an invalid operator index. "
175  "subgraph:{} operator:{} at {}",
176  location.m_Function,
177  subgraphIndex,
178  operatorIndex,
179  location.FileLine()));
180  }
181 }
182 
183 #define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
184  CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
185 
186 void CheckTensor(const TfLiteParserImpl::ModelPtr & model,
187  size_t subgraphIndex,
188  size_t tensorIndex,
189  const CheckLocation & location)
190 {
191  // not checking model, because I assume CHECK_MODEL already run
192  // and checked that. An assert would do.
193  ARMNN_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
194 
195  // also subgraph index should be checked by CHECK_MODEL so
196  // I only add an assert here
197  ARMNN_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
198 
199  // the tensor index is the only one to check here
200  if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
201  {
202  throw ParseException(
203  fmt::format("{} was called with an invalid tensor index. "
204  "subgraph:{} tensor:{} at {}",
205  location.m_Function,
206  subgraphIndex,
207  tensorIndex,
208  location.FileLine()));
209  }
210 }
211 
212 #define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
213  CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
214 
215 void CheckTensorPtr(TfLiteParserImpl::TensorRawPtr rawPtr,
216  const CheckLocation & location)
217 {
218  if (rawPtr == nullptr)
219  {
220  throw ParseException(
221  fmt::format("{} was called with a null tensor pointer at {}", location.m_Function, location.FileLine()));
222  }
223 }
224 
225 #define CHECK_TENSOR_PTR(TENSOR_PTR) \
226  CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
227 
228 void CheckBuffer(const TfLiteParserImpl::ModelPtr & model,
229  size_t bufferIndex,
230  const CheckLocation & location)
231 {
232  if (model.get() == nullptr)
233  {
234  throw ParseException(
235  fmt::format("{} was called with invalid (null) model. "
236  "Possible reason is that the model is not yet loaded and Unpack(ed). "
237  "buffer:{} at {}",
238  location.m_Function,
239  bufferIndex,
240  location.FileLine()));
241  }
242  else if (bufferIndex >= model->buffers.size())
243  {
244  throw ParseException(
245  fmt::format("{} was called with an invalid buffer index. "
246  "buffer index:{} at {}",
247  location.m_Function,
248  bufferIndex,
249  location.FileLine()));
250  }
251  else if (model->buffers[bufferIndex].get() == nullptr)
252  {
253  throw ParseException(
254  fmt::format("The buffer #{} is null. {}",
255  bufferIndex,
256  location.AsString()));
257  }
258 }
259 
260 #define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
261  CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
262 
263 void CheckBufferSize(TfLiteParserImpl::BufferRawPtr bufferPtr,
264  const armnn::TensorInfo & tensorInfo,
265  uint32_t bufferId,
266  const CheckLocation & location)
267 {
268  if (bufferPtr == nullptr)
269  {
270  throw ParseException(
271  fmt::format("BufferPtr is null for buffer:{}. {}",
272  bufferId,
273  location.AsString()));
274  }
275  else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
276  tensorInfo.GetNumBytes() > bufferPtr->data.size())
277  {
278  std::stringstream ss;
279  ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
280  << "For tensor: " << tensorInfo.GetShape()
281  << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
282  << tensorInfo.GetNumElements() << " elements. " << location.AsString();
283  throw ParseException(ss.str());
284  }
285 }
286 
287 #define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
288  CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
289 
290 bool IsActivationSupported(tflite::ActivationFunctionType activationType)
291 {
292  switch(activationType)
293  {
294  case tflite::ActivationFunctionType_NONE:
295  case tflite::ActivationFunctionType_RELU:
296  case tflite::ActivationFunctionType_RELU6:
297  case tflite::ActivationFunctionType_TANH:
298  {
299  return true;
300  }
301  default:
302  {
303  return false;
304  }
305  }
306 }
307 
308 #define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
309  do { \
310  if (IsActivationSupported(OPTION->fused_activation_function) == false) \
311  { \
312  throw ParseException( \
313  fmt::format("TfLite parser doesn't suppport fused activation: " \
314  "{}/{} in {} subgraph:{} operator:{} at {}", \
315  OPTION->fused_activation_function, \
316  tflite::EnumNameActivationFunctionType(\
317  OPTION->fused_activation_function), \
318  __func__, \
319  SUBGRAPH_INDEX, \
320  OPERATOR_INDEX, \
321  CHECK_LOCATION().FileLine())); \
322  } \
323  } while(false)
324 
325 
326 std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
327 {
328  std::vector<unsigned int> result;
329  result.reserve(in.size());
330  for (auto & i : in)
331  {
332  // If the location of the input data is -1 then the input should be ignored.
333  if (i == -1)
334  {
335  continue;
336  }
337  result.push_back(CHECKED_NON_NEGATIVE(i));
338  }
339  return result;
340 }
341 
342 void CalcPadding(uint32_t inputSize,
343  uint32_t filterSize,
344  uint32_t stride,
345  uint32_t dilation,
346  uint32_t& paddingFront,
347  uint32_t& paddingBack,
348  tflite::Padding padding)
349 {
350  paddingFront = 0;
351  paddingBack = 0;
352  if (padding == tflite::Padding_SAME)
353  {
354  uint32_t outputSize = (inputSize + stride - 1) / stride;
355  uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
356  uint32_t temp = (outputSize - 1) * stride + dilatedSize;
357  if (temp > inputSize)
358  {
359  paddingFront = (temp - inputSize) / 2;
360  paddingBack = (temp - inputSize) - paddingFront;
361  }
362  }
363 }
364 
366  const std::vector<unsigned int>& shape,
367  const bool outputTensor = false)
368 {
369  armnn::DataType type;
370  CHECK_TENSOR_PTR(tensorPtr);
371 
372  switch (tensorPtr->type)
373  {
374  case tflite::TensorType_UINT8:
376  break;
377  case tflite::TensorType_FLOAT32:
379  break;
380  case tflite::TensorType_INT8:
381  if (tensorPtr->quantization->zero_point.size() == 1)
382  {
383  // Per-tensor
385  }
386  else
387  {
388  // Per-channel
390  }
391  break;
392  case tflite::TensorType_INT16:
394  break;
395  case tflite::TensorType_INT32:
397  break;
398  case tflite::TensorType_INT64:
400  break;
401  case tflite::TensorType_BOOL:
403  break;
404  default:
405  {
406  CheckLocation location = CHECK_LOCATION();
407  throw ParseException(
408  fmt::format("Unsupported data type {} = {} for tensor: {}. {}",
409  tensorPtr->type,
410  tflite::EnumNameTensorType(tensorPtr->type),
411  tensorPtr->name,
412  location.AsString()));
413  }
414  }
415  TensorShape tensorShape;
416 
417  std::vector<unsigned int> safeShape = shape;
418  if (shape.size() == 0)
419  {
420  safeShape.push_back(1);
421  }
422 
423  if (!outputTensor)
424  {
425  tensorShape = TensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()), safeShape.data());
426  }
427  else
428  {
429  size_t shapeSignatureSize = tensorPtr->shape_signature.size();
430 
431  // If a shape signature exists we will use that to infer dynamic tensors
432  if (shapeSignatureSize != 0)
433  {
434  // If the shape is incompatible with the shape signature override the shape
435  if (shapeSignatureSize != shape.size())
436  {
437  safeShape = {};
438 
439  for (unsigned int i = 0; i < shapeSignatureSize; ++i)
440  {
441  unsigned int dim = tensorPtr->shape_signature[i] > -1 ?
442  static_cast<unsigned int>(tensorPtr->shape_signature[i]) : 0;
443  safeShape.push_back(dim);
444  }
445  }
446 
447  std::unique_ptr<bool[]> dimMask = std::make_unique<bool[]>(tensorPtr->shape_signature.size());
448  for (unsigned int i = 0; i < tensorPtr->shape_signature.size(); ++i)
449  {
450  dimMask[i] = tensorPtr->shape_signature[i] == -1 ? false : true;
451  }
452  tensorShape = TensorShape(static_cast<unsigned int>(safeShape.size()), safeShape.data(), dimMask.get());
453  }
454  // If there is no shape signature treat the tensor as dynamic if the shape has a size of zero
455  else if (shape.size() == 0)
456  {
457  tensorShape = TensorShape(1, false);
458  }
459  else
460  {
461  tensorShape = TensorShape(armnn::numeric_cast<unsigned int>(shape.size()), shape.data());
462  }
463  }
464 
465  float quantizationScale = 0.0f;
466  int32_t quantizationOffset = 0;
467 
468  if (tensorPtr->quantization.get())
469  {
470  if (tensorPtr->quantization->scale.size() <= 1)
471  {
472  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
473  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
474 
475  if (tensorPtr->quantization->scale.size() == 1)
476  {
477  quantizationScale = tensorPtr->quantization->scale[0];
478  }
479  if (tensorPtr->quantization->zero_point.size() == 1)
480  {
481  // NOTE: we lose precision here when converting from 64 bit to 32
482  // but this is what we support at the moment in ArmNN
483  quantizationOffset = armnn::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
484  }
485 
486  armnn::TensorInfo result(tensorShape,
487  type,
488  quantizationScale,
489  quantizationOffset);
490  return result;
491  }
492  else
493  {
494  std::vector<float> quantizationScales;
495  std::vector<int32_t> quantizationOffsets;
496 
497  // Scale
498  std::copy(tensorPtr->quantization->scale.begin(),
499  tensorPtr->quantization->scale.end(),
500  std::back_inserter(quantizationScales));
501 
502  // QSymmS8 Per-axis
503  armnn::TensorInfo result(tensorShape,
504  type,
505  quantizationScales,
506  armnn::numeric_cast<unsigned int>(tensorPtr->quantization->quantized_dimension));
507  return result;
508  }
509  }
510  else
511  {
512  armnn::TensorInfo result(tensorShape,
513  type,
514  quantizationScale,
515  quantizationOffset);
516  return result;
517  }
518 }
519 
521 {
522  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
523  return ToTensorInfo(tensorPtr, dimensions);
524 }
525 
527  const bool outputTensor)
528 {
529  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
530  return ToTensorInfo(tensorPtr, dimensions, outputTensor);
531 }
532 
533 template<typename T>
534 std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
535 CreateConstTensorImpl(TfLiteParserImpl::BufferRawPtr bufferPtr,
537  armnn::TensorInfo& tensorInfo,
539 {
540  IgnoreUnused(tensorPtr);
541  ARMNN_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
542  ARMNN_ASSERT_MSG(bufferPtr != nullptr,
543  fmt::format("Buffer for buffer:{} is null", tensorPtr->buffer).c_str());
544 
545  std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
546 
547  if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
548  {
549  tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
550  armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
551  reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
552  }
553  else
554  {
555  ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
556  }
557 
558  // Make sure isConstant flag is set.
559  tensorInfo.SetConstant();
560 
561  return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
562 }
563 
564 armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
565 {
566  // generate the binding id by shifting the tensor id by 8 bit
567  // and add the subgraph id, which allows 256 subgraphs
568  return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
569 }
570 
571 bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
572 {
573  const unsigned int actualSize = actual.GetNumDimensions();
574  if (actualSize != expected.size())
575  {
576  return false;
577  }
578 
579  for (unsigned int i = 0u; i < actualSize; i++)
580  {
581  if (expected[i] < 0 ||
582  actual[i] != static_cast<unsigned int>(expected[i]))
583  {
584  return false;
585  }
586  }
587 
588  return true;
589 }
590 
591 void CheckMatchingQuantization(const TensorInfo& first,
592  const TensorInfo& second,
593  const std::string& descName,
594  std::string const& firstName,
595  std::string const& secondName)
596 {
597  if (!first.IsQuantized() ||
598  !second.IsQuantized())
599  {
600  // Not a quantized type, ignore the validation
601  return;
602  }
603 
604  DataType firstDataType = first.GetDataType();
605  DataType secondDataType = second.GetDataType();
606 
607  if (firstDataType != secondDataType)
608  {
609  throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
610  " must be of the same quantized type, " +
611  firstName + " is " + GetDataTypeName(firstDataType) + ", " +
612  secondName + " is " + GetDataTypeName(secondDataType));
613  }
614 
615  if (!first.IsTypeSpaceMatch(second))
616  {
617  throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
618  " must have the same quantization space, " +
619  firstName + " has offset " + std::to_string(first.GetQuantizationOffset()) +
620  " and scale " + std::to_string(first.GetQuantizationScale()) + ", " +
621  secondName + " has offset " + std::to_string(second.GetQuantizationOffset()) +
622  " and scale " + std::to_string(second.GetQuantizationScale()));
623  }
624 }
625 
626 } // <anonymous>
627 
628 TfLiteParserImpl::TfLiteParserImpl(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
629 : m_Options(options)
630 , m_Network(nullptr, nullptr)
631 , m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParserImpl::ParseUnsupportedOperator)
632 {
633  // register supported operators
634  m_ParserFunctions[tflite::BuiltinOperator_ABS] = &TfLiteParserImpl::ParseAbs;
635  m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParserImpl::ParseAdd;
636  m_ParserFunctions[tflite::BuiltinOperator_ARG_MIN] = &TfLiteParserImpl::ParseArgMin;
637  m_ParserFunctions[tflite::BuiltinOperator_ARG_MAX] = &TfLiteParserImpl::ParseArgMax;
638  m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParserImpl::ParseAveragePool2D;
639  m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParserImpl::ParseBatchToSpaceND;
640  m_ParserFunctions[tflite::BuiltinOperator_CAST] = &TfLiteParserImpl::ParseCast;
641  m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParserImpl::ParseConcatenation;
642  m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParserImpl::ParseConv2D;
643  // Conv3D support was added in TF 2.5, so for backwards compatibility a hash define is needed.
644  #if defined(ARMNN_POST_TFLITE_2_3)
645  m_ParserFunctions[tflite::BuiltinOperator_CONV_3D] = &TfLiteParserImpl::ParseConv3D;
646  #endif
647  m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParserImpl::ParseCustomOperator;
648  m_ParserFunctions[tflite::BuiltinOperator_DEPTH_TO_SPACE] = &TfLiteParserImpl::ParseDepthToSpace;
649  m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParserImpl::ParseDepthwiseConv2D;
650  m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParserImpl::ParseDequantize;
651  m_ParserFunctions[tflite::BuiltinOperator_DIV] = &TfLiteParserImpl::ParseDiv;
652  m_ParserFunctions[tflite::BuiltinOperator_ELU] = &TfLiteParserImpl::ParseElu;
653  m_ParserFunctions[tflite::BuiltinOperator_EQUAL] = &TfLiteParserImpl::ParseEqual;
654  m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParserImpl::ParseExp;
655  m_ParserFunctions[tflite::BuiltinOperator_EXPAND_DIMS] = &TfLiteParserImpl::ParseExpandDims;
656  m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParserImpl::ParseFullyConnected;
657  m_ParserFunctions[tflite::BuiltinOperator_GATHER] = &TfLiteParserImpl::ParseGather;
658  m_ParserFunctions[tflite::BuiltinOperator_GREATER] = &TfLiteParserImpl::ParseGreater;
659  m_ParserFunctions[tflite::BuiltinOperator_GREATER_EQUAL] = &TfLiteParserImpl::ParseGreaterOrEqual;
660  m_ParserFunctions[tflite::BuiltinOperator_HARD_SWISH] = &TfLiteParserImpl::ParseHardSwish;
661  m_ParserFunctions[tflite::BuiltinOperator_LEAKY_RELU] = &TfLiteParserImpl::ParseLeakyRelu;
662  m_ParserFunctions[tflite::BuiltinOperator_LESS] = &TfLiteParserImpl::ParseLess;
663  m_ParserFunctions[tflite::BuiltinOperator_LESS_EQUAL] = &TfLiteParserImpl::ParseLessOrEqual;
664  m_ParserFunctions[tflite::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION]
665  = &TfLiteParserImpl::ParseLocalResponseNormalization;
666  m_ParserFunctions[tflite::BuiltinOperator_LOGICAL_NOT] = &TfLiteParserImpl::ParseLogicalNot;
667  m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParserImpl::ParseLogistic;
668  m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParserImpl::ParseL2Normalization;
669  m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParserImpl::ParseMaxPool2D;
670  m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParserImpl::ParseMaximum;
671  m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParserImpl::ParseMean;
672  m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParserImpl::ParseMinimum;
673  m_ParserFunctions[tflite::BuiltinOperator_MIRROR_PAD] = &TfLiteParserImpl::ParseMirrorPad;
674  m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParserImpl::ParseMul;
675  m_ParserFunctions[tflite::BuiltinOperator_NEG] = &TfLiteParserImpl::ParseNeg;
676  m_ParserFunctions[tflite::BuiltinOperator_NOT_EQUAL] = &TfLiteParserImpl::ParseNotEqual;
677  m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParserImpl::ParsePack;
678  m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParserImpl::ParsePad;
679  m_ParserFunctions[tflite::BuiltinOperator_PRELU] = &TfLiteParserImpl::ParsePrelu;
680  m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParserImpl::ParseQuantize;
681  m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParserImpl::ParseRelu;
682  m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParserImpl::ParseRelu6;
683  m_ParserFunctions[tflite::BuiltinOperator_REDUCE_MAX] = &TfLiteParserImpl::ParseReduceMax;
684  m_ParserFunctions[tflite::BuiltinOperator_REDUCE_MIN] = &TfLiteParserImpl::ParseReduceMin;
685  m_ParserFunctions[tflite::BuiltinOperator_REDUCE_PROD] = &TfLiteParserImpl::ParseReduceProd;
686  m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParserImpl::ParseReshape;
687  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParserImpl::ParseResizeBilinear;
688  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParserImpl::ParseResizeNearestNeighbor;
689  m_ParserFunctions[tflite::BuiltinOperator_RSQRT] = &TfLiteParserImpl::ParseRsqrt;
690  m_ParserFunctions[tflite::BuiltinOperator_SHAPE] = &TfLiteParserImpl::ParseShape;
691  m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParserImpl::ParseSlice;
692  m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParserImpl::ParseSoftmax;
693  m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParserImpl::ParseSpaceToBatchND;
694  m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParserImpl::ParseSplit;
695  m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParserImpl::ParseSplitV;
696  m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParserImpl::ParseSqueeze;
697  m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParserImpl::ParseStridedSlice;
698  m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParserImpl::ParseSub;
699  m_ParserFunctions[tflite::BuiltinOperator_SUM] = &TfLiteParserImpl::ParseSum;
700  m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParserImpl::ParseTanH;
701  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParserImpl::ParseTranspose;
702  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParserImpl::ParseTransposeConv;
703  m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParserImpl::ParseUnpack;
704 
705  // register supported custom operators
706  m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParserImpl::ParseDetectionPostProcess;
707 }
708 
709 void TfLiteParserImpl::ResetParser()
710 {
711  m_Network = armnn::INetworkPtr(nullptr, nullptr);
712  m_Model = nullptr;
713  m_SubgraphConnections.clear();
714 }
715 
717 {
718  ResetParser();
719  m_Model = LoadModelFromFile(graphFile);
720  return CreateNetworkFromModel();
721 }
722 
723 INetworkPtr TfLiteParserImpl::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
724 {
725  ResetParser();
726  m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
727  return CreateNetworkFromModel();
728 }
729 
730 
731 armnn::INetworkPtr TfLiteParserImpl::LoadModel(std::unique_ptr<tflite::ModelT> model)
732 {
733  ResetParser();
734  m_Model = std::move(model);
735 
736  return CreateNetworkFromModel();
737 }
738 
739 INetworkPtr TfLiteParserImpl::CreateNetworkFromModel()
740 {
741 
742  using NetworkOptions = std::vector<BackendOptions>;
743  NetworkOptions networkOptions = {};
744  if (m_Options && m_Options.value().m_InferAndValidate)
745  {
746  BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod",
747  {
748  { "InferAndValidate", true }
749  });
750 
751  networkOptions.push_back(shapeInferenceMethodOption);
752  }
753 
754  m_Network = INetwork::Create(networkOptions);
755  ARMNN_ASSERT(m_Model.get() != nullptr);
756 
757  if (m_Model->subgraphs.size() != 1)
758  {
759  throw ParseException(
760  fmt::format("Current TfLite parser only supports 1 subgraph. Current one has: {} {}",
761  m_Model->subgraphs.size(),
762  CHECK_LOCATION().AsString()));
763  }
764 
765  size_t subgraphIndex = 0;
766  size_t operatorIndex = 0;
767  try
768  {
769  for (SubgraphPtr const& subgraph : m_Model->subgraphs)
770  {
771  m_SubgraphConnections.emplace_back(subgraph->tensors.size());
772  for (OperatorPtr const& op : subgraph->operators)
773  {
774  auto const& opCodePtr = m_Model->operator_codes[op->opcode_index];
775 
776 // work around the introduction of the deprecated_builtin_code introduced in 2.4 in a backwards compatible manner
777 #if defined(ARMNN_POST_TFLITE_2_3)
778  auto builtinCode = std::max(opCodePtr->builtin_code,
779  static_cast<tflite::BuiltinOperator>(opCodePtr->deprecated_builtin_code));
780 #else
781  auto builtinCode = opCodePtr->builtin_code;
782 #endif
783 
784  if (builtinCode > tflite::BuiltinOperator_MAX)
785  {
786  throw ParseException(fmt::format("Operator code {} is out of range 0-{}. "
787  "subgraph:{} operator idx:{}. {}",
788  builtinCode, tflite::BuiltinOperator_MAX, subgraphIndex,
789  operatorIndex, CHECK_LOCATION().AsString()));
790  }
791 
792  // lookup and call the parser function
793  auto& parserFunction = m_ParserFunctions[builtinCode];
794  (this->*parserFunction)(subgraphIndex, operatorIndex);
795  ++operatorIndex;
796  }
797 
798  SetupInputLayers(subgraphIndex);
799  SetupOutputLayers(subgraphIndex);
800  SetupConstantLayers(subgraphIndex);
801 
802  ++subgraphIndex;
803  operatorIndex = 0;
804  }
805  }
806  catch (const ParseException& e)
807  {
808  std::stringstream errorString;
809  errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
810  << subgraphIndex << " error: " << e.what();
811  ARMNN_LOG(error) << errorString.str();
812  std::stringstream errors;
813  errors << errorString.str() << "\n";
814  throw ParseException(errors.str());
815  }
816 
817  // establish the connections from the layer outputs to the inputs of the subsequent layers
818  for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
819  {
820  for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
821  {
822  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
823  {
824  for (size_t inputSlotIdx = 0;
825  inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
826  ++inputSlotIdx)
827  {
828  m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
829  *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
830  }
831  }
832  }
833  }
834 
835  return std::move(m_Network);
836 }
837 
838 void TfLiteParserImpl::RegisterProducerOfTensor(size_t subgraphIndex,
839  size_t tensorIndex,
840  armnn::IOutputSlot* slot)
841 {
842  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
843  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
844  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
845 
846  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
847 
848  // assuming there is only one producer for that tensor
849  if (tensorSlots.outputSlot != nullptr)
850  {
851  throw ParseException(fmt::format("Another layer has already registered itself as the producer of "
852  "subgraph:{} tensor:{} {}",
853  subgraphIndex,
854  tensorIndex,
855  CHECK_LOCATION().AsString()));
856  }
857 
858  tensorSlots.outputSlot = slot;
859 }
860 
861 void TfLiteParserImpl::RegisterConsumerOfTensor(size_t subgraphIndex,
862  size_t tensorIndex,
863  armnn::IInputSlot* slot)
864 {
865  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
866  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
867  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
868 
869  TensorSlots& tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
870  tensorSlots.inputSlots.push_back(slot);
871 }
872 
873 void TfLiteParserImpl::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
874 {
875  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
876 
877  // NOTE: By default we presume the custom operator is not supported
878  auto customParserFunction = &TfLiteParserImpl::ParseUnsupportedOperator;
879 
880  // Identify custom code defined for custom operator
881  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
882  const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
883 
884  // Find parser function that correspondes to custom code (if any)
885  auto iterator = m_CustomParserFunctions.find(customCode);
886  if (iterator != m_CustomParserFunctions.end())
887  {
888  customParserFunction = iterator->second;
889  }
890 
891  // Run parser function
892  (this->*customParserFunction)(subgraphIndex, operatorIndex);
893 }
894 
895 void TfLiteParserImpl::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
896 {
897  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
898 
899  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
900 
901  auto opcodeIndex = operatorPtr->opcode_index;
902 
903 // work around the introduction of the deprecated_builtin_code introduced in 2.4 in a backwards compatible manner
904 #if defined(ARMNN_POST_TFLITE_2_3)
905  auto opcode = std::max(m_Model->operator_codes[opcodeIndex]->builtin_code,
906  static_cast<tflite::BuiltinOperator>(m_Model->operator_codes[opcodeIndex]->deprecated_builtin_code));
907 #else
908  auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
909 #endif
910 
911  if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
912  {
913  // Do not add StandInLayer, throw ParseException instead
914  throw ParseException(
915  fmt::format("Operator not supported. "
916  "subgraph:{} operator:{} "
917  "opcode_index:{} opcode:{} / {} {}",
918  subgraphIndex,
919  operatorIndex,
920  opcodeIndex,
921  opcode,
922  tflite::EnumNameBuiltinOperator(opcode),
923  CHECK_LOCATION().AsString()));
924  }
925 
926  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
927  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
928 
929  const unsigned int numInputs = armnn::numeric_cast<unsigned int>(inputs.size());
930  const unsigned int numOutputs = armnn::numeric_cast<unsigned int>(outputs.size());
931 
932  StandInDescriptor descriptor(numInputs, numOutputs);
933  auto layerName = fmt::format("StandIn:{}:{}:{}", subgraphIndex, operatorIndex, opcode);
934 
935  // Add a non-executable StandInLayer as a placeholder for any unsupported operator
936  IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
937  ARMNN_ASSERT(layer != nullptr);
938 
939  for (unsigned int i = 0u; i < numOutputs; ++i)
940  {
941  layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i], true));
942  }
943 
944  auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
945  auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
946 
947  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
948  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
949 }
950 
951 void TfLiteParserImpl::ParseCast(size_t subgraphIndex, size_t operatorIndex)
952 {
953  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
954 
955  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
956  CHECK_VALID_SIZE(inputs.size(), 1);
957  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
958  CHECK_VALID_SIZE(outputs.size(), 1);
959 
960  auto layerName = fmt::format("Cast:{}:{}", subgraphIndex, operatorIndex);
961 
962  IConnectableLayer* layer = m_Network->AddCastLayer(layerName.c_str());
963  ARMNN_ASSERT(layer != nullptr);
964 
965  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
966  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
967 
968  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
969  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
970 
971  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
972  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
973 }
974 
975 void TfLiteParserImpl::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
976 {
977  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
978 
979  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
980  const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
981 
982  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
983 
985  desc.m_BiasEnabled = false;
986  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
987  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
989  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
990  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
991 
992  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
993  CHECK_VALID_SIZE(inputs.size(), 2, 3);
994 
995  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
996  CHECK_VALID_SIZE(outputs.size(), 1);
997 
998  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
999  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1000 
1001  // assuming input is NHWC
1002  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1003  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1004 
1005  // assuming the filter is OHWI : Output, H, W, Input
1006  // which is essentially the same as NHWC
1007  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1008  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1009 
1010  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1011  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1012  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1013  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1014 
1015  auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
1016  armnn::IConnectableLayer* layer = nullptr;
1017 
1018  auto layerName = fmt::format("Conv2D:{}:{}", subgraphIndex, operatorIndex);
1019 
1020  if (inputs.size() == 3)
1021  {
1022  desc.m_BiasEnabled = true;
1023  armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
1024  auto biasTensorAndData = CreateConstTensorNonPermuted(inputs[2], biasTensorInfo);
1025  layer = m_Network->AddConvolution2dLayer(desc,
1026  filterTensorAndData,
1027  Optional<ConstTensor>(biasTensorAndData),
1028  layerName.c_str());
1029  }
1030  else
1031  {
1032  layer = m_Network->AddConvolution2dLayer(desc,
1033  filterTensorAndData,
1034  EmptyOptional(),
1035  layerName.c_str());
1036  }
1037 
1038  ARMNN_ASSERT(layer != nullptr);
1039 
1040  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1041  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1042 
1043  // register the input connection slots for the layer, connections are made after all layers have been created
1044  // only the tensors for the inputs are relevant, exclude the const tensors
1045  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1046  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1047 
1048  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1049  // register the output connection slots for the layer, connections are made after all layers have been created
1050  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1051  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1052 }
1053 
1054 // Conv3D support was added in TF 2.5, so for backwards compatibility a hash define is needed.
1055 #if defined(ARMNN_POST_TFLITE_2_3)
1056 void TfLiteParserImpl::ParseConv3D(size_t subgraphIndex, size_t operatorIndex)
1057 {
1058  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1059 
1060  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1061  const auto* options = operatorPtr->builtin_options.AsConv3DOptions();
1062 
1063  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1064 
1066  desc.m_BiasEnabled = false;
1068  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1069  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1070  desc.m_StrideZ = CHECKED_NON_NEGATIVE(options->stride_d);
1071  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1072  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1073  desc.m_DilationZ = CHECKED_NON_NEGATIVE(options->dilation_d_factor);
1074 
1075  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1076  CHECK_VALID_SIZE(inputs.size(), 2, 3);
1077 
1078  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1079  CHECK_VALID_SIZE(outputs.size(), 1);
1080 
1081  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1082  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1083 
1084  // Assuming input is NDHWC
1085  unsigned int inputDepth = inputTensorInfo.GetShape()[1];
1086  unsigned int inputHeight = inputTensorInfo.GetShape()[2];
1087  unsigned int inputWidth = inputTensorInfo.GetShape()[3];
1088 
1089  // Assuming the filter is DHWIO : Depth, Height, Width, OutputChannels, InputChannels
1090  unsigned int filterDepth = filterTensorInfo.GetShape()[0];
1091  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1092  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1093 
1094  CalcPadding(inputDepth, filterDepth, desc.m_StrideZ,
1095  desc.m_DilationY, desc.m_PadFront, desc.m_PadBack, options->padding);
1096  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1097  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1098  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1099  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1100 
1101  auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
1102 
1103  auto layerName = fmt::format("Conv3D:{}:{}", subgraphIndex, operatorIndex);
1104 
1105  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1106  // Add the first input and weights tensor to the registration list.
1107  // The constant weights will be added by SetupConstantLayers.
1108  std::vector<unsigned int> tensorIndexesToRegister = {inputTensorIndexes[0], inputTensorIndexes[1]};
1109 
1110  if (inputs.size() == 3)
1111  {
1112  desc.m_BiasEnabled = true;
1113 
1114  // Add the biases input to the registration list, a constant layer will be added by SetupConstantLayers.
1115  tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
1116  }
1117 
1118  armnn::IConnectableLayer* layer = m_Network->AddConvolution3dLayer(desc, layerName.c_str());
1119  ARMNN_ASSERT(layer != nullptr);
1120 
1121  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1122  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1123 
1124  // Register the input connection slots for the layer, connections are made after all layers have been created
1125  RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister);
1126 
1127  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1128  // Register the output connection slots for the layer, connections are made after all layers have been created
1129  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1130  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1131 }
1132 #endif
1133 
1134 void TfLiteParserImpl::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
1135 {
1136  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1137 
1138  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1139  const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
1140 
1141  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1142 
1144  desc.m_BiasEnabled = false;
1145  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1146  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1148  CHECKED_NON_NEGATIVE(options->depth_multiplier);
1149 
1150  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1151  CHECK_VALID_SIZE(inputs.size(), 2, 3);
1152  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1153  CHECK_VALID_SIZE(outputs.size(), 1);
1154  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1155  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1156 
1157  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1158  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1159 
1160  // Assuming input is NHWC
1161  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1162  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1163 
1164  // TensorflowLite weights come in the format [1, H, W, I * M]
1165  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1166  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1167 
1168  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1169  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1170  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1171  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1172 
1173  // ArmNN uses the same filter tensor layout at TfLite [1, H, W, O] no need for any permutation
1174  auto filterTensor = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
1175  armnn::IConnectableLayer* layer = nullptr;
1176  auto layerName = fmt::format("DepthwiseConv2D:{}:{}", subgraphIndex, operatorIndex);
1177 
1178  if (inputs.size() == 3)
1179  {
1180  desc.m_BiasEnabled = true;
1181  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
1182  auto biasTensorAndData = CreateConstTensorNonPermuted(inputs[2], biasTensorInfo);
1183  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1184  filterTensor,
1185  Optional<ConstTensor>(biasTensorAndData),
1186  layerName.c_str());
1187  }
1188  else
1189  {
1190  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1191  filterTensor,
1192  EmptyOptional(),
1193  layerName.c_str());
1194  }
1195  ARMNN_ASSERT(layer != nullptr);
1196 
1197  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1198  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1199 
1200  // register the input connection slots for the layer, connections are made after all layers have been created
1201  // only the tensors for the inputs are relevant, exclude the const tensors
1202  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1203  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1204 
1205  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1206  // register the output connection slots for the layer, connections are made after all layers have been created
1207  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1208  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1209 }
1210 
1211 void TfLiteParserImpl::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
1212 {
1213  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1214 
1215  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1216  CHECK_VALID_SIZE(inputs.size(), 1);
1217 
1218  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1219  CHECK_VALID_SIZE(outputs.size(), 1);
1220 
1221  auto layerName = fmt::format("Dequantize:{}:{}", subgraphIndex, operatorIndex);
1222 
1223  IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
1224  ARMNN_ASSERT(layer != nullptr);
1225 
1226  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1227  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1228 
1229  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1230  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1231 
1232  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1233  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1234 }
1235 
1236 void TfLiteParserImpl::ParseExpandDims(size_t subgraphIndex, size_t operatorIndex)
1237 {
1238  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1239 
1240  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1241  CHECK_VALID_SIZE(inputs.size(), 2);
1242 
1243  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1244  CHECK_VALID_SIZE(outputs.size(), 1);
1245 
1246  auto layerName = fmt::format("ExpandDims:{}:{}", subgraphIndex, operatorIndex);
1247 
1248  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1249  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1250 
1251  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1252 
1253  ReshapeDescriptor reshapeDesc;
1254 
1255  if (outputTensorInfo.GetShape().AreAllDimensionsSpecified())
1256  {
1257  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1258  }
1259  else
1260  {
1261  int32_t axis = inputs[1]->shape[0];
1262 
1263  int32_t inputDimSize = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions());
1264 
1265  if (axis > inputDimSize || axis < 0 - (inputDimSize + 1))
1266  {
1267  throw ParseException("axis must be in range [0 - (inputDimSize + 1), inputDimSize] inclusive");
1268  }
1269 
1270  if(axis < 0)
1271  {
1272  axis = inputDimSize + axis + 1;
1273  }
1274 
1275  std::vector<unsigned int> shape(static_cast<unsigned int>(inputDimSize) + 1);
1276  unsigned int inputShapeIndex = 0;
1277  for (unsigned int i = 0; i < static_cast<unsigned int>(inputDimSize + 1); ++i)
1278  {
1279  if (i == static_cast<unsigned int>(axis))
1280  {
1281  shape[i] = 1;
1282  }
1283  else
1284  {
1285  shape[i] = inputTensorInfo.GetShape()[inputShapeIndex];
1286  ++inputShapeIndex;
1287  }
1288  }
1289 
1290  reshapeDesc.m_TargetShape = TensorShape(static_cast<unsigned int>(inputDimSize + 1), shape.data());
1291  }
1292 
1293  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1294  ARMNN_ASSERT(layer != nullptr);
1295  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1296 
1297  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1298  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1299 
1300  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1301  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1302 }
1303 
1304 void TfLiteParserImpl::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1305 {
1306  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1307 
1308  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1309  CHECK_VALID_SIZE(inputs.size(), 1, 2);
1310 
1311  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1312  CHECK_VALID_SIZE(outputs.size(), 1);
1313 
1314  auto layerName = fmt::format("Transpose:{}:{}", subgraphIndex, operatorIndex);
1315  TransposeDescriptor desc;
1316 
1317  if (inputs.size() == 2)
1318  {
1319  armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1320  BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1321  auto numPermVecElements = permuteTensorInfo.GetNumElements();
1322  std::vector<unsigned int> permuteShape(numPermVecElements);
1323  ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
1324  PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
1325 
1326  desc = TransposeDescriptor(permutationVector);
1327  }
1328 
1329  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1330  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1331  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1332 
1333  IConnectableLayer* layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
1334  ARMNN_ASSERT(layer != nullptr);
1335  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1336 
1337  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1338  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1339 
1340  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1341  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1342 }
1343 
1344 void TfLiteParserImpl::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1345 {
1346  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1347 
1348  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1349  const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1350 
1352  desc.m_BiasEnabled = false;
1353  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1354  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1356 
1357  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1358  if (inputs.size() == 4)
1359  {
1360  desc.m_BiasEnabled = true;
1361  }
1362  else
1363  {
1364  CHECK_VALID_SIZE(inputs.size(), 3);
1365  }
1366 
1367  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1368  CHECK_VALID_SIZE(outputs.size(), 1);
1369 
1370  if (inputs[0])
1371  {
1372  armnn::TensorInfo tensorInfo = ToTensorInfo(inputs[0]);
1373  std::vector<int> output_shape(tensorInfo.GetNumElements());
1374  if (tensorInfo.GetDataType() == DataType::Signed32)
1375  {
1376  ::memcpy(output_shape.data(), GetBuffer(m_Model, inputs[0]->buffer)->data.data(), tensorInfo.GetNumBytes());
1377  }
1378  if (tensorInfo.GetDataType() == DataType::QAsymmU8)
1379  {
1380  for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
1381  {
1382  output_shape[i] = GetBuffer(m_Model, inputs[0]->buffer)->data.data()[i];
1383  }
1384  }
1385  // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
1386  for (int dimension : output_shape)
1387  {
1388  desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
1389  }
1390  desc.m_OutputShapeEnabled = true;
1391  }
1392  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
1393  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1394 
1395  // TfLite uses NHWC tensors
1396  const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1397  const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1398 
1399  const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1400  const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1401 
1402  CalcPadding(inputHeight,
1403  filterHeight,
1404  desc.m_StrideY,
1405  1, // DilationY
1406  desc.m_PadTop,
1407  desc.m_PadBottom,
1408  options->padding);
1409 
1410  CalcPadding(inputWidth,
1411  filterWidth,
1412  desc.m_StrideX,
1413  1, // DilationX
1414  desc.m_PadLeft,
1415  desc.m_PadRight,
1416  options->padding);
1417 
1418  auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
1419 
1420  armnn::IConnectableLayer* layer = nullptr;
1421  auto layerName = fmt::format("TransposeConv:{}:{}", subgraphIndex, operatorIndex);
1422 
1423  if (desc.m_BiasEnabled)
1424  {
1425  auto biasTensorInfo = ToTensorInfo(inputs[3]);
1426  auto biasConstTensor = CreateConstTensorNonPermuted(inputs[3], biasTensorInfo);
1427  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1428  filterTensorAndData,
1429  biasConstTensor,
1430  layerName.c_str());
1431  }
1432  else
1433  {
1434  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1435  filterTensorAndData,
1436  EmptyOptional(),
1437  layerName.c_str());
1438  }
1439 
1440  ARMNN_ASSERT(layer != nullptr);
1441 
1442  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1443  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1444 
1445  // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1446  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1447  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
1448 
1449  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1450  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1451 }
1452 
1453 void TfLiteParserImpl::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1454 {
1455  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1456 }
1457 
1458 void TfLiteParserImpl::ParseBatchToSpaceND(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 cropsTensorInfo = ToTensorInfo(inputs[2]);
1472  BufferRawPtr cropsBufferPtr = 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> cropsVector(cropsTensorInfo.GetNumElements());
1478  ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1479 
1480  size_t step = 2;
1481  std::vector<std::pair<unsigned int, unsigned int>> crops;
1482  for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1483  {
1484  crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1485  }
1486 
1488  desc.m_BlockShape = blockShape;
1489  desc.m_Crops = crops;
1491 
1492  auto layerName = fmt::format("BatchToSpaceND:{}:{}", 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->AddBatchToSpaceNdLayer(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 void TfLiteParserImpl::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1510 {
1511  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1512 
1513  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1514  CHECK_VALID_SIZE(inputs.size(), 1);
1515 
1516  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1517  CHECK_VALID_SIZE(outputs.size(), 1);
1518 
1521  auto layerName = fmt::format("L2Normalization:{}:{}", subgraphIndex, operatorIndex);
1522  IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1523 
1524  ARMNN_ASSERT(layer != nullptr);
1525 
1526  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1527  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1528 
1529  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1530  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1531 
1532  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1533  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1534 }
1535 
1536 void TfLiteParserImpl::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1537 {
1538  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1539 }
1540 
1541 void TfLiteParserImpl::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1542 {
1543  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1544 
1545  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1546  CHECK_VALID_SIZE(inputs.size(), 2);
1547 
1548  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1549  CHECK_VALID_SIZE(outputs.size(), 1);
1550 
1551  auto layerName = fmt::format("Maximum:{}:{}", subgraphIndex, operatorIndex);
1552 
1553  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1554  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1555  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1556 
1557  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1558  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1559 
1560  IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1561  ARMNN_ASSERT(layer != nullptr);
1562  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1563 
1564  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1565  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1566 
1567  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1568  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1569 }
1570 
1571 void TfLiteParserImpl::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1572 {
1573  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1574 
1575  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1576  CHECK_VALID_SIZE(inputs.size(), 2);
1577 
1578  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1579  CHECK_VALID_SIZE(outputs.size(), 1);
1580 
1581  auto layerName = fmt::format("Minimum:{}:{}", subgraphIndex, operatorIndex);
1582 
1583  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1584  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1585  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1586 
1587  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1588  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1589 
1590  IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1591  ARMNN_ASSERT(layer != nullptr);
1592  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1593 
1594  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1595  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1596 
1597  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1598  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1599 }
1600 
1601 void TfLiteParserImpl::ParsePool(size_t subgraphIndex,
1602  size_t operatorIndex,
1603  PoolingAlgorithm algorithm)
1604 {
1605  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1606 
1607  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1608  const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1609 
1610  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1611 
1612  std::string layerName;
1613 
1614  switch (algorithm)
1615  {
1616  case PoolingAlgorithm::Average:
1617  layerName =
1618  fmt::format("AveragePool2D:{}:{}", subgraphIndex, operatorIndex);
1619  break;
1620  case PoolingAlgorithm::Max:
1621  layerName =
1622  fmt::format("MaxPool2D:{}:{}", subgraphIndex, operatorIndex);
1623  break;
1624  default:
1625  ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1626  }
1627 
1628  Pooling2dDescriptor desc;
1629 
1630  desc.m_PoolType = algorithm;
1631  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1632  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1633  desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1634  desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1635  desc.m_PaddingMethod = PaddingMethod::Exclude;
1636  desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1638 
1639  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1640  CHECK_VALID_SIZE(inputs.size(), 1);
1641  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1642 
1643  // assuming input is NHWC
1644  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1645  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1646 
1647  CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1648  desc.m_PadTop, desc.m_PadBottom, options->padding);
1649  CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1650  desc.m_PadLeft, desc.m_PadRight, options->padding);
1651 
1652  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1653  CHECK_VALID_SIZE(outputs.size(), 1);
1654 
1655  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1656  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1657 
1658  IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1659  ARMNN_ASSERT(layer != nullptr);
1660  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1661 
1662  // register the input connection slots for the layer, connections are made after all layers have been created
1663  // only the tensors for the inputs are relevant, exclude the const tensors
1664  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1665  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1666 
1667  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1668  // register the output connection slots for the layer, connections are made after all layers have been created
1669  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1670  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1671 }
1672 
1673 void TfLiteParserImpl::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1674 {
1675  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1676 
1677  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1678  CHECK_VALID_SIZE(inputs.size(), 3);
1679  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1680  CHECK_VALID_SIZE(outputs.size(), 1);
1681 
1682  SliceDescriptor desc;
1683 
1684  // set begin tensor info for slice descriptor
1685  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1686  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1687 
1688  std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1689  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1690 
1691  // set size tensor info for slice descriptor
1692  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1693  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1694 
1695  std::vector<int> signedSize(sizeTensorInfo.GetNumElements());
1696  ::memcpy(signedSize.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1697  std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1698  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1699 
1700  for (unsigned int i = 0; i < signedSize.size(); ++i)
1701  {
1702  int signedValue = signedSize[i];
1703 
1704  if (signedValue < -1 || signedValue > static_cast<int>(inputTensorInfo.GetShape()[i] - begin[i]))
1705  {
1706  throw ParseException(fmt::format("Invalid value for size {} size must be in range "
1707  "[-1, inputDimSize - begin] [-1, {}] inclusive {}",
1708  signedValue,
1709  inputTensorInfo.GetShape()[i] - begin[i],
1710  CHECK_LOCATION().AsString()));
1711  }
1712 
1713  if (signedValue == -1)
1714  {
1715  size[i] = inputTensorInfo.GetShape()[i] - begin[i];
1716  }
1717  else
1718  {
1719  size[i] = static_cast<unsigned int>(signedValue);
1720  }
1721  }
1722 
1723  desc = SliceDescriptor(begin, size);
1724 
1725  auto layerName = fmt::format("Slice:{}:{}", subgraphIndex, operatorIndex);
1726 
1727  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1728  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1729 
1730  IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1731  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1732 
1733  // register the input connection slots for the layer, connections are made after all layers have been created
1734  // only the tensors for the inputs are relevant, exclude the const tensors
1735  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1736  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1737 
1738  // register the output connection slots for the layer, connections are made after all layers have been created
1739  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1740  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1741 }
1742 
1743 void TfLiteParserImpl::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1744 {
1745  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1746  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1747  const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1748 
1749  SoftmaxDescriptor desc;
1750  desc.m_Beta = options->beta;
1751 
1752  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1753  CHECK_VALID_SIZE(inputs.size(), 1);
1754  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1755  CHECK_VALID_SIZE(outputs.size(), 1);
1756 
1757  auto layerName = fmt::format("Softmax:{}:{}", subgraphIndex, operatorIndex);
1758  IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1759 
1760  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1761  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1762 
1763  // register the input connection slots for the layer, connections are made after all layers have been created
1764  // only the tensors for the inputs are relevant, exclude the const tensors
1765  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1766  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1767 
1768  // register the output connection slots for the layer, connections are made after all layers have been created
1769  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1770  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1771 }
1772 
1773 void TfLiteParserImpl::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1774 {
1775  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1776 
1777  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1778  CHECK_VALID_SIZE(inputs.size(), 3);
1779 
1780  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1781  CHECK_VALID_SIZE(outputs.size(), 1);
1782 
1783  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1784  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1785 
1786  armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1787  BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1788 
1789  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1790  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1791 
1792  std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1793  ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1794 
1795  size_t step = 2;
1796  std::vector<std::pair<unsigned int, unsigned int>> padList;
1797  for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1798  {
1799  padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1800  }
1801 
1803  desc.m_BlockShape = blockShape;
1804  desc.m_PadList = padList;
1806 
1807  auto layerName = fmt::format("SpaceToBatchND:{}:{}", subgraphIndex, operatorIndex);
1808 
1809  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1810  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1811  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1812 
1813  IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1814  ARMNN_ASSERT(layer != nullptr);
1815  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1816 
1817  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1818  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1819 
1820  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1821  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1822 }
1823 
1825  const armnn::TensorInfo & inputTensorInfo)
1826 {
1827  CHECK_VALID_SIZE(squeezeDims.size(), 0, 1, 2, 3, 4);
1828  static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1829 
1830  if (inputTensorInfo.GetNumDimensions() > 4)
1831  {
1832  std::stringstream ss;
1833  ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1834  << " shape:" << inputTensorInfo.GetShape() << " "
1835  << CHECK_LOCATION().AsString();
1836  throw ParseException(ss.str());
1837  }
1838 
1839  if (squeezeDims.empty())
1840  {
1841  squeezeDims.assign(dimensionSequence,
1842  dimensionSequence+inputTensorInfo.GetNumDimensions());
1843  }
1844 
1845  std::vector<uint32_t> outputDims;
1846  for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1847  {
1848  bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1849  auto currentDimension = inputTensorInfo.GetShape()[i];
1850  if (skipSqueeze || currentDimension != 1)
1851  {
1852  outputDims.push_back(currentDimension);
1853  }
1854  }
1855 
1856  if (outputDims.size() > 4)
1857  {
1858  std::stringstream ss;
1859  ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1860  << " shape:" << inputTensorInfo.GetShape() << " "
1861  << CHECK_LOCATION().AsString();
1862  throw ParseException(ss.str());
1863  }
1864 
1865  TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1866  outputDims.data());
1867 
1868  // we need to preserve the tensor type and the quantization data as well
1869  TensorInfo outTensorInfo = inputTensorInfo;
1870  outTensorInfo.SetShape(outShape);
1871 
1872  return outTensorInfo;
1873 }
1874 
1875 void TfLiteParserImpl::ParseShape(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  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1882  CHECK_VALID_SIZE(outputs.size(), 1);
1883 
1884  auto layerName = fmt::format("Shape:{}:{}", subgraphIndex, operatorIndex);
1885 
1886  IConnectableLayer* layer = m_Network->AddShapeLayer(layerName.c_str());
1887  ARMNN_ASSERT(layer != nullptr);
1888 
1889 
1890  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1891  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1892 
1893  // Check if output tensor type is Signed32 or Signed64
1894  if (outputTensorInfo.GetDataType() != armnn::DataType::Signed32 &&
1895  outputTensorInfo.GetDataType() != armnn::DataType::Signed64)
1896  {
1897  throw ParseException(
1898  fmt::format(
1899  "Output tensor data type is not supported. (Supported types: Signed32 & Signed64) {}",
1900  CHECK_LOCATION().AsString()));
1901  }
1902 
1903  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1904  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1905 
1906  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1907  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1908 }
1909 
1910 void TfLiteParserImpl::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1911 {
1912  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1913 
1914  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1915  CHECK_VALID_SIZE(inputs.size(), 1);
1916 
1917  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1918  CHECK_VALID_SIZE(outputs.size(), 1);
1919 
1920  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1921  const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1922  auto layerName = fmt::format("Squeeze:{}:{}", subgraphIndex, operatorIndex);
1923 
1924  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1925 
1926  std::vector<uint32_t> squeezeDim;
1927  // A single negative dim index is interpreted as a negative index in python
1928  // Meaning the index will be the shape size plus the negative index value
1929  if (options->squeeze_dims.size() == 1 && options->squeeze_dims[0] < 0)
1930  {
1931  int32_t dim = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions()) + options->squeeze_dims[0];
1932  squeezeDim.push_back(static_cast<uint32_t>(dim));
1933  }
1934  else
1935  {
1936  squeezeDim = AsUnsignedVector(options->squeeze_dims);
1937  }
1938 
1939  armnn::TensorInfo outputTensorInfo = TfLiteParserImpl::OutputShapeOfSqueeze(squeezeDim, inputTensorInfo);
1940 
1941  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1942 
1943  ReshapeDescriptor reshapeDesc;
1944  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1945 
1946  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1947  ARMNN_ASSERT(layer != nullptr);
1948  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1949 
1950  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1951  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1952 
1953  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1954  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1955 }
1956 
1957 void TfLiteParserImpl::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1958 {
1959  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1960 
1961  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1962  CHECK_VALID_SIZE(inputs.size(), 4);
1963 
1964  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1965  CHECK_VALID_SIZE(outputs.size(), 1);
1966 
1967  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1968  const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1969 
1971  desc.m_BeginMask = options->begin_mask;
1972  desc.m_EllipsisMask = options->ellipsis_mask;
1973  desc.m_EndMask = options->end_mask;
1974  desc.m_NewAxisMask = options->new_axis_mask;
1975  desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1977 
1978  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1979  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1980 
1981  std::vector<int> begin(beginTensorInfo.GetNumElements());
1982  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1983 
1984  armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1985  BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1986 
1987  std::vector<int> end(endTensorInfo.GetNumElements());
1988  ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1989 
1990  armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1991  BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1992 
1993  std::vector<int> stride(strideTensorInfo.GetNumElements());
1994  ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1995 
1996  desc.m_Begin = begin;
1997  desc.m_End = end;
1998  desc.m_Stride = stride;
1999 
2000  auto layerName = fmt::format("StridedSlice:{}:{}", subgraphIndex, operatorIndex);
2001  IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
2002  ARMNN_ASSERT(layer != nullptr);
2003 
2004  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2005  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2006 
2007  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2008  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2009 
2010  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2011  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2012 }
2013 
2014 void TfLiteParserImpl::ParseSub(size_t subgraphIndex, size_t operatorIndex)
2015 {
2016  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2017 
2018  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2019  const auto * options = operatorPtr->builtin_options.AsSubOptions();
2020 
2021  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2022  CHECK_VALID_SIZE(inputs.size(), 2);
2023 
2024  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2025  CHECK_VALID_SIZE(outputs.size(), 1);
2026 
2027  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2028  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
2029 
2030  auto layerName = fmt::format("Sub:{}:{}", subgraphIndex, operatorIndex);
2031  IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
2032  ARMNN_ASSERT(layer != nullptr);
2033 
2034  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2035  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2036 
2037  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2038  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2039 
2040  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2041 
2042  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2043  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2044 }
2045 
2046 void TfLiteParserImpl::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
2047 {
2048  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2049 
2050  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2051  const auto * options = operatorPtr->builtin_options.AsDivOptions();
2052 
2053  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2054  CHECK_VALID_SIZE(inputs.size(), 2);
2055 
2056  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2057  CHECK_VALID_SIZE(outputs.size(), 1);
2058 
2059  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2060  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
2061 
2062  auto layerName = fmt::format("Div:{}:{}", subgraphIndex, operatorIndex);
2063  IConnectableLayer* layer = m_Network->AddDivisionLayer(layerName.c_str());
2064  ARMNN_ASSERT(layer != nullptr);
2065 
2066  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2067  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2068 
2069  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2070  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2071  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2072 
2073  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2074  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2075 }
2076 
2077 void TfLiteParserImpl::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
2078 {
2079  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2080 
2081  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2082  const auto * options = operatorPtr->builtin_options.AsAddOptions();
2083 
2084  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2085  CHECK_VALID_SIZE(inputs.size(), 2);
2086 
2087  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2088  CHECK_VALID_SIZE(outputs.size(), 1);
2089 
2090  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2091  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
2092 
2093  auto layerName = fmt::format("Add:{}:{}", subgraphIndex, operatorIndex);
2094  IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
2095  ARMNN_ASSERT(layer != nullptr);
2096 
2097  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2098  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2099 
2100  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2101  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2102  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2103 
2104  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2105  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2106 }
2107 
2108 void TfLiteParserImpl::ParseMul(size_t subgraphIndex, size_t operatorIndex)
2109 {
2110  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2111 
2112  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2113  const auto * options = operatorPtr->builtin_options.AsMulOptions();
2114 
2115  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2116  CHECK_VALID_SIZE(inputs.size(), 2);
2117 
2118  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2119  CHECK_VALID_SIZE(outputs.size(), 1);
2120 
2121  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2122  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
2123 
2124  auto layerName = fmt::format("Mul:{}:{}", subgraphIndex, operatorIndex);
2125  IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
2126  ARMNN_ASSERT(layer != nullptr);
2127 
2128  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2129  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2130 
2131  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2132  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2133  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2134 
2135  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2136  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2137 }
2138 
2139 void TfLiteParserImpl::ParseMean(size_t subgraphIndex, size_t operatorIndex)
2140 {
2141  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2142 
2143  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2144 
2145  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2146  CHECK_VALID_SIZE(outputs.size(), 1);
2147 
2148  armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
2149  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2150 
2151  armnn::MeanDescriptor desc;
2152  std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
2153  ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
2154  desc.m_Axis = axis;
2155 
2156  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2157  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2158 
2159  desc.m_KeepDims =
2160  inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
2161  true : false;
2162 
2163  auto layerName = fmt::format("Mean:{}:{}", subgraphIndex, operatorIndex);
2164  IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
2165  ARMNN_ASSERT(layer != nullptr);
2166 
2167  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2168 
2169  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2170  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2171 
2172  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2173  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2174 }
2175 
2176 void TfLiteParserImpl::ParsePad(size_t subgraphIndex, size_t operatorIndex)
2177 {
2178  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2179 
2180  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2181 
2182  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2183  CHECK_VALID_SIZE(outputs.size(), 1);
2184 
2185  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2186 
2187  armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
2188  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2189 
2190  std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
2191  ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
2192 
2193  size_t step = 2;
2194  armnn::PadDescriptor desc;
2195  if (inputTensorInfo.IsQuantized())
2196  {
2197  desc.m_PadValue = static_cast<float>(inputTensorInfo.GetQuantizationOffset());
2198  }
2199  for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
2200  {
2201  desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
2202  }
2203 
2204  auto layerName = fmt::format("Pad:{}:{}", subgraphIndex, operatorIndex);
2205  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2206 
2207  IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
2208  ARMNN_ASSERT(layer != nullptr);
2209  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2210 
2211  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2212  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2213 
2214  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2215  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2216 }
2217 
2218 void TfLiteParserImpl::ParseMirrorPad(size_t subgraphIndex, size_t operatorIndex)
2219 {
2220  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2221 
2222  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2223  CHECK_VALID_SIZE(inputs.size(), 2);
2224 
2225  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2226  CHECK_VALID_SIZE(outputs.size(), 1);
2227 
2228  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2229 
2230  armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
2231  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2232 
2233  std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
2234  ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
2235 
2236  size_t step = 2;
2237  armnn::PadDescriptor desc;
2238  for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
2239  {
2240  desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
2241  }
2242 
2243  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2244  const auto* options = operatorPtr->builtin_options.AsMirrorPadOptions();
2245 
2246  if (options->mode == tflite::MirrorPadMode_REFLECT)
2247  {
2248  desc.m_PaddingMode = PaddingMode::Reflect;
2249  }
2250  else if (options->mode == tflite::MirrorPadMode_SYMMETRIC)
2251  {
2252  desc.m_PaddingMode = PaddingMode::Symmetric;
2253  }
2254  else
2255  {
2256  ARMNN_THROW_PARSE_EXCEPTION("PaddingMode must be either REFLECT or SYMMETRIC");
2257  }
2258 
2259  // If padding mode is Reflect then both paddings must be no greater than inputShape(i) - 1.
2260  // If padding mode is Symmetric then both paddings must be no greater than inputShape(i).
2261  auto inputShape = inputTensorInfo.GetShape();
2262  auto padList = desc.m_PadList;
2263 
2264  const unsigned int isReflect = static_cast<unsigned int>(desc.m_PaddingMode == PaddingMode::Reflect);
2265  for(unsigned int i = 0; i < padList.size(); ++i)
2266  {
2267  if(padList.at(i).first > (inputShape[i] - isReflect) ||
2268  padList.at(i).second > (inputShape[i] - isReflect))
2269  {
2270  ARMNN_THROW_PARSE_EXCEPTION("Padding values must be less (Reflect) or "
2271  "equal (Symmetric) to the dimension size.");
2272  }
2273  }
2274 
2275  auto layerName = fmt::format("MirrorPad:{}:{}", subgraphIndex, operatorIndex);
2276  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2277 
2278  IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
2279  ARMNN_ASSERT(layer != nullptr);
2280  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2281 
2282  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2283  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2284 
2285  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2286  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2287 }
2288 
2289 void TfLiteParserImpl::ParsePrelu(size_t subgraphIndex, size_t operatorIndex)
2290 {
2291  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2292 
2293  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2294  CHECK_VALID_SIZE(inputs.size(), 2);
2295 
2296  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2297  CHECK_VALID_SIZE(outputs.size(), 1);
2298 
2299  auto layerName = fmt::format("Prelu:{}:{}", subgraphIndex, operatorIndex);
2300 
2301  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2302  armnn::TensorInfo alphaTensorInfo = ToTensorInfo(inputs[1]);
2303  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2304  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2305 
2306  IConnectableLayer* layer = m_Network->AddPreluLayer(layerName.c_str());
2307  ARMNN_ASSERT(layer != nullptr);
2308  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2309 
2310  if (IsConstTensor(inputs[1]))
2311  {
2312  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2313  armnn::IInputSlot* slot = &(layer->GetInputSlot(0));
2314  RegisterConsumerOfTensor(subgraphIndex, inputTensorIndexes[0], slot);
2315 
2316  auto alphaTensorAndData = CreateConstTensorNonPermuted(inputs[1], alphaTensorInfo);
2317  std::string constLayerName = fmt::format("Constant:{}", inputs[1]->name);
2318  IConnectableLayer* constLayer =
2319  m_Network->AddConstantLayer(alphaTensorAndData, constLayerName.c_str());
2320  ARMNN_ASSERT(constLayer != nullptr);
2321 
2322  constLayer->GetOutputSlot(0).SetTensorInfo(alphaTensorInfo);
2323  constLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
2324  RegisterOutputSlots(subgraphIndex,
2325  VIRTUAL_OPERATOR_ID,
2326  constLayer,
2327  { inputTensorIndexes[1] });
2328  }
2329  else
2330  {
2331  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2332  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIndexes);
2333  }
2334 
2335  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2336  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2337 }
2338 
2339 void TfLiteParserImpl::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
2340 {
2341  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2342 
2343  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2344  CHECK_VALID_SIZE(inputs.size(), 1);
2345 
2346  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2347  CHECK_VALID_SIZE(outputs.size(), 1);
2348 
2349  auto layerName = fmt::format("Quantize:{}:{}", subgraphIndex, operatorIndex);
2350 
2351  IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
2352  ARMNN_ASSERT(layer != nullptr);
2353 
2354  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2355  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2356 
2357  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2358  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2359 
2360  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2361  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2362 }
2363 
2364 void TfLiteParserImpl::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
2365 {
2366  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
2367 }
2368 
2369 void TfLiteParserImpl::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
2370 {
2371  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
2372 }
2373 
2374 void TfLiteParserImpl::ParseLeakyRelu(size_t subgraphIndex, size_t operatorIndex)
2375 {
2376  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::LeakyReLu);
2377 }
2378 
2379 void TfLiteParserImpl::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
2380 {
2381  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
2382 }
2383 
2384 void TfLiteParserImpl::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
2385 {
2386  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
2387 }
2388 
2389 void TfLiteParserImpl::ParseElu(size_t subgraphIndex, size_t operatorIndex)
2390 {
2391  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::Elu);
2392 }
2393 
2394 void TfLiteParserImpl::ParseHardSwish(size_t subgraphIndex, size_t operatorIndex)
2395 {
2396  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::HardSwish);
2397 }
2398 
2399 void TfLiteParserImpl::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
2400 {
2401  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2402  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2403  IgnoreUnused(operatorPtr);
2404 
2405  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2406  CHECK_VALID_SIZE(inputs.size(), 1);
2407 
2408  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2409  CHECK_VALID_SIZE(outputs.size(), 1);
2410 
2411  auto layerName = fmt::format("Activation:");
2412  ActivationDescriptor activationDesc;
2413  activationDesc.m_Function = activationType;
2414 
2415  switch (activationType)
2416  {
2417  case ActivationFunction::ReLu:
2418  {
2419  layerName += fmt::format("RELU:{}:{}", subgraphIndex, operatorIndex);
2420  break;
2421  }
2422  case ActivationFunction::BoundedReLu:
2423  {
2424  layerName += fmt::format("RELU6:{}:{}", subgraphIndex, operatorIndex);
2425  activationDesc.m_A = 6.0f;
2426  activationDesc.m_B = 0.0f;
2427  break;
2428  }
2429  case ActivationFunction::Sigmoid:
2430  {
2431  layerName += fmt::format("SIGMOID:{}:{}", subgraphIndex, operatorIndex);
2432  break;
2433  }
2434  case ActivationFunction::TanH:
2435  {
2436  layerName += fmt::format("TANH:{}:{}", subgraphIndex, operatorIndex);
2437  activationDesc.m_A = 1.0f;
2438  activationDesc.m_B = 1.0f;
2439  break;
2440  }
2441  case ActivationFunction::LeakyReLu:
2442  {
2443  layerName += fmt::format("LEAKYRELU:{}:{}", subgraphIndex, operatorIndex);
2444  const auto * options = operatorPtr->builtin_options.AsLeakyReluOptions();
2445  activationDesc.m_A = options->alpha;
2446  break;
2447  }
2448  case ActivationFunction::Elu:
2449  {
2450  layerName += fmt::format("ELU:{}:{}", subgraphIndex, operatorIndex);
2451  activationDesc.m_A = 1.0f;
2452  break;
2453  }
2454  case ActivationFunction::HardSwish:
2455  {
2456  layerName += fmt::format("HARDSWISH:{}:{}", subgraphIndex, operatorIndex);
2457  break;
2458  }
2459  default:
2460  {
2461  throw ParseException(
2462  fmt::format("Unexpected ActivationFunction[{}] when creating layerName {} ",
2463  static_cast<int>(activationType), CHECK_LOCATION().AsString()));
2464  }
2465  }
2466 
2467  IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2468 
2469  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2470  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2471 
2472  // register the input connection slots for the layer, connections are made after all layers have been created
2473  // only the tensors for the inputs are relevant, exclude the const tensors
2474  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2475  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2476 
2477  // register the output connection slots for the layer, connections are made after all layers have been created
2478  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2479  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2480 }
2482  const std::vector<int32_t> & targetDimsIn)
2483 {
2484  std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
2485  const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
2486 
2487  if (stretchDim != targetDimsIn.end())
2488  {
2489  if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
2490  {
2491  throw ParseException(
2492  fmt::format("At most one component of shape can be -1 {}", CHECK_LOCATION().AsString()));
2493  }
2494 
2495  auto targetNumElements =
2496  armnn::numeric_cast<unsigned int>(
2497  std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
2498 
2499  auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
2500  outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
2501  }
2502 
2503  TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
2504 
2505  TensorInfo reshapeInfo = inputTensorInfo;
2506  reshapeInfo.SetShape(outputShape);
2507 
2508  return reshapeInfo;
2509 }
2510 
2511 void TfLiteParserImpl::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
2512 {
2513  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2514 
2515  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2516 
2517  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2518  CHECK_VALID_SIZE(outputs.size(), 1);
2519 
2520  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2521  const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
2522  auto layerName = fmt::format("Reshape:{}:{}", subgraphIndex, operatorIndex);
2523 
2524  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2525  armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
2526  CheckMatchingQuantization(inputTensorInfo, actualOutputTensorInfo, layerName, "Input 0", "Output 0");
2527 
2528  // Extracting new shape for the output
2529  // There are two ways it can be passed
2530  // * First is to define the target shape in the operator built-in options
2531  // * Second is to pass it as a second input tensor
2532  std::vector<int32_t> targetShape;
2533  bool targetShapeFound = false;
2534  // Check if built-in options were given
2535  if (options != nullptr)
2536  {
2537  // make sure the parameter is given
2538  if (options->new_shape.empty() == false)
2539  {
2540  targetShape = options->new_shape;
2541  targetShapeFound = true;
2542  }
2543  }
2544 
2545  // If there is no built-in option given or if the built-in new_shape parameter was empty
2546  if (!targetShapeFound)
2547  {
2548  // Check for a second input tensor
2549  if (inputs.size() > 1 && inputs[1] != nullptr)
2550  {
2551  if (inputs[1]->is_variable)
2552  {
2553  ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
2554  }
2555 
2556  if (inputs[1]->shape.size() != 1)
2557  {
2558  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
2559  }
2560 
2561  if (inputs[1]->type != tflite::TensorType_INT32)
2562  {
2563  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
2564  }
2565 
2566  // Extract target shape from input
2567  auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2568  auto values = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
2569  if (!values)
2570  {
2571  ARMNN_THROW_PARSE_EXCEPTION("Reshape operator target shape input buffer data is null");
2572  }
2573  for (int i=0; i < inputs[1]->shape[0]; ++i)
2574  {
2575  targetShape.push_back(values[i]);
2576  }
2577  }
2578  else
2579  {
2580  ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2581  "At least one method required");
2582  }
2583  }
2584 
2585  armnn::TensorInfo reshapeOutputTensorInfo =
2586  TfLiteParserImpl::OutputShapeOfReshape(inputTensorInfo, targetShape);
2587 
2588  // Check for valid input size and that reshape parameters equal output shape
2589  const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2590  if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
2591  {
2592  std::stringstream ss;
2593  ss << "New shape defined in reshape parameters "
2594  << reshapeOutputTensorShape
2595  << " does not equal output shape "
2596  << actualOutputTensorInfo.GetShape()
2597  << ": "
2598  << CHECK_LOCATION().AsString();
2599  throw ParseException(ss.str());
2600  }
2601 
2602  ReshapeDescriptor reshapeDesc;
2603  reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
2604 
2605  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
2606  ARMNN_ASSERT(layer != nullptr);
2607  layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
2608 
2609  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2610  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2611 
2612  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2613  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2614 }
2615 
2616 void TfLiteParserImpl::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2617 {
2618  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2619 }
2620 
2621 void TfLiteParserImpl::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2622 {
2623  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2624 }
2625 
2626 void TfLiteParserImpl::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2627 {
2628  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2629 
2630  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2631  CHECK_VALID_SIZE(inputs.size(), 2);
2632 
2633  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2634  CHECK_VALID_SIZE(outputs.size(), 1);
2635 
2636  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2637 
2638  // Data for the parsed tensor args (size) must be stored locally.
2639  std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2640 
2641  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2642  ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2643 
2644  ResizeDescriptor desc;
2645  desc.m_Method = resizeMethod;
2646  desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
2647  desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2648  desc.m_DataLayout = armnn::DataLayout::NHWC;
2649 
2650  auto layerName = fmt::format("Resize:");
2651 
2652  switch (resizeMethod)
2653  {
2654  case ResizeMethod::Bilinear:
2655  {
2656  layerName += fmt::format("BILINEAR:{}:{}", subgraphIndex, operatorIndex);
2657 
2658  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2659  const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2660 
2661  desc.m_AlignCorners = options->align_corners;
2662  break;
2663  }
2664  case ResizeMethod::NearestNeighbor:
2665  {
2666  layerName += fmt::format("NEARESTNEIGHBOR:{}:{}", subgraphIndex, operatorIndex);
2667  break;
2668  }
2669  default:
2670  {
2671  throw ParseException(
2672  fmt::format("Unexpected ResizeMethod[{}] when creating layerName {} ",
2673  static_cast<int>(resizeMethod), CHECK_LOCATION().AsString()));
2674  }
2675  }
2676 
2677  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2678  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2679  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2680 
2681  IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
2682  ARMNN_ASSERT(layer != nullptr);
2683  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2684 
2685  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2686  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2687 
2688  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2689  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2690 }
2691 
2692 void TfLiteParserImpl::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2693 {
2694  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2695 
2696  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2697  const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2698 
2699  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2700 
2701  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2702  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2703  CHECK_VALID_SIZE(outputs.size(), 1);
2704 
2705  unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2706  uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
2707 
2708  const unsigned int concatDimInput = static_cast<unsigned int>(
2709  (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
2710 
2711  OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2712  concatDescriptor.SetConcatAxis(concatDimInput);
2713 
2714  unsigned int mergeDimOrigin = 0;
2715 
2716  for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2717  {
2718  TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2719 
2720  // This set up concatDescriptor view origin
2722  inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
2723  }
2724 
2725  auto layerName = fmt::format("Concatenation:{}:{}", subgraphIndex, operatorIndex);
2726  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2727 
2728  IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
2729  ARMNN_ASSERT(layer != nullptr);
2730  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2731 
2732  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2733  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2734 
2735  // add fused activation layer
2736  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2737 
2738  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2739  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2740 }
2741 
2742 void TfLiteParserImpl::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2743 {
2744  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2745 
2746  const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2747  const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2748 
2749  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2750 
2752  desc.m_BiasEnabled = false;
2753  desc.m_TransposeWeightMatrix = true;
2754 
2755  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2756  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2757  CHECK_VALID_SIZE(outputs.size(), 1);
2758 
2759  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2760 
2761  // Fully Connected Layer accepts two dimensional weights input
2762  int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2763  if (weightsDimension != 2)
2764  {
2765  throw ParseException(
2766  fmt::format("Dimension {} for Fully Connected weights is not supported by Armnn. "
2767  "Node {}",
2768  weightsDimension,
2769  CHECK_LOCATION().AsString()));
2770  }
2771 
2772  armnn::IConnectableLayer* layer = nullptr;
2773  auto layerName = fmt::format("FullyConnected:{}:{}", subgraphIndex, operatorIndex);
2774 
2775  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2776  // Add the first input tensor to the registration list
2777  std::vector<unsigned int> tensorIndexesToRegister = {inputTensorIndexes[0]};
2778  std::vector<unsigned int> ignoreInputWhenRegister = {};
2779 
2780  desc.m_ConstantWeights = IsConstTensor(inputs[1]);
2781 
2782  // Add the weights input to the registration list, constant layers will be added by SetupConstantLayers if constant.
2783  tensorIndexesToRegister.emplace_back(inputTensorIndexes[1]);
2784 
2785  if (inputs.size() == 3)
2786  {
2787  desc.m_BiasEnabled = true;
2788 
2789  // Add the biases input to the registration list, constant layer will be added by SetupConstantLayers.
2790  tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
2791  }
2792 
2793  // Filters and biases are always passed to fully connected as inputs
2794  layer = m_Network->AddFullyConnectedLayer(desc, layerName.c_str());
2795 
2796  ARMNN_ASSERT(layer != nullptr);
2797  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2798 
2799  unsigned int startingSlotIndex = 0;
2800  if (inputTensorInfo.GetNumDimensions() > 2)
2801  {
2802  // Add reshape to flatten to 2D [batch_size, input_size],
2803  // where "input_size" corresponds to the number of inputs to the layer,
2804  // matching the second dimension of weights,
2805  // and "batch_size" is calculated by dividing the number of elements by "input_size".
2806  std::vector<unsigned int> reshapedDimensions(2);
2807  reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2808  reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2809 
2810  if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2811  {
2812  throw ParseException(
2813  fmt::format("Failed to deduce input tensor shape from filter size {} {}",
2814  reshapedDimensions[1],
2815  CHECK_LOCATION().AsString()));
2816  }
2817 
2818  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2819  reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2820 
2821  std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
2822  armnn::ReshapeDescriptor reshapeDescriptor;
2823  reshapeDescriptor.m_TargetShape = reshapedTensorInfo.GetShape();
2824  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(reshapeDescriptor, layerName.c_str());
2825 
2826  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2827  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2828 
2829  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2830  // Fc layer connects to the reshape layer, so we skip the first input slot when registering fc's input slots
2831  tensorIndexesToRegister.erase(tensorIndexesToRegister.begin());
2832  startingSlotIndex = 1;
2833  }
2834 
2835  RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister, startingSlotIndex);
2836 
2837  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2838  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2839 
2840  // we need to add the activation layer and fortunately we don't need to care about the data layout
2841  armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2842  options->fused_activation_function);
2843 
2844  // register the output connection slots for the layer, connections are made after all layers have been created
2845  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2846  RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2847 }
2848 
2849 void TfLiteParserImpl::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2850 {
2851  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2852 
2853  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2854 
2855  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2856  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2857  CHECK_VALID_SIZE(outputs.size(), 4);
2858 
2859  // Obtain custom options from flexbuffers
2860  auto custom_options = operatorPtr->custom_options;
2861  const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2862 
2863  // Obtain descriptor information from tf lite
2865  desc.m_MaxDetections = m["max_detections"].AsUInt32();
2866  desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2867  desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2868  desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2869  desc.m_NumClasses = m["num_classes"].AsUInt32();
2870  desc.m_ScaleH = m["h_scale"].AsFloat();
2871  desc.m_ScaleW = m["w_scale"].AsFloat();
2872  desc.m_ScaleX = m["x_scale"].AsFloat();
2873  desc.m_ScaleY = m["y_scale"].AsFloat();
2874 
2875  if (!(m["use_regular_nms"].IsNull()))
2876  {
2877  desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
2878  }
2879  if (!(m["detections_per_class"].IsNull()))
2880  {
2881  desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2882  }
2883 
2884  if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2885  {
2886  throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2887  "must be positive and less than or equal to 1.");
2888  }
2889 
2890  armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2891  auto anchorTensorAndData = CreateConstTensorNonPermuted(inputs[2], anchorTensorInfo);
2892 
2893  auto layerName = fmt::format("DetectionPostProcess:{}:{}", subgraphIndex, operatorIndex);
2894  IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData,
2895  layerName.c_str());
2896 
2897  ARMNN_ASSERT(layer != nullptr);
2898 
2899  // The model does not specify the output shapes.
2900  // The output shapes are calculated from the max_detection and max_classes_per_detection.
2901  unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2902  m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2903  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2904  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2905  m_OverridenOutputShapes.push_back({ 1 });
2906 
2907  for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2908  {
2909  armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
2910  layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2911  }
2912 
2913  // Register the input connection slots for the layer, connections are made after all layers have been created
2914  // only the tensors for the inputs are relevant, exclude the const tensors
2915  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2916  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2917 
2918  // Register the output connection slots for the layer, connections are made after all layers have been created
2919  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2920  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2921  outputTensorIndexes[1],
2922  outputTensorIndexes[2],
2923  outputTensorIndexes[3]});
2924 }
2925 
2926 /// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2927 void TfLiteParserImpl::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2928 {
2929  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2930 
2931  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2932  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2933  CHECK_VALID_SIZE(outputs.size(), 1);
2934 
2935  if (inputs.size() < 1)
2936  {
2937  throw ParseException("Pack must have at least one input.");
2938  }
2939 
2940  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2941  const auto* options = operatorPtr->builtin_options.AsPackOptions();
2942 
2943  StackDescriptor desc;
2944  desc.m_Axis = static_cast<uint32_t>(options->axis);
2945  desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2946 
2947  // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2948  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2949  desc.m_InputShape = inputTensorInfo.GetShape();
2950 
2951  auto layerName = fmt::format("Pack:{}:{}", subgraphIndex, operatorIndex);
2952  IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2953 
2954  ARMNN_ASSERT(layer != nullptr);
2955 
2956  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2957  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2958 
2959  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2960  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2961 
2962  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2963  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2964 }
2965 
2966 void TfLiteParserImpl::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2967 {
2968  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2969 
2970  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2971  const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2972 
2973  // This unpackAxis indicates the axis to unpack
2974  const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2975 
2976  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2977  CHECK_VALID_SIZE(inputs.size(), 1);
2978 
2979  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2980 
2981  if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2982  {
2983  throw ParseException(
2984  fmt::format("The unpack axis: {} cannot be greater than or equal to "
2985  "the number of input dimension {} {}",
2986  unpackAxis,
2987  inputTensorInfo.GetNumDimensions(),
2988  CHECK_LOCATION().AsString()));
2989  }
2990 
2991  unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2992  // If num is not defined, automatically infer from the length of the dimension axis.
2993  if(unpackNum == 0)
2994  {
2995  unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2996  }
2997 
2998  // If unpack number cannot be inferred and is still zero, throw ParseException.
2999  if(unpackNum == 0)
3000  {
3001  throw ParseException("Number to unpack must greater than zero.");
3002  }
3003 
3004  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3005  CHECK_VALID_SIZE(outputs.size(), unpackNum);
3006 
3007  auto inputDimSize = inputTensorInfo.GetNumDimensions();
3008  std::vector<unsigned int> unpackDimSizes(inputDimSize);
3009 
3010  // Add current input shape to unpackDimSizes
3011  for (unsigned int i = 0; i < inputDimSize; ++i)
3012  {
3013  unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
3014  }
3015 
3016  if (unpackDimSizes[unpackAxis] != unpackNum)
3017  {
3018  throw ParseException("Number to unpack must be the same as length of the dimension to "
3019  "unpack along.");
3020  }
3021 
3022  unpackDimSizes[unpackAxis] /= unpackNum;
3023 
3024  SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
3025  for (unsigned int j = 0; j < unpackNum; ++j)
3026  {
3027  // Set the size of the views.
3028  for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
3029  {
3030  splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
3031  }
3032  splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
3033  }
3034 
3035  auto layerName = fmt::format("Unpack:{}:{}", subgraphIndex, operatorIndex);
3036  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
3037  ARMNN_ASSERT(layer != nullptr);
3038 
3039  TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
3040  unpackDimSizes.data());
3041 
3042  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3043  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3044 
3045  std::vector<unsigned int> reshapeDims;
3046  for (unsigned int axis = 0; axis < splitOutShape.GetNumDimensions(); ++axis)
3047  {
3048  if (axis != unpackAxis)
3049  {
3050  reshapeDims.push_back(splitOutShape[axis]);
3051  }
3052  }
3053 
3054  TensorShape reshapeOutputShape(splitOutShape.GetNumDimensions() -1, reshapeDims.data());
3055 
3056  // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
3057  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
3058  {
3059  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k], true);
3060  std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
3062  desc.m_TargetShape = reshapeOutputShape;
3063  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
3064 
3065  layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
3066  outputTensorInfo.GetDataType(),
3067  outputTensorInfo.GetQuantizationScale(),
3068  outputTensorInfo.GetQuantizationOffset()));
3069  layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
3070 
3071  reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3072 
3073  uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
3074  armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
3075  RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
3076  }
3077 }
3078 
3079 void TfLiteParserImpl::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
3080 {
3081  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3082 
3083  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3084  const auto * options = operatorPtr->builtin_options.AsSplitOptions();
3085 
3086  const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
3087 
3088  // If number of splits cannot be inferred and is zero, throw ParseException.
3089  if(numSplits == 0)
3090  {
3091  throw ParseException("Number to splits must greater than zero.");
3092  }
3093 
3094  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3095  CHECK_VALID_SIZE(inputs.size(), 2);
3096  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3097  CHECK_VALID_SIZE(outputs.size(), numSplits);
3098 
3099  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
3100  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
3101  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
3102 
3103  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3104  if (axisBufferPtr == nullptr)
3105  {
3106  throw ParseException(
3107  fmt::format("Operation has invalid inputs. Failed to read axis. {}",
3108  CHECK_LOCATION().AsString()));
3109  }
3110 
3111  std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
3112  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
3113  int32_t axis = axisData[0];
3114 
3115  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3116  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3117  {
3118  // Square bracket denotes inclusive n while parenthesis denotes exclusive n
3119  // E.g. Rank 4 tensor can have axis in range [-4, 3)
3120  // -1 == 3, -2 == 2, -3 == 1, -4 == 0
3121  throw ParseException(
3122  fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
3123  axis,
3124  CHECK_LOCATION().AsString()));
3125  }
3126 
3127  const unsigned int splitDim = armnnUtils::GetUnsignedAxis(inputTensorInfo.GetNumDimensions(), axis);
3128 
3129  auto inputDimSize = inputTensorInfo.GetNumDimensions();
3130  if (inputDimSize > MaxNumOfTensorDimensions)
3131  {
3132  throw ParseException(
3133  fmt::format("The number of dimensions: {} for input tensors of the split op cannot be greater than {} {}",
3134  inputTensorInfo.GetNumDimensions(),
3136  CHECK_LOCATION().AsString()));
3137  }
3138 
3139  std::vector<unsigned int> splitterDimSizes(inputDimSize);
3140 
3141  // Add current input shape to splitterDimSizes
3142  for (unsigned int i = 0; i < inputDimSize; ++i)
3143  {
3144  splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
3145  }
3146 
3147  if (splitterDimSizes[splitDim] % numSplits != 0)
3148  {
3149  throw ParseException("Number of splits must evenly divide the dimension");
3150  }
3151  splitterDimSizes[splitDim] /= numSplits;
3152 
3153  SplitterDescriptor splitDesc(numSplits, inputDimSize);
3154  for (unsigned int j = 0; j < numSplits; ++j)
3155  {
3156  // Set the size of the views.
3157  for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
3158  {
3159  splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
3160  }
3161  splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
3162  }
3163 
3164  auto layerName = fmt::format("Split:{}:{}", subgraphIndex, operatorIndex);
3165  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
3166  ARMNN_ASSERT(layer != nullptr);
3167 
3168  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3169  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
3170 
3171  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
3172  {
3173  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
3174  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
3175  }
3176 
3177  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3178  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3179 }
3180 
3181 unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
3182 {
3183  int numDims = armnn::numeric_cast<int>(numDimsIn);
3184  int v = idx < 0 ? numDims + idx : idx;
3185  ARMNN_ASSERT(v >= 0);
3186  ARMNN_ASSERT(v < numDims);
3187 
3188  return static_cast<unsigned int>(v);
3189 }
3190 
3191 void TfLiteParserImpl::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
3192 {
3193  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3194 
3195  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3196  const auto * options = operatorPtr->builtin_options.AsSplitVOptions();
3197 
3198  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3199  CHECK_VALID_SIZE(inputs.size(), 3);
3200 
3201  auto& inputTensor = inputs[0];
3202  auto& splitsTensor = inputs[1];
3203  auto& axisTensor = inputs[2];
3204 
3205  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
3206  armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
3207  armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
3208  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
3209 
3210  // Inputs
3211  auto inputDimSize = inputTensorInfo.GetNumDimensions();
3212  if (inputDimSize > MaxNumOfTensorDimensions)
3213  {
3214  throw ParseException(
3215  fmt::format("The number of dimensions: {} for input tensors of the "
3216  "SplitV op cannot be greater than {} {}",
3217  inputTensorInfo.GetNumDimensions(),
3219  CHECK_LOCATION().AsString()));
3220  }
3221 
3222  // Get split axis
3223  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
3224  if (axisBufferPtr == nullptr)
3225  {
3226  throw ParseException(
3227  fmt::format("Operation has invalid inputs. Failed to read axis. {}",
3228  CHECK_LOCATION().AsString()));
3229  }
3230 
3231  std::vector<int> axisData(axisTensorInfo.GetNumElements());
3232  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
3233  int32_t axis = axisData[0];
3234 
3235  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3236  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3237  {
3238  // Square bracket denotes inclusive n while parenthesis denotes exclusive n
3239  // E.g. Rank 4 tensor can have axis in range [-4, 3)
3240  // -1 == 3, -2 == 2, -3 == 1, -4 == 0
3241  throw ParseException(
3242  fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
3243  axis,
3244  CHECK_LOCATION().AsString()));
3245  }
3246  const unsigned int splitDim = ComputeWrappedIndex(axis, inputTensorInfo.GetNumDimensions());
3247 
3248  // Set split sizes
3249  CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
3250  unsigned int numSplits{0};
3251 
3252  if(options)
3253  {
3254  numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
3255  }
3256  else
3257  {
3258  numSplits = splitsInfo.GetNumElements();
3259  }
3260 
3261  if (numSplits <=0)
3262  {
3263  throw ParseException("SplitV has invalid number of splits");
3264  }
3265 
3266  std::vector<int> splitsData(numSplits);
3267  BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
3268  ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
3269 
3270  unsigned int idx = 0;
3271  int numInferred{0};
3272  unsigned int inferIdx{0};
3273  int splitSum{0};
3274  for (auto split : splitsData)
3275  {
3276  if (split < 0)
3277  {
3278  numInferred++;
3279  inferIdx = idx;
3280  }
3281  else
3282  {
3283  splitSum += split;
3284  }
3285  idx++;
3286  }
3287  // Check for inferred Axis
3288  if (numInferred == 0)
3289  {
3290  if (splitSum != armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]))
3291  {
3292  throw ParseException("SplitV split_sizes does not sum to the dimension of value along split_dim.");
3293  }
3294  }
3295  else if (numInferred == 1)
3296  {
3297  splitsData[inferIdx] = armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - splitSum;
3298  }
3299  else
3300  {
3301  throw ParseException("Cannot infer split size for more than one split");
3302  }
3303 
3304  //Ouput size validation
3305  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3306  CHECK_VALID_SIZE(outputs.size(), numSplits);
3307 
3308  // Setup Armnn descriptor
3309  SplitterDescriptor splitDesc(numSplits, inputDimSize);
3310  unsigned int accumSplit = 0;
3311  for (unsigned int j = 0; j < numSplits; ++j)
3312  {
3313  unsigned int splitSize = armnn::numeric_cast<unsigned int>(splitsData[j]);
3314 
3315  // Set the size of the views.
3316  for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
3317  {
3318  unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
3319  if (dimIdx == splitDim)
3320  {
3321  dimSize = splitSize;
3322  }
3323  splitDesc.SetViewSize(j, dimIdx, dimSize);
3324  }
3325 
3326  splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
3327  accumSplit += splitSize;
3328  }
3329 
3330  auto layerName = fmt::format("SplitV:{}:{}", subgraphIndex, operatorIndex);
3331  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
3332  ARMNN_ASSERT(layer != nullptr);
3333 
3334  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3335  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3336 
3337  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
3338  {
3339  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
3340  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
3341  }
3342 
3343  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3344  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3345 }
3346 
3347 void TfLiteParserImpl::ParseArgMin(size_t subgraphIndex, size_t operatorIndex)
3348 {
3349  ParseArgMinMax(subgraphIndex, operatorIndex, armnn::ArgMinMaxFunction::Min);
3350 }
3351 
3352 void TfLiteParserImpl::ParseArgMax(size_t subgraphIndex, size_t operatorIndex)
3353 {
3354  ParseArgMinMax(subgraphIndex, operatorIndex, armnn::ArgMinMaxFunction::Max);
3355 }
3356 
3357 void TfLiteParserImpl::ParseArgMinMax(size_t subgraphIndex, size_t operatorIndex, ArgMinMaxFunction argMinMaxFunction)
3358 {
3359  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3360  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3361  CHECK_VALID_SIZE(inputs.size(), 2);
3362 
3363  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3364  CHECK_VALID_SIZE(outputs.size(), 1);
3365 
3366  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3367  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[1]);
3368  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
3369  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
3370 
3371  // Check if output tensor type is Signed32 or Signed64
3372  if (outputTensorInfo.GetDataType() != armnn::DataType::Signed32 &&
3373  outputTensorInfo.GetDataType() != armnn::DataType::Signed64)
3374  {
3375  throw ParseException(
3376  fmt::format(
3377  "Output tensor data type is not supported. (Supported types: Signed32 & Signed64) {}",
3378  CHECK_LOCATION().AsString()));
3379  }
3380 
3381  // Get const axis value from model and set it to descriptor.
3382  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3383  if (axisBufferPtr == nullptr)
3384  {
3385  throw ParseException(
3386  fmt::format("Operation has invalid inputs. Failed to read axis. {}",
3387  CHECK_LOCATION().AsString()));
3388  }
3389 
3390  std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
3391  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
3392  int32_t axis = axisData.front();
3393 
3394  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3395  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3396  {
3397  // Square bracket denotes inclusive n while parenthesis denotes exclusive n
3398  // E.g. Rank 4 tensor can have axis in range [-4, 3)
3399  // -1 == 3, -2 == 2, -3 == 1, -4 == 0
3400  throw ParseException(
3401  fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
3402  axis,
3403  CHECK_LOCATION().AsString()));
3404  }
3405 
3406  ArgMinMaxDescriptor desc;
3407  desc.m_Axis = axis;
3408  desc.m_Function = argMinMaxFunction;
3409 
3410  // Register a ArgMin/ArgMax layer.
3411  auto layerName = argMinMaxFunction == ArgMinMaxFunction::Max ? "ArgMax:{}:{}" : "ArgMin:{}:{}";
3412  auto layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3413  IConnectableLayer *layer = m_Network->AddArgMinMaxLayer(desc, layerNameFormatted.c_str());
3414  ARMNN_ASSERT(layer != nullptr);
3415  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3416 
3417  // Register input tensor to the layer.
3418  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3419  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3420 
3421  // Register output tensor to the layer.
3422  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3423  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3424 }
3425 
3426 void TfLiteParserImpl::ParseGather(size_t subgraphIndex, size_t operatorIndex)
3427 {
3428  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3429 
3430  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3431  CHECK_VALID_SIZE(inputs.size(), 2);
3432  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3433  CHECK_VALID_SIZE(outputs.size(), 1);
3434 
3435  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3436  armnn::TensorInfo indicesTensorInfo = ToTensorInfo(inputs[1]);
3437  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3438 
3439  armnn::GatherDescriptor gatherDescriptor;
3440 
3441  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3442  const auto * options = operatorPtr->builtin_options.AsGatherOptions();
3443  auto axis = options->axis;
3444 
3445  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3446  auto indicesDimensions = indicesTensorInfo.GetNumDimensions();
3447  auto outputDimensions = outputTensorInfo.GetNumDimensions();
3448  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3449  {
3450  throw ParseException(
3451  fmt::format("Operation has invalid axis: {} It is out of bounds [ -{}, {} ) {}",
3452  axis,
3453  inputDimensions, inputDimensions,
3454  CHECK_LOCATION().AsString()));
3455  }
3456  if (outputDimensions != static_cast<unsigned int>(inputDimensions) + indicesDimensions - 1)
3457  {
3458  throw ParseException(
3459  fmt::format("Operation has invalid output dimensions: {} Output must be an ({} + {} - 1) -D tensor {}",
3460  outputDimensions,
3461  inputDimensions, indicesDimensions,
3462  CHECK_LOCATION().AsString()));
3463  }
3464 
3465  gatherDescriptor.m_Axis = axis;
3466 
3467  auto layerName = fmt::format("Gather:{}:{}", subgraphIndex, operatorIndex);
3468  IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, layerName.c_str());
3469  ARMNN_ASSERT(layer != nullptr);
3470  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3471 
3472  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3473  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
3474 
3475  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3476  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3477 }
3478 
3479 void TfLiteParserImpl::ParseDepthToSpace(size_t subgraphIndex, size_t operatorIndex)
3480 {
3481  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3482 
3483  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3484  CHECK_VALID_SIZE(inputs.size(), 1);
3485  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3486  CHECK_VALID_SIZE(outputs.size(), 1);
3487 
3488  armnn::DepthToSpaceDescriptor descriptor;
3489 
3490  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3491  const auto * options = operatorPtr->builtin_options.AsDepthToSpaceOptions();
3492  auto blockSize = options->block_size;
3493  if (blockSize < 2)
3494  {
3495  throw ParseException(
3496  fmt::format("Operation has invalid block size: {} Block size should be >= 2 {}",
3497  blockSize,
3498  CHECK_LOCATION().AsString()));
3499  }
3500  descriptor.m_BlockSize = armnn::numeric_cast<uint32_t>(blockSize);
3501 
3502  auto layerName = fmt::format("DepthToSpace:{}:{}", subgraphIndex, operatorIndex);
3503  IConnectableLayer* layer = m_Network->AddDepthToSpaceLayer(descriptor, layerName.c_str());
3504  ARMNN_ASSERT(layer != nullptr);
3505  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3506  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3507 
3508  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3509  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3510 
3511  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3512  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3513 }
3514 
3515 void TfLiteParserImpl::ParseSum(size_t subgraphIndex, size_t operatorIndex)
3516 {
3517  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Sum);
3518 }
3519 
3520 void TfLiteParserImpl::ParseReduceProd(size_t subgraphIndex, size_t operatorIndex)
3521 {
3522  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Prod);
3523 }
3524 
3525 void TfLiteParserImpl::ParseReduceMax(size_t subgraphIndex, size_t operatorIndex)
3526 {
3527  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Max);
3528 }
3529 
3530 void TfLiteParserImpl::ParseReduceMin(size_t subgraphIndex, size_t operatorIndex)
3531 {
3532  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Min);
3533 }
3534 
3535 void TfLiteParserImpl::ParseReduce(size_t subgraphIndex, size_t operatorIndex, ReduceOperation reduceOperation)
3536 {
3537  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3538 
3539  const auto &operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3540  const auto *options = operatorPtr->builtin_options.AsReducerOptions();
3541 
3542  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3543  CHECK_VALID_SIZE(inputs.size(), 2);
3544 
3545  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3546  CHECK_VALID_SIZE(outputs.size(), 1);
3547 
3548  auto layerName = fmt::format("Reduce:{}:{}", subgraphIndex, operatorIndex);
3549 
3550  armnn::TensorInfo inputTensorInfo0 = ToTensorInfo(inputs[0]);
3551  armnn::TensorInfo inputTensorInfo1 = ToTensorInfo(inputs[1]);
3552 
3553  ReduceDescriptor desc;
3554  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3555  // Get const axis value from model and set it to descriptor.
3556  if (axisBufferPtr != nullptr)
3557  {
3558  std::vector<int32_t> axisData(inputTensorInfo1.GetNumElements());
3559  ::memcpy(axisData.data(), axisBufferPtr->data.data(), inputTensorInfo1.GetNumBytes());
3560 
3561  // Convert the axis to unsigned int and remove duplicates.
3562  auto rank = static_cast<int32_t>(inputTensorInfo0.GetNumDimensions());
3563  std::set<unsigned int> uniqueAxis;
3564  std::transform(axisData.begin(),
3565  axisData.end(),
3566  std::inserter(uniqueAxis, uniqueAxis.begin()),
3567  [rank](int i)->unsigned int{
3568  return static_cast<uint32_t>(((i + rank) % rank)); });
3569  desc.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
3570  }
3571  else
3572  {
3573  for (uint32_t i = 0; i < inputTensorInfo0.GetNumDimensions(); ++i)
3574  {
3575  desc.m_vAxis.push_back(i);
3576  }
3577  }
3578 
3579  desc.m_KeepDims = options->keep_dims;
3580  desc.m_ReduceOperation = reduceOperation;
3581 
3582  // Register a new layer object, Sum.
3583  IConnectableLayer *layer = m_Network->AddReduceLayer(desc, layerName.c_str());
3584 
3585  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
3586  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3587 
3588  // Register input tensor to the layer.
3589  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3590  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3591 
3592  // Register output tensor to the layer.
3593  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3594  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3595 }
3596 
3597 void TfLiteParserImpl::ParseAbs(size_t subgraphIndex, size_t operatorIndex)
3598 {
3599  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Abs);
3600 }
3601 
3602 void TfLiteParserImpl::ParseExp(size_t subgraphIndex, size_t operatorIndex)
3603 {
3604  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Exp);
3605 }
3606 
3607 void TfLiteParserImpl::ParseLocalResponseNormalization(size_t subgraphIndex, size_t operatorIndex)
3608 {
3609  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3610 
3611  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3612  CHECK_VALID_SIZE(inputs.size(), 1);
3613 
3614  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3615  CHECK_VALID_SIZE(outputs.size(), 1);
3616 
3617  auto layerName = fmt::format("LRN:{}:{}", subgraphIndex, operatorIndex);
3618  std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3619 
3620  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3621 
3622  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3623  const auto* options = operatorPtr->builtin_options.AsLocalResponseNormalizationOptions();
3624 
3625  armnn::NormalizationDescriptor descriptor;
3629  descriptor.m_NormSize = static_cast<uint32_t>(options->radius);
3630  descriptor.m_K = options->bias;
3631  descriptor.m_Alpha = options->alpha;
3632  descriptor.m_Beta = options->beta;
3633 
3634  // ArmNN expects normSize to be the full size of the normalization
3635  // window rather than the radius as in TfLite.
3636  descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
3637 
3638  IConnectableLayer* layer = m_Network->AddNormalizationLayer(descriptor, layerNameFormatted.c_str());
3639  ARMNN_ASSERT(layer != nullptr);
3640 
3641  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3642  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3643 
3644  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3645  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3646 
3647  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3648  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3649 }
3650 
3651 void TfLiteParserImpl::ParseLogicalNot(size_t subgraphIndex, size_t operatorIndex)
3652 {
3653  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::LogicalNot);
3654 }
3655 
3656 void TfLiteParserImpl::ParseNeg(size_t subgraphIndex, size_t operatorIndex)
3657 {
3658  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Neg);
3659 }
3660 
3661 void TfLiteParserImpl::ParseRsqrt(size_t subgraphIndex, size_t operatorIndex)
3662 {
3663  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Rsqrt);
3664 }
3665 
3666 void TfLiteParserImpl::ParseElementwiseUnary(size_t subgraphIndex, size_t operatorIndex, UnaryOperation unaryOperation)
3667 {
3668  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3669 
3670  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3671  CHECK_VALID_SIZE(inputs.size(), 1);
3672 
3673  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3674  CHECK_VALID_SIZE(outputs.size(), 1);
3675 
3676  std::string layerName = std::string(GetUnaryOperationAsCString(unaryOperation)) + ":{}:{}";
3677  std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3678 
3680  desc.m_Operation = unaryOperation;
3681  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerNameFormatted.c_str());
3682  ARMNN_ASSERT(layer != nullptr);
3683 
3684  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3685  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3686 
3687  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3688  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3689 
3690  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3691  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3692 }
3693 
3694 void TfLiteParserImpl::ParseEqual(size_t subgraphIndex, size_t operatorIndex)
3695 {
3696  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Equal);
3697 }
3698 
3699 void TfLiteParserImpl::ParseNotEqual(size_t subgraphIndex, size_t operatorIndex)
3700 {
3701  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::NotEqual);
3702 }
3703 
3704 void TfLiteParserImpl::ParseGreater(size_t subgraphIndex, size_t operatorIndex)
3705 {
3706  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Greater);
3707 }
3708 
3709 void TfLiteParserImpl::ParseGreaterOrEqual(size_t subgraphIndex, size_t operatorIndex)
3710 {
3711  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::GreaterOrEqual);
3712 }
3713 
3714 void TfLiteParserImpl::ParseLess(size_t subgraphIndex, size_t operatorIndex)
3715 {
3716  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Less);
3717 }
3718 
3719 void TfLiteParserImpl::ParseLessOrEqual(size_t subgraphIndex, size_t operatorIndex)
3720 {
3721  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::LessOrEqual);
3722 }
3723 
3724 void TfLiteParserImpl::ParseComparison(size_t subgraphIndex, size_t operatorIndex,
3725  ComparisonOperation comparisonOperation)
3726 {
3727  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3728 
3729  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3730  CHECK_VALID_SIZE(inputs.size(), 2);
3731 
3732  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3733  CHECK_VALID_SIZE(outputs.size(), 1);
3734 
3735  auto layerName = std::string(GetComparisonOperationAsCString(comparisonOperation)) + ":{}:{}";
3736  std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3737 
3738  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3739  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
3740  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerNameFormatted, "Input 0", "Input 1");
3741 
3742  ComparisonDescriptor desc;
3743  desc.m_Operation = comparisonOperation;
3744  IConnectableLayer* layer = m_Network->AddComparisonLayer(desc, layerNameFormatted.c_str());
3745  ARMNN_ASSERT(layer != nullptr);
3746 
3747  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3748  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3749 
3750  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3751  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
3752 
3753  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3754  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3755 }
3756 
3757 armnn::IConnectableLayer* TfLiteParserImpl::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
3758  unsigned int outputSlot,
3759  tflite::ActivationFunctionType activationType)
3760 {
3761  ActivationDescriptor activationDesc;
3762  std::string layerName = prevLayer->GetName();
3763 
3764  switch(activationType)
3765  {
3766  case tflite::ActivationFunctionType_NONE:
3767  {
3768  // this is a no-op: return previous layer
3769  return prevLayer;
3770  }
3771  case tflite::ActivationFunctionType_RELU:
3772  {
3773  activationDesc.m_Function = ActivationFunction::ReLu;
3774  layerName += ":RELU";
3775  break;
3776  }
3777  case tflite::ActivationFunctionType_RELU6:
3778  {
3779  activationDesc.m_Function = ActivationFunction::BoundedReLu;
3780  activationDesc.m_A = 6.0f;
3781  activationDesc.m_B = 0.0f;
3782  layerName += ":RELU6";
3783  break;
3784  }
3785  case tflite::ActivationFunctionType_TANH:
3786  {
3787  activationDesc.m_Function = ActivationFunction::TanH;
3788  activationDesc.m_A = 1.0f;
3789  activationDesc.m_B = 1.0f;
3790  layerName += ":TANH";
3791  break;
3792  }
3793 
3794  // I only put these here as a reminder what others we could support
3795  case tflite::ActivationFunctionType_RELU_N1_TO_1:
3796  case tflite::ActivationFunctionType_SIGN_BIT:
3797  default:
3798  {
3799  throw ParseException(
3800  fmt::format("TfLite parser doesn't suppport fused activation: "
3801  "{}/{} {} ",
3802  activationType,
3803  tflite::EnumNameActivationFunctionType(activationType),
3804  CHECK_LOCATION().AsString()));
3805 
3806  }
3807  }
3808 
3809  IConnectableLayer* activationLayer =
3810  m_Network->AddActivationLayer(activationDesc, layerName.c_str());
3811 
3812  auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
3813  prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
3814  activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
3815  return activationLayer;
3816 }
3817 
3819 {
3820  if (fileName == nullptr)
3821  {
3822  throw InvalidArgumentException(fmt::format("Invalid (null) file name {}",
3823  CHECK_LOCATION().AsString()));
3824  }
3825  std::error_code errorCode;
3826  fs::path pathToFile(fileName);
3827  if (!fs::exists(pathToFile, errorCode))
3828  {
3829  //fmt::format() could not be used here (format error)
3830  std::stringstream msg;
3831  msg << "Cannot find the file (" << fileName << ") errorCode: " << errorCode
3832  << " " << CHECK_LOCATION().AsString();
3833 
3834  throw FileNotFoundException(msg.str());
3835  }
3836  std::ifstream file(fileName, std::ios::binary);
3837  std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
3838  return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
3839  fileContent.size());
3840 }
3841 
3842 TfLiteParserImpl::ModelPtr TfLiteParserImpl::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
3843 {
3844  if (binaryContent == nullptr)
3845  {
3846  throw InvalidArgumentException(fmt::format("Invalid (null) binary content {}",
3847  CHECK_LOCATION().AsString()));
3848  }
3849  flatbuffers::Verifier verifier(binaryContent, len);
3850  if (verifier.VerifyBuffer<tflite::Model>() == false)
3851  {
3852  throw ParseException(
3853  fmt::format("Buffer doesn't conform to the expected Tensorflow Lite "
3854  "flatbuffers format. size:{} {}",
3855  len,
3856  CHECK_LOCATION().AsString()));
3857  }
3858  return tflite::UnPackModel(binaryContent);
3859 }
3860 
3862  size_t subgraphIndex,
3863  size_t operatorIndex)
3864 {
3865  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3866 
3867  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3868  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3869 
3870  size_t inputCount = operatorPtr->inputs.size();
3871  TensorRawPtrVector result;
3872  for (size_t i=0; i<inputCount; ++i)
3873  {
3874  // If the input location is -1 then assume input is turned off.
3875  if (operatorPtr->inputs[i] == -1)
3876  {
3877  continue;
3878  }
3879  else
3880  {
3881  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
3882  result.push_back(subgraphPtr->tensors[inputId].get());
3883  }
3884  }
3885  return result;
3886 }
3887 
3889  size_t subgraphIndex,
3890  size_t operatorIndex)
3891 {
3892  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3893 
3894  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3895  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3896 
3897  size_t outputCount = operatorPtr->outputs.size();
3898  TensorRawPtrVector result(outputCount);
3899  for (size_t i=0; i<outputCount; ++i)
3900  {
3901  uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
3902  CHECK_TENSOR(model, subgraphIndex, outputId);
3903  result[i] = subgraphPtr->tensors[outputId].get();
3904  }
3905  return result;
3906 }
3907 
3909  size_t subgraphIndex)
3910 {
3911  CHECK_SUBGRAPH(model, subgraphIndex);
3912  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3913 
3914  size_t inputCount = subgraphPtr->inputs.size();
3915  TensorIdRawPtrVector result(inputCount);
3916  for (size_t i=0; i<inputCount; ++i)
3917  {
3918  uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
3919  CHECK_TENSOR(model, subgraphIndex, inputId);
3920  result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
3921  }
3922  return result;
3923 }
3924 
3926  size_t subgraphIndex)
3927 {
3928  CHECK_SUBGRAPH(model, subgraphIndex);
3929  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3930 
3931  size_t outputCount = subgraphPtr->outputs.size();
3932  TensorIdRawPtrVector result(outputCount);
3933  for (size_t i=0; i<outputCount; ++i)
3934  {
3935  uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
3936  result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
3937  }
3938  return result;
3939 }
3940 
3941 std::vector<int32_t>& TfLiteParserImpl::GetInputTensorIds(const ModelPtr& model,
3942  size_t subgraphIndex,
3943  size_t operatorIndex)
3944 {
3945  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3946  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3947  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3948  return operatorPtr->inputs;
3949 }
3950 
3951 std::vector<int32_t>& TfLiteParserImpl::GetOutputTensorIds(const ModelPtr& model,
3952  size_t subgraphIndex,
3953  size_t operatorIndex)
3954 {
3955  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3956  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3957  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3958  return operatorPtr->outputs;
3959 }
3960 
3961 void TfLiteParserImpl::RegisterInputSlots(size_t subgraphIndex,
3962  size_t operatorIndex,
3963  IConnectableLayer* layer,
3964  const std::vector<unsigned int>& tensorIndexes,
3965  unsigned int startingSlotIndex)
3966 {
3967  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3968  ARMNN_ASSERT(layer != nullptr);
3969 
3970  if (tensorIndexes.size() + startingSlotIndex != layer->GetNumInputSlots())
3971  {
3972  throw ParseException(
3973  fmt::format("The number of tensor inputs ({}) does not match the number expected ({})"
3974  " for subgraph:{} operator index:{} {}",
3975  tensorIndexes.size(),
3976  layer->GetNumInputSlots(),
3977  subgraphIndex,
3978  operatorIndex,
3979  CHECK_LOCATION().AsString()));
3980  }
3981 
3982  for (unsigned int index = 0; index < tensorIndexes.size() ; ++index)
3983  {
3984  unsigned int tensorIndex = tensorIndexes[index];
3985  armnn::IInputSlot* slot = &(layer->GetInputSlot(startingSlotIndex + index));
3986  RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
3987  }
3988 }
3989 
3990 void TfLiteParserImpl::RegisterOutputSlots(size_t subgraphIndex,
3991  size_t operatorIndex,
3992  IConnectableLayer* layer,
3993  const std::vector<unsigned int>& tensorIndexes)
3994 {
3995  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3996  ARMNN_ASSERT(layer != nullptr);
3997  if (tensorIndexes.size() != layer->GetNumOutputSlots())
3998  {
3999  throw ParseException(
4000  fmt::format("The number of tensor outputs ({}) does not match the number expected ({})"
4001  " for subgraph:{} operator index:{} {}",
4002  tensorIndexes.size(),
4003  layer->GetNumOutputSlots(),
4004  subgraphIndex,
4005  operatorIndex,
4006  CHECK_LOCATION().AsString()));
4007  }
4008 
4009  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
4010  {
4011  unsigned int tensorIndex = tensorIndexes[slotIndex];
4012  armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
4013  RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
4014  }
4015 }
4016 
4017 void TfLiteParserImpl::SetupInputLayers(size_t subgraphIndex)
4018 {
4019  CHECK_SUBGRAPH(m_Model, subgraphIndex);
4020 
4021  auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
4022  for (auto const & tensorIdAndPtr : inputs)
4023  {
4024  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
4025  IConnectableLayer* layer =
4026  m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
4027 
4028  auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
4029  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
4030 
4031  RegisterOutputSlots(subgraphIndex,
4032  VIRTUAL_OPERATOR_ID,
4033  layer,
4034  { static_cast<uint32_t>(tensorIdAndPtr.first) });
4035  }
4036 }
4037 
4038 void TfLiteParserImpl::SetupOutputLayers(size_t subgraphIndex)
4039 {
4040  CHECK_SUBGRAPH(m_Model, subgraphIndex);
4041 
4042  auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
4043  for (auto const & tensorIdAndPtr : outputs)
4044  {
4045  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
4046  IConnectableLayer* layer =
4047  m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
4048 
4049  RegisterInputSlots(subgraphIndex,
4050  VIRTUAL_OPERATOR_ID,
4051  layer,
4052  { static_cast<uint32_t>(tensorIdAndPtr.first) });
4053  }
4054 }
4055 
4056 void TfLiteParserImpl::SetupConstantLayers(size_t subgraphIndex)
4057 {
4058  CHECK_SUBGRAPH(m_Model, subgraphIndex);
4059 
4060  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
4061  for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
4062  {
4063  for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
4064  {
4065  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
4066  m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
4067  {
4068  TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
4069 
4070  if(IsConstTensor(tensorPtr))
4071  {
4072  armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
4073  auto tensorAndData = CreateConstTensorNonPermuted(tensorPtr, tensorInfo);
4074 
4075  std::string layerName = fmt::format("Constant:{}", tensorPtr->name);
4076  IConnectableLayer *layer = m_Network->AddConstantLayer(tensorAndData, layerName.c_str());
4077 
4078  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
4079  RegisterOutputSlots(subgraphIndex,
4080  VIRTUAL_OPERATOR_ID,
4081  layer,
4082  { tensorIndex });
4083  }
4084  else
4085  {
4086  throw ParseException(
4087  fmt::format("Invalid Tensor: Tensor should be constant. {}",
4088  CHECK_LOCATION().AsString()));
4089  }
4090  }
4091  }
4092  }
4093 }
4094 
4095 // example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
4097 {
4098  CHECK_BUFFER(model, bufferIndex);
4099  return model->buffers[bufferIndex].get();
4100 }
4101 
4102 template<typename T>
4103 std::pair<armnn::ConstTensor, TfLiteParserImpl::SupportedDataStorage>
4104 TfLiteParserImpl::CreateConstTensorAndStoreData(TfLiteParserImpl::BufferRawPtr bufferPtr,
4106  armnn::TensorInfo& tensorInfo,
4108 {
4109  // Make sure isConstant flag is set.
4110  tensorInfo.SetConstant();
4111 
4112  auto constData = CreateConstTensorImpl<T>(bufferPtr,
4113  tensorPtr,
4114  tensorInfo,
4115  permutationVector);
4116  TfLiteParserImpl::SupportedDataStorage storage(std::move(constData.second));
4117  return std::make_pair(constData.first, std::move(storage));
4118 }
4119 
4120 bool TfLiteParserImpl::IsConstTensor(TensorRawPtr tensorPtr)
4121 {
4122  CHECK_TENSOR_PTR(tensorPtr);
4123  bool isConst = true;
4124 
4125  auto buffer = GetBuffer(m_Model, tensorPtr->buffer);
4126  if (buffer->data.size() == 0)
4127  {
4128  isConst = false;
4129  }
4130 
4131  return isConst;
4132 }
4133 
4134 std::pair<armnn::ConstTensor, TfLiteParserImpl::SupportedDataStorage>
4135 TfLiteParserImpl::CreateConstTensorPermuted(TensorRawPtr tensorPtr,
4136  armnn::TensorInfo& tensorInfo,
4138 {
4139  CHECK_TENSOR_PTR(tensorPtr);
4140  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
4141  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
4142 
4143  // Make sure isConstant flag is set.
4144  tensorInfo.SetConstant();
4145 
4146  switch (tensorInfo.GetDataType())
4147  {
4149  return CreateConstTensorAndStoreData<float>(bufferPtr,
4150  tensorPtr,
4151  tensorInfo,
4152  permutationVector);
4154  return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
4155  tensorPtr,
4156  tensorInfo,
4157  permutationVector);
4159  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
4160  tensorPtr,
4161  tensorInfo,
4162  permutationVector);
4164  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
4165  tensorPtr,
4166  tensorInfo,
4167  permutationVector);
4169  return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
4170  tensorPtr,
4171  tensorInfo,
4172  permutationVector);
4173  default:
4174  {
4175  std::stringstream errString;
4176  errString << "Unexpected datatype when creating const tensor: "
4177  << armnn::GetDataTypeName(tensorInfo.GetDataType())
4178  << " shape:" << tensorInfo.GetShape()
4179  << CHECK_LOCATION().AsString();
4180  throw ParseException(errString.str());
4181  }
4182  }
4183 }
4184 
4185 armnn::ConstTensor TfLiteParserImpl::CreateConstTensorNonPermuted(TensorRawPtr tensorPtr,
4186  armnn::TensorInfo& tensorInfo)
4187 {
4188  CHECK_TENSOR_PTR(tensorPtr);
4189  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
4190  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
4191 
4192  // Make sure isConstant flag is set.
4193  tensorInfo.SetConstant();
4194 
4195  return ConstTensor(tensorInfo, bufferPtr->data.data());
4196 }
4197 
4199  const std::string& name) const
4200 {
4201  CHECK_SUBGRAPH(m_Model, subgraphId);
4202  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
4203  for (auto const & input : inputs)
4204  {
4205  if (input.second->name == name)
4206  {
4207  auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
4208  auto inputTensorInfo = ToTensorInfo(input.second);
4209  // Input tensors are always treated as constant tensors during network execution.
4210  inputTensorInfo.SetConstant(true);
4211  return std::make_pair(bindingId, inputTensorInfo);
4212  }
4213  }
4214 
4215  std::stringstream bindings;
4216  for (auto const & input : inputs)
4217  {
4218  bindings << "'" << input.second->name << "' ";
4219  }
4220 
4221  throw ParseException(
4222  fmt::format("No input binding found for subgraph:{} and name:{}. "
4223  "Possible inputs are: [{}] {}",
4224  subgraphId,
4225  name,
4226  bindings.str(),
4227  CHECK_LOCATION().AsString()));
4228 }
4229 
4231  const std::string& name) const
4232 {
4233  CHECK_SUBGRAPH(m_Model, subgraphId);
4234  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
4235  for (unsigned int i = 0; i < outputs.size(); ++i)
4236  {
4237  auto const output = outputs[i];
4238  if (output.second->name == name)
4239  {
4240  auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
4241  std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
4242  m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
4243  return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
4244  }
4245  }
4246 
4247  std::stringstream bindings;
4248  for (auto const & output : outputs)
4249  {
4250  bindings << "'" << output.second->name << "' ";
4251  }
4252 
4253  throw ParseException(
4254  fmt::format("No output binding found for subgraph:{} and name:{}. "
4255  "Possible outputs are: [{}] {}",
4256  subgraphId,
4257  name,
4258  bindings.str(),
4259  CHECK_LOCATION().AsString()));
4260 }
4261 
4263 {
4264  return m_Model->subgraphs.size();
4265 }
4266 
4267 std::vector<std::string> TfLiteParserImpl::GetSubgraphInputTensorNames(size_t subgraphId) const
4268 {
4269  CHECK_SUBGRAPH(m_Model, subgraphId);
4270  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
4271  std::vector<std::string> result;
4272  result.reserve(inputs.size());
4273  for (auto const & input : inputs)
4274  {
4275  result.push_back(input.second->name);
4276  }
4277  return result;
4278 }
4279 
4280 std::vector<std::string> TfLiteParserImpl::GetSubgraphOutputTensorNames(size_t subgraphId) const
4281 {
4282  CHECK_SUBGRAPH(m_Model, subgraphId);
4283  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
4284  std::vector<std::string> result;
4285  result.reserve(outputs.size());
4286  for (auto const & output : outputs)
4287  {
4288  result.push_back(output.second->name);
4289  }
4290  return result;
4291 }
4292 
4293 const std::string TfLiteParserImpl::GetVersion()
4294 {
4295  return TFLITE_PARSER_VERSION;
4296 }
4297 
4298 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
4299 : m_FloatData(std::move(data))
4300 , m_Uint8Data(nullptr)
4301 , m_Int8Data(nullptr)
4302 , m_Int32Data(nullptr)
4303 {
4304 }
4305 
4306 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
4307 : m_FloatData(nullptr)
4308 , m_Uint8Data(std::move(data))
4309 , m_Int8Data(nullptr)
4310 , m_Int32Data(nullptr)
4311 {
4312 }
4313 
4314 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
4315 : m_FloatData(nullptr)
4316 , m_Uint8Data(nullptr)
4317 , m_Int8Data(std::move(data))
4318 , m_Int32Data(nullptr)
4319 {
4320 }
4321 
4322 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
4323 : m_FloatData(nullptr)
4324 , m_Uint8Data(nullptr)
4325 , m_Int8Data(nullptr)
4326 , m_Int32Data(std::move(data))
4327 {
4328 }
4329 
4330 } // armnnTfLiteParser
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)
std::unique_ptr< tflite::ModelT > ModelPtr
static TensorIdRawPtrVector GetSubgraphOutputs(const ModelPtr &model, size_t subgraphIndex)
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
UnaryOperation m_Operation
Specifies the elementwiseUnary operation to execute.
uint32_t m_Axis
0-based axis along which to stack the input tensors.
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:434
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
float m_K
Kappa value used for the across channel normalization equation.
A TransposeConvolution2dDescriptor for the TransposeConvolution2dLayer.
#define ARMNN_THROW_PARSE_EXCEPTION(msg)
const TensorShape & GetShape() const
Definition: Tensor.hpp:191
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
const tflite::TensorT * TensorRawPtr
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.
bool AreAllDimensionsSpecified() const
Checks if there is at least one dimension not specified.
Definition: Tensor.cpp:241
std::vector< int > m_Begin
Begin values for the input that will be sliced.
const tflite::BufferT * BufferRawPtr
uint32_t m_PadBack
Padding back value in the depth dimension.
float m_PadValue
Optional value to use for padding, defaults to 0.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
A ComparisonDescriptor for the ComparisonLayer.
Definition: Descriptors.hpp:78
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.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
A Convolution2dDescriptor for the Convolution2dLayer.
float m_Alpha
Alpha value for the normalization equation.
uint32_t m_PadLeft
Padding left value in the width dimension.
bool m_KeepDims
if true then output shape has no change.
bool m_BiasEnabled
Enable/disable bias.
std::vector< unsigned int > m_OutputShape
unsigned int GetNumBytes() const
Definition: Tensor.cpp:429
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
float m_Beta
Exponentiation value.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a flatbuffers binary file on disk.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
BindingPointInfo GetNetworkOutputBindingInfo(size_t subgraphId, const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
ArgMinMaxFunction m_Function
Specify if the function is to find Min or Max.
Definition: Descriptors.hpp:70
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)
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:202
uint32_t m_PadTop
Padding top value in the height dimension.
std::vector< BackendOptions > NetworkOptions
uint32_t m_PadBottom
Padding bottom value in the height dimension.
std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const
Return the output tensor names for a given subgraph.
bool m_BiasEnabled
Enable/disable bias.
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
uint32_t m_PadRight
Padding right value in the width dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
ReduceOperation m_ReduceOperation
Specifies the reduction operation to execute.
std::unique_ptr< ITfLiteParser, void(*)(ITfLiteParser *parser)> ITfLiteParserPtr
std::unique_ptr< tflite::OperatorT > OperatorPtr
unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
Copyright (c) 2021 ARM Limited and Contributors.
void IgnoreUnused(Ts &&...)
uint32_t m_PadBottom
Padding bottom value in the height dimension.
int32_t m_BeginMask
Begin mask value.
static armnn::TensorInfo OutputShapeOfReshape(const armnn::TensorInfo &inputTensorInfo, const std::vector< int32_t > &targetDimsIn)
SizeType GetSize() const
Definition: Types.hpp:311
uint32_t m_DilationY
Dilation along y axis.
int32_t m_EndMask
End mask value.
A SpaceToDepthDescriptor for the SpaceToDepthLayer.
PoolingAlgorithm
Definition: Types.hpp:123
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_DilationX
Dilation along x axis.
uint32_t m_DilationY
Dilation factor value for height dimension.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
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:277
#define TFLITE_PARSER_VERSION
TFLITE_PARSER_VERSION: "X.Y.Z" where: X = Major version number Y = Minor version number Z = Patch ver...
Definition: Version.hpp:25
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
NormalizationAlgorithmMethod m_NormMethodType
Normalization method algorithm to use (LocalBrightness, LocalContrast).
#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX)
constexpr const char * GetDataTypeName(DataType dataType)
Definition: TypesUtils.hpp:202
void SetShape(const TensorShape &newShape)
Definition: Tensor.hpp:193
armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent)
Create the network from a flatbuffers binary.
A ResizeBilinearDescriptor for the ResizeBilinearLayer.
static BufferRawPtr GetBuffer(const ModelPtr &model, size_t bufferIndex)
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).
constexpr char const * GetUnaryOperationAsCString(UnaryOperation operation)
Definition: TypesUtils.hpp:71
TensorShape m_TargetShape
Target shape value.
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a flatbuffers binary file on disk.
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.
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)
ComparisonOperation
Definition: Types.hpp:95
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
ReduceOperation
Definition: Types.hpp:130
BindingPointInfo GetNetworkInputBindingInfo(size_t subgraphId, const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
bool CheckShape(const armnn::TensorShape &actual, const std::vector< uint32_t > &expected)
static ModelPtr LoadModelFromBinary(const uint8_t *binaryContent, size_t len)
DataType
Definition: Types.hpp:35
float m_NmsIouThreshold
Intersection over union threshold.
uint32_t m_PadRight
Padding right value in the width dimension.
std::vector< TensorIdRawPtr > TensorIdRawPtrVector
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...
static std::vector< int32_t > & GetInputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
std::vector< unsigned int > m_BlockShape
Block shape values.
An output connection slot for a layer.
Definition: INetwork.hpp:37
A L2NormalizationDescriptor for the L2NormalizationLayer.
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:480
An ArgMinMaxDescriptor for ArgMinMaxLayer.
Definition: Descriptors.hpp:56
static const std::string GetVersion()
Retrieve version in X.Y.Z form.
float GetQuantizationScale() const
Definition: Tensor.cpp:463
DataType GetDataType() const
Definition: Tensor.hpp:198
An OriginsDescriptor for the ConcatLayer.
A ReduceDescriptor for the REDUCE operators.
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.
static ModelPtr LoadModelFromFile(const char *fileName)
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
A GatherDescriptor for the GatherLayer.
#define CHECK_VALID_SIZE(ACTUAL,...)
uint32_t m_NumClasses
Number of classes.
#define CHECKED_NON_NEGATIVE(VALUE)
std::vector< TensorRawPtr > TensorRawPtrVector
size_t GetSubgraphCount() const
Return the number of subgraphs in the parsed model.
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.
uint32_t m_PadFront
Padding front value in the depth dimension.
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:25
#define CHECK_LOCATION()
Definition: Exceptions.hpp:209
uint32_t m_NumInputs
Number of input tensors.
uint32_t m_PadLeft
Padding left value in the width dimension.
A SliceDescriptor for the SliceLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
armnn::INetworkPtr LoadModel(std::unique_ptr< tflite::ModelT > model)
A Convolution3dDescriptor for the Convolution3dLayer.
std::unique_ptr< tflite::SubGraphT > SubgraphPtr
uint32_t m_PadRight
Padding right value in the width dimension.
PaddingMode m_PaddingMode
Specifies the Padding mode (Constant, Reflect or Symmetric)
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_TENSOR_PTR(TENSOR_PTR)
std::vector< uint32_t > m_vAxis
The indices of the dimensions to reduce.
float m_ScaleH
Center size encoding scale height.
ComparisonOperation m_Operation
Specifies the comparison operation to execute.
Definition: Descriptors.hpp:94
std::vector< int > m_End
End values for the input that will be sliced.
A SpaceToBatchNdDescriptor for the SpaceToBatchNdLayer.
static TensorIdRawPtrVector GetSubgraphInputs(const ModelPtr &model, size_t subgraphIndex)
DataLayout m_DataLayout
The data layout to be used (NDHWC, NCDHW).
Struct for the users to pass backend specific options.
NormalizationAlgorithmChannel m_NormChannelType
Normalization channel algorithm to use (Across, Within).
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
Definition: Descriptors.hpp:50
static TensorRawPtrVector GetInputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
uint32_t m_DilationX
Dilation along x axis.
const armnnSerializer::TensorInfo * TensorRawPtr
static TensorRawPtrVector GetOutputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
std::pair< armnn::ConstTensor, std::unique_ptr< T[]> > CreateConstTensorImpl(const T *bufferPtr, armnn::TensorInfo &tensorInfo, const armnn::Optional< armnn::PermutationVector &> permutationVector)
Definition: OnnxParser.cpp:566
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
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
static std::vector< int32_t > & GetOutputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX)
int32_t m_Axis
The axis in params to gather indices from.
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:98
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.
uint32_t m_PadTop
Padding top value in the height dimension.
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
ArgMinMaxFunction
Definition: Types.hpp:89
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
constexpr char const * GetComparisonOperationAsCString(ComparisonOperation operation)
Definition: TypesUtils.hpp:57
void SetConcatAxis(unsigned int concatAxis)
Set the concatenation axis value.
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
ResizeMethod
Definition: Types.hpp:139
A MeanDescriptor for the MeanLayer.
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
Definition: Tensor.cpp:516
UnaryOperation
Definition: Types.hpp:111
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
armnn::TensorInfo ToTensorInfo(TensorRawPtr tensorPtr)
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:72
virtual const char * GetName() const =0
Returns the name of the layer.
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:197
virtual int Connect(IInputSlot &destination)=0
Krichevsky 2012: Local Brightness Normalization.
const char * m_Function
Definition: Exceptions.hpp:16
A Pooling2dDescriptor for the Pooling2dLayer.
A NormalizationDescriptor for the NormalizationLayer.
static armnn::TensorInfo OutputShapeOfSqueeze(std::vector< uint32_t > squeezeDims, const armnn::TensorInfo &inputTensorInfo)
std::vector< std::string > GetSubgraphInputTensorNames(size_t subgraphId) const
Return the input tensor names for a given subgraph.
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:195
#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID)
uint32_t m_DilationZ
Dilation along z axis.
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
Definition: Descriptors.hpp:52
bool IsQuantized() const
Definition: Tensor.cpp:506
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
Definition: Permute.cpp:98
A SoftmaxDescriptor for the SoftmaxLayer.
float m_Beta
Beta value for the normalization equation.
uint32_t m_StrideZ
Stride value when proceeding through input for the depth dimension.
uint32_t m_NormSize
Depth radius value.
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:48
An input connection slot for a layer.
Definition: INetwork.hpp:24
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_DilationY
Dilation along y axis.
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const
Definition: Tensor.hpp:196
ActivationFunction
Definition: Types.hpp:73
uint32_t m_PadRight
Padding right value in the width dimension.
bool m_ConstantWeights
Enable/disable constant weights and biases.