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