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