ArmNN
 21.08
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  m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParserImpl::ParseCustomOperator;
644  m_ParserFunctions[tflite::BuiltinOperator_DEPTH_TO_SPACE] = &TfLiteParserImpl::ParseDepthToSpace;
645  m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParserImpl::ParseDepthwiseConv2D;
646  m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParserImpl::ParseDequantize;
647  m_ParserFunctions[tflite::BuiltinOperator_DIV] = &TfLiteParserImpl::ParseDiv;
648  m_ParserFunctions[tflite::BuiltinOperator_ELU] = &TfLiteParserImpl::ParseElu;
649  m_ParserFunctions[tflite::BuiltinOperator_EQUAL] = &TfLiteParserImpl::ParseEqual;
650  m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParserImpl::ParseExp;
651  m_ParserFunctions[tflite::BuiltinOperator_EXPAND_DIMS] = &TfLiteParserImpl::ParseExpandDims;
652  m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParserImpl::ParseFullyConnected;
653  m_ParserFunctions[tflite::BuiltinOperator_GATHER] = &TfLiteParserImpl::ParseGather;
654  m_ParserFunctions[tflite::BuiltinOperator_GREATER] = &TfLiteParserImpl::ParseGreater;
655  m_ParserFunctions[tflite::BuiltinOperator_GREATER_EQUAL] = &TfLiteParserImpl::ParseGreaterOrEqual;
656  m_ParserFunctions[tflite::BuiltinOperator_HARD_SWISH] = &TfLiteParserImpl::ParseHardSwish;
657  m_ParserFunctions[tflite::BuiltinOperator_LEAKY_RELU] = &TfLiteParserImpl::ParseLeakyRelu;
658  m_ParserFunctions[tflite::BuiltinOperator_LESS] = &TfLiteParserImpl::ParseLess;
659  m_ParserFunctions[tflite::BuiltinOperator_LESS_EQUAL] = &TfLiteParserImpl::ParseLessOrEqual;
660  m_ParserFunctions[tflite::BuiltinOperator_LOGICAL_NOT] = &TfLiteParserImpl::ParseLogicalNot;
661  m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParserImpl::ParseLogistic;
662  m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParserImpl::ParseL2Normalization;
663  m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParserImpl::ParseMaxPool2D;
664  m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParserImpl::ParseMaximum;
665  m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParserImpl::ParseMean;
666  m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParserImpl::ParseMinimum;
667  m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParserImpl::ParseMul;
668  m_ParserFunctions[tflite::BuiltinOperator_NEG] = &TfLiteParserImpl::ParseNeg;
669  m_ParserFunctions[tflite::BuiltinOperator_NOT_EQUAL] = &TfLiteParserImpl::ParseNotEqual;
670  m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParserImpl::ParsePack;
671  m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParserImpl::ParsePad;
672  m_ParserFunctions[tflite::BuiltinOperator_PRELU] = &TfLiteParserImpl::ParsePrelu;
673  m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParserImpl::ParseQuantize;
674  m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParserImpl::ParseRelu;
675  m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParserImpl::ParseRelu6;
676  m_ParserFunctions[tflite::BuiltinOperator_REDUCE_MAX] = &TfLiteParserImpl::ParseReduceMax;
677  m_ParserFunctions[tflite::BuiltinOperator_REDUCE_MIN] = &TfLiteParserImpl::ParseReduceMin;
678  m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParserImpl::ParseReshape;
679  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParserImpl::ParseResizeBilinear;
680  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParserImpl::ParseResizeNearestNeighbor;
681  m_ParserFunctions[tflite::BuiltinOperator_RSQRT] = &TfLiteParserImpl::ParseRsqrt;
682  m_ParserFunctions[tflite::BuiltinOperator_SHAPE] = &TfLiteParserImpl::ParseShape;
683  m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParserImpl::ParseSlice;
684  m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParserImpl::ParseSoftmax;
685  m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParserImpl::ParseSpaceToBatchND;
686  m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParserImpl::ParseSplit;
687  m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParserImpl::ParseSplitV;
688  m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParserImpl::ParseSqueeze;
689  m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParserImpl::ParseStridedSlice;
690  m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParserImpl::ParseSub;
691  m_ParserFunctions[tflite::BuiltinOperator_SUM] = &TfLiteParserImpl::ParseSum;
692  m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParserImpl::ParseTanH;
693  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParserImpl::ParseTranspose;
694  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParserImpl::ParseTransposeConv;
695  m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParserImpl::ParseUnpack;
696 
697  // register supported custom operators
698  m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParserImpl::ParseDetectionPostProcess;
699 }
700 
701 void TfLiteParserImpl::ResetParser()
702 {
703  m_Network = armnn::INetworkPtr(nullptr, nullptr);
704  m_Model = nullptr;
705  m_SubgraphConnections.clear();
706 }
707 
709 {
710  ResetParser();
711  m_Model = LoadModelFromFile(graphFile);
712  return CreateNetworkFromModel();
713 }
714 
715 INetworkPtr TfLiteParserImpl::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
716 {
717  ResetParser();
718  m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
719  return CreateNetworkFromModel();
720 }
721 
722 
723 armnn::INetworkPtr TfLiteParserImpl::LoadModel(std::unique_ptr<tflite::ModelT> model)
724 {
725  ResetParser();
726  m_Model = std::move(model);
727 
728  return CreateNetworkFromModel();
729 }
730 
731 INetworkPtr TfLiteParserImpl::CreateNetworkFromModel()
732 {
733 
734  using NetworkOptions = std::vector<BackendOptions>;
735  NetworkOptions networkOptions = {};
736  if (m_Options && m_Options.value().m_InferAndValidate)
737  {
738  BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod",
739  {
740  { "InferAndValidate", true }
741  });
742 
743  networkOptions.push_back(shapeInferenceMethodOption);
744  }
745 
746  m_Network = INetwork::Create(networkOptions);
747  ARMNN_ASSERT(m_Model.get() != nullptr);
748 
749  if (m_Model->subgraphs.size() != 1)
750  {
751  throw ParseException(
752  fmt::format("Current TfLite parser only supports 1 subgraph. Current one has: {} {}",
753  m_Model->subgraphs.size(),
754  CHECK_LOCATION().AsString()));
755  }
756 
757  size_t subgraphIndex = 0;
758  size_t operatorIndex = 0;
759  try
760  {
761  for (SubgraphPtr const& subgraph : m_Model->subgraphs)
762  {
763  m_SubgraphConnections.emplace_back(subgraph->tensors.size());
764  for (OperatorPtr const& op : subgraph->operators)
765  {
766  auto const& opCodePtr = m_Model->operator_codes[op->opcode_index];
767  auto builtinCode = opCodePtr->builtin_code;
768 
769  if (builtinCode > tflite::BuiltinOperator_MAX)
770  {
771  throw ParseException(fmt::format("Operator code {} is out of range 0-{}. "
772  "subgraph:{} operator idx:{}. {}",
773  builtinCode, tflite::BuiltinOperator_MAX, subgraphIndex,
774  operatorIndex, CHECK_LOCATION().AsString()));
775  }
776 
777  // lookup and call the parser function
778  auto& parserFunction = m_ParserFunctions[builtinCode];
779  (this->*parserFunction)(subgraphIndex, operatorIndex);
780  ++operatorIndex;
781  }
782 
783  SetupInputLayers(subgraphIndex);
784  SetupOutputLayers(subgraphIndex);
785  SetupConstantLayers(subgraphIndex);
786 
787  ++subgraphIndex;
788  operatorIndex = 0;
789  }
790  }
791  catch (const ParseException& e)
792  {
793  std::stringstream errorString;
794  errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
795  << subgraphIndex << " error: " << e.what();
796  ARMNN_LOG(error) << errorString.str();
797  std::stringstream errors;
798  errors << errorString.str() << "\n";
799  throw ParseException(errors.str());
800  }
801 
802  // establish the connections from the layer outputs to the inputs of the subsequent layers
803  for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
804  {
805  for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
806  {
807  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
808  {
809  for (size_t inputSlotIdx = 0;
810  inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
811  ++inputSlotIdx)
812  {
813  m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
814  *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
815  }
816  }
817  }
818  }
819 
820  return std::move(m_Network);
821 }
822 
823 void TfLiteParserImpl::RegisterProducerOfTensor(size_t subgraphIndex,
824  size_t tensorIndex,
825  armnn::IOutputSlot* slot)
826 {
827  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
828  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
829  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
830 
831  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
832 
833  // assuming there is only one producer for that tensor
834  if (tensorSlots.outputSlot != nullptr)
835  {
836  throw ParseException(fmt::format("Another layer has already registered itself as the producer of "
837  "subgraph:{} tensor:{} {}",
838  subgraphIndex,
839  tensorIndex,
840  CHECK_LOCATION().AsString()));
841  }
842 
843  tensorSlots.outputSlot = slot;
844 }
845 
846 void TfLiteParserImpl::RegisterConsumerOfTensor(size_t subgraphIndex,
847  size_t tensorIndex,
848  armnn::IInputSlot* slot)
849 {
850  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
851  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
852  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
853 
854  TensorSlots& tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
855  tensorSlots.inputSlots.push_back(slot);
856 }
857 
858 void TfLiteParserImpl::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
859 {
860  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
861 
862  // NOTE: By default we presume the custom operator is not supported
863  auto customParserFunction = &TfLiteParserImpl::ParseUnsupportedOperator;
864 
865  // Identify custom code defined for custom operator
866  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
867  const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
868 
869  // Find parser function that correspondes to custom code (if any)
870  auto iterator = m_CustomParserFunctions.find(customCode);
871  if (iterator != m_CustomParserFunctions.end())
872  {
873  customParserFunction = iterator->second;
874  }
875 
876  // Run parser function
877  (this->*customParserFunction)(subgraphIndex, operatorIndex);
878 }
879 
880 void TfLiteParserImpl::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
881 {
882  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
883 
884  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
885 
886  auto opcodeIndex = operatorPtr->opcode_index;
887  auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
888 
889  if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
890  {
891  // Do not add StandInLayer, throw ParseException instead
892  throw ParseException(
893  fmt::format("Operator not supported. "
894  "subgraph:{} operator:{} "
895  "opcode_index:{} opcode:{} / {} {}",
896  subgraphIndex,
897  operatorIndex,
898  opcodeIndex,
899  opcode,
900  tflite::EnumNameBuiltinOperator(opcode),
901  CHECK_LOCATION().AsString()));
902  }
903 
904  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
905  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
906 
907  const unsigned int numInputs = armnn::numeric_cast<unsigned int>(inputs.size());
908  const unsigned int numOutputs = armnn::numeric_cast<unsigned int>(outputs.size());
909 
910  StandInDescriptor descriptor(numInputs, numOutputs);
911  auto layerName = fmt::format("StandIn:{}:{}:{}", subgraphIndex, operatorIndex, opcode);
912 
913  // Add a non-executable StandInLayer as a placeholder for any unsupported operator
914  IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
915  ARMNN_ASSERT(layer != nullptr);
916 
917  for (unsigned int i = 0u; i < numOutputs; ++i)
918  {
919  layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i], true));
920  }
921 
922  auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
923  auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
924 
925  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
926  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
927 }
928 
929 void TfLiteParserImpl::ParseCast(size_t subgraphIndex, size_t operatorIndex)
930 {
931  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
932 
933  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
934  CHECK_VALID_SIZE(inputs.size(), 1);
935  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
936  CHECK_VALID_SIZE(outputs.size(), 1);
937 
938  auto layerName = fmt::format("Cast:{}:{}", subgraphIndex, operatorIndex);
939 
940  IConnectableLayer* layer = m_Network->AddCastLayer(layerName.c_str());
941  ARMNN_ASSERT(layer != nullptr);
942 
943  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
944  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
945 
946  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
947  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
948 
949  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
950  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
951 }
952 
953 void TfLiteParserImpl::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
954 {
955  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
956 
957  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
958  const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
959 
960  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
961 
963  desc.m_BiasEnabled = false;
964  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
965  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
967  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
968  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
969 
970  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
971  CHECK_VALID_SIZE(inputs.size(), 2, 3);
972 
973  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
974  CHECK_VALID_SIZE(outputs.size(), 1);
975 
976  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
977  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
978 
979  // assuming input is NHWC
980  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
981  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
982 
983  // assuming the filter is OHWI : Output, H, W, Input
984  // which is essentially the same as NHWC
985  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
986  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
987 
988  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
989  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
990  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
991  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
992 
993  auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
994  armnn::IConnectableLayer* layer = nullptr;
995 
996  auto layerName = fmt::format("Conv2D:{}:{}", subgraphIndex, operatorIndex);
997 
998  if (inputs.size() == 3)
999  {
1000  desc.m_BiasEnabled = true;
1001  armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
1002  auto biasTensorAndData = CreateConstTensorNonPermuted(inputs[2], biasTensorInfo);
1003  layer = m_Network->AddConvolution2dLayer(desc,
1004  filterTensorAndData,
1005  Optional<ConstTensor>(biasTensorAndData),
1006  layerName.c_str());
1007  }
1008  else
1009  {
1010  layer = m_Network->AddConvolution2dLayer(desc,
1011  filterTensorAndData,
1012  EmptyOptional(),
1013  layerName.c_str());
1014  }
1015 
1016  ARMNN_ASSERT(layer != nullptr);
1017 
1018  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1019  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1020 
1021  // register the input connection slots for the layer, connections are made after all layers have been created
1022  // only the tensors for the inputs are relevant, exclude the const tensors
1023  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1024  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1025 
1026  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1027  // register the output connection slots for the layer, connections are made after all layers have been created
1028  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1029  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1030 }
1031 
1032 void TfLiteParserImpl::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
1033 {
1034  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1035 
1036  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1037  const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
1038 
1039  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1040 
1042  desc.m_BiasEnabled = false;
1043  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1044  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1046  CHECKED_NON_NEGATIVE(options->depth_multiplier);
1047 
1048  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1049  CHECK_VALID_SIZE(inputs.size(), 2, 3);
1050  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1051  CHECK_VALID_SIZE(outputs.size(), 1);
1052  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1053  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1054 
1055  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1056  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1057 
1058  // Assuming input is NHWC
1059  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1060  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1061 
1062  // TensorflowLite weights come in the format [1, H, W, I * M]
1063  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1064  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1065 
1066  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1067  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1068  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1069  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1070 
1071  // ArmNN uses the same filter tensor layout at TfLite [1, H, W, O] no need for any permutation
1072  auto filterTensor = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
1073  armnn::IConnectableLayer* layer = nullptr;
1074  auto layerName = fmt::format("DepthwiseConv2D:{}:{}", subgraphIndex, operatorIndex);
1075 
1076  if (inputs.size() == 3)
1077  {
1078  desc.m_BiasEnabled = true;
1079  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
1080  auto biasTensorAndData = CreateConstTensorNonPermuted(inputs[2], biasTensorInfo);
1081  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1082  filterTensor,
1083  Optional<ConstTensor>(biasTensorAndData),
1084  layerName.c_str());
1085  }
1086  else
1087  {
1088  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1089  filterTensor,
1090  EmptyOptional(),
1091  layerName.c_str());
1092  }
1093  ARMNN_ASSERT(layer != nullptr);
1094 
1095  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1096  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1097 
1098  // register the input connection slots for the layer, connections are made after all layers have been created
1099  // only the tensors for the inputs are relevant, exclude the const tensors
1100  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1101  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1102 
1103  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1104  // register the output connection slots for the layer, connections are made after all layers have been created
1105  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1106  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1107 }
1108 
1109 void TfLiteParserImpl::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
1110 {
1111  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1112 
1113  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1114  CHECK_VALID_SIZE(inputs.size(), 1);
1115 
1116  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1117  CHECK_VALID_SIZE(outputs.size(), 1);
1118 
1119  auto layerName = fmt::format("Dequantize:{}:{}", subgraphIndex, operatorIndex);
1120 
1121  IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
1122  ARMNN_ASSERT(layer != nullptr);
1123 
1124  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1125  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1126 
1127  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1128  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1129 
1130  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1131  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1132 }
1133 
1134 void TfLiteParserImpl::ParseExpandDims(size_t subgraphIndex, size_t operatorIndex)
1135 {
1136  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1137 
1138  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1139  CHECK_VALID_SIZE(inputs.size(), 2);
1140 
1141  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1142  CHECK_VALID_SIZE(outputs.size(), 1);
1143 
1144  auto layerName = fmt::format("ExpandDims:{}:{}", subgraphIndex, operatorIndex);
1145 
1146  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1147  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1148 
1149  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1150 
1151  ReshapeDescriptor reshapeDesc;
1152 
1153  if (outputTensorInfo.GetShape().AreAllDimensionsSpecified())
1154  {
1155  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1156  }
1157  else
1158  {
1159  int32_t axis = inputs[1]->shape[0];
1160 
1161  int32_t inputDimSize = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions());
1162 
1163  if (axis > inputDimSize || axis < 0 - (inputDimSize + 1))
1164  {
1165  throw ParseException("axis must be in range [0 - (inputDimSize + 1), inputDimSize] inclusive");
1166  }
1167 
1168  if(axis < 0)
1169  {
1170  axis = inputDimSize + axis + 1;
1171  }
1172 
1173  std::vector<unsigned int> shape(static_cast<unsigned int>(inputDimSize) + 1);
1174  unsigned int inputShapeIndex = 0;
1175  for (unsigned int i = 0; i < static_cast<unsigned int>(inputDimSize + 1); ++i)
1176  {
1177  if (i == static_cast<unsigned int>(axis))
1178  {
1179  shape[i] = 1;
1180  }
1181  else
1182  {
1183  shape[i] = inputTensorInfo.GetShape()[inputShapeIndex];
1184  ++inputShapeIndex;
1185  }
1186  }
1187 
1188  reshapeDesc.m_TargetShape = TensorShape(static_cast<unsigned int>(inputDimSize + 1), shape.data());
1189  }
1190 
1191  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1192  ARMNN_ASSERT(layer != nullptr);
1193  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1194 
1195  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1196  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1197 
1198  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1199  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1200 }
1201 
1202 void TfLiteParserImpl::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1203 {
1204  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1205 
1206  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1207  CHECK_VALID_SIZE(inputs.size(), 1, 2);
1208 
1209  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1210  CHECK_VALID_SIZE(outputs.size(), 1);
1211 
1212  auto layerName = fmt::format("Transpose:{}:{}", subgraphIndex, operatorIndex);
1213  TransposeDescriptor desc;
1214 
1215  if (inputs.size() == 2)
1216  {
1217  armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1218  BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1219  auto numPermVecElements = permuteTensorInfo.GetNumElements();
1220  std::vector<unsigned int> permuteShape(numPermVecElements);
1221  ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
1222  PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
1223 
1224  desc = TransposeDescriptor(permutationVector);
1225  }
1226 
1227  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1228  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1229  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1230 
1231  IConnectableLayer* layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
1232  ARMNN_ASSERT(layer != nullptr);
1233  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1234 
1235  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1236  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1237 
1238  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1239  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1240 }
1241 
1242 void TfLiteParserImpl::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1243 {
1244  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1245 
1246  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1247  const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1248 
1250  desc.m_BiasEnabled = false;
1251  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1252  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1254 
1255  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1256  if (inputs.size() == 4)
1257  {
1258  desc.m_BiasEnabled = true;
1259  }
1260  else
1261  {
1262  CHECK_VALID_SIZE(inputs.size(), 3);
1263  }
1264 
1265  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1266  CHECK_VALID_SIZE(outputs.size(), 1);
1267 
1268  if (inputs[0])
1269  {
1270  armnn::TensorInfo tensorInfo = ToTensorInfo(inputs[0]);
1271  std::vector<int> output_shape(tensorInfo.GetNumElements());
1272  if (tensorInfo.GetDataType() == DataType::Signed32)
1273  {
1274  ::memcpy(output_shape.data(), GetBuffer(m_Model, inputs[0]->buffer)->data.data(), tensorInfo.GetNumBytes());
1275  }
1276  if (tensorInfo.GetDataType() == DataType::QAsymmU8)
1277  {
1278  for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
1279  {
1280  output_shape[i] = GetBuffer(m_Model, inputs[0]->buffer)->data.data()[i];
1281  }
1282  }
1283  // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
1284  for (int dimension : output_shape)
1285  {
1286  desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
1287  }
1288  desc.m_OutputShapeEnabled = true;
1289  }
1290  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
1291  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1292 
1293  // TfLite uses NHWC tensors
1294  const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1295  const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1296 
1297  const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1298  const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1299 
1300  CalcPadding(inputHeight,
1301  filterHeight,
1302  desc.m_StrideY,
1303  1, // DilationY
1304  desc.m_PadTop,
1305  desc.m_PadBottom,
1306  options->padding);
1307 
1308  CalcPadding(inputWidth,
1309  filterWidth,
1310  desc.m_StrideX,
1311  1, // DilationX
1312  desc.m_PadLeft,
1313  desc.m_PadRight,
1314  options->padding);
1315 
1316  auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo);
1317 
1318  armnn::IConnectableLayer* layer = nullptr;
1319  auto layerName = fmt::format("TransposeConv:{}:{}", subgraphIndex, operatorIndex);
1320 
1321  if (desc.m_BiasEnabled)
1322  {
1323  auto biasTensorInfo = ToTensorInfo(inputs[3]);
1324  auto biasConstTensor = CreateConstTensorNonPermuted(inputs[3], biasTensorInfo);
1325  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1326  filterTensorAndData,
1327  biasConstTensor,
1328  layerName.c_str());
1329  }
1330  else
1331  {
1332  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1333  filterTensorAndData,
1334  EmptyOptional(),
1335  layerName.c_str());
1336  }
1337 
1338  ARMNN_ASSERT(layer != nullptr);
1339 
1340  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1341  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1342 
1343  // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1344  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1345  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
1346 
1347  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1348  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1349 }
1350 
1351 void TfLiteParserImpl::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1352 {
1353  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1354 }
1355 
1356 void TfLiteParserImpl::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1357 {
1358  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1359 
1360  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1361  CHECK_VALID_SIZE(inputs.size(), 3);
1362 
1363  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1364  CHECK_VALID_SIZE(outputs.size(), 1);
1365 
1366  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1367  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1368 
1369  armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1370  BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1371 
1372  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1373  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1374 
1375  std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1376  ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1377 
1378  size_t step = 2;
1379  std::vector<std::pair<unsigned int, unsigned int>> crops;
1380  for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1381  {
1382  crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1383  }
1384 
1386  desc.m_BlockShape = blockShape;
1387  desc.m_Crops = crops;
1389 
1390  auto layerName = fmt::format("BatchToSpaceND:{}:{}", subgraphIndex, operatorIndex);
1391 
1392  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1393  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1394  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1395 
1396  IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1397  ARMNN_ASSERT(layer != nullptr);
1398  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1399 
1400  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1401  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1402 
1403  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1404  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1405 }
1406 
1407 void TfLiteParserImpl::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1408 {
1409  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1410 
1411  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1412  CHECK_VALID_SIZE(inputs.size(), 1);
1413 
1414  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1415  CHECK_VALID_SIZE(outputs.size(), 1);
1416 
1419  auto layerName = fmt::format("L2Normalization:{}:{}", subgraphIndex, operatorIndex);
1420  IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1421 
1422  ARMNN_ASSERT(layer != nullptr);
1423 
1424  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1425  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1426 
1427  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1428  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1429 
1430  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1431  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1432 }
1433 
1434 void TfLiteParserImpl::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1435 {
1436  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1437 }
1438 
1439 void TfLiteParserImpl::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1440 {
1441  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1442 
1443  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1444  CHECK_VALID_SIZE(inputs.size(), 2);
1445 
1446  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1447  CHECK_VALID_SIZE(outputs.size(), 1);
1448 
1449  auto layerName = fmt::format("Maximum:{}:{}", subgraphIndex, operatorIndex);
1450 
1451  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1452  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1453  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1454 
1455  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1456  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1457 
1458  IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1459  ARMNN_ASSERT(layer != nullptr);
1460  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1461 
1462  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1463  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1464 
1465  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1466  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1467 }
1468 
1469 void TfLiteParserImpl::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1470 {
1471  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1472 
1473  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1474  CHECK_VALID_SIZE(inputs.size(), 2);
1475 
1476  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1477  CHECK_VALID_SIZE(outputs.size(), 1);
1478 
1479  auto layerName = fmt::format("Minimum:{}:{}", subgraphIndex, operatorIndex);
1480 
1481  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1482  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1483  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1484 
1485  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1486  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1487 
1488  IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1489  ARMNN_ASSERT(layer != nullptr);
1490  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1491 
1492  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1493  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1494 
1495  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1496  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1497 }
1498 
1499 void TfLiteParserImpl::ParsePool(size_t subgraphIndex,
1500  size_t operatorIndex,
1501  PoolingAlgorithm algorithm)
1502 {
1503  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1504 
1505  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1506  const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1507 
1508  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1509 
1510  std::string layerName;
1511 
1512  switch (algorithm)
1513  {
1514  case PoolingAlgorithm::Average:
1515  layerName =
1516  fmt::format("AveragePool2D:{}:{}", subgraphIndex, operatorIndex);
1517  break;
1518  case PoolingAlgorithm::Max:
1519  layerName =
1520  fmt::format("MaxPool2D:{}:{}", subgraphIndex, operatorIndex);
1521  break;
1522  default:
1523  ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1524  }
1525 
1526  Pooling2dDescriptor desc;
1527 
1528  desc.m_PoolType = algorithm;
1529  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1530  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1531  desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1532  desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1533  desc.m_PaddingMethod = PaddingMethod::Exclude;
1534  desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1536 
1537  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1538  CHECK_VALID_SIZE(inputs.size(), 1);
1539  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1540 
1541  // assuming input is NHWC
1542  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1543  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1544 
1545  CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1546  desc.m_PadTop, desc.m_PadBottom, options->padding);
1547  CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1548  desc.m_PadLeft, desc.m_PadRight, options->padding);
1549 
1550  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1551  CHECK_VALID_SIZE(outputs.size(), 1);
1552 
1553  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1554  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1555 
1556  IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1557  ARMNN_ASSERT(layer != nullptr);
1558  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1559 
1560  // register the input connection slots for the layer, connections are made after all layers have been created
1561  // only the tensors for the inputs are relevant, exclude the const tensors
1562  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1563  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1564 
1565  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1566  // register the output connection slots for the layer, connections are made after all layers have been created
1567  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1568  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1569 }
1570 
1571 void TfLiteParserImpl::ParseSlice(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(), 3);
1577  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1578  CHECK_VALID_SIZE(outputs.size(), 1);
1579 
1580  SliceDescriptor desc;
1581 
1582  // set begin tensor info for slice descriptor
1583  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1584  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1585 
1586  std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1587  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1588 
1589  // set size tensor info for slice descriptor
1590  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1591  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1592 
1593  std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1594  ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1595  desc = SliceDescriptor(begin, size);
1596 
1597  auto layerName = fmt::format("Slice:{}:{}", subgraphIndex, operatorIndex);
1598 
1599  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1600  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1601  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1602 
1603  IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1604  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1605 
1606  // register the input connection slots for the layer, connections are made after all layers have been created
1607  // only the tensors for the inputs are relevant, exclude the const tensors
1608  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1609  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1610 
1611  // register the output connection slots for the layer, connections are made after all layers have been created
1612  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1613  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1614 }
1615 
1616 void TfLiteParserImpl::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1617 {
1618  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1619  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1620  const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1621 
1622  SoftmaxDescriptor desc;
1623  desc.m_Beta = options->beta;
1624 
1625  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1626  CHECK_VALID_SIZE(inputs.size(), 1);
1627  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1628  CHECK_VALID_SIZE(outputs.size(), 1);
1629 
1630  auto layerName = fmt::format("Softmax:{}:{}", subgraphIndex, operatorIndex);
1631  IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1632 
1633  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1634  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1635 
1636  // register the input connection slots for the layer, connections are made after all layers have been created
1637  // only the tensors for the inputs are relevant, exclude the const tensors
1638  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1639  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1640 
1641  // register the output connection slots for the layer, connections are made after all layers have been created
1642  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1643  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1644 }
1645 
1646 void TfLiteParserImpl::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1647 {
1648  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1649 
1650  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1651  CHECK_VALID_SIZE(inputs.size(), 3);
1652 
1653  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1654  CHECK_VALID_SIZE(outputs.size(), 1);
1655 
1656  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1657  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1658 
1659  armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1660  BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1661 
1662  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1663  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1664 
1665  std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1666  ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1667 
1668  size_t step = 2;
1669  std::vector<std::pair<unsigned int, unsigned int>> padList;
1670  for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1671  {
1672  padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1673  }
1674 
1676  desc.m_BlockShape = blockShape;
1677  desc.m_PadList = padList;
1679 
1680  auto layerName = fmt::format("SpaceToBatchND:{}:{}", subgraphIndex, operatorIndex);
1681 
1682  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1683  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1684  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1685 
1686  IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1687  ARMNN_ASSERT(layer != nullptr);
1688  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1689 
1690  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1691  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1692 
1693  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1694  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1695 }
1696 
1698  const armnn::TensorInfo & inputTensorInfo)
1699 {
1700  CHECK_VALID_SIZE(squeezeDims.size(), 0, 1, 2, 3, 4);
1701  static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1702 
1703  if (inputTensorInfo.GetNumDimensions() > 4)
1704  {
1705  std::stringstream ss;
1706  ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1707  << " shape:" << inputTensorInfo.GetShape() << " "
1708  << CHECK_LOCATION().AsString();
1709  throw ParseException(ss.str());
1710  }
1711 
1712  if (squeezeDims.empty())
1713  {
1714  squeezeDims.assign(dimensionSequence,
1715  dimensionSequence+inputTensorInfo.GetNumDimensions());
1716  }
1717 
1718  std::vector<uint32_t> outputDims;
1719  for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1720  {
1721  bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1722  auto currentDimension = inputTensorInfo.GetShape()[i];
1723  if (skipSqueeze || currentDimension != 1)
1724  {
1725  outputDims.push_back(currentDimension);
1726  }
1727  }
1728 
1729  if (outputDims.size() > 4)
1730  {
1731  std::stringstream ss;
1732  ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1733  << " shape:" << inputTensorInfo.GetShape() << " "
1734  << CHECK_LOCATION().AsString();
1735  throw ParseException(ss.str());
1736  }
1737 
1738  TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1739  outputDims.data());
1740 
1741  // we need to preserve the tensor type and the quantization data as well
1742  TensorInfo outTensorInfo = inputTensorInfo;
1743  outTensorInfo.SetShape(outShape);
1744 
1745  return outTensorInfo;
1746 }
1747 
1748 void TfLiteParserImpl::ParseShape(size_t subgraphIndex, size_t operatorIndex)
1749 {
1750  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
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("Shape:{}:{}", subgraphIndex, operatorIndex);
1758 
1759  IConnectableLayer* layer = m_Network->AddShapeLayer(layerName.c_str());
1760  ARMNN_ASSERT(layer != nullptr);
1761 
1762 
1763  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1764  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1765 
1766  // Check if output tensor type is Signed32 or Signed64
1767  if (outputTensorInfo.GetDataType() != armnn::DataType::Signed32 &&
1768  outputTensorInfo.GetDataType() != armnn::DataType::Signed64)
1769  {
1770  throw ParseException(
1771  fmt::format(
1772  "Output tensor data type is not supported. (Supported types: Signed32 & Signed64) {}",
1773  CHECK_LOCATION().AsString()));
1774  }
1775 
1776  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1777  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1778 
1779  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1780  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1781 }
1782 
1783 void TfLiteParserImpl::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1784 {
1785  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1786 
1787  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1788  CHECK_VALID_SIZE(inputs.size(), 1);
1789 
1790  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1791  CHECK_VALID_SIZE(outputs.size(), 1);
1792 
1793  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1794  const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1795  auto layerName = fmt::format("Squeeze:{}:{}", subgraphIndex, operatorIndex);
1796 
1797  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1798 
1799  std::vector<uint32_t> squeezeDim;
1800  // A single negative dim index is interpreted as a negative index in python
1801  // Meaning the index will be the shape size plus the negative index value
1802  if (options->squeeze_dims.size() == 1 && options->squeeze_dims[0] < 0)
1803  {
1804  int32_t dim = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions()) + options->squeeze_dims[0];
1805  squeezeDim.push_back(static_cast<uint32_t>(dim));
1806  }
1807  else
1808  {
1809  squeezeDim = AsUnsignedVector(options->squeeze_dims);
1810  }
1811 
1812  armnn::TensorInfo outputTensorInfo = TfLiteParserImpl::OutputShapeOfSqueeze(squeezeDim, inputTensorInfo);
1813 
1814  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1815 
1816  ReshapeDescriptor reshapeDesc;
1817  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1818 
1819  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1820  ARMNN_ASSERT(layer != nullptr);
1821  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1822 
1823  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1824  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1825 
1826  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1827  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1828 }
1829 
1830 void TfLiteParserImpl::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1831 {
1832  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1833 
1834  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1835  CHECK_VALID_SIZE(inputs.size(), 4);
1836 
1837  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1838  CHECK_VALID_SIZE(outputs.size(), 1);
1839 
1840  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1841  const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1842 
1844  desc.m_BeginMask = options->begin_mask;
1845  desc.m_EllipsisMask = options->ellipsis_mask;
1846  desc.m_EndMask = options->end_mask;
1847  desc.m_NewAxisMask = options->new_axis_mask;
1848  desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1850 
1851  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1852  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1853 
1854  std::vector<int> begin(beginTensorInfo.GetNumElements());
1855  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1856 
1857  armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1858  BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1859 
1860  std::vector<int> end(endTensorInfo.GetNumElements());
1861  ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1862 
1863  armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1864  BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1865 
1866  std::vector<int> stride(strideTensorInfo.GetNumElements());
1867  ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1868 
1869  desc.m_Begin = begin;
1870  desc.m_End = end;
1871  desc.m_Stride = stride;
1872 
1873  auto layerName = fmt::format("StridedSlice:{}:{}", subgraphIndex, operatorIndex);
1874  IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1875  ARMNN_ASSERT(layer != nullptr);
1876 
1877  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1878  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1879 
1880  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1881  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1882 
1883  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1884  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1885 }
1886 
1887 void TfLiteParserImpl::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1888 {
1889  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1890 
1891  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1892  const auto * options = operatorPtr->builtin_options.AsSubOptions();
1893 
1894  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1895  CHECK_VALID_SIZE(inputs.size(), 2);
1896 
1897  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1898  CHECK_VALID_SIZE(outputs.size(), 1);
1899 
1900  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1901  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1902 
1903  auto layerName = fmt::format("Sub:{}:{}", subgraphIndex, operatorIndex);
1904  IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1905  ARMNN_ASSERT(layer != nullptr);
1906 
1907  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1908  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1909 
1910  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1911  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1912 
1913  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1914 
1915  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1916  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1917 }
1918 
1919 void TfLiteParserImpl::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
1920 {
1921  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1922 
1923  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1924  const auto * options = operatorPtr->builtin_options.AsDivOptions();
1925 
1926  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1927  CHECK_VALID_SIZE(inputs.size(), 2);
1928 
1929  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1930  CHECK_VALID_SIZE(outputs.size(), 1);
1931 
1932  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1933  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1934 
1935  auto layerName = fmt::format("Div:{}:{}", subgraphIndex, operatorIndex);
1936  IConnectableLayer* layer = m_Network->AddDivisionLayer(layerName.c_str());
1937  ARMNN_ASSERT(layer != nullptr);
1938 
1939  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1940  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1941 
1942  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1943  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1944  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1945 
1946  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1947  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1948 }
1949 
1950 void TfLiteParserImpl::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1951 {
1952  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1953 
1954  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1955  const auto * options = operatorPtr->builtin_options.AsAddOptions();
1956 
1957  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1958  CHECK_VALID_SIZE(inputs.size(), 2);
1959 
1960  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1961  CHECK_VALID_SIZE(outputs.size(), 1);
1962 
1963  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1964  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1965 
1966  auto layerName = fmt::format("Add:{}:{}", subgraphIndex, operatorIndex);
1967  IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1968  ARMNN_ASSERT(layer != nullptr);
1969 
1970  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1971  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1972 
1973  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1974  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1975  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1976 
1977  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1978  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1979 }
1980 
1981 void TfLiteParserImpl::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1982 {
1983  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1984 
1985  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1986  const auto * options = operatorPtr->builtin_options.AsMulOptions();
1987 
1988  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1989  CHECK_VALID_SIZE(inputs.size(), 2);
1990 
1991  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1992  CHECK_VALID_SIZE(outputs.size(), 1);
1993 
1994  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1995  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1996 
1997  auto layerName = fmt::format("Mul:{}:{}", subgraphIndex, operatorIndex);
1998  IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1999  ARMNN_ASSERT(layer != nullptr);
2000 
2001  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2002  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2003 
2004  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2005  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2006  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2007 
2008  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2009  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2010 }
2011 
2012 void TfLiteParserImpl::ParseMean(size_t subgraphIndex, size_t operatorIndex)
2013 {
2014  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2015 
2016  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2017 
2018  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2019  CHECK_VALID_SIZE(outputs.size(), 1);
2020 
2021  armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
2022  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2023 
2024  armnn::MeanDescriptor desc;
2025  std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
2026  ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
2027  desc.m_Axis = axis;
2028 
2029  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2030  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2031 
2032  desc.m_KeepDims =
2033  inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
2034  true : false;
2035 
2036  auto layerName = fmt::format("Mean:{}:{}", subgraphIndex, operatorIndex);
2037  IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
2038  ARMNN_ASSERT(layer != nullptr);
2039 
2040  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2041 
2042  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2043  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2044 
2045  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2046  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2047 }
2048 
2049 void TfLiteParserImpl::ParsePad(size_t subgraphIndex, size_t operatorIndex)
2050 {
2051  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2052 
2053  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2054 
2055  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2056  CHECK_VALID_SIZE(outputs.size(), 1);
2057 
2058  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2059 
2060  armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
2061  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2062 
2063  std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
2064  ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
2065 
2066  size_t step = 2;
2067  armnn::PadDescriptor desc;
2068  if (inputTensorInfo.IsQuantized())
2069  {
2070  desc.m_PadValue = static_cast<float>(inputTensorInfo.GetQuantizationOffset());
2071  }
2072  for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
2073  {
2074  desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
2075  }
2076 
2077  auto layerName = fmt::format("Pad:{}:{}", subgraphIndex, operatorIndex);
2078  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2079 
2080  IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
2081  ARMNN_ASSERT(layer != nullptr);
2082  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2083 
2084  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2085  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2086 
2087  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2088  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2089 }
2090 
2091 void TfLiteParserImpl::ParsePrelu(size_t subgraphIndex, size_t operatorIndex)
2092 {
2093  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2094 
2095  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2096  CHECK_VALID_SIZE(inputs.size(), 2);
2097 
2098  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2099  CHECK_VALID_SIZE(outputs.size(), 1);
2100 
2101  auto layerName = fmt::format("Prelu:{}:{}", subgraphIndex, operatorIndex);
2102 
2103  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2104  armnn::TensorInfo alphaTensorInfo = ToTensorInfo(inputs[1]);
2105  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2106  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2107 
2108  IConnectableLayer* layer = m_Network->AddPreluLayer(layerName.c_str());
2109  ARMNN_ASSERT(layer != nullptr);
2110  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2111 
2112  if (IsConstTensor(inputs[1]))
2113  {
2114  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2115  armnn::IInputSlot* slot = &(layer->GetInputSlot(0));
2116  RegisterConsumerOfTensor(subgraphIndex, inputTensorIndexes[0], slot);
2117 
2118  auto alphaTensorAndData = CreateConstTensorNonPermuted(inputs[1], alphaTensorInfo);
2119  std::string constLayerName = fmt::format("Constant:{}", inputs[1]->name);
2120  IConnectableLayer* constLayer =
2121  m_Network->AddConstantLayer(alphaTensorAndData, constLayerName.c_str());
2122  ARMNN_ASSERT(constLayer != nullptr);
2123 
2124  constLayer->GetOutputSlot(0).SetTensorInfo(alphaTensorInfo);
2125  constLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
2126  RegisterOutputSlots(subgraphIndex,
2127  VIRTUAL_OPERATOR_ID,
2128  constLayer,
2129  { inputTensorIndexes[1] });
2130  }
2131  else
2132  {
2133  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2134  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIndexes);
2135  }
2136 
2137  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2138  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2139 }
2140 
2141 void TfLiteParserImpl::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
2142 {
2143  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2144 
2145  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2146  CHECK_VALID_SIZE(inputs.size(), 1);
2147 
2148  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2149  CHECK_VALID_SIZE(outputs.size(), 1);
2150 
2151  auto layerName = fmt::format("Quantize:{}:{}", subgraphIndex, operatorIndex);
2152 
2153  IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
2154  ARMNN_ASSERT(layer != nullptr);
2155 
2156  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2157  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2158 
2159  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2160  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2161 
2162  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2163  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2164 }
2165 
2166 void TfLiteParserImpl::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
2167 {
2168  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
2169 }
2170 
2171 void TfLiteParserImpl::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
2172 {
2173  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
2174 }
2175 
2176 void TfLiteParserImpl::ParseLeakyRelu(size_t subgraphIndex, size_t operatorIndex)
2177 {
2178  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::LeakyReLu);
2179 }
2180 
2181 void TfLiteParserImpl::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
2182 {
2183  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
2184 }
2185 
2186 void TfLiteParserImpl::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
2187 {
2188  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
2189 }
2190 
2191 void TfLiteParserImpl::ParseElu(size_t subgraphIndex, size_t operatorIndex)
2192 {
2193  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::Elu);
2194 }
2195 
2196 void TfLiteParserImpl::ParseHardSwish(size_t subgraphIndex, size_t operatorIndex)
2197 {
2198  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::HardSwish);
2199 }
2200 
2201 void TfLiteParserImpl::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
2202 {
2203  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2204  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2205  IgnoreUnused(operatorPtr);
2206 
2207  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2208  CHECK_VALID_SIZE(inputs.size(), 1);
2209 
2210  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2211  CHECK_VALID_SIZE(outputs.size(), 1);
2212 
2213  auto layerName = fmt::format("Activation:");
2214  ActivationDescriptor activationDesc;
2215  activationDesc.m_Function = activationType;
2216 
2217  switch (activationType)
2218  {
2219  case ActivationFunction::ReLu:
2220  {
2221  layerName += fmt::format("RELU:{}:{}", subgraphIndex, operatorIndex);
2222  break;
2223  }
2224  case ActivationFunction::BoundedReLu:
2225  {
2226  layerName += fmt::format("RELU6:{}:{}", subgraphIndex, operatorIndex);
2227  activationDesc.m_A = 6.0f;
2228  activationDesc.m_B = 0.0f;
2229  break;
2230  }
2231  case ActivationFunction::Sigmoid:
2232  {
2233  layerName += fmt::format("SIGMOID:{}:{}", subgraphIndex, operatorIndex);
2234  break;
2235  }
2236  case ActivationFunction::TanH:
2237  {
2238  layerName += fmt::format("TANH:{}:{}", subgraphIndex, operatorIndex);
2239  activationDesc.m_A = 1.0f;
2240  activationDesc.m_B = 1.0f;
2241  break;
2242  }
2243  case ActivationFunction::LeakyReLu:
2244  {
2245  layerName += fmt::format("LEAKYRELU:{}:{}", subgraphIndex, operatorIndex);
2246  const auto * options = operatorPtr->builtin_options.AsLeakyReluOptions();
2247  activationDesc.m_A = options->alpha;
2248  break;
2249  }
2250  case ActivationFunction::Elu:
2251  {
2252  layerName += fmt::format("ELU:{}:{}", subgraphIndex, operatorIndex);
2253  activationDesc.m_A = 1.0f;
2254  break;
2255  }
2256  case ActivationFunction::HardSwish:
2257  {
2258  layerName += fmt::format("HARDSWISH:{}:{}", subgraphIndex, operatorIndex);
2259  break;
2260  }
2261  default:
2262  {
2263  throw ParseException(
2264  fmt::format("Unexpected ActivationFunction[{}] when creating layerName {} ",
2265  static_cast<int>(activationType), CHECK_LOCATION().AsString()));
2266  }
2267  }
2268 
2269  IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2270 
2271  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2272  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2273 
2274  // register the input connection slots for the layer, connections are made after all layers have been created
2275  // only the tensors for the inputs are relevant, exclude the const tensors
2276  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2277  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2278 
2279  // register the output connection slots for the layer, connections are made after all layers have been created
2280  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2281  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2282 }
2284  const std::vector<int32_t> & targetDimsIn)
2285 {
2286  std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
2287  const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
2288 
2289  if (stretchDim != targetDimsIn.end())
2290  {
2291  if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
2292  {
2293  throw ParseException(
2294  fmt::format("At most one component of shape can be -1 {}", CHECK_LOCATION().AsString()));
2295  }
2296 
2297  auto targetNumElements =
2298  armnn::numeric_cast<unsigned int>(
2299  std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
2300 
2301  auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
2302  outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
2303  }
2304 
2305  TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
2306 
2307  TensorInfo reshapeInfo = inputTensorInfo;
2308  reshapeInfo.SetShape(outputShape);
2309 
2310  return reshapeInfo;
2311 }
2312 
2313 void TfLiteParserImpl::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
2314 {
2315  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2316 
2317  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2318 
2319  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2320  CHECK_VALID_SIZE(outputs.size(), 1);
2321 
2322  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2323  const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
2324  auto layerName = fmt::format("Reshape:{}:{}", subgraphIndex, operatorIndex);
2325 
2326  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2327  armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
2328  CheckMatchingQuantization(inputTensorInfo, actualOutputTensorInfo, layerName, "Input 0", "Output 0");
2329 
2330  // Extracting new shape for the output
2331  // There are two ways it can be passed
2332  // * First is to define the target shape in the operator built-in options
2333  // * Second is to pass it as a second input tensor
2334  std::vector<int32_t> targetShape;
2335  bool targetShapeFound = false;
2336  // Check if built-in options were given
2337  if (options != nullptr)
2338  {
2339  // make sure the parameter is given
2340  if (options->new_shape.empty() == false)
2341  {
2342  targetShape = options->new_shape;
2343  targetShapeFound = true;
2344  }
2345  }
2346 
2347  // If there is no built-in option given or if the built-in new_shape parameter was empty
2348  if (!targetShapeFound)
2349  {
2350  // Check for a second input tensor
2351  if (inputs.size() > 1 && inputs[1] != nullptr)
2352  {
2353  if (inputs[1]->is_variable)
2354  {
2355  ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
2356  }
2357 
2358  if (inputs[1]->shape.size() != 1)
2359  {
2360  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
2361  }
2362 
2363  if (inputs[1]->type != tflite::TensorType_INT32)
2364  {
2365  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
2366  }
2367 
2368  // Extract target shape from input
2369  auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2370  auto values = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
2371  if (!values)
2372  {
2373  ARMNN_THROW_PARSE_EXCEPTION("Reshape operator target shape input buffer data is null");
2374  }
2375  for (int i=0; i < inputs[1]->shape[0]; ++i)
2376  {
2377  targetShape.push_back(values[i]);
2378  }
2379  }
2380  else
2381  {
2382  ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2383  "At least one method required");
2384  }
2385  }
2386 
2387  armnn::TensorInfo reshapeOutputTensorInfo =
2388  TfLiteParserImpl::OutputShapeOfReshape(inputTensorInfo, targetShape);
2389 
2390  // Check for valid input size and that reshape parameters equal output shape
2391  const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2392  if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
2393  {
2394  std::stringstream ss;
2395  ss << "New shape defined in reshape parameters "
2396  << reshapeOutputTensorShape
2397  << " does not equal output shape "
2398  << actualOutputTensorInfo.GetShape()
2399  << ": "
2400  << CHECK_LOCATION().AsString();
2401  throw ParseException(ss.str());
2402  }
2403 
2404  ReshapeDescriptor reshapeDesc;
2405  reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
2406 
2407  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
2408  ARMNN_ASSERT(layer != nullptr);
2409  layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
2410 
2411  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2412  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2413 
2414  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2415  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2416 }
2417 
2418 void TfLiteParserImpl::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2419 {
2420  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2421 }
2422 
2423 void TfLiteParserImpl::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2424 {
2425  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2426 }
2427 
2428 void TfLiteParserImpl::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2429 {
2430  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2431 
2432  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2433  CHECK_VALID_SIZE(inputs.size(), 2);
2434 
2435  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2436  CHECK_VALID_SIZE(outputs.size(), 1);
2437 
2438  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2439 
2440  // Data for the parsed tensor args (size) must be stored locally.
2441  std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2442 
2443  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2444  ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2445 
2446  ResizeDescriptor desc;
2447  desc.m_Method = resizeMethod;
2448  desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
2449  desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2450  desc.m_DataLayout = armnn::DataLayout::NHWC;
2451 
2452  auto layerName = fmt::format("Resize:");
2453 
2454  switch (resizeMethod)
2455  {
2456  case ResizeMethod::Bilinear:
2457  {
2458  layerName += fmt::format("BILINEAR:{}:{}", subgraphIndex, operatorIndex);
2459 
2460  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2461  const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2462 
2463  desc.m_AlignCorners = options->align_corners;
2464  break;
2465  }
2466  case ResizeMethod::NearestNeighbor:
2467  {
2468  layerName += fmt::format("NEARESTNEIGHBOR:{}:{}", subgraphIndex, operatorIndex);
2469  break;
2470  }
2471  default:
2472  {
2473  throw ParseException(
2474  fmt::format("Unexpected ResizeMethod[{}] when creating layerName {} ",
2475  static_cast<int>(resizeMethod), CHECK_LOCATION().AsString()));
2476  }
2477  }
2478 
2479  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2480  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2481  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2482 
2483  IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
2484  ARMNN_ASSERT(layer != nullptr);
2485  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2486 
2487  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2488  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2489 
2490  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2491  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2492 }
2493 
2494 void TfLiteParserImpl::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2495 {
2496  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2497 
2498  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2499  const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2500 
2501  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2502 
2503  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2504  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2505  CHECK_VALID_SIZE(outputs.size(), 1);
2506 
2507  unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2508  uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
2509 
2510  const unsigned int concatDimInput = static_cast<unsigned int>(
2511  (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
2512 
2513  OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2514  concatDescriptor.SetConcatAxis(concatDimInput);
2515 
2516  unsigned int mergeDimOrigin = 0;
2517 
2518  for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2519  {
2520  TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2521 
2522  // This set up concatDescriptor view origin
2524  inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
2525  }
2526 
2527  auto layerName = fmt::format("Concatenation:{}:{}", subgraphIndex, operatorIndex);
2528  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2529 
2530  IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
2531  ARMNN_ASSERT(layer != nullptr);
2532  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2533 
2534  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2535  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2536 
2537  // add fused activation layer
2538  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2539 
2540  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2541  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2542 }
2543 
2544 void TfLiteParserImpl::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2545 {
2546  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2547 
2548  const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2549  const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2550 
2551  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2552 
2554  desc.m_BiasEnabled = false;
2555  desc.m_TransposeWeightMatrix = true;
2556 
2557  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2558  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2559  CHECK_VALID_SIZE(outputs.size(), 1);
2560 
2561  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2562 
2563  // Fully Connected Layer accepts two dimensional weights input
2564  int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2565  if (weightsDimension != 2)
2566  {
2567  throw ParseException(
2568  fmt::format("Dimension {} for Fully Connected weights is not supported by Armnn. "
2569  "Node {}",
2570  weightsDimension,
2571  CHECK_LOCATION().AsString()));
2572  }
2573 
2574  armnn::IConnectableLayer* layer = nullptr;
2575  auto layerName = fmt::format("FullyConnected:{}:{}", subgraphIndex, operatorIndex);
2576 
2577  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2578  // Add the first input tensor to the registration list
2579  std::vector<unsigned int> tensorIndexesToRegister = {inputTensorIndexes[0]};
2580  std::vector<unsigned int> ignoreInputWhenRegister = {};
2581 
2582  desc.m_ConstantWeights = IsConstTensor(inputs[1]);
2583 
2584  // Add the weights input to the registration list, constant layers will be added by SetupConstantLayers if constant.
2585  tensorIndexesToRegister.emplace_back(inputTensorIndexes[1]);
2586 
2587  if (inputs.size() == 3)
2588  {
2589  desc.m_BiasEnabled = true;
2590 
2591  // Add the biases input to the registration list, constant layer will be added by SetupConstantLayers.
2592  tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
2593  }
2594 
2595  // Filters and biases are always passed to fully connected as inputs
2596  layer = m_Network->AddFullyConnectedLayer(desc, layerName.c_str());
2597 
2598  ARMNN_ASSERT(layer != nullptr);
2599  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2600 
2601  unsigned int startingSlotIndex = 0;
2602  if (inputTensorInfo.GetNumDimensions() > 2)
2603  {
2604  // Add reshape to flatten to 2D [batch_size, input_size],
2605  // where "input_size" corresponds to the number of inputs to the layer,
2606  // matching the second dimension of weights,
2607  // and "batch_size" is calculated by dividing the number of elements by "input_size".
2608  std::vector<unsigned int> reshapedDimensions(2);
2609  reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2610  reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2611 
2612  if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2613  {
2614  throw ParseException(
2615  fmt::format("Failed to deduce input tensor shape from filter size {} {}",
2616  reshapedDimensions[1],
2617  CHECK_LOCATION().AsString()));
2618  }
2619 
2620  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2621  reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2622 
2623  std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
2624  armnn::ReshapeDescriptor reshapeDescriptor;
2625  reshapeDescriptor.m_TargetShape = reshapedTensorInfo.GetShape();
2626  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(reshapeDescriptor, layerName.c_str());
2627 
2628  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2629  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2630 
2631  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2632  // Fc layer connects to the reshape layer, so we skip the first input slot when registering fc's input slots
2633  tensorIndexesToRegister.erase(tensorIndexesToRegister.begin());
2634  startingSlotIndex = 1;
2635  }
2636 
2637  RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister, startingSlotIndex);
2638 
2639  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2640  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2641 
2642  // we need to add the activation layer and fortunately we don't need to care about the data layout
2643  armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2644  options->fused_activation_function);
2645 
2646  // register the output connection slots for the layer, connections are made after all layers have been created
2647  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2648  RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2649 }
2650 
2651 void TfLiteParserImpl::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2652 {
2653  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2654 
2655  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2656 
2657  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2658  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2659  CHECK_VALID_SIZE(outputs.size(), 4);
2660 
2661  // Obtain custom options from flexbuffers
2662  auto custom_options = operatorPtr->custom_options;
2663  const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2664 
2665  // Obtain descriptor information from tf lite
2667  desc.m_MaxDetections = m["max_detections"].AsUInt32();
2668  desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2669  desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2670  desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2671  desc.m_NumClasses = m["num_classes"].AsUInt32();
2672  desc.m_ScaleH = m["h_scale"].AsFloat();
2673  desc.m_ScaleW = m["w_scale"].AsFloat();
2674  desc.m_ScaleX = m["x_scale"].AsFloat();
2675  desc.m_ScaleY = m["y_scale"].AsFloat();
2676 
2677  if (!(m["use_regular_nms"].IsNull()))
2678  {
2679  desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
2680  }
2681  if (!(m["detections_per_class"].IsNull()))
2682  {
2683  desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2684  }
2685 
2686  if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2687  {
2688  throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2689  "must be positive and less than or equal to 1.");
2690  }
2691 
2692  armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2693  auto anchorTensorAndData = CreateConstTensorNonPermuted(inputs[2], anchorTensorInfo);
2694 
2695  auto layerName = fmt::format("DetectionPostProcess:{}:{}", subgraphIndex, operatorIndex);
2696  IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData,
2697  layerName.c_str());
2698 
2699  ARMNN_ASSERT(layer != nullptr);
2700 
2701  // The model does not specify the output shapes.
2702  // The output shapes are calculated from the max_detection and max_classes_per_detection.
2703  unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2704  m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2705  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2706  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2707  m_OverridenOutputShapes.push_back({ 1 });
2708 
2709  for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2710  {
2711  armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
2712  layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2713  }
2714 
2715  // Register the input connection slots for the layer, connections are made after all layers have been created
2716  // only the tensors for the inputs are relevant, exclude the const tensors
2717  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2718  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2719 
2720  // Register the output connection slots for the layer, connections are made after all layers have been created
2721  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2722  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2723  outputTensorIndexes[1],
2724  outputTensorIndexes[2],
2725  outputTensorIndexes[3]});
2726 }
2727 
2728 /// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2729 void TfLiteParserImpl::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2730 {
2731  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2732 
2733  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2734  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2735  CHECK_VALID_SIZE(outputs.size(), 1);
2736 
2737  if (inputs.size() < 1)
2738  {
2739  throw ParseException("Pack must have at least one input.");
2740  }
2741 
2742  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2743  const auto* options = operatorPtr->builtin_options.AsPackOptions();
2744 
2745  StackDescriptor desc;
2746  desc.m_Axis = static_cast<uint32_t>(options->axis);
2747  desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2748 
2749  // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2750  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2751  desc.m_InputShape = inputTensorInfo.GetShape();
2752 
2753  auto layerName = fmt::format("Pack:{}:{}", subgraphIndex, operatorIndex);
2754  IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2755 
2756  ARMNN_ASSERT(layer != nullptr);
2757 
2758  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2759  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2760 
2761  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2762  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2763 
2764  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2765  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2766 }
2767 
2768 void TfLiteParserImpl::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2769 {
2770  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2771 
2772  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2773  const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2774 
2775  // This unpackAxis indicates the axis to unpack
2776  const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2777 
2778  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2779  CHECK_VALID_SIZE(inputs.size(), 1);
2780 
2781  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2782 
2783  if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2784  {
2785  throw ParseException(
2786  fmt::format("The unpack axis: {} cannot be greater than or equal to "
2787  "the number of input dimension {} {}",
2788  unpackAxis,
2789  inputTensorInfo.GetNumDimensions(),
2790  CHECK_LOCATION().AsString()));
2791  }
2792 
2793  unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2794  // If num is not defined, automatically infer from the length of the dimension axis.
2795  if(unpackNum == 0)
2796  {
2797  unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2798  }
2799 
2800  // If unpack number cannot be inferred and is still zero, throw ParseException.
2801  if(unpackNum == 0)
2802  {
2803  throw ParseException("Number to unpack must greater than zero.");
2804  }
2805 
2806  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2807  CHECK_VALID_SIZE(outputs.size(), unpackNum);
2808 
2809  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2810  std::vector<unsigned int> unpackDimSizes(inputDimSize);
2811 
2812  // Add current input shape to unpackDimSizes
2813  for (unsigned int i = 0; i < inputDimSize; ++i)
2814  {
2815  unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2816  }
2817 
2818  if (unpackDimSizes[unpackAxis] != unpackNum)
2819  {
2820  throw ParseException("Number to unpack must be the same as length of the dimension to "
2821  "unpack along.");
2822  }
2823 
2824  unpackDimSizes[unpackAxis] /= unpackNum;
2825 
2826  SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2827  for (unsigned int j = 0; j < unpackNum; ++j)
2828  {
2829  // Set the size of the views.
2830  for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2831  {
2832  splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2833  }
2834  splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2835  }
2836 
2837  auto layerName = fmt::format("Unpack:{}:{}", subgraphIndex, operatorIndex);
2838  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2839  ARMNN_ASSERT(layer != nullptr);
2840 
2841  TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2842  unpackDimSizes.data());
2843 
2844  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2845  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2846 
2847  std::vector<unsigned int> reshapeDims;
2848  for (unsigned int axis = 0; axis < splitOutShape.GetNumDimensions(); ++axis)
2849  {
2850  if (axis != unpackAxis)
2851  {
2852  reshapeDims.push_back(splitOutShape[axis]);
2853  }
2854  }
2855 
2856  TensorShape reshapeOutputShape(splitOutShape.GetNumDimensions() -1, reshapeDims.data());
2857 
2858  // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2859  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2860  {
2861  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k], true);
2862  std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
2864  desc.m_TargetShape = reshapeOutputShape;
2865  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2866 
2867  layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2868  outputTensorInfo.GetDataType(),
2869  outputTensorInfo.GetQuantizationScale(),
2870  outputTensorInfo.GetQuantizationOffset()));
2871  layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2872 
2873  reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2874 
2875  uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2876  armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2877  RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2878  }
2879 }
2880 
2881 void TfLiteParserImpl::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2882 {
2883  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2884 
2885  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2886  const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2887 
2888  const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2889 
2890  // If number of splits cannot be inferred and is zero, throw ParseException.
2891  if(numSplits == 0)
2892  {
2893  throw ParseException("Number to splits must greater than zero.");
2894  }
2895 
2896  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2897  CHECK_VALID_SIZE(inputs.size(), 2);
2898  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2899  CHECK_VALID_SIZE(outputs.size(), numSplits);
2900 
2901  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2902  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
2903  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2904 
2905  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2906  if (axisBufferPtr == nullptr)
2907  {
2908  throw ParseException(
2909  fmt::format("Operation has invalid inputs. Failed to read axis. {}",
2910  CHECK_LOCATION().AsString()));
2911  }
2912 
2913  std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
2914  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2915  int32_t axis = axisData[0];
2916 
2917  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
2918  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
2919  {
2920  // Square bracket denotes inclusive n while parenthesis denotes exclusive n
2921  // E.g. Rank 4 tensor can have axis in range [-4, 3)
2922  // -1 == 3, -2 == 2, -3 == 1, -4 == 0
2923  throw ParseException(
2924  fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
2925  axis,
2926  CHECK_LOCATION().AsString()));
2927  }
2928 
2929  const unsigned int splitDim = armnnUtils::GetUnsignedAxis(inputTensorInfo.GetNumDimensions(), axis);
2930 
2931  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2932  if (inputDimSize > MaxNumOfTensorDimensions)
2933  {
2934  throw ParseException(
2935  fmt::format("The number of dimensions: {} for input tensors of the split op cannot be greater than {} {}",
2936  inputTensorInfo.GetNumDimensions(),
2938  CHECK_LOCATION().AsString()));
2939  }
2940 
2941  std::vector<unsigned int> splitterDimSizes(inputDimSize);
2942 
2943  // Add current input shape to splitterDimSizes
2944  for (unsigned int i = 0; i < inputDimSize; ++i)
2945  {
2946  splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2947  }
2948 
2949  if (splitterDimSizes[splitDim] % numSplits != 0)
2950  {
2951  throw ParseException("Number of splits must evenly divide the dimension");
2952  }
2953  splitterDimSizes[splitDim] /= numSplits;
2954 
2955  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2956  for (unsigned int j = 0; j < numSplits; ++j)
2957  {
2958  // Set the size of the views.
2959  for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2960  {
2961  splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2962  }
2963  splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2964  }
2965 
2966  auto layerName = fmt::format("Split:{}:{}", subgraphIndex, operatorIndex);
2967  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2968  ARMNN_ASSERT(layer != nullptr);
2969 
2970  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2971  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
2972 
2973  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2974  {
2975  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
2976  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2977  }
2978 
2979  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2980  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2981 }
2982 
2983 unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
2984 {
2985  int numDims = armnn::numeric_cast<int>(numDimsIn);
2986  int v = idx < 0 ? numDims + idx : idx;
2987  ARMNN_ASSERT(v >= 0);
2988  ARMNN_ASSERT(v < numDims);
2989 
2990  return static_cast<unsigned int>(v);
2991 }
2992 
2993 void TfLiteParserImpl::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
2994 {
2995  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2996 
2997  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2998  const auto * options = operatorPtr->builtin_options.AsSplitVOptions();
2999 
3000  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3001  CHECK_VALID_SIZE(inputs.size(), 3);
3002 
3003  auto& inputTensor = inputs[0];
3004  auto& splitsTensor = inputs[1];
3005  auto& axisTensor = inputs[2];
3006 
3007  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
3008  armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
3009  armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
3010  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
3011 
3012  // Inputs
3013  auto inputDimSize = inputTensorInfo.GetNumDimensions();
3014  if (inputDimSize > MaxNumOfTensorDimensions)
3015  {
3016  throw ParseException(
3017  fmt::format("The number of dimensions: {} for input tensors of the "
3018  "SplitV op cannot be greater than {} {}",
3019  inputTensorInfo.GetNumDimensions(),
3021  CHECK_LOCATION().AsString()));
3022  }
3023 
3024  // Get split axis
3025  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
3026  if (axisBufferPtr == nullptr)
3027  {
3028  throw ParseException(
3029  fmt::format("Operation has invalid inputs. Failed to read axis. {}",
3030  CHECK_LOCATION().AsString()));
3031  }
3032 
3033  std::vector<int> axisData(axisTensorInfo.GetNumElements());
3034  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
3035  int32_t axis = axisData[0];
3036 
3037  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3038  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3039  {
3040  // Square bracket denotes inclusive n while parenthesis denotes exclusive n
3041  // E.g. Rank 4 tensor can have axis in range [-4, 3)
3042  // -1 == 3, -2 == 2, -3 == 1, -4 == 0
3043  throw ParseException(
3044  fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
3045  axis,
3046  CHECK_LOCATION().AsString()));
3047  }
3048  const unsigned int splitDim = ComputeWrappedIndex(axis, inputTensorInfo.GetNumDimensions());
3049 
3050  // Set split sizes
3051  CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
3052  unsigned int numSplits{0};
3053 
3054  if(options)
3055  {
3056  numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
3057  }
3058  else
3059  {
3060  numSplits = splitsInfo.GetNumElements();
3061  }
3062 
3063  if (numSplits <=0)
3064  {
3065  throw ParseException("SplitV has invalid number of splits");
3066  }
3067 
3068  std::vector<int> splitsData(numSplits);
3069  BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
3070  ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
3071 
3072  unsigned int idx = 0;
3073  int numInferred{0};
3074  unsigned int inferIdx{0};
3075  int splitSum{0};
3076  for (auto split : splitsData)
3077  {
3078  if (split < 0)
3079  {
3080  numInferred++;
3081  inferIdx = idx;
3082  }
3083  else
3084  {
3085  splitSum += split;
3086  }
3087  idx++;
3088  }
3089  // Check for inferred Axis
3090  if (numInferred == 0)
3091  {
3092  if (splitSum != armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]))
3093  {
3094  throw ParseException("SplitV split_sizes does not sum to the dimension of value along split_dim.");
3095  }
3096  }
3097  else if (numInferred == 1)
3098  {
3099  splitsData[inferIdx] = armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - splitSum;
3100  }
3101  else
3102  {
3103  throw ParseException("Cannot infer split size for more than one split");
3104  }
3105 
3106  //Ouput size validation
3107  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3108  CHECK_VALID_SIZE(outputs.size(), numSplits);
3109 
3110  // Setup Armnn descriptor
3111  SplitterDescriptor splitDesc(numSplits, inputDimSize);
3112  unsigned int accumSplit = 0;
3113  for (unsigned int j = 0; j < numSplits; ++j)
3114  {
3115  unsigned int splitSize = armnn::numeric_cast<unsigned int>(splitsData[j]);
3116 
3117  // Set the size of the views.
3118  for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
3119  {
3120  unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
3121  if (dimIdx == splitDim)
3122  {
3123  dimSize = splitSize;
3124  }
3125  splitDesc.SetViewSize(j, dimIdx, dimSize);
3126  }
3127 
3128  splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
3129  accumSplit += splitSize;
3130  }
3131 
3132  auto layerName = fmt::format("SplitV:{}:{}", subgraphIndex, operatorIndex);
3133  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
3134  ARMNN_ASSERT(layer != nullptr);
3135 
3136  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3137  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3138 
3139  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
3140  {
3141  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
3142  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
3143  }
3144 
3145  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3146  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3147 }
3148 
3149 void TfLiteParserImpl::ParseArgMin(size_t subgraphIndex, size_t operatorIndex)
3150 {
3151  ParseArgMinMax(subgraphIndex, operatorIndex, armnn::ArgMinMaxFunction::Min);
3152 }
3153 
3154 void TfLiteParserImpl::ParseArgMax(size_t subgraphIndex, size_t operatorIndex)
3155 {
3156  ParseArgMinMax(subgraphIndex, operatorIndex, armnn::ArgMinMaxFunction::Max);
3157 }
3158 
3159 void TfLiteParserImpl::ParseArgMinMax(size_t subgraphIndex, size_t operatorIndex, ArgMinMaxFunction argMinMaxFunction)
3160 {
3161  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3162  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3163  CHECK_VALID_SIZE(inputs.size(), 2);
3164 
3165  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3166  CHECK_VALID_SIZE(outputs.size(), 1);
3167 
3168  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3169  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[1]);
3170  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
3171  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
3172 
3173  // Check if output tensor type is Signed32 or Signed64
3174  if (outputTensorInfo.GetDataType() != armnn::DataType::Signed32 &&
3175  outputTensorInfo.GetDataType() != armnn::DataType::Signed64)
3176  {
3177  throw ParseException(
3178  fmt::format(
3179  "Output tensor data type is not supported. (Supported types: Signed32 & Signed64) {}",
3180  CHECK_LOCATION().AsString()));
3181  }
3182 
3183  // Get const axis value from model and set it to descriptor.
3184  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3185  if (axisBufferPtr == nullptr)
3186  {
3187  throw ParseException(
3188  fmt::format("Operation has invalid inputs. Failed to read axis. {}",
3189  CHECK_LOCATION().AsString()));
3190  }
3191 
3192  std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
3193  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
3194  int32_t axis = axisData.front();
3195 
3196  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3197  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3198  {
3199  // Square bracket denotes inclusive n while parenthesis denotes exclusive n
3200  // E.g. Rank 4 tensor can have axis in range [-4, 3)
3201  // -1 == 3, -2 == 2, -3 == 1, -4 == 0
3202  throw ParseException(
3203  fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
3204  axis,
3205  CHECK_LOCATION().AsString()));
3206  }
3207 
3208  ArgMinMaxDescriptor desc;
3209  desc.m_Axis = axis;
3210  desc.m_Function = argMinMaxFunction;
3211 
3212  // Register a ArgMin/ArgMax layer.
3213  auto layerName = argMinMaxFunction == ArgMinMaxFunction::Max ? "ArgMax:{}:{}" : "ArgMin:{}:{}";
3214  auto layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3215  IConnectableLayer *layer = m_Network->AddArgMinMaxLayer(desc, layerNameFormatted.c_str());
3216  ARMNN_ASSERT(layer != nullptr);
3217  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3218 
3219  // Register input tensor to the layer.
3220  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3221  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3222 
3223  // Register output tensor to the layer.
3224  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3225  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3226 }
3227 
3228 void TfLiteParserImpl::ParseGather(size_t subgraphIndex, size_t operatorIndex)
3229 {
3230  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3231 
3232  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3233  CHECK_VALID_SIZE(inputs.size(), 2);
3234  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3235  CHECK_VALID_SIZE(outputs.size(), 1);
3236 
3237  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3238  armnn::TensorInfo indicesTensorInfo = ToTensorInfo(inputs[1]);
3239  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3240 
3241  armnn::GatherDescriptor gatherDescriptor;
3242 
3243  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3244  const auto * options = operatorPtr->builtin_options.AsGatherOptions();
3245  auto axis = options->axis;
3246 
3247  auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
3248  auto indicesDimensions = indicesTensorInfo.GetNumDimensions();
3249  auto outputDimensions = outputTensorInfo.GetNumDimensions();
3250  if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
3251  {
3252  throw ParseException(
3253  fmt::format("Operation has invalid axis: {} It is out of bounds [ -{}, {} ) {}",
3254  axis,
3255  inputDimensions, inputDimensions,
3256  CHECK_LOCATION().AsString()));
3257  }
3258  if (outputDimensions != static_cast<unsigned int>(inputDimensions) + indicesDimensions - 1)
3259  {
3260  throw ParseException(
3261  fmt::format("Operation has invalid output dimensions: {} Output must be an ({} + {} - 1) -D tensor {}",
3262  outputDimensions,
3263  inputDimensions, indicesDimensions,
3264  CHECK_LOCATION().AsString()));
3265  }
3266 
3267  gatherDescriptor.m_Axis = axis;
3268 
3269  auto layerName = fmt::format("Gather:{}:{}", subgraphIndex, operatorIndex);
3270  IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, layerName.c_str());
3271  ARMNN_ASSERT(layer != nullptr);
3272  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3273 
3274  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3275  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
3276 
3277  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3278  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3279 }
3280 
3281 void TfLiteParserImpl::ParseDepthToSpace(size_t subgraphIndex, size_t operatorIndex)
3282 {
3283  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3284 
3285  TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3286  CHECK_VALID_SIZE(inputs.size(), 1);
3287  TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3288  CHECK_VALID_SIZE(outputs.size(), 1);
3289 
3290  armnn::DepthToSpaceDescriptor descriptor;
3291 
3292  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3293  const auto * options = operatorPtr->builtin_options.AsDepthToSpaceOptions();
3294  auto blockSize = options->block_size;
3295  if (blockSize < 2)
3296  {
3297  throw ParseException(
3298  fmt::format("Operation has invalid block size: {} Block size should be >= 2 {}",
3299  blockSize,
3300  CHECK_LOCATION().AsString()));
3301  }
3302  descriptor.m_BlockSize = armnn::numeric_cast<uint32_t>(blockSize);
3303 
3304  auto layerName = fmt::format("DepthToSpace:{}:{}", subgraphIndex, operatorIndex);
3305  IConnectableLayer* layer = m_Network->AddDepthToSpaceLayer(descriptor, layerName.c_str());
3306  ARMNN_ASSERT(layer != nullptr);
3307  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3308  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3309 
3310  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3311  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3312 
3313  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3314  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3315 }
3316 
3317 void TfLiteParserImpl::ParseSum(size_t subgraphIndex, size_t operatorIndex)
3318 {
3319  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Sum);
3320 }
3321 
3322 void TfLiteParserImpl::ParseReduceMax(size_t subgraphIndex, size_t operatorIndex)
3323 {
3324  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Max);
3325 }
3326 
3327 void TfLiteParserImpl::ParseReduceMin(size_t subgraphIndex, size_t operatorIndex)
3328 {
3329  ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Min);
3330 }
3331 
3332 void TfLiteParserImpl::ParseReduce(size_t subgraphIndex, size_t operatorIndex, ReduceOperation reduceOperation)
3333 {
3334  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3335 
3336  const auto &operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3337  const auto *options = operatorPtr->builtin_options.AsReducerOptions();
3338 
3339  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3340  CHECK_VALID_SIZE(inputs.size(), 2);
3341 
3342  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3343  CHECK_VALID_SIZE(outputs.size(), 1);
3344 
3345  auto layerName = fmt::format("Reduce:{}:{}", subgraphIndex, operatorIndex);
3346 
3347  armnn::TensorInfo inputTensorInfo0 = ToTensorInfo(inputs[0]);
3348  armnn::TensorInfo inputTensorInfo1 = ToTensorInfo(inputs[1]);
3349 
3350  ReduceDescriptor desc;
3351  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3352  // Get const axis value from model and set it to descriptor.
3353  if (axisBufferPtr != nullptr)
3354  {
3355  std::vector<int32_t> axisData(inputTensorInfo1.GetNumElements());
3356  ::memcpy(axisData.data(), axisBufferPtr->data.data(), inputTensorInfo1.GetNumBytes());
3357 
3358  // Convert the axis to unsigned int and remove duplicates.
3359  auto rank = static_cast<int32_t>(inputTensorInfo0.GetNumDimensions());
3360  std::set<unsigned int> uniqueAxis;
3361  std::transform(axisData.begin(),
3362  axisData.end(),
3363  std::inserter(uniqueAxis, uniqueAxis.begin()),
3364  [rank](int i)->unsigned int{
3365  return static_cast<uint32_t>(((i + rank) % rank)); });
3366  desc.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
3367  }
3368  else
3369  {
3370  for (uint32_t i = 0; i < inputTensorInfo0.GetNumDimensions(); ++i)
3371  {
3372  desc.m_vAxis.push_back(i);
3373  }
3374  }
3375 
3376  desc.m_KeepDims = options->keep_dims;
3377  desc.m_ReduceOperation = reduceOperation;
3378 
3379  // Register a new layer object, Sum.
3380  IConnectableLayer *layer = m_Network->AddReduceLayer(desc, layerName.c_str());
3381 
3382  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
3383  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3384 
3385  // Register input tensor to the layer.
3386  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3387  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3388 
3389  // Register output tensor to the layer.
3390  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3391  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3392 }
3393 
3394 void TfLiteParserImpl::ParseAbs(size_t subgraphIndex, size_t operatorIndex)
3395 {
3396  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Abs);
3397 }
3398 
3399 void TfLiteParserImpl::ParseExp(size_t subgraphIndex, size_t operatorIndex)
3400 {
3401  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Exp);
3402 }
3403 
3404 void TfLiteParserImpl::ParseLogicalNot(size_t subgraphIndex, size_t operatorIndex)
3405 {
3406  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::LogicalNot);
3407 }
3408 
3409 void TfLiteParserImpl::ParseNeg(size_t subgraphIndex, size_t operatorIndex)
3410 {
3411  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Neg);
3412 }
3413 
3414 void TfLiteParserImpl::ParseRsqrt(size_t subgraphIndex, size_t operatorIndex)
3415 {
3416  ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Rsqrt);
3417 }
3418 
3419 void TfLiteParserImpl::ParseElementwiseUnary(size_t subgraphIndex, size_t operatorIndex, UnaryOperation unaryOperation)
3420 {
3421  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3422 
3423  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3424  CHECK_VALID_SIZE(inputs.size(), 1);
3425 
3426  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3427  CHECK_VALID_SIZE(outputs.size(), 1);
3428 
3429  std::string layerName = std::string(GetUnaryOperationAsCString(unaryOperation)) + ":{}:{}";
3430  std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3431 
3433  desc.m_Operation = unaryOperation;
3434  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerNameFormatted.c_str());
3435  ARMNN_ASSERT(layer != nullptr);
3436 
3437  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3438  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3439 
3440  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3441  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3442 
3443  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3444  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3445 }
3446 
3447 void TfLiteParserImpl::ParseEqual(size_t subgraphIndex, size_t operatorIndex)
3448 {
3449  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Equal);
3450 }
3451 
3452 void TfLiteParserImpl::ParseNotEqual(size_t subgraphIndex, size_t operatorIndex)
3453 {
3454  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::NotEqual);
3455 }
3456 
3457 void TfLiteParserImpl::ParseGreater(size_t subgraphIndex, size_t operatorIndex)
3458 {
3459  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Greater);
3460 }
3461 
3462 void TfLiteParserImpl::ParseGreaterOrEqual(size_t subgraphIndex, size_t operatorIndex)
3463 {
3464  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::GreaterOrEqual);
3465 }
3466 
3467 void TfLiteParserImpl::ParseLess(size_t subgraphIndex, size_t operatorIndex)
3468 {
3469  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Less);
3470 }
3471 
3472 void TfLiteParserImpl::ParseLessOrEqual(size_t subgraphIndex, size_t operatorIndex)
3473 {
3474  ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::LessOrEqual);
3475 }
3476 
3477 void TfLiteParserImpl::ParseComparison(size_t subgraphIndex, size_t operatorIndex,
3478  ComparisonOperation comparisonOperation)
3479 {
3480  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3481 
3482  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3483  CHECK_VALID_SIZE(inputs.size(), 2);
3484 
3485  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3486  CHECK_VALID_SIZE(outputs.size(), 1);
3487 
3488  auto layerName = std::string(GetComparisonOperationAsCString(comparisonOperation)) + ":{}:{}";
3489  std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
3490 
3491  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3492  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
3493  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerNameFormatted, "Input 0", "Input 1");
3494 
3495  ComparisonDescriptor desc;
3496  desc.m_Operation = comparisonOperation;
3497  IConnectableLayer* layer = m_Network->AddComparisonLayer(desc, layerNameFormatted.c_str());
3498  ARMNN_ASSERT(layer != nullptr);
3499 
3500  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
3501  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3502 
3503  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3504  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
3505 
3506  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3507  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3508 }
3509 
3510 armnn::IConnectableLayer* TfLiteParserImpl::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
3511  unsigned int outputSlot,
3512  tflite::ActivationFunctionType activationType)
3513 {
3514  ActivationDescriptor activationDesc;
3515  std::string layerName = prevLayer->GetName();
3516 
3517  switch(activationType)
3518  {
3519  case tflite::ActivationFunctionType_NONE:
3520  {
3521  // this is a no-op: return previous layer
3522  return prevLayer;
3523  }
3524  case tflite::ActivationFunctionType_RELU:
3525  {
3526  activationDesc.m_Function = ActivationFunction::ReLu;
3527  layerName += ":RELU";
3528  break;
3529  }
3530  case tflite::ActivationFunctionType_RELU6:
3531  {
3532  activationDesc.m_Function = ActivationFunction::BoundedReLu;
3533  activationDesc.m_A = 6.0f;
3534  activationDesc.m_B = 0.0f;
3535  layerName += ":RELU6";
3536  break;
3537  }
3538  case tflite::ActivationFunctionType_TANH:
3539  {
3540  activationDesc.m_Function = ActivationFunction::TanH;
3541  activationDesc.m_A = 1.0f;
3542  activationDesc.m_B = 1.0f;
3543  layerName += ":TANH";
3544  break;
3545  }
3546 
3547  // I only put these here as a reminder what others we could support
3548  case tflite::ActivationFunctionType_RELU_N1_TO_1:
3549  case tflite::ActivationFunctionType_SIGN_BIT:
3550  default:
3551  {
3552  throw ParseException(
3553  fmt::format("TfLite parser doesn't suppport fused activation: "
3554  "{}/{} {} ",
3555  activationType,
3556  tflite::EnumNameActivationFunctionType(activationType),
3557  CHECK_LOCATION().AsString()));
3558 
3559  }
3560  }
3561 
3562  IConnectableLayer* activationLayer =
3563  m_Network->AddActivationLayer(activationDesc, layerName.c_str());
3564 
3565  auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
3566  prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
3567  activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
3568  return activationLayer;
3569 }
3570 
3572 {
3573  if (fileName == nullptr)
3574  {
3575  throw InvalidArgumentException(fmt::format("Invalid (null) file name {}",
3576  CHECK_LOCATION().AsString()));
3577  }
3578  std::error_code errorCode;
3579  fs::path pathToFile(fileName);
3580  if (!fs::exists(pathToFile, errorCode))
3581  {
3582  //fmt::format() could not be used here (format error)
3583  std::stringstream msg;
3584  msg << "Cannot find the file (" << fileName << ") errorCode: " << errorCode
3585  << " " << CHECK_LOCATION().AsString();
3586 
3587  throw FileNotFoundException(msg.str());
3588  }
3589  std::ifstream file(fileName, std::ios::binary);
3590  std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
3591  return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
3592  fileContent.size());
3593 }
3594 
3595 TfLiteParserImpl::ModelPtr TfLiteParserImpl::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
3596 {
3597  if (binaryContent == nullptr)
3598  {
3599  throw InvalidArgumentException(fmt::format("Invalid (null) binary content {}",
3600  CHECK_LOCATION().AsString()));
3601  }
3602  flatbuffers::Verifier verifier(binaryContent, len);
3603  if (verifier.VerifyBuffer<tflite::Model>() == false)
3604  {
3605  throw ParseException(
3606  fmt::format("Buffer doesn't conform to the expected Tensorflow Lite "
3607  "flatbuffers format. size:{} {}",
3608  len,
3609  CHECK_LOCATION().AsString()));
3610  }
3611  return tflite::UnPackModel(binaryContent);
3612 }
3613 
3615  size_t subgraphIndex,
3616  size_t operatorIndex)
3617 {
3618  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3619 
3620  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3621  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3622 
3623  size_t inputCount = operatorPtr->inputs.size();
3624  TensorRawPtrVector result;
3625  for (size_t i=0; i<inputCount; ++i)
3626  {
3627  // If the input location is -1 then assume input is turned off.
3628  if (operatorPtr->inputs[i] == -1)
3629  {
3630  continue;
3631  }
3632  else
3633  {
3634  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
3635  result.push_back(subgraphPtr->tensors[inputId].get());
3636  }
3637  }
3638  return result;
3639 }
3640 
3642  size_t subgraphIndex,
3643  size_t operatorIndex)
3644 {
3645  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3646 
3647  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3648  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3649 
3650  size_t outputCount = operatorPtr->outputs.size();
3651  TensorRawPtrVector result(outputCount);
3652  for (size_t i=0; i<outputCount; ++i)
3653  {
3654  uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
3655  CHECK_TENSOR(model, subgraphIndex, outputId);
3656  result[i] = subgraphPtr->tensors[outputId].get();
3657  }
3658  return result;
3659 }
3660 
3662  size_t subgraphIndex)
3663 {
3664  CHECK_SUBGRAPH(model, subgraphIndex);
3665  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3666 
3667  size_t inputCount = subgraphPtr->inputs.size();
3668  TensorIdRawPtrVector result(inputCount);
3669  for (size_t i=0; i<inputCount; ++i)
3670  {
3671  uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
3672  CHECK_TENSOR(model, subgraphIndex, inputId);
3673  result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
3674  }
3675  return result;
3676 }
3677 
3679  size_t subgraphIndex)
3680 {
3681  CHECK_SUBGRAPH(model, subgraphIndex);
3682  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3683 
3684  size_t outputCount = subgraphPtr->outputs.size();
3685  TensorIdRawPtrVector result(outputCount);
3686  for (size_t i=0; i<outputCount; ++i)
3687  {
3688  uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
3689  result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
3690  }
3691  return result;
3692 }
3693 
3694 std::vector<int32_t>& TfLiteParserImpl::GetInputTensorIds(const ModelPtr& model,
3695  size_t subgraphIndex,
3696  size_t operatorIndex)
3697 {
3698  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3699  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3700  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3701  return operatorPtr->inputs;
3702 }
3703 
3704 std::vector<int32_t>& TfLiteParserImpl::GetOutputTensorIds(const ModelPtr& model,
3705  size_t subgraphIndex,
3706  size_t operatorIndex)
3707 {
3708  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3709  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3710  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3711  return operatorPtr->outputs;
3712 }
3713 
3714 void TfLiteParserImpl::RegisterInputSlots(size_t subgraphIndex,
3715  size_t operatorIndex,
3716  IConnectableLayer* layer,
3717  const std::vector<unsigned int>& tensorIndexes,
3718  unsigned int startingSlotIndex)
3719 {
3720  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3721  ARMNN_ASSERT(layer != nullptr);
3722 
3723  if (tensorIndexes.size() + startingSlotIndex != layer->GetNumInputSlots())
3724  {
3725  throw ParseException(
3726  fmt::format("The number of tensor inputs ({}) does not match the number expected ({})"
3727  " for subgraph:{} operator index:{} {}",
3728  tensorIndexes.size(),
3729  layer->GetNumInputSlots(),
3730  subgraphIndex,
3731  operatorIndex,
3732  CHECK_LOCATION().AsString()));
3733  }
3734 
3735  for (unsigned int index = 0; index < tensorIndexes.size() ; ++index)
3736  {
3737  unsigned int tensorIndex = tensorIndexes[index];
3738  armnn::IInputSlot* slot = &(layer->GetInputSlot(startingSlotIndex + index));
3739  RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
3740  }
3741 }
3742 
3743 void TfLiteParserImpl::RegisterOutputSlots(size_t subgraphIndex,
3744  size_t operatorIndex,
3745  IConnectableLayer* layer,
3746  const std::vector<unsigned int>& tensorIndexes)
3747 {
3748  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3749  ARMNN_ASSERT(layer != nullptr);
3750  if (tensorIndexes.size() != layer->GetNumOutputSlots())
3751  {
3752  throw ParseException(
3753  fmt::format("The number of tensor outputs ({}) does not match the number expected ({})"
3754  " for subgraph:{} operator index:{} {}",
3755  tensorIndexes.size(),
3756  layer->GetNumOutputSlots(),
3757  subgraphIndex,
3758  operatorIndex,
3759  CHECK_LOCATION().AsString()));
3760  }
3761 
3762  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
3763  {
3764  unsigned int tensorIndex = tensorIndexes[slotIndex];
3765  armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
3766  RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
3767  }
3768 }
3769 
3770 void TfLiteParserImpl::SetupInputLayers(size_t subgraphIndex)
3771 {
3772  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3773 
3774  auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
3775  for (auto const & tensorIdAndPtr : inputs)
3776  {
3777  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3778  IConnectableLayer* layer =
3779  m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3780 
3781  auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
3782  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3783 
3784  RegisterOutputSlots(subgraphIndex,
3785  VIRTUAL_OPERATOR_ID,
3786  layer,
3787  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3788  }
3789 }
3790 
3791 void TfLiteParserImpl::SetupOutputLayers(size_t subgraphIndex)
3792 {
3793  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3794 
3795  auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
3796  for (auto const & tensorIdAndPtr : outputs)
3797  {
3798  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3799  IConnectableLayer* layer =
3800  m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3801 
3802  RegisterInputSlots(subgraphIndex,
3803  VIRTUAL_OPERATOR_ID,
3804  layer,
3805  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3806  }
3807 }
3808 
3809 void TfLiteParserImpl::SetupConstantLayers(size_t subgraphIndex)
3810 {
3811  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3812 
3813  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
3814  for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
3815  {
3816  for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
3817  {
3818  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
3819  m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
3820  {
3821  TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
3822 
3823  if(IsConstTensor(tensorPtr))
3824  {
3825  armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
3826  auto tensorAndData = CreateConstTensorNonPermuted(tensorPtr, tensorInfo);
3827 
3828  std::string layerName = fmt::format("Constant:{}", tensorPtr->name);
3829  IConnectableLayer *layer = m_Network->AddConstantLayer(tensorAndData, layerName.c_str());
3830 
3831  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3832  RegisterOutputSlots(subgraphIndex,
3833  VIRTUAL_OPERATOR_ID,
3834  layer,
3835  { tensorIndex });
3836  }
3837  else
3838  {
3839  throw ParseException(
3840  fmt::format("Invalid Tensor: Tensor should be constant. {}",
3841  CHECK_LOCATION().AsString()));
3842  }
3843  }
3844  }
3845  }
3846 }
3847 
3848 // example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3850 {
3851  CHECK_BUFFER(model, bufferIndex);
3852  return model->buffers[bufferIndex].get();
3853 }
3854 
3855 template<typename T>
3856 std::pair<armnn::ConstTensor, TfLiteParserImpl::SupportedDataStorage>
3857 TfLiteParserImpl::CreateConstTensorAndStoreData(TfLiteParserImpl::BufferRawPtr bufferPtr,
3859  armnn::TensorInfo& tensorInfo,
3861 {
3862  // Make sure isConstant flag is set.
3863  tensorInfo.SetConstant();
3864 
3865  auto constData = CreateConstTensorImpl<T>(bufferPtr,
3866  tensorPtr,
3867  tensorInfo,
3868  permutationVector);
3869  TfLiteParserImpl::SupportedDataStorage storage(std::move(constData.second));
3870  return std::make_pair(constData.first, std::move(storage));
3871 }
3872 
3873 bool TfLiteParserImpl::IsConstTensor(TensorRawPtr tensorPtr)
3874 {
3875  CHECK_TENSOR_PTR(tensorPtr);
3876  bool isConst = true;
3877 
3878  auto buffer = GetBuffer(m_Model, tensorPtr->buffer);
3879  if (buffer->data.size() == 0)
3880  {
3881  isConst = false;
3882  }
3883 
3884  return isConst;
3885 }
3886 
3887 std::pair<armnn::ConstTensor, TfLiteParserImpl::SupportedDataStorage>
3888 TfLiteParserImpl::CreateConstTensorPermuted(TensorRawPtr tensorPtr,
3889  armnn::TensorInfo& tensorInfo,
3891 {
3892  CHECK_TENSOR_PTR(tensorPtr);
3893  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3894  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3895 
3896  // Make sure isConstant flag is set.
3897  tensorInfo.SetConstant();
3898 
3899  switch (tensorInfo.GetDataType())
3900  {
3902  return CreateConstTensorAndStoreData<float>(bufferPtr,
3903  tensorPtr,
3904  tensorInfo,
3905  permutationVector);
3907  return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
3908  tensorPtr,
3909  tensorInfo,
3910  permutationVector);
3912  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3913  tensorPtr,
3914  tensorInfo,
3915  permutationVector);
3917  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3918  tensorPtr,
3919  tensorInfo,
3920  permutationVector);
3922  return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
3923  tensorPtr,
3924  tensorInfo,
3925  permutationVector);
3926  default:
3927  {
3928  std::stringstream errString;
3929  errString << "Unexpected datatype when creating const tensor: "
3930  << armnn::GetDataTypeName(tensorInfo.GetDataType())
3931  << " shape:" << tensorInfo.GetShape()
3932  << CHECK_LOCATION().AsString();
3933  throw ParseException(errString.str());
3934  }
3935  }
3936 }
3937 
3938 armnn::ConstTensor TfLiteParserImpl::CreateConstTensorNonPermuted(TensorRawPtr tensorPtr,
3939  armnn::TensorInfo& tensorInfo)
3940 {
3941  CHECK_TENSOR_PTR(tensorPtr);
3942  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3943  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3944 
3945  // Make sure isConstant flag is set.
3946  tensorInfo.SetConstant();
3947 
3948  return ConstTensor(tensorInfo, bufferPtr->data.data());
3949 }
3950 
3952  const std::string& name) const
3953 {
3954  CHECK_SUBGRAPH(m_Model, subgraphId);
3955  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3956  for (auto const & input : inputs)
3957  {
3958  if (input.second->name == name)
3959  {
3960  auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
3961  return std::make_pair(bindingId, ToTensorInfo(input.second));
3962  }
3963  }
3964 
3965  std::stringstream bindings;
3966  for (auto const & input : inputs)
3967  {
3968  bindings << "'" << input.second->name << "' ";
3969  }
3970 
3971  throw ParseException(
3972  fmt::format("No input binding found for subgraph:{} and name:{}. "
3973  "Possible inputs are: [{}] {}",
3974  subgraphId,
3975  name,
3976  bindings.str(),
3977  CHECK_LOCATION().AsString()));
3978 }
3979 
3981  const std::string& name) const
3982 {
3983  CHECK_SUBGRAPH(m_Model, subgraphId);
3984  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3985  for (unsigned int i = 0; i < outputs.size(); ++i)
3986  {
3987  auto const output = outputs[i];
3988  if (output.second->name == name)
3989  {
3990  auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
3991  std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
3992  m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
3993  return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
3994  }
3995  }
3996 
3997  std::stringstream bindings;
3998  for (auto const & output : outputs)
3999  {
4000  bindings << "'" << output.second->name << "' ";
4001  }
4002 
4003  throw ParseException(
4004  fmt::format("No output binding found for subgraph:{} and name:{}. "
4005  "Possible outputs are: [{}] {}",
4006  subgraphId,
4007  name,
4008  bindings.str(),
4009  CHECK_LOCATION().AsString()));
4010 }
4011 
4013 {
4014  return m_Model->subgraphs.size();
4015 }
4016 
4017 std::vector<std::string> TfLiteParserImpl::GetSubgraphInputTensorNames(size_t subgraphId) const
4018 {
4019  CHECK_SUBGRAPH(m_Model, subgraphId);
4020  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
4021  std::vector<std::string> result;
4022  result.reserve(inputs.size());
4023  for (auto const & input : inputs)
4024  {
4025  result.push_back(input.second->name);
4026  }
4027  return result;
4028 }
4029 
4030 std::vector<std::string> TfLiteParserImpl::GetSubgraphOutputTensorNames(size_t subgraphId) const
4031 {
4032  CHECK_SUBGRAPH(m_Model, subgraphId);
4033  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
4034  std::vector<std::string> result;
4035  result.reserve(outputs.size());
4036  for (auto const & output : outputs)
4037  {
4038  result.push_back(output.second->name);
4039  }
4040  return result;
4041 }
4042 
4043 const std::string TfLiteParserImpl::GetVersion()
4044 {
4045  return TFLITE_PARSER_VERSION;
4046 }
4047 
4048 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
4049 : m_FloatData(std::move(data))
4050 , m_Uint8Data(nullptr)
4051 , m_Int8Data(nullptr)
4052 , m_Int32Data(nullptr)
4053 {
4054 }
4055 
4056 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
4057 : m_FloatData(nullptr)
4058 , m_Uint8Data(std::move(data))
4059 , m_Int8Data(nullptr)
4060 , m_Int32Data(nullptr)
4061 {
4062 }
4063 
4064 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
4065 : m_FloatData(nullptr)
4066 , m_Uint8Data(nullptr)
4067 , m_Int8Data(std::move(data))
4068 , m_Int32Data(nullptr)
4069 {
4070 }
4071 
4072 TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
4073 : m_FloatData(nullptr)
4074 , m_Uint8Data(nullptr)
4075 , m_Int8Data(nullptr)
4076 , m_Int32Data(std::move(data))
4077 {
4078 }
4079 
4080 } // 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.
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
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.
A Convolution2dDescriptor for the Convolution2dLayer.
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
std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const
Return the output tensor names for a given subgraph.
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.
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:275
uint32_t m_DilationY
Dilation along y axis.
int32_t m_EndMask
End mask value.
A SpaceToDepthDescriptor for the SpaceToDepthLayer.
PoolingAlgorithm
Definition: Types.hpp:116
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding values for the input dimension: heightPad{top, bottom} widthPad{left, right}.
uint32_t m_DilationY
Dilation factor value for height dimension.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
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:244
#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
#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX)
constexpr const char * GetDataTypeName(DataType dataType)
Definition: TypesUtils.hpp:193
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 ResizeDescriptor for the ResizeLayer.
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:88
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
ReduceOperation
Definition: Types.hpp:123
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.
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:197
uint32_t m_NumInputs
Number of input tensors.
A SliceDescriptor for the SliceLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
armnn::INetworkPtr LoadModel(std::unique_ptr< tflite::ModelT > model)
std::unique_ptr< tflite::SubGraphT > SubgraphPtr
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)
Struct for the users to pass backend specific options.
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
Definition: Descriptors.hpp: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:506
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.
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
ArgMinMaxFunction
Definition: Types.hpp:82
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:131
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:104
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:172
virtual int Connect(IInputSlot &destination)=0
const char * m_Function
Definition: Exceptions.hpp:16
A Pooling2dDescriptor for the Pooling2dLayer.
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)
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.
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_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const
Definition: Tensor.hpp:196
ActivationFunction
Definition: Types.hpp:66
uint32_t m_PadRight
Padding right value in the width dimension.
bool m_ConstantWeights
Enable/disable constant weights and biases.