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