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