ArmNN
 20.08
TfLiteParser.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "TfLiteParser.hpp"
7 
9 #include <armnn/Descriptors.hpp>
10 #include <armnn/Exceptions.hpp>
11 #include <armnn/Logging.hpp>
12 #include <armnn/Tensor.hpp>
13 #include <armnn/TypesUtils.hpp>
14 #include <armnn/utility/Assert.hpp>
17 
18 // armnnUtils:
19 #include <armnnUtils/Permute.hpp>
20 #include <Filesystem.hpp>
21 
22 #include <ParserHelper.hpp>
23 #include <VerificationHelpers.hpp>
24 
25 // The generated code based on the Tf Lite schema:
26 #include <schema_generated.h>
27 
28 #include <flatbuffers/flexbuffers.h>
29 
30 #include <boost/format.hpp>
31 #include <boost/numeric/conversion/cast.hpp>
32 
33 #include <fstream>
34 #include <algorithm>
35 #include <limits>
36 #include <numeric>
37 #include <sstream>
38 
39 #define ARMNN_THROW_PARSE_EXCEPTION(msg) \
40  { \
41  throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
42  << ": " \
43  << CHECK_LOCATION().AsString()).str()); \
44  }
45 
46 using namespace armnn;
48 namespace armnnTfLiteParser
49 {
50 namespace
51 {
52 
53 const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
54 
55 void CheckSubgraph(const TfLiteParser::ModelPtr & model,
56  size_t subgraphIndex,
57  const CheckLocation & location)
58 {
59  if (model.get() == nullptr)
60  {
61  throw ParseException(
62  boost::str(
63  boost::format("%1% was called with invalid (null) model. "
64  "Possible reason is that the model is not yet loaded and Unpack(ed). "
65  "subgraph:%2% at %3%") %
66  location.m_Function %
67  subgraphIndex %
68  location.FileLine()));
69  }
70  else if (subgraphIndex >= model->subgraphs.size())
71  {
72  throw ParseException(
73  boost::str(
74  boost::format("%1% was called with an invalid subgraph index. "
75  "subgraph:%2% at %3%") %
76  location.m_Function %
77  subgraphIndex %
78  location.FileLine()));
79  }
80 }
81 
82 #define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
83  CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
84 
85 void CheckModel(const TfLiteParser::ModelPtr & model,
86  size_t subgraphIndex,
87  size_t operatorIndex,
88  const CheckLocation & location)
89 {
90  if (model.get() == nullptr)
91  {
92  throw ParseException(
93  boost::str(
94  boost::format("%1% was called with invalid (null) model. "
95  "Possible reason is that the model is not yet loaded and Unpack(ed). "
96  "subgraph:%2% operator:%3% at %4%") %
97  location.m_Function %
98  subgraphIndex %
99  operatorIndex %
100  location.FileLine()));
101  }
102  else if (subgraphIndex >= model->subgraphs.size())
103  {
104  throw ParseException(
105  boost::str(
106  boost::format("%1% was called with an invalid subgraph index. "
107  "subgraph:%2% operator:%3% at %4%") %
108  location.m_Function %
109  subgraphIndex %
110  operatorIndex %
111  location.FileLine()));
112  }
113  else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
114  operatorIndex != VIRTUAL_OPERATOR_ID)
115  {
116  throw ParseException(
117  boost::str(
118  boost::format("%1% was called with an invalid operator index. "
119  "subgraph:%2% operator:%3% at %4%") %
120  location.m_Function %
121  subgraphIndex %
122  operatorIndex %
123  location.FileLine()));
124  }
125 }
126 
127 #define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
128  CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
129 
130 void CheckTensor(const TfLiteParser::ModelPtr & model,
131  size_t subgraphIndex,
132  size_t tensorIndex,
133  const CheckLocation & location)
134 {
135  // not checking model, because I assume CHECK_MODEL already run
136  // and checked that. An assert would do.
137  ARMNN_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
138 
139  // also subgraph index should be checked by CHECK_MODEL so
140  // I only add an assert here
141  ARMNN_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
142 
143  // the tensor index is the only one to check here
144  if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
145  {
146  throw ParseException(
147  boost::str(
148  boost::format("%1% was called with an invalid tensor index. "
149  "subgraph:%2% tensor:%3% at %4%") %
150  location.m_Function %
151  subgraphIndex %
152  tensorIndex %
153  location.FileLine()));
154  }
155 }
156 
157 #define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
158  CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
159 
160 void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
161  const CheckLocation & location)
162 {
163  if (rawPtr == nullptr)
164  {
165  throw ParseException(
166  boost::str(
167  boost::format("%1% was called with a null tensor pointer. "
168  "at %2%") %
169  location.m_Function %
170  location.FileLine()));
171 
172  }
173 }
174 
175 #define CHECK_TENSOR_PTR(TENSOR_PTR) \
176  CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
177 
178 void CheckBuffer(const TfLiteParser::ModelPtr & model,
179  size_t bufferIndex,
180  const CheckLocation & location)
181 {
182  if (model.get() == nullptr)
183  {
184  throw ParseException(
185  boost::str(
186  boost::format("%1% was called with invalid (null) model. "
187  "Possible reason is that the model is not yet loaded and Unpack(ed). "
188  "buffer:%2% at %3%") %
189  location.m_Function %
190  bufferIndex %
191  location.FileLine()));
192  }
193  else if (bufferIndex >= model->buffers.size())
194  {
195  throw ParseException(
196  boost::str(
197  boost::format("%1% was called with an invalid buffer index. "
198  "buffer index:%2% at %3%") %
199  location.m_Function %
200  bufferIndex %
201  location.FileLine()));
202  }
203  else if (model->buffers[bufferIndex].get() == nullptr)
204  {
205  throw ParseException(
206  boost::str(
207  boost::format("The buffer #%1% is null. %3%") %
208  bufferIndex %
209  location.AsString()));
210  }
211 }
212 
213 #define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
214  CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
215 
216 void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
217  const armnn::TensorInfo & tensorInfo,
218  uint32_t bufferId,
219  const CheckLocation & location)
220 {
221  if (bufferPtr == nullptr)
222  {
223  throw ParseException(
224  boost::str(
225  boost::format("BufferPtr is null for buffer:%1%. %2%") %
226  bufferId %
227  location.AsString()));
228  }
229  else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
230  tensorInfo.GetNumBytes() > bufferPtr->data.size())
231  {
232  std::stringstream ss;
233  ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
234  << "For tensor: " << tensorInfo.GetShape()
235  << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
236  << tensorInfo.GetNumElements() << " elements. " << location.AsString();
237  throw ParseException(ss.str());
238  }
239 }
240 
241 #define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
242  CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
243 
244 bool IsActivationSupported(tflite::ActivationFunctionType activationType)
245 {
246  switch(activationType)
247  {
248  case tflite::ActivationFunctionType_NONE:
249  case tflite::ActivationFunctionType_RELU:
250  case tflite::ActivationFunctionType_RELU6:
251  case tflite::ActivationFunctionType_TANH:
252  {
253  return true;
254  }
255  default:
256  {
257  return false;
258  }
259  }
260 }
261 
262 #define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
263  do { \
264  if (IsActivationSupported(OPTION->fused_activation_function) == false) \
265  { \
266  throw ParseException( \
267  boost::str( \
268  boost::format("TfLite parser doesn't suppport fused activation: " \
269  "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
270  OPTION->fused_activation_function % \
271  tflite::EnumNameActivationFunctionType(\
272  OPTION->fused_activation_function) % \
273  __func__ % \
274  SUBGRAPH_INDEX % \
275  OPERATOR_INDEX % \
276  CHECK_LOCATION().FileLine())); \
277  } \
278  } while(false)
279 
280 
281 std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
282 {
283  std::vector<unsigned int> result;
284  result.reserve(in.size());
285  for (auto & i : in)
286  {
287  result.push_back(CHECKED_NON_NEGATIVE(i));
288  }
289  return result;
290 }
291 
292 void CalcPadding(uint32_t inputSize,
293  uint32_t filterSize,
294  uint32_t stride,
295  uint32_t dilation,
296  uint32_t& paddingFront,
297  uint32_t& paddingBack,
298  tflite::Padding padding)
299 {
300  paddingFront = 0;
301  paddingBack = 0;
302  if (padding == tflite::Padding_SAME)
303  {
304  uint32_t outputSize = (inputSize + stride - 1) / stride;
305  uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
306  uint32_t temp = (outputSize - 1) * stride + dilatedSize;
307  if (temp > inputSize)
308  {
309  paddingFront = (temp - inputSize) / 2;
310  paddingBack = (temp - inputSize) - paddingFront;
311  }
312  }
313 }
314 
316  const std::vector<unsigned int>& shapes,
317  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3},
318  const bool outputTensor = false)
319 {
320  armnn::DataType type;
321  CHECK_TENSOR_PTR(tensorPtr);
322 
323  switch (tensorPtr->type)
324  {
325  case tflite::TensorType_UINT8:
327  break;
328  case tflite::TensorType_FLOAT32:
330  break;
331  case tflite::TensorType_INT8:
332  if (tensorPtr->quantization->zero_point.size() == 1)
333  {
334  // Per-tensor
336  }
337  else
338  {
339  // Per-channel
341  }
342  break;
343  case tflite::TensorType_INT16:
345  break;
346  case tflite::TensorType_INT32:
348  break;
349 
350  default:
351  {
352  CheckLocation location = CHECK_LOCATION();
353  throw ParseException(
354  boost::str(
355  boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
356  tensorPtr->type %
357  tflite::EnumNameTensorType(tensorPtr->type) %
358  tensorPtr->name %
359  location.AsString()));
360  }
361  }
362  std::vector<unsigned int> safeShape = shapes;
363  bool isDynamic = false;
364  if (safeShape.size() == 0)
365  {
366  safeShape.push_back(1);
367  if (outputTensor)
368  {
369  isDynamic = true;
370  }
371  }
372 
373  float quantizationScale = 0.0f;
374  int32_t quantizationOffset = 0;
375 
376  if (tensorPtr->quantization.get())
377  {
378  if (tensorPtr->quantization->scale.size() <= 1)
379  {
380  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
381  CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
382 
383  if (tensorPtr->quantization->scale.size() == 1)
384  {
385  quantizationScale = tensorPtr->quantization->scale[0];
386  }
387  if (tensorPtr->quantization->zero_point.size() == 1)
388  {
389  // NOTE: we lose precision here when converting from 64 bit to 32
390  // but this is what we support at the moment in ArmNN
391  quantizationOffset = boost::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
392  }
393 
394  TensorShape tensorShape(boost::numeric_cast<unsigned int>(safeShape.size()),
395  safeShape.data());
396  if (isDynamic)
397  {
398  tensorShape = TensorShape(1, false);
399  }
400  armnn::TensorInfo result(tensorShape,
401  type,
402  quantizationScale,
403  quantizationOffset);
404  return result;
405  }
406  else
407  {
408  std::vector<float> quantizationScales;
409  std::vector<int32_t> quantizationOffsets;
410 
411  // Scale
412  std::copy(tensorPtr->quantization->scale.begin(),
413  tensorPtr->quantization->scale.end(),
414  std::back_inserter(quantizationScales));
415 
416  // QSymmS8 Per-axis
417  TensorShape tensorShape(boost::numeric_cast<unsigned int>(safeShape.size()),
418  safeShape.data());
419  if (isDynamic)
420  {
421  tensorShape = TensorShape(1, false);
422  }
423  armnn::TensorInfo result(tensorShape,
424  type,
425  quantizationScales,
426  dimensionMappings[boost::numeric_cast<unsigned int>(
427  tensorPtr->quantization->quantized_dimension)]);
428  return result;
429  }
430  }
431  else
432  {
433  TensorShape tensorShape(boost::numeric_cast<unsigned int>(safeShape.size()),
434  safeShape.data());
435  if (isDynamic)
436  {
437  tensorShape = TensorShape(1, false);
438  }
439  armnn::TensorInfo result(tensorShape,
440  type,
441  quantizationScale,
442  quantizationOffset);
443  return result;
444  }
445 }
446 
448  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
449 {
450  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
451  return ToTensorInfo(tensorPtr, dimensions, dimensionMappings);
452 }
453 
455  const bool outputTensor)
456 {
457  auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
458  const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3};
459  return ToTensorInfo(tensorPtr, dimensions, dimensionMappings, outputTensor);
460 }
461 
462 template<typename T>
463 std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
464 CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
465  TfLiteParser::TensorRawPtr tensorPtr,
466  armnn::TensorInfo& tensorInfo,
468 {
469  IgnoreUnused(tensorPtr);
470  ARMNN_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
471  ARMNN_ASSERT_MSG(bufferPtr != nullptr,
472  boost::str(
473  boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
474 
475  std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
476 
477  if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
478  {
479  tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
480  armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
481  reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
482  }
483  else
484  {
485  ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
486  }
487 
488  return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
489 }
490 
491 armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
492 {
493  // generate the binding id by shifting the tensor id by 8 bit
494  // and add the subgraph id, which allows 256 subgraphs
495  return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
496 }
497 
498 bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
499 {
500  const unsigned int actualSize = actual.GetNumDimensions();
501  if (actualSize != expected.size())
502  {
503  return false;
504  }
505 
506  for (unsigned int i = 0u; i < actualSize; i++)
507  {
508  if (expected[i] < 0 ||
509  actual[i] != static_cast<unsigned int>(expected[i]))
510  {
511  return false;
512  }
513  }
514 
515  return true;
516 }
517 
518 void CheckMatchingQuantization(const TensorInfo& first,
519  const TensorInfo& second,
520  const std::string& descName,
521  std::string const& firstName,
522  std::string const& secondName)
523 {
524  if (!first.IsQuantized() ||
525  !second.IsQuantized())
526  {
527  // Not a quantized type, ignore the validation
528  return;
529  }
530 
531  DataType firstDataType = first.GetDataType();
532  DataType secondDataType = second.GetDataType();
533 
534  if (firstDataType != secondDataType)
535  {
536  throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
537  " must be of the same quantized type, " +
538  firstName + " is " + GetDataTypeName(firstDataType) + ", " +
539  secondName + " is " + GetDataTypeName(secondDataType));
540  }
541 
542  if (!first.IsTypeSpaceMatch(second))
543  {
544  throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
545  " must have the same quantization space, " +
546  firstName + " has offset " + std::to_string(first.GetQuantizationOffset()) +
547  " and scale " + std::to_string(first.GetQuantizationScale()) + ", " +
548  secondName + " has offset " + std::to_string(second.GetQuantizationOffset()) +
549  " and scale " + std::to_string(second.GetQuantizationScale()));
550  }
551 }
552 
553 } // <anonymous>
554 
555 TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
556 : m_Options(options)
557 , m_Network(nullptr, nullptr)
558 , m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
559 {
560  // register supported operators
561  m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
562  m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
563  m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
564  m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
565  m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
566  m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
567  m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
568  m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
569  m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParser::ParseExp;
570  m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
571  m_ParserFunctions[tflite::BuiltinOperator_HARD_SWISH] = &TfLiteParser::ParseHardSwish;
572  m_ParserFunctions[tflite::BuiltinOperator_LEAKY_RELU] = &TfLiteParser::ParseLeakyRelu;
573  m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
574  m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
575  m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
576  m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
577  m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
578  m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
579  m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
580  m_ParserFunctions[tflite::BuiltinOperator_NEG] = &TfLiteParser::ParseNeg;
581  m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
582  m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
583  m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParser::ParseQuantize;
584  m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
585  m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
586  m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
587  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
588  m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
589  m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
590  m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
591  m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
592  m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
593  m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParser::ParseSplitV;
594  m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
595  m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
596  m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
597  m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
598  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
599  m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
600  m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
601  m_ParserFunctions[tflite::BuiltinOperator_DIV] = &TfLiteParser::ParseDiv;
602  // register supported custom operators
603  m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
604 }
605 
606 void TfLiteParser::ResetParser()
607 {
608  m_Network = armnn::INetworkPtr(nullptr, nullptr);
609  m_Model = nullptr;
610  m_SubgraphConnections.clear();
611 }
612 
613 void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
614  size_t operatorIndex,
615  IConnectableLayer *layer)
616 {
617  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
618  ARMNN_ASSERT(layer != nullptr);
619 
620  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
621  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
622 
623  ARMNN_ASSERT(operatorPtr->inputs.size() > 1);
624 
625  uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
626  TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get();
627  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
628  TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get();
629 
630  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
631  armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
632 
633  uint32_t inputSlotId = 1;
634  uint32_t reshapeSlotId = 0;
635 
636  if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
637  {
638  uint32_t id = reshapedInputId;
639  reshapedInputId = inputId;
640  inputId = id;
641 
642  reshapedTensorInfo = ToTensorInfo(tensorPtr1);
643  inputTensorInfo = ToTensorInfo(tensorPtr);
644 
645  inputSlotId = 0;
646  reshapeSlotId = 1;
647  }
648 
649  uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
650 
651  std::vector<unsigned> reshapedDim;
652  for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
653  {
654  reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
655  }
656 
657  std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
658  std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
659 
660  reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
661 
662  std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
664  desc.m_TargetShape = reshapedTensorInfo.GetShape();
665  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
666 
667  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
668  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(reshapeSlotId));
669 
670  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
671 
672  armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(inputSlotId));
673  RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
674 }
675 
677 {
678  ResetParser();
679  m_Model = LoadModelFromFile(graphFile);
680  return CreateNetworkFromModel();
681 }
682 
683 INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
684 {
685  ResetParser();
686  m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
687  return CreateNetworkFromModel();
688 }
689 
690 INetworkPtr TfLiteParser::CreateNetworkFromModel()
691 {
692 
693  using NetworkOptions = std::vector<BackendOptions>;
694  NetworkOptions networkOptions = {};
695  if (m_Options && m_Options.value().m_InferAndValidate)
696  {
697  BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod",
698  {
699  { "InferAndValidate", true }
700  });
701 
702  networkOptions.push_back(shapeInferenceMethodOption);
703  }
704 
705  m_Network = INetwork::Create(networkOptions);
706  ARMNN_ASSERT(m_Model.get() != nullptr);
707 
708  if (m_Model->subgraphs.size() != 1)
709  {
710  throw ParseException(
711  boost::str(
712  boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
713  m_Model->subgraphs.size() %
714  CHECK_LOCATION().AsString()));
715  }
716 
717  size_t subgraphIndex = 0;
718  size_t operatorIndex = 0;
719  try
720  {
721  for (SubgraphPtr const& subgraph : m_Model->subgraphs)
722  {
723  m_SubgraphConnections.emplace_back(subgraph->tensors.size());
724  for (OperatorPtr const& op : subgraph->operators)
725  {
726  auto const& opCodePtr = m_Model->operator_codes[op->opcode_index];
727  auto builtinCode = opCodePtr->builtin_code;
728 
729  if (builtinCode > tflite::BuiltinOperator_MAX)
730  {
731  throw ParseException(boost::str(boost::format("Operator code %1% is out of range 0-%2%. "
732  "subgraph:%3% operator idx:%4%. %5%") %
733  builtinCode % tflite::BuiltinOperator_MAX % subgraphIndex %
734  operatorIndex % CHECK_LOCATION().AsString()));
735  }
736 
737  // lookup and call the parser function
738  auto& parserFunction = m_ParserFunctions[builtinCode];
739  (this->*parserFunction)(subgraphIndex, operatorIndex);
740  ++operatorIndex;
741  }
742 
743  SetupInputLayers(subgraphIndex);
744  SetupOutputLayers(subgraphIndex);
745  SetupConstantLayers(subgraphIndex);
746 
747  ++subgraphIndex;
748  operatorIndex = 0;
749  }
750  }
751  catch (const ParseException& e)
752  {
753  std::stringstream errorString;
754  errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
755  << subgraphIndex << " error: " << e.what();
756  ARMNN_LOG(error) << errorString.str();
757  std::stringstream errors;
758  errors << errorString.str() << "\n";
759  throw ParseException(errors.str());
760  }
761 
762  // establish the connections from the layer outputs to the inputs of the subsequent layers
763  for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
764  {
765  for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
766  {
767  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
768  {
769  for (size_t inputSlotIdx = 0;
770  inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
771  ++inputSlotIdx)
772  {
773  m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
774  *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
775  }
776  }
777  }
778  }
779 
780  // if InferAndValidate set make sure all the TensorInfo set and all the dynamic output tensors are inferred
781  if (m_Options && m_Options.value().m_InferAndValidate)
782  {
783  for (subgraphIndex = 0;
784  subgraphIndex < m_SubgraphConnections.size();
785  ++subgraphIndex)
786  {
787  if (m_SubgraphConnections[subgraphIndex].size() > 0)
788  {
789  // get the last output slot on the layer
790  auto outputSlot =
791  m_SubgraphConnections[subgraphIndex][m_SubgraphConnections[subgraphIndex].size() - 1].outputSlot;
792  if (outputSlot != nullptr)
793  {
794  outputSlot->IsTensorInfoSet();
795  }
796  }
797  }
798  }
799 
800  return std::move(m_Network);
801 }
802 
803 void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
804  size_t tensorIndex,
805  armnn::IOutputSlot* slot)
806 {
807  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
808  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
809  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
810 
811  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
812 
813  // assuming there is only one producer for that tensor
814  if (tensorSlots.outputSlot != nullptr)
815  {
816  throw ParseException(boost::str(
817  boost::format("Another layer has already registered itself as the producer of "
818  "subgraph:%1% tensor:%2% %3%") %
819  subgraphIndex %
820  tensorIndex %
821  CHECK_LOCATION().AsString()));
822  }
823 
824  tensorSlots.outputSlot = slot;
825 }
826 
827 void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
828  size_t tensorIndex,
829  armnn::IInputSlot* slot)
830 {
831  CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
832  ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
833  ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
834 
835  TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
836  tensorSlots.inputSlots.push_back(slot);
837 }
838 
839 void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
840 {
841  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
842 
843  // NOTE: By default we presume the custom operator is not supported
844  auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
845 
846  // Identify custom code defined for custom operator
847  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
848  const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
849 
850  // Find parser function that correspondes to custom code (if any)
851  auto iterator = m_CustomParserFunctions.find(customCode);
852  if (iterator != m_CustomParserFunctions.end())
853  {
854  customParserFunction = iterator->second;
855  }
856 
857  // Run parser function
858  (this->*customParserFunction)(subgraphIndex, operatorIndex);
859 }
860 
861 void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
862 {
863  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
864 
865  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
866 
867  auto opcodeIndex = operatorPtr->opcode_index;
868  auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
869 
870  if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
871  {
872  // Do not add StandInLayer, throw ParseException instead
873  throw ParseException(
874  boost::str(
875  boost::format("Operator not supported. "
876  "subgraph:%1% operator:%2% "
877  "opcode_index:%3% opcode:%4% / %5% %6%") %
878  subgraphIndex %
879  operatorIndex %
880  opcodeIndex %
881  opcode %
882  tflite::EnumNameBuiltinOperator(opcode) %
883  CHECK_LOCATION().AsString()));
884  }
885 
886  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
887  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
888 
889  const unsigned int numInputs = boost::numeric_cast<unsigned int>(inputs.size());
890  const unsigned int numOutputs = boost::numeric_cast<unsigned int>(outputs.size());
891 
892  StandInDescriptor descriptor(numInputs, numOutputs);
893  auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
894 
895  // Add a non-executable StandInLayer as a placeholder for any unsupported operator
896  IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
897  ARMNN_ASSERT(layer != nullptr);
898 
899  for (unsigned int i = 0u; i < numOutputs; ++i)
900  {
901  layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i], true));
902  }
903 
904  auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
905  auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
906 
907  RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
908  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
909 }
910 
911 void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
912 {
913  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
914 
915  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
916  const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
917 
918  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
919 
921  desc.m_BiasEnabled = false;
922  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
923  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
925  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
926  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
927 
928  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
929  CHECK_VALID_SIZE(inputs.size(), 2, 3);
930 
931  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
932  CHECK_VALID_SIZE(outputs.size(), 1);
933 
934  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
935  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
936 
937  // assuming input is NHWC
938  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
939  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
940 
941  // assuming the filter is OHWI : Output, H, W, Input
942  // which is essentially the same as NHWC
943  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
944  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
945 
946  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
947  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
948  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
949  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
950 
951  auto filterTensorAndData = CreateConstTensor(inputs[1],
952  filterTensorInfo,
954  armnn::IConnectableLayer* layer = nullptr;
955 
956  auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
957 
958  if (inputs.size() == 3)
959  {
960  desc.m_BiasEnabled = true;
961  armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
962  auto biasTensorAndData = CreateConstTensor(inputs[2],
963  biasTensorInfo,
965  layer = m_Network->AddConvolution2dLayer(desc,
966  filterTensorAndData.first,
967  Optional<ConstTensor>(biasTensorAndData.first),
968  layerName.c_str());
969  }
970  else
971  {
972  layer = m_Network->AddConvolution2dLayer(desc,
973  filterTensorAndData.first,
974  EmptyOptional(),
975  layerName.c_str());
976  }
977 
978  ARMNN_ASSERT(layer != nullptr);
979 
980  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
981  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
982 
983  // register the input connection slots for the layer, connections are made after all layers have been created
984  // only the tensors for the inputs are relevant, exclude the const tensors
985  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
986  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
987 
988  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
989  // register the output connection slots for the layer, connections are made after all layers have been created
990  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
991  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
992 }
993 
994 void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
995 {
996  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
997 
998  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
999  const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
1000 
1001  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1002 
1004  desc.m_BiasEnabled = false;
1005  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1006  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1008  CHECKED_NON_NEGATIVE(options->depth_multiplier);
1009 
1010  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1011  CHECK_VALID_SIZE(inputs.size(), 2, 3);
1012  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1013  CHECK_VALID_SIZE(outputs.size(), 1);
1014  desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1015  desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1016 
1017  // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
1018  PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
1019 
1020  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1021  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
1022 
1023  // Assuming input is NHWC
1024  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1025  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1026 
1027  // TensorflowLite weights come in the format [1, H, W, I * M]
1028  unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1029  unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1030 
1031  // Reshape weights as [ H, W, I, M ]
1032  filterTensorInfo.SetShape({ filterHeight,
1033  filterWidth,
1034  inputTensorInfo.GetShape()[3],
1035  filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
1036 
1037  CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1038  desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1039  CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1040  desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1041 
1042  auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
1043  armnn::IConnectableLayer* layer = nullptr;
1044  auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
1045 
1046  if (inputs.size() == 3)
1047  {
1048  desc.m_BiasEnabled = true;
1049  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
1050  auto biasTensorAndData = CreateConstTensor(inputs[2],
1051  biasTensorInfo,
1053  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1054  filterTensorAndData.first,
1055  Optional<ConstTensor>(biasTensorAndData.first),
1056  layerName.c_str());
1057  }
1058  else
1059  {
1060  layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1061  filterTensorAndData.first,
1062  EmptyOptional(),
1063  layerName.c_str());
1064  }
1065  ARMNN_ASSERT(layer != nullptr);
1066 
1067  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1068  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1069 
1070  // register the input connection slots for the layer, connections are made after all layers have been created
1071  // only the tensors for the inputs are relevant, exclude the const tensors
1072  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1073  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1074 
1075  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1076  // register the output connection slots for the layer, connections are made after all layers have been created
1077  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1078  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1079 }
1080 
1081 void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
1082 {
1083  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1084 
1085  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1086  CHECK_VALID_SIZE(inputs.size(), 1);
1087 
1088  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1089  CHECK_VALID_SIZE(outputs.size(), 1);
1090 
1091  auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
1092 
1093  IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
1094  ARMNN_ASSERT(layer != nullptr);
1095 
1096  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1097  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1098 
1099  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1100  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1101 
1102  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1103  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1104 }
1105 
1106 void TfLiteParser::ParseExp(size_t subgraphIndex, size_t operatorIndex)
1107 {
1108  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1109 
1110  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1111  CHECK_VALID_SIZE(inputs.size(), 1);
1112 
1113  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1114  CHECK_VALID_SIZE(outputs.size(), 1);
1115 
1116  auto layerName = boost::str(boost::format("Exp:%1%:%2%") % subgraphIndex % operatorIndex);
1117 
1119  desc.m_Operation = UnaryOperation::Exp;
1120  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerName.c_str());
1121  ARMNN_ASSERT(layer != nullptr);
1122 
1123  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1124  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1125 
1126  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1127  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1128 
1129  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1130  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1131 }
1132 
1133 void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1134 {
1135  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1136 
1137  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1138  CHECK_VALID_SIZE(inputs.size(), 1, 2);
1139 
1140  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1141  CHECK_VALID_SIZE(outputs.size(), 1);
1142 
1143  auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
1144  TransposeDescriptor desc;
1145 
1146  if (inputs.size() == 2)
1147  {
1148  armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1149  BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1150  auto numPermVecElements = permuteTensorInfo.GetNumElements();
1151  std::vector<unsigned int> permuteShape(numPermVecElements);
1152  ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
1153  PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
1154 
1155  desc = TransposeDescriptor(permutationVector);
1156  }
1157 
1158  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1159  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1160  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1161 
1162  IConnectableLayer* layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
1163  ARMNN_ASSERT(layer != nullptr);
1164  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1165 
1166  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1167  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1168 
1169  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1170  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1171 }
1172 
1173 void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1174 {
1175  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1176 
1177  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1178  const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1179 
1181  desc.m_BiasEnabled = false;
1182  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1183  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1185 
1186  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1187  CHECK_VALID_SIZE(inputs.size(), 3);
1188 
1189  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1190  CHECK_VALID_SIZE(outputs.size(), 1);
1191 
1192  if (inputs[0])
1193  {
1194  armnn::TensorInfo tensorInfo = ToTensorInfo(inputs[0]);
1195  std::vector<int> output_shape(tensorInfo.GetNumElements());
1196  if (tensorInfo.GetDataType() == DataType::Signed32)
1197  {
1198  ::memcpy(output_shape.data(), GetBuffer(m_Model, inputs[0]->buffer)->data.data(), tensorInfo.GetNumBytes());
1199  }
1200  if (tensorInfo.GetDataType() == DataType::QAsymmU8)
1201  {
1202  for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
1203  {
1204  output_shape[i] = GetBuffer(m_Model, inputs[0]->buffer)->data.data()[i];
1205  }
1206  }
1207  // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
1208  for (int dimension : output_shape)
1209  {
1210  desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
1211  }
1212  desc.m_OutputShapeEnabled = true;
1213  }
1214  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
1215  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1216 
1217  // TfLite uses NHWC tensors
1218  const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1219  const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1220 
1221  const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1222  const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1223 
1224  CalcPadding(inputHeight,
1225  filterHeight,
1226  desc.m_StrideY,
1227  1, // DilationY
1228  desc.m_PadTop,
1229  desc.m_PadBottom,
1230  options->padding);
1231 
1232  CalcPadding(inputWidth,
1233  filterWidth,
1234  desc.m_StrideX,
1235  1, // DilationX
1236  desc.m_PadLeft,
1237  desc.m_PadRight,
1238  options->padding);
1239 
1240  auto filterTensorAndData = CreateConstTensor(inputs[1],
1241  filterTensorInfo,
1243 
1244  armnn::IConnectableLayer* layer = nullptr;
1245  auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1246 
1247  layer = m_Network->AddTransposeConvolution2dLayer(desc,
1248  filterTensorAndData.first,
1249  EmptyOptional(),
1250  layerName.c_str());
1251 
1252  ARMNN_ASSERT(layer != nullptr);
1253 
1254  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1255  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1256 
1257  // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1258  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1259  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
1260 
1261  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1262  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1263 }
1264 
1265 void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1266 {
1267  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1268 }
1269 
1270 void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1271 {
1272  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1273 
1274  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1275  CHECK_VALID_SIZE(inputs.size(), 3);
1276 
1277  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1278  CHECK_VALID_SIZE(outputs.size(), 1);
1279 
1280  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1281  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1282 
1283  armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1284  BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1285 
1286  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1287  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1288 
1289  std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1290  ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1291 
1292  size_t step = 2;
1293  std::vector<std::pair<unsigned int, unsigned int>> crops;
1294  for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1295  {
1296  crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1297  }
1298 
1300  desc.m_BlockShape = blockShape;
1301  desc.m_Crops = crops;
1303 
1304  auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
1305 
1306  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1307  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1308  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1309 
1310  IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1311  ARMNN_ASSERT(layer != nullptr);
1312  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1313 
1314  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1315  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1316 
1317  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1318  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1319 }
1320 
1321 void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1322 {
1323  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1324 
1325  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1326  CHECK_VALID_SIZE(inputs.size(), 1);
1327 
1328  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1329  CHECK_VALID_SIZE(outputs.size(), 1);
1330 
1333  auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1334  IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1335 
1336  ARMNN_ASSERT(layer != nullptr);
1337 
1338  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1339  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1340 
1341  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1342  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1343 
1344  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1345  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1346 }
1347 
1348 void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1349 {
1350  ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1351 }
1352 
1353 void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1354 {
1355  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1356 
1357  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1358  CHECK_VALID_SIZE(inputs.size(), 2);
1359 
1360  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1361  CHECK_VALID_SIZE(outputs.size(), 1);
1362 
1363  auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
1364 
1365  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1366  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1367  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1368 
1369  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1370  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1371 
1372  IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1373  ARMNN_ASSERT(layer != nullptr);
1374  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1375 
1376  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1377  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1378  {
1379  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1380  }
1381  else
1382  {
1383  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1384  }
1385 
1386  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1387  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1388 }
1389 
1390 void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1391 {
1392  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1393 
1394  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1395  CHECK_VALID_SIZE(inputs.size(), 2);
1396 
1397  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1398  CHECK_VALID_SIZE(outputs.size(), 1);
1399 
1400  auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
1401 
1402  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1403  TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1404  CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
1405 
1406  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1407  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1408 
1409  IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1410  ARMNN_ASSERT(layer != nullptr);
1411  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1412 
1413  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1414  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1415  {
1416  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1417  }
1418  else
1419  {
1420  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1421  }
1422 
1423  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1424  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1425 }
1426 
1427 void TfLiteParser::ParsePool(size_t subgraphIndex,
1428  size_t operatorIndex,
1429  PoolingAlgorithm algorithm)
1430 {
1431  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1432 
1433  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1434  const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1435 
1436  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1437 
1438  std::string layerName;
1439 
1440  switch (algorithm)
1441  {
1442  case PoolingAlgorithm::Average:
1443  layerName =
1444  boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1445  break;
1446  case PoolingAlgorithm::Max:
1447  layerName =
1448  boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1449  break;
1450  default:
1451  ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
1452  }
1453 
1454  Pooling2dDescriptor desc;
1455 
1456  desc.m_PoolType = algorithm;
1457  desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1458  desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1459  desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1460  desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1461  desc.m_PaddingMethod = PaddingMethod::Exclude;
1462  desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
1464 
1465  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1466  CHECK_VALID_SIZE(inputs.size(), 1);
1467  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1468 
1469  // assuming input is NHWC
1470  unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1471  unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1472 
1473  CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1474  desc.m_PadTop, desc.m_PadBottom, options->padding);
1475  CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1476  desc.m_PadLeft, desc.m_PadRight, options->padding);
1477 
1478  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1479  CHECK_VALID_SIZE(outputs.size(), 1);
1480 
1481  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1482  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1483 
1484  IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1485  ARMNN_ASSERT(layer != nullptr);
1486  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1487 
1488  // register the input connection slots for the layer, connections are made after all layers have been created
1489  // only the tensors for the inputs are relevant, exclude the const tensors
1490  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1491  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1492 
1493  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1494  // register the output connection slots for the layer, connections are made after all layers have been created
1495  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1496  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1497 }
1498 
1499 void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1500 {
1501  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1502 
1503  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1504  CHECK_VALID_SIZE(inputs.size(), 3);
1505  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1506  CHECK_VALID_SIZE(outputs.size(), 1);
1507 
1508  SliceDescriptor desc;
1509 
1510  // set begin tensor info for slice descriptor
1511  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1512  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1513 
1514  std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1515  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1516 
1517  // set size tensor info for slice descriptor
1518  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1519  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1520 
1521  std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1522  ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1523  desc = SliceDescriptor(begin, size);
1524 
1525  auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
1526 
1527  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1528  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1529  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1530 
1531  IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
1532  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1533 
1534  // register the input connection slots for the layer, connections are made after all layers have been created
1535  // only the tensors for the inputs are relevant, exclude the const tensors
1536  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1537  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1538 
1539  // register the output connection slots for the layer, connections are made after all layers have been created
1540  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1541  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1542 }
1543 
1544 void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1545 {
1546  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1547  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1548  const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1549 
1550  SoftmaxDescriptor desc;
1551  desc.m_Beta = options->beta;
1552 
1553  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1554  CHECK_VALID_SIZE(inputs.size(), 1);
1555  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1556  CHECK_VALID_SIZE(outputs.size(), 1);
1557 
1558  auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1559  IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1560 
1561  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1562  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1563 
1564  // register the input connection slots for the layer, connections are made after all layers have been created
1565  // only the tensors for the inputs are relevant, exclude the const tensors
1566  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1567  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1568 
1569  // register the output connection slots for the layer, connections are made after all layers have been created
1570  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1571  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1572 }
1573 
1574 void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1575 {
1576  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1577 
1578  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1579  CHECK_VALID_SIZE(inputs.size(), 3);
1580 
1581  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1582  CHECK_VALID_SIZE(outputs.size(), 1);
1583 
1584  armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1585  BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1586 
1587  armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1588  BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1589 
1590  std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1591  ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1592 
1593  std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1594  ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1595 
1596  size_t step = 2;
1597  std::vector<std::pair<unsigned int, unsigned int>> padList;
1598  for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1599  {
1600  padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1601  }
1602 
1604  desc.m_BlockShape = blockShape;
1605  desc.m_PadList = padList;
1607 
1608  auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
1609 
1610  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1611  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1612  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1613 
1614  IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1615  ARMNN_ASSERT(layer != nullptr);
1616  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1617 
1618  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1619  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1620 
1621  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1622  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1623 }
1624 
1625 armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1626  const armnn::TensorInfo & inputTensorInfo)
1627 {
1628  CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1629  std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1630  static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1631 
1632  if (inputTensorInfo.GetNumDimensions() > 4)
1633  {
1634  std::stringstream ss;
1635  ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1636  << " shape:" << inputTensorInfo.GetShape() << " "
1637  << CHECK_LOCATION().AsString();
1638  throw ParseException(ss.str());
1639  }
1640 
1641  if (squeezeDims.empty())
1642  {
1643  squeezeDims.assign(dimensionSequence,
1644  dimensionSequence+inputTensorInfo.GetNumDimensions());
1645  }
1646 
1647  std::vector<uint32_t> outputDims;
1648  for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1649  {
1650  bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1651  auto currentDimension = inputTensorInfo.GetShape()[i];
1652  if (skipSqueeze || currentDimension != 1)
1653  {
1654  outputDims.push_back(currentDimension);
1655  }
1656  }
1657 
1658  if (outputDims.size() > 4)
1659  {
1660  std::stringstream ss;
1661  ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1662  << " shape:" << inputTensorInfo.GetShape() << " "
1663  << CHECK_LOCATION().AsString();
1664  throw ParseException(ss.str());
1665  }
1666 
1667  TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1668  outputDims.data());
1669 
1670  // we need to preserve the tensor type and the quantization data as well
1671  TensorInfo outTensorInfo = inputTensorInfo;
1672  outTensorInfo.SetShape(outShape);
1673 
1674  return outTensorInfo;
1675 }
1676 
1677 void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1678 {
1679  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1680 
1681  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1682  CHECK_VALID_SIZE(inputs.size(), 1);
1683 
1684  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1685  CHECK_VALID_SIZE(outputs.size(), 1);
1686 
1687  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1688  const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1689  auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1690 
1691  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1692  armnn::TensorInfo outputTensorInfo =
1693  TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1694  inputTensorInfo);
1695  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1696 
1697  ReshapeDescriptor reshapeDesc;
1698  reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1699 
1700  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1701  ARMNN_ASSERT(layer != nullptr);
1702  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1703 
1704  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1705  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1706 
1707  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1708  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1709 }
1710 
1711 void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1712 {
1713  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1714 
1715  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1716  CHECK_VALID_SIZE(inputs.size(), 4);
1717 
1718  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1719  CHECK_VALID_SIZE(outputs.size(), 1);
1720 
1721  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1722  const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1723 
1725  desc.m_BeginMask = options->begin_mask;
1726  desc.m_EllipsisMask = options->ellipsis_mask;
1727  desc.m_EndMask = options->end_mask;
1728  desc.m_NewAxisMask = options->new_axis_mask;
1729  desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1731 
1732  armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1733  BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1734 
1735  std::vector<int> begin(beginTensorInfo.GetNumElements());
1736  ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1737 
1738  armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1739  BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1740 
1741  std::vector<int> end(endTensorInfo.GetNumElements());
1742  ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1743 
1744  armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1745  BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1746 
1747  std::vector<int> stride(strideTensorInfo.GetNumElements());
1748  ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1749 
1750  desc.m_Begin = begin;
1751  desc.m_End = end;
1752  desc.m_Stride = stride;
1753 
1754  auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1755  IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
1756  ARMNN_ASSERT(layer != nullptr);
1757 
1758  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1759  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1760 
1761  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1762  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1763 
1764  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1765  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1766 }
1767 
1768 void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1769 {
1770  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1771 
1772  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1773  const auto * options = operatorPtr->builtin_options.AsSubOptions();
1774 
1775  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1776  CHECK_VALID_SIZE(inputs.size(), 2);
1777 
1778  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1779  CHECK_VALID_SIZE(outputs.size(), 1);
1780 
1781  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1782  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1783 
1784  auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1785  IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
1786  ARMNN_ASSERT(layer != nullptr);
1787 
1788  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1789  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1790 
1791  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1792  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1793  {
1794  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1795  }
1796  else
1797  {
1798  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1799  }
1800 
1801  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1802 
1803  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1804  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1805 }
1806 
1807 void TfLiteParser::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
1808 {
1809  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1810 
1811  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1812  const auto * options = operatorPtr->builtin_options.AsDivOptions();
1813 
1814  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1815  CHECK_VALID_SIZE(inputs.size(), 2);
1816 
1817  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1818  CHECK_VALID_SIZE(outputs.size(), 1);
1819 
1820  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1821  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1822 
1823  auto layerName = boost::str(boost::format("Div:%1%:%2%") % subgraphIndex % operatorIndex);
1824  IConnectableLayer* layer = m_Network->AddDivisionLayer(layerName.c_str());
1825  ARMNN_ASSERT(layer != nullptr);
1826 
1827  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1828  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1829 
1830  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1831  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1832  {
1833  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1834  }
1835  else
1836  {
1837  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1838  }
1839 
1840  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1841 
1842  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1843  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1844 }
1845 
1846 void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1847 {
1848  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1849 
1850  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1851  const auto * options = operatorPtr->builtin_options.AsAddOptions();
1852 
1853  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1854  CHECK_VALID_SIZE(inputs.size(), 2);
1855 
1856  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1857  CHECK_VALID_SIZE(outputs.size(), 1);
1858 
1859  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1860  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1861 
1862  auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1863  IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1864  ARMNN_ASSERT(layer != nullptr);
1865 
1866  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1867  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1868 
1869  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1870  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1871  {
1872  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1873  }
1874  else
1875  {
1876  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1877  }
1878 
1879  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1880 
1881  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1882  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1883 }
1884 
1885 void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1886 {
1887  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1888 
1889  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1890  const auto * options = operatorPtr->builtin_options.AsMulOptions();
1891 
1892  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1893  CHECK_VALID_SIZE(inputs.size(), 2);
1894 
1895  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1896  CHECK_VALID_SIZE(outputs.size(), 1);
1897 
1898  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1899  armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1900 
1901  auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1902  IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1903  ARMNN_ASSERT(layer != nullptr);
1904 
1905  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1906  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1907 
1908  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1909  if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1910  {
1911  AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1912  }
1913  else
1914  {
1915  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1916  }
1917 
1918  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1919 
1920  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1921  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1922 }
1923 
1924 void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1925 {
1926  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1927 
1928  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1929 
1930  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1931  CHECK_VALID_SIZE(outputs.size(), 1);
1932 
1933  armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1934  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1935 
1936  armnn::MeanDescriptor desc;
1937  std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1938  ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1939  desc.m_Axis = axis;
1940 
1941  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1942  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1943 
1944  desc.m_KeepDims =
1945  inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1946  true : false;
1947 
1948  auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1949  IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1950  ARMNN_ASSERT(layer != nullptr);
1951 
1952  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1953 
1954  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1955  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1956 
1957  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1958  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1959 }
1960 
1961 void TfLiteParser::ParseNeg(size_t subgraphIndex, size_t operatorIndex)
1962 {
1963  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1964 
1965  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1966  CHECK_VALID_SIZE(inputs.size(), 1);
1967 
1968  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1969  CHECK_VALID_SIZE(outputs.size(), 1);
1970 
1971  auto layerName = boost::str(boost::format("Neg:%1%:%2%") % subgraphIndex % operatorIndex);
1973  IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(descriptor, layerName.c_str());
1974  ARMNN_ASSERT(layer != nullptr);
1975 
1976  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1977  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1978 
1979  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1980  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1981 
1982  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1983  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1984 }
1985 
1986 void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1987 {
1988  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1989 
1990  TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1991 
1992  TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1993  CHECK_VALID_SIZE(outputs.size(), 1);
1994 
1995  armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1996  BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1997 
1998  std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1999  ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
2000 
2001  size_t step = 2;
2002  armnn::PadDescriptor desc;
2003  for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
2004  {
2005  desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
2006  }
2007 
2008  auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
2009  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2010 
2011  IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
2012  ARMNN_ASSERT(layer != nullptr);
2013  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2014 
2015  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2016  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2017 
2018  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2019  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2020 }
2021 
2022 void TfLiteParser::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
2023 {
2024  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2025 
2026  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2027  CHECK_VALID_SIZE(inputs.size(), 1);
2028 
2029  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2030  CHECK_VALID_SIZE(outputs.size(), 1);
2031 
2032  auto layerName = boost::str(boost::format("Quantize:%1%:%2%") % subgraphIndex % operatorIndex);
2033 
2034  IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
2035  ARMNN_ASSERT(layer != nullptr);
2036 
2037  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2038  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2039 
2040  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2041  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2042 
2043  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2044  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2045 }
2046 
2047 void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
2048 {
2049  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
2050 }
2051 
2052 void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
2053 {
2054  ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
2055 }
2056 
2057 void TfLiteParser::ParseLeakyRelu(size_t subgraphIndex, size_t operatorIndex)
2058 {
2059  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::LeakyReLu);
2060 }
2061 
2062 void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
2063 {
2064  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
2065 }
2066 
2067 void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
2068 {
2069  ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
2070 }
2071 
2072 void TfLiteParser::ParseHardSwish(size_t subgraphIndex, size_t operatorIndex)
2073 {
2074  ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::HardSwish);
2075 }
2076 
2077 void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
2078 {
2079  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2080  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2081  IgnoreUnused(operatorPtr);
2082 
2083  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2084  CHECK_VALID_SIZE(inputs.size(), 1);
2085 
2086  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2087  CHECK_VALID_SIZE(outputs.size(), 1);
2088 
2089  auto layerName = str(boost::format("Activation:"));
2090  ActivationDescriptor activationDesc;
2091  activationDesc.m_Function = activationType;
2092 
2093  switch (activationType)
2094  {
2095  case ActivationFunction::ReLu:
2096  {
2097  layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
2098  break;
2099  }
2100  case ActivationFunction::BoundedReLu:
2101  {
2102  layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
2103  activationDesc.m_A = 6.0f;
2104  activationDesc.m_B = 0.0f;
2105  break;
2106  }
2107  case ActivationFunction::Sigmoid:
2108  {
2109  layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
2110  break;
2111  }
2112  case ActivationFunction::TanH:
2113  {
2114  layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
2115  activationDesc.m_A = 1.0f;
2116  activationDesc.m_B = 1.0f;
2117  break;
2118  }
2119  case ActivationFunction::LeakyReLu:
2120  {
2121  layerName += str(boost::format("LEAKYRELU:%1%:%2%") % subgraphIndex % operatorIndex);
2122  const auto * options = operatorPtr->builtin_options.AsLeakyReluOptions();
2123  activationDesc.m_A = options->alpha;
2124  break;
2125  }
2126  case ActivationFunction::HardSwish:
2127  layerName += str(boost::format("HARDSWISH:%1%:%2%") % subgraphIndex % operatorIndex);
2128  break;
2129  default:
2130  {
2131  throw ParseException(
2132  boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
2133  " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
2134  }
2135  }
2136 
2137  IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2138 
2139  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2140  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2141 
2142  // register the input connection slots for the layer, connections are made after all layers have been created
2143  // only the tensors for the inputs are relevant, exclude the const tensors
2144  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2145  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2146 
2147  // register the output connection slots for the layer, connections are made after all layers have been created
2148  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2149  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2150 }
2152  const std::vector<int32_t> & targetDimsIn)
2153 {
2154  std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
2155  const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
2156 
2157  if (stretchDim != targetDimsIn.end())
2158  {
2159  if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
2160  {
2161  throw ParseException(
2162  boost::str(
2163  boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
2164  }
2165 
2166  auto targetNumElements =
2167  boost::numeric_cast<unsigned int>(
2168  std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
2169 
2170  auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
2171  outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
2172  }
2173 
2174  TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
2175 
2176  TensorInfo reshapeInfo = inputTensorInfo;
2177  reshapeInfo.SetShape(outputShape);
2178 
2179  return reshapeInfo;
2180 }
2181 
2182 void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
2183 {
2184  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2185 
2186  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2187 
2188  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2189  CHECK_VALID_SIZE(outputs.size(), 1);
2190 
2191  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2192  const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
2193  auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
2194 
2195  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2196  armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
2197  CheckMatchingQuantization(inputTensorInfo, actualOutputTensorInfo, layerName, "Input 0", "Output 0");
2198 
2199  // Extracting new shape for the output
2200  // There are two ways it can be passed
2201  // * First is to define the target shape in the operator built-in options
2202  // * Second is to pass it as a second input tensor
2203  std::vector<int32_t> targetShape;
2204  bool targetShapeFound = false;
2205  // Check if built-in options were given
2206  if (options != nullptr)
2207  {
2208  // make sure the parameter is given
2209  if (options->new_shape.empty() == false)
2210  {
2211  targetShape = options->new_shape;
2212  targetShapeFound = true;
2213  }
2214  }
2215 
2216  // If there is no built-in option given or if the built-in new_shape parameter was empty
2217  if (!targetShapeFound)
2218  {
2219  // Check for a second input tensor
2220  if (inputs.size() > 1 && inputs[1] != nullptr)
2221  {
2222  if (inputs[1]->is_variable)
2223  {
2224  ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
2225  }
2226 
2227  if (inputs[1]->shape.size() != 1)
2228  {
2229  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
2230  }
2231 
2232  if (inputs[1]->type != tflite::TensorType_INT32)
2233  {
2234  ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
2235  }
2236 
2237  // Extract target shape from input
2238  auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2239  auto values = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
2240  for (int i=0; i < inputs[1]->shape[0]; ++i)
2241  {
2242  targetShape.push_back(values[i]);
2243  }
2244  }
2245  else
2246  {
2247  ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2248  "At least one method required");
2249  }
2250  }
2251 
2252  armnn::TensorInfo reshapeOutputTensorInfo =
2253  TfLiteParser::OutputShapeOfReshape(inputTensorInfo, targetShape);
2254 
2255  // Check for valid input size and that reshape parameters equal output shape
2256  const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2257  if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
2258  {
2259  std::stringstream ss;
2260  ss << "New shape defined in reshape parameters "
2261  << reshapeOutputTensorShape
2262  << " does not equal output shape "
2263  << actualOutputTensorInfo.GetShape()
2264  << ": "
2265  << CHECK_LOCATION().AsString();
2266  throw ParseException(ss.str());
2267  }
2268 
2269  ReshapeDescriptor reshapeDesc;
2270  reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
2271 
2272  IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
2273  ARMNN_ASSERT(layer != nullptr);
2274  layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
2275 
2276  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2277  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2278 
2279  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2280  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2281 }
2282 
2283 void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2284 {
2285  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2286 }
2287 
2288 void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2289 {
2290  ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2291 }
2292 
2293 void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2294 {
2295  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2296 
2297  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2298  CHECK_VALID_SIZE(inputs.size(), 2);
2299 
2300  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2301  CHECK_VALID_SIZE(outputs.size(), 1);
2302 
2303  armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2304 
2305  // Data for the parsed tensor args (size) must be stored locally.
2306  std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2307 
2308  BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2309  ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2310 
2311  ResizeDescriptor desc;
2312  desc.m_Method = resizeMethod;
2313  desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
2314  desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2315  desc.m_DataLayout = armnn::DataLayout::NHWC;
2316 
2317  auto layerName = str(boost::format("Resize:"));
2318 
2319  switch (resizeMethod)
2320  {
2321  case ResizeMethod::Bilinear:
2322  {
2323  layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
2324 
2325  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2326  const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2327 
2328  desc.m_AlignCorners = options->align_corners;
2329  break;
2330  }
2331  case ResizeMethod::NearestNeighbor:
2332  {
2333  layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
2334  break;
2335  }
2336  default:
2337  {
2338  throw ParseException(
2339  boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
2340  " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
2341  }
2342  }
2343 
2344  TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2345  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2346  CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2347 
2348  IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
2349  ARMNN_ASSERT(layer != nullptr);
2350  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2351 
2352  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2353  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2354 
2355  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2356  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2357 }
2358 
2359 void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2360 {
2361  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2362 
2363  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2364  const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2365 
2366  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2367 
2368  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2369  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2370  CHECK_VALID_SIZE(outputs.size(), 1);
2371 
2372  unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2373  uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
2374 
2375  const unsigned int concatDimInput = static_cast<unsigned int>(
2376  (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
2377 
2378  OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2379  concatDescriptor.SetConcatAxis(concatDimInput);
2380 
2381  unsigned int mergeDimOrigin = 0;
2382 
2383  for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2384  {
2385  TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2386 
2387  // This set up concatDescriptor view origin
2389  inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
2390  }
2391 
2392  auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
2393  TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2394 
2395  IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
2396  ARMNN_ASSERT(layer != nullptr);
2397  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2398 
2399  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2400  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2401 
2402  // add fused activation layer
2403  layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2404 
2405  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2406  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2407 }
2408 
2409 void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2410 {
2411  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2412 
2413  const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2414  const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2415 
2416  CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2417 
2419  desc.m_BiasEnabled = false;
2420  desc.m_TransposeWeightMatrix = true;
2421 
2422  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2423  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2424  CHECK_VALID_SIZE(outputs.size(), 1);
2425 
2426  armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2427 
2428  // Fully Connected Layer accepts two dimensional weights input
2429  int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2430  if (weightsDimension != 2)
2431  {
2432  throw ParseException(
2433  boost::str(
2434  boost::format(
2435  "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2436  "Node %2%")
2437  % weightsDimension
2438  % CHECK_LOCATION().AsString()));
2439  }
2440 
2441  auto filterTensorAndData = CreateConstTensor(inputs[1],
2442  filterTensorInfo,
2444  armnn::IConnectableLayer* layer = nullptr;
2445  auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2446 
2447  if (inputs.size() == 3)
2448  {
2449  desc.m_BiasEnabled = true;
2450  TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
2451  auto biasTensorAndData = CreateConstTensor(inputs[2],
2452  biasTensorInfo,
2454  layer = m_Network->AddFullyConnectedLayer(desc,
2455  filterTensorAndData.first,
2456  Optional<ConstTensor>(biasTensorAndData.first),
2457  layerName.c_str());
2458  }
2459  else
2460  {
2461  layer = m_Network->AddFullyConnectedLayer(desc,
2462  filterTensorAndData.first,
2463  EmptyOptional(),
2464  layerName.c_str());
2465  }
2466  ARMNN_ASSERT(layer != nullptr);
2467 
2468  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2469 
2470  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2471 
2472  if (inputTensorInfo.GetNumDimensions() > 2)
2473  {
2474  // Add reshape to flatten to 2D [batch_size, input_size],
2475  // where "input_size" corresponds to the number of inputs to the layer,
2476  // matching the second dimension of weights,
2477  // and "batch_size" is calculated by dividing the number of elements by "input_size".
2478  std::vector<unsigned int> reshapedDimensions(2);
2479  reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2480  reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2481 
2482  if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2483  {
2484  throw ParseException(
2485  boost::str(
2486  boost::format(
2487  "Failed to deduce input tensor shape from filter size %1%")
2488  % reshapedDimensions[1]
2489  % CHECK_LOCATION().AsString()));
2490  }
2491 
2492  armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2493  reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2494 
2495  std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2497  desc.m_TargetShape = reshapedTensorInfo.GetShape();
2498  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2499 
2500  reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2501  reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2502 
2503  RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2504  }
2505  else
2506  {
2507  // register the input connection slot for the layer
2508  // only the tensors for the inputs are relevant, exclude the const tensors
2509  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2510  }
2511 
2512  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2513  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2514 
2515  // we need to add the activation layer and fortunately we don't need to care about the data layout
2516  armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2517  options->fused_activation_function);
2518 
2519  // register the output connection slots for the layer, connections are made after all layers have been created
2520  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2521  RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2522 }
2523 
2524 void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2525 {
2526  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2527 
2528  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2529 
2530  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2531  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2532  CHECK_VALID_SIZE(outputs.size(), 4);
2533 
2534  // Obtain custom options from flexbuffers
2535  auto custom_options = operatorPtr->custom_options;
2536  const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2537 
2538  // Obtain descriptor information from tf lite
2540  desc.m_MaxDetections = m["max_detections"].AsUInt32();
2541  desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2542  desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2543  desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2544  desc.m_NumClasses = m["num_classes"].AsUInt32();
2545  desc.m_ScaleH = m["h_scale"].AsFloat();
2546  desc.m_ScaleW = m["w_scale"].AsFloat();
2547  desc.m_ScaleX = m["x_scale"].AsFloat();
2548  desc.m_ScaleY = m["y_scale"].AsFloat();
2549 
2550  if (!(m["use_regular_nms"].IsNull()))
2551  {
2552  desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
2553  }
2554  if (!(m["detections_per_class"].IsNull()))
2555  {
2556  desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2557  }
2558 
2559  if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2560  {
2561  throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2562  "must be positive and less than or equal to 1.");
2563  }
2564 
2565  armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2566  auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2568 
2569  auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2570  IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2571  layerName.c_str());
2572 
2573  ARMNN_ASSERT(layer != nullptr);
2574 
2575  // The model does not specify the output shapes.
2576  // The output shapes are calculated from the max_detection and max_classes_per_detection.
2577  unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2578  m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2579  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2580  m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2581  m_OverridenOutputShapes.push_back({ 1 });
2582 
2583  for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2584  {
2585  armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
2586  layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2587  }
2588 
2589  // Register the input connection slots for the layer, connections are made after all layers have been created
2590  // only the tensors for the inputs are relevant, exclude the const tensors
2591  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2592  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2593 
2594  // Register the output connection slots for the layer, connections are made after all layers have been created
2595  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2596  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2597  outputTensorIndexes[1],
2598  outputTensorIndexes[2],
2599  outputTensorIndexes[3]});
2600 }
2601 
2602 /// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2603 void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2604 {
2605  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2606 
2607  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2608  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2609  CHECK_VALID_SIZE(outputs.size(), 1);
2610 
2611  if (inputs.size() < 1)
2612  {
2613  throw ParseException("Pack must have at least one input.");
2614  }
2615 
2616  const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2617  const auto* options = operatorPtr->builtin_options.AsPackOptions();
2618 
2619  StackDescriptor desc;
2620  desc.m_Axis = static_cast<uint32_t>(options->axis);
2621  desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2622 
2623  // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2624  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2625  desc.m_InputShape = inputTensorInfo.GetShape();
2626 
2627  auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2628  IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2629 
2630  ARMNN_ASSERT(layer != nullptr);
2631 
2632  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2633  layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2634 
2635  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2636  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2637 
2638  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2639  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2640 }
2641 
2642 void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2643 {
2644  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2645 
2646  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2647  const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2648 
2649  // This unpackAxis indicates the axis to unpack
2650  const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2651 
2652  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2653  CHECK_VALID_SIZE(inputs.size(), 1);
2654 
2655  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2656 
2657  if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2658  {
2659  throw ParseException(
2660  boost::str(
2661  boost::format(
2662  "The unpack axis: %1% cannot be greater than or equal to "
2663  "the number of input dimension %2% %3%")
2664  % unpackAxis
2665  % inputTensorInfo.GetNumDimensions()
2666  % CHECK_LOCATION().AsString()));
2667  }
2668 
2669  unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2670  // If num is not defined, automatically infer from the length of the dimension axis.
2671  if(unpackNum == 0)
2672  {
2673  unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2674  }
2675 
2676  // If unpack number cannot be inferred and is still zero, throw ParseException.
2677  if(unpackNum == 0)
2678  {
2679  throw ParseException("Number to unpack must greater than zero.");
2680  }
2681 
2682  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2683  CHECK_VALID_SIZE(outputs.size(), unpackNum);
2684 
2685  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2686  std::vector<unsigned int> unpackDimSizes(inputDimSize);
2687 
2688  // Add current input shape to unpackDimSizes
2689  for (unsigned int i = 0; i < inputDimSize; ++i)
2690  {
2691  unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2692  }
2693 
2694  if (unpackDimSizes[unpackAxis] != unpackNum)
2695  {
2696  throw ParseException("Number to unpack must be the same as length of the dimension to "
2697  "unpack along.");
2698  }
2699 
2700  unpackDimSizes[unpackAxis] /= unpackNum;
2701 
2702  SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2703  for (unsigned int j = 0; j < unpackNum; ++j)
2704  {
2705  // Set the size of the views.
2706  for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2707  {
2708  splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2709  }
2710  splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2711  }
2712 
2713  auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2714  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2715  ARMNN_ASSERT(layer != nullptr);
2716 
2717  TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2718  unpackDimSizes.data());
2719 
2720  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2721  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2722 
2723  // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2724  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2725  {
2726  armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k], true);
2727  std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2729  desc.m_TargetShape = outputTensorInfo.GetShape();
2730  armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2731 
2732  layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2733  outputTensorInfo.GetDataType(),
2734  outputTensorInfo.GetQuantizationScale(),
2735  outputTensorInfo.GetQuantizationOffset()));
2736  layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2737 
2738  reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2739 
2740  uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2741  armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2742  RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2743  }
2744 }
2745 
2746 void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2747 {
2748  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2749 
2750  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2751  const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2752 
2753  const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2754 
2755  // If number of splits cannot be inferred and is zero, throw ParseException.
2756  if(numSplits == 0)
2757  {
2758  throw ParseException("Number to splits must greater than zero.");
2759  }
2760 
2761  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2762  CHECK_VALID_SIZE(inputs.size(), 2);
2763  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2764  CHECK_VALID_SIZE(outputs.size(), numSplits);
2765 
2766  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2767  armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
2768 
2769  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2770  std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2771  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2772 
2773  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2774  const unsigned int splitDim = axisData[0];
2775 
2776  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2777  if (inputDimSize > MaxNumOfTensorDimensions)
2778  {
2779  throw ParseException(
2780  boost::str(
2781  boost::format(
2782  "The number of dimensions: %1% for input tensors of the "
2783  "split op cannot be greater than %2% %3%")
2784  % inputTensorInfo.GetNumDimensions()
2786  % CHECK_LOCATION().AsString()));
2787  }
2788 
2789  std::vector<unsigned int> splitterDimSizes(inputDimSize);
2790 
2791  // Add current input shape to splitterDimSizes
2792  for (unsigned int i = 0; i < inputDimSize; ++i)
2793  {
2794  splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2795  }
2796 
2797  if (splitterDimSizes[splitDim] % numSplits != 0)
2798  {
2799  throw ParseException("Number of splits must evenly divide the dimension");
2800  }
2801  splitterDimSizes[splitDim] /= numSplits;
2802 
2803  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2804  for (unsigned int j = 0; j < numSplits; ++j)
2805  {
2806  // Set the size of the views.
2807  for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2808  {
2809  splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2810  }
2811  splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2812  }
2813 
2814  auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2815  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2816  ARMNN_ASSERT(layer != nullptr);
2817 
2818  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2819  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
2820 
2821  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2822  {
2823  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
2824  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2825  }
2826 
2827  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2828  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2829 }
2830 
2831 unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
2832 {
2833  int numDims = armnn::numeric_cast<int>(numDimsIn);
2834  int v = idx < 0 ? numDims + idx : idx;
2835  ARMNN_ASSERT(v >= 0);
2836  ARMNN_ASSERT(v < numDims);
2837 
2838  return static_cast<unsigned int>(v);
2839 }
2840 
2841 void TfLiteParser::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
2842 {
2843  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2844 
2845  const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2846  const auto * options = operatorPtr->builtin_options.AsSplitVOptions();
2847 
2848  auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2849  CHECK_VALID_SIZE(inputs.size(), 3);
2850 
2851  auto& inputTensor = inputs[0];
2852  auto& splitsTensor = inputs[1];
2853  auto& axisTensor = inputs[2];
2854 
2855  armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
2856  armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
2857  armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
2858  ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2859 
2860  // Inputs
2861  auto inputDimSize = inputTensorInfo.GetNumDimensions();
2862  if (inputDimSize > MaxNumOfTensorDimensions)
2863  {
2864  throw ParseException(
2865  boost::str(
2866  boost::format(
2867  "The number of dimensions: %1% for input tensors of the "
2868  "SplitV op cannot be greater than %2% %3%")
2869  % inputTensorInfo.GetNumDimensions()
2871  % CHECK_LOCATION().AsString()));
2872  }
2873 
2874  // Get split axis
2875  BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
2876  std::vector<int> axisData(axisTensorInfo.GetNumElements());
2877  ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2878  const unsigned int splitDim = ComputeWrappedIndex(axisData[0], inputTensorInfo.GetNumDimensions());
2879 
2880  // Set split sizes
2881  CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
2882  unsigned int numSplits{0};
2883 
2884  if(options)
2885  {
2886  numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2887  }
2888  else
2889  {
2890  numSplits = splitsInfo.GetNumElements();
2891  }
2892 
2893  if (numSplits <=0)
2894  {
2895  throw ParseException("SplitV has invalid number of splits");
2896  }
2897 
2898  std::vector<int> splitsData(numSplits);
2899  BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
2900  ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
2901 
2902  unsigned int idx = 0;
2903  int numInferred{0};
2904  unsigned int inferIdx{0};
2905  int splitSum{0};
2906  for (auto split : splitsData)
2907  {
2908  if (split < 0)
2909  {
2910  numInferred++;
2911  inferIdx = idx;
2912  }
2913  else
2914  {
2915  splitSum += split;
2916  }
2917  idx++;
2918  }
2919  // Check for inferred Axis
2920  if (numInferred == 0)
2921  {
2922  if (splitSum != numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]))
2923  {
2924  throw ParseException("SplitV split_sizes does not sum to the dimension of value along split_dim.");
2925  }
2926  }
2927  else if (numInferred == 1)
2928  {
2929  splitsData[inferIdx] = numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - splitSum;
2930  }
2931  else
2932  {
2933  throw ParseException("Cannot infer split size for more than one split");
2934  }
2935 
2936  //Ouput size validation
2937  auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2938  CHECK_VALID_SIZE(outputs.size(), numSplits);
2939 
2940  // Setup Armnn descriptor
2941  SplitterDescriptor splitDesc(numSplits, inputDimSize);
2942  unsigned int accumSplit = 0;
2943  for (unsigned int j = 0; j < numSplits; ++j)
2944  {
2945  unsigned int splitSize = numeric_cast<unsigned int>(splitsData[j]);
2946 
2947  // Set the size of the views.
2948  for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
2949  {
2950  unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
2951  if (dimIdx == splitDim)
2952  {
2953  dimSize = splitSize;
2954  }
2955  splitDesc.SetViewSize(j, dimIdx, dimSize);
2956  }
2957 
2958  splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
2959  accumSplit += splitSize;
2960  }
2961 
2962  auto layerName = boost::str(boost::format("SplitV:%1%:%2%") % subgraphIndex % operatorIndex);
2963  IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
2964  ARMNN_ASSERT(layer != nullptr);
2965 
2966  auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2967  RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2968 
2969  for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2970  {
2971  armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
2972  layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2973  }
2974 
2975  auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2976  RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2977 }
2978 
2979 armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2980  unsigned int outputSlot,
2981  tflite::ActivationFunctionType activationType)
2982 {
2983  ActivationDescriptor activationDesc;
2984  std::string layerName = prevLayer->GetName();
2985 
2986  switch(activationType)
2987  {
2988  case tflite::ActivationFunctionType_NONE:
2989  {
2990  // this is a no-op: return previous layer
2991  return prevLayer;
2992  }
2993  case tflite::ActivationFunctionType_RELU:
2994  {
2995  activationDesc.m_Function = ActivationFunction::ReLu;
2996  layerName += ":RELU";
2997  break;
2998  }
2999  case tflite::ActivationFunctionType_RELU6:
3000  {
3001  activationDesc.m_Function = ActivationFunction::BoundedReLu;
3002  activationDesc.m_A = 6.0f;
3003  activationDesc.m_B = 0.0f;
3004  layerName += ":RELU6";
3005  break;
3006  }
3007  case tflite::ActivationFunctionType_TANH:
3008  {
3009  activationDesc.m_Function = ActivationFunction::TanH;
3010  activationDesc.m_A = 1.0f;
3011  activationDesc.m_B = 1.0f;
3012  layerName += ":TANH";
3013  break;
3014  }
3015 
3016  // I only put these here as a reminder what others we could support
3017  case tflite::ActivationFunctionType_RELU_N1_TO_1:
3018  case tflite::ActivationFunctionType_SIGN_BIT:
3019  default:
3020  {
3021  throw ParseException(
3022  boost::str(
3023  boost::format("TfLite parser doesn't suppport fused activation: "
3024  "%1%/%2% %3% ") %
3025  activationType %
3026  tflite::EnumNameActivationFunctionType(activationType) %
3027  CHECK_LOCATION().AsString()));
3028 
3029  }
3030  }
3031 
3032  IConnectableLayer* activationLayer =
3033  m_Network->AddActivationLayer(activationDesc, layerName.c_str());
3034 
3035  auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
3036  prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
3037  activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
3038  return activationLayer;
3039 }
3040 
3042 {
3043  if (fileName == nullptr)
3044  {
3045  throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
3046  CHECK_LOCATION().AsString()));
3047  }
3048  std::error_code errorCode;
3049  fs::path pathToFile(fileName);
3050  if (!fs::exists(pathToFile, errorCode))
3051  {
3052  std::string locationString = CHECK_LOCATION().AsString();
3053  std::string msg = boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
3054  fileName %
3055  errorCode %
3056  locationString);
3057  throw FileNotFoundException(msg);
3058  }
3059  std::ifstream file(fileName, std::ios::binary);
3060  std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
3061  return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
3062  fileContent.size());
3063 }
3064 
3065 TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
3066 {
3067  if (binaryContent == nullptr)
3068  {
3069  throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
3070  CHECK_LOCATION().AsString()));
3071  }
3072  flatbuffers::Verifier verifier(binaryContent, len);
3073  if (verifier.VerifyBuffer<tflite::Model>() == false)
3074  {
3075  throw ParseException(
3076  boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
3077  "flatbuffers format. size:%1% %2%") %
3078  len %
3079  CHECK_LOCATION().AsString()));
3080  }
3081  return tflite::UnPackModel(binaryContent);
3082 }
3083 
3085  size_t subgraphIndex,
3086  size_t operatorIndex)
3087 {
3088  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3089 
3090  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3091  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3092 
3093  size_t inputCount = operatorPtr->inputs.size();
3094  TensorRawPtrVector result(inputCount);
3095  for (size_t i=0; i<inputCount; ++i)
3096  {
3097  uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
3098  result[i] = subgraphPtr->tensors[inputId].get();
3099  }
3100  return result;
3101 }
3102 
3104  size_t subgraphIndex,
3105  size_t operatorIndex)
3106 {
3107  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3108 
3109  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3110  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3111 
3112  size_t outputCount = operatorPtr->outputs.size();
3113  TensorRawPtrVector result(outputCount);
3114  for (size_t i=0; i<outputCount; ++i)
3115  {
3116  uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
3117  CHECK_TENSOR(model, subgraphIndex, outputId);
3118  result[i] = subgraphPtr->tensors[outputId].get();
3119  }
3120  return result;
3121 }
3122 
3124  size_t subgraphIndex)
3125 {
3126  CHECK_SUBGRAPH(model, subgraphIndex);
3127  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3128 
3129  size_t inputCount = subgraphPtr->inputs.size();
3130  TensorIdRawPtrVector result(inputCount);
3131  for (size_t i=0; i<inputCount; ++i)
3132  {
3133  uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
3134  CHECK_TENSOR(model, subgraphIndex, inputId);
3135  result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
3136  }
3137  return result;
3138 }
3139 
3141  size_t subgraphIndex)
3142 {
3143  CHECK_SUBGRAPH(model, subgraphIndex);
3144  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3145 
3146  size_t outputCount = subgraphPtr->outputs.size();
3147  TensorIdRawPtrVector result(outputCount);
3148  for (size_t i=0; i<outputCount; ++i)
3149  {
3150  uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
3151  result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
3152  }
3153  return result;
3154 }
3155 
3156 std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
3157  size_t subgraphIndex,
3158  size_t operatorIndex)
3159 {
3160  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3161  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3162  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3163  return operatorPtr->inputs;
3164 }
3165 
3166 std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
3167  size_t subgraphIndex,
3168  size_t operatorIndex)
3169 {
3170  CHECK_MODEL(model, subgraphIndex, operatorIndex);
3171  const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3172  const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
3173  return operatorPtr->outputs;
3174 }
3175 
3176 void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
3177  size_t operatorIndex,
3178  IConnectableLayer* layer,
3179  const std::vector<unsigned int>& tensorIndexes)
3180 {
3181  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3182  ARMNN_ASSERT(layer != nullptr);
3183  if (tensorIndexes.size() != layer->GetNumInputSlots())
3184  {
3185  throw ParseException(
3186  boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
3187  " for subgraph:%3% operator index:%4% %5%") %
3188  tensorIndexes.size() %
3189  layer->GetNumInputSlots() %
3190  subgraphIndex %
3191  operatorIndex %
3192  CHECK_LOCATION().AsString()));
3193  }
3194 
3195  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
3196  {
3197  unsigned int tensorIndex = tensorIndexes[slotIndex];
3198  armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
3199  RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
3200  }
3201 }
3202 
3203 void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
3204  size_t operatorIndex,
3205  IConnectableLayer* layer,
3206  const std::vector<unsigned int>& tensorIndexes)
3207 {
3208  CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3209  ARMNN_ASSERT(layer != nullptr);
3210  if (tensorIndexes.size() != layer->GetNumOutputSlots())
3211  {
3212  throw ParseException(
3213  boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
3214  " for subgraph:%3% operator index:%4% %5%") %
3215  tensorIndexes.size() %
3216  layer->GetNumOutputSlots() %
3217  subgraphIndex %
3218  operatorIndex %
3219  CHECK_LOCATION().AsString()));
3220  }
3221 
3222  for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
3223  {
3224  unsigned int tensorIndex = tensorIndexes[slotIndex];
3225  armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
3226  RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
3227  }
3228 }
3229 
3230 void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
3231 {
3232  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3233 
3234  auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
3235  for (auto const & tensorIdAndPtr : inputs)
3236  {
3237  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3238  IConnectableLayer* layer =
3239  m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3240 
3241  auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
3242  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3243 
3244  RegisterOutputSlots(subgraphIndex,
3245  VIRTUAL_OPERATOR_ID,
3246  layer,
3247  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3248  }
3249 }
3250 
3251 void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
3252 {
3253  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3254 
3255  auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
3256  for (auto const & tensorIdAndPtr : outputs)
3257  {
3258  auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3259  IConnectableLayer* layer =
3260  m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3261 
3262  RegisterInputSlots(subgraphIndex,
3263  VIRTUAL_OPERATOR_ID,
3264  layer,
3265  { static_cast<uint32_t>(tensorIdAndPtr.first) });
3266  }
3267 }
3268 
3269 void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
3270 {
3271  CHECK_SUBGRAPH(m_Model, subgraphIndex);
3272 
3273  const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
3274  for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
3275  {
3276  for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
3277  {
3278  if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
3279  m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
3280  {
3281  TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
3282  armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
3283  auto tensorAndData = CreateConstTensor(tensorPtr,
3284  tensorInfo,
3286 
3287  std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
3288  IConnectableLayer *layer =
3289  m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
3290 
3291  layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3292  RegisterOutputSlots(subgraphIndex,
3293  VIRTUAL_OPERATOR_ID,
3294  layer,
3295  { tensorIndex });
3296 
3297  }
3298  }
3299  }
3300 }
3301 
3302 // example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3304 {
3305  CHECK_BUFFER(model, bufferIndex);
3306  return model->buffers[bufferIndex].get();
3307 }
3308 
3309 template<typename T>
3310 std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3311 TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
3312  TfLiteParser::TensorRawPtr tensorPtr,
3313  armnn::TensorInfo& tensorInfo,
3315 {
3316  auto constData = CreateConstTensorImpl<T>(bufferPtr,
3317  tensorPtr,
3318  tensorInfo,
3319  permutationVector);
3320  TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
3321  return std::make_pair(constData.first, std::move(storage));
3322 }
3323 
3324 std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3325 TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
3326  armnn::TensorInfo& tensorInfo,
3328 {
3329  CHECK_TENSOR_PTR(tensorPtr);
3330  auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3331  CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3332 
3333  switch (tensorInfo.GetDataType())
3334  {
3336  return CreateConstTensorAndStoreData<float>(bufferPtr,
3337  tensorPtr,
3338  tensorInfo,
3339  permutationVector);
3341  return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
3342  tensorPtr,
3343  tensorInfo,
3344  permutationVector);
3346  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3347  tensorPtr,
3348  tensorInfo,
3349  permutationVector);
3351  return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3352  tensorPtr,
3353  tensorInfo,
3354  permutationVector);
3356  return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
3357  tensorPtr,
3358  tensorInfo,
3359  permutationVector);
3360  default:
3361  {
3362  std::stringstream errString;
3363  errString << "Unexpected datatype when creating const tensor: "
3364  << armnn::GetDataTypeName(tensorInfo.GetDataType())
3365  << " shape:" << tensorInfo.GetShape()
3366  << CHECK_LOCATION().AsString();
3367  throw ParseException(errString.str());
3368  }
3369  }
3370 }
3371 
3373  const std::string& name) const
3374 {
3375  CHECK_SUBGRAPH(m_Model, subgraphId);
3376  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3377  for (auto const & input : inputs)
3378  {
3379  if (input.second->name == name)
3380  {
3381  auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
3382  return std::make_pair(bindingId, ToTensorInfo(input.second));
3383  }
3384  }
3385 
3386  std::stringstream bindings;
3387  for (auto const & input : inputs)
3388  {
3389  bindings << "'" << input.second->name << "' ";
3390  }
3391 
3392  throw ParseException(
3393  boost::str(
3394  boost::format("No input binding found for subgraph:%1% and name:%2%. "
3395  "Possible inputs are: [%3%] %4%") %
3396  subgraphId %
3397  name %
3398  bindings.str() %
3399  CHECK_LOCATION().AsString()));
3400 }
3401 
3403  const std::string& name) const
3404 {
3405  CHECK_SUBGRAPH(m_Model, subgraphId);
3406  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3407  for (unsigned int i = 0; i < outputs.size(); ++i)
3408  {
3409  auto const output = outputs[i];
3410  if (output.second->name == name)
3411  {
3412  auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
3413  std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
3414  m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
3415  return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
3416  }
3417  }
3418 
3419  std::stringstream bindings;
3420  for (auto const & output : outputs)
3421  {
3422  bindings << "'" << output.second->name << "' ";
3423  }
3424 
3425  throw ParseException(
3426  boost::str(
3427  boost::format("No output binding found for subgraph:%1% and name:%2%. "
3428  "Possible outputs are: [%3%] %4%") %
3429  subgraphId %
3430  name %
3431  bindings.str() %
3432  CHECK_LOCATION().AsString()));
3433 }
3434 
3436 {
3437  return m_Model->subgraphs.size();
3438 }
3439 
3440 std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
3441 {
3442  CHECK_SUBGRAPH(m_Model, subgraphId);
3443  auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3444  std::vector<std::string> result;
3445  result.reserve(inputs.size());
3446  for (auto const & input : inputs)
3447  {
3448  result.push_back(input.second->name);
3449  }
3450  return result;
3451 }
3452 
3453 std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
3454 {
3455  CHECK_SUBGRAPH(m_Model, subgraphId);
3456  auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3457  std::vector<std::string> result;
3458  result.reserve(outputs.size());
3459  for (auto const & output : outputs)
3460  {
3461  result.push_back(output.second->name);
3462  }
3463  return result;
3464 }
3465 
3467 {
3468  return new TfLiteParser(options);
3469 }
3470 
3472 {
3474 }
3475 
3477 {
3478  delete parser;
3479 }
3480 
3481 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
3482 : m_FloatData(std::move(data))
3483 , m_Uint8Data(nullptr)
3484 , m_Int8Data(nullptr)
3485 , m_Int32Data(nullptr)
3486 {
3487 }
3488 
3489 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
3490 : m_FloatData(nullptr)
3491 , m_Uint8Data(std::move(data))
3492 , m_Int8Data(nullptr)
3493 , m_Int32Data(nullptr)
3494 {
3495 }
3496 
3497 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
3498 : m_FloatData(nullptr)
3499 , m_Uint8Data(nullptr)
3500 , m_Int8Data(std::move(data))
3501 , m_Int32Data(nullptr)
3502 {
3503 }
3504 
3505 TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
3506 : m_FloatData(nullptr)
3507 , m_Uint8Data(nullptr)
3508 , m_Int8Data(nullptr)
3509 , m_Int32Data(std::move(data))
3510 {
3511 }
3512 
3513 } // armnnTfLiteParser
virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile) override
Create the network from a flatbuffers binary file on disk.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX)
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
virtual BindingPointInfo GetNetworkOutputBindingInfo(size_t subgraphId, const std::string &name) const override
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
UnaryOperation m_Operation
Specifies the elementwiseUnary operation to execute.
uint32_t m_Axis
0-based axis along which to stack the input tensors.
std::unique_ptr< tflite::SubGraphT > SubgraphPtr
static BufferRawPtr GetBuffer(const ModelPtr &model, size_t bufferIndex)
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
float m_ScaleW
Center size encoding scale weight.
bool IsTypeSpaceMatch(const TensorInfo &other) const
Check that the types are the same and, if quantize, that the quantization parameters are the same...
Definition: Tensor.cpp:424
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
static ModelPtr LoadModelFromBinary(const uint8_t *binaryContent, size_t len)
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
A TransposeConvolution2dDescriptor for the TransposeConvolution2dLayer.
#define ARMNN_THROW_PARSE_EXCEPTION(msg)
const TensorShape & GetShape() const
Definition: Tensor.hpp:187
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
virtual armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent) override
Create the network from a flatbuffers binary.
std::string AsString() const
Definition: Exceptions.hpp:29
int32_t m_ShrinkAxisMask
Shrink axis mask value. If set, the nth specification shrinks the dimensionality by 1...
A ReshapeDescriptor for the ReshapeLayer.
std::vector< int > m_Begin
Begin values for the input that will be sliced.
std::vector< TensorRawPtr > TensorRawPtrVector
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
float m_ScaleX
Center size encoding scale x.
TensorShape m_InputShape
Required shape of all input tensors.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
static ModelPtr LoadModelFromFile(const char *fileName)
bool m_BiasEnabled
Enable/disable bias.
std::vector< unsigned int > m_OutputShape
unsigned int GetNumBytes() const
Definition: Tensor.cpp:419
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
float m_Beta
Exponentiation value.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_DetectionsPerClass
Detections per classes, used in Regular NMS.
bool m_OutputShapeEnabled
Output shape if it has been specified.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_BUFFER(MODEL, BUFFER_INDEX)
static TensorRawPtrVector GetInputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:163
uint32_t m_PadTop
Padding top value in the height dimension.
std::vector< BackendOptions > NetworkOptions
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t &outPadHead, uint32_t &outPadTail, bool samePadding)
Definition: TfParser.cpp:420
uint32_t m_PadRight
Padding right value in the width dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
std::unique_ptr< ITfLiteParser, void(*)(ITfLiteParser *parser)> ITfLiteParserPtr
virtual BindingPointInfo GetNetworkInputBindingInfo(size_t subgraphId, const std::string &name) const override
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
Copyright (c) 2020 ARM Limited.
void IgnoreUnused(Ts &&...)
uint32_t m_PadBottom
Padding bottom value in the height dimension.
int32_t m_BeginMask
Begin mask value.
virtual size_t GetSubgraphCount() const override
Return the number of subgraphs in the parsed model.
SizeType GetSize() const
Definition: Types.hpp:225
uint32_t m_DilationY
Dilation along y axis.
int32_t m_EndMask
End mask value.
PoolingAlgorithm
Definition: Types.hpp:96
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding values for the input dimension: heightPad{top, bottom} widthPad{left, right}.
uint32_t m_DilationY
Dilation factor value for height dimension.
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
static armnn::TensorInfo OutputShapeOfReshape(const armnn::TensorInfo &inputTensorInfo, const std::vector< int32_t > &targetDimsIn)
static ITfLiteParserPtr Create(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition: Types.hpp:194
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX)
constexpr const char * GetDataTypeName(DataType dataType)
Definition: TypesUtils.hpp:168
void SetShape(const TensorShape &newShape)
Definition: Tensor.hpp:189
A ResizeDescriptor for the ResizeLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_MaxClassesPerDetection
Maximum numbers of classes per detection, used in Fast NMS.
std::vector< unsigned int > m_Axis
Values for the dimensions to reduce.
A StackDescriptor for the StackLayer.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
TensorShape m_TargetShape
Target shape value.
static TensorIdRawPtrVector GetSubgraphInputs(const ModelPtr &model, size_t subgraphIndex)
static TensorRawPtrVector GetOutputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
uint32_t m_MaxDetections
Maximum numbers of detections.
A PadDescriptor for the PadLayer.
armnn::TensorInfo ToTensorInfo(Deserializer::TensorRawPtr tensorPtr)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
std::unique_ptr< onnx::ModelProto > ModelPtr
Definition: OnnxParser.hpp:23
#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
void CheckTensor(const ConstTensor &t)
Definition: TensorTest.cpp:102
static TensorIdRawPtrVector GetSubgraphOutputs(const ModelPtr &model, size_t subgraphIndex)
bool CheckShape(const armnn::TensorShape &actual, const std::vector< uint32_t > &expected)
DataType
Definition: Types.hpp:32
float m_NmsIouThreshold
Intersection over union threshold.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationX
Dilation factor value for width dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
std::string FileLine() const
Definition: Exceptions.hpp:37
Status SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
Set the size of the views.
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
int32_t m_NewAxisMask
New axis mask value.
bool m_KeepDims
Enable/disable keep dimensions. If true, then the reduced dimensions that are of length 1 are kept...
armnnSerializer::TensorInfo * TensorRawPtr
std::vector< unsigned int > m_BlockShape
Block shape values.
static void Destroy(ITfLiteParser *parser)
An output connection slot for a layer.
Definition: INetwork.hpp:37
A L2NormalizationDescriptor for the L2NormalizationLayer.
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:470
float GetQuantizationScale() const
Definition: Tensor.cpp:453
static std::vector< int32_t > & GetOutputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
DataType GetDataType() const
Definition: Tensor.hpp:194
An OriginsDescriptor for the ConcatLayer.
bool has_value() const noexcept
Definition: Optional.hpp:53
A FullyConnectedDescriptor for the FullyConnectedLayer.
int32_t m_EllipsisMask
Ellipsis mask value.
bool m_BiasEnabled
Enable/disable bias.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:298
static ITfLiteParser * CreateRaw(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
#define CHECK_VALID_SIZE(ACTUAL,...)
uint32_t m_NumClasses
Number of classes.
#define CHECKED_NON_NEGATIVE(VALUE)
uint32_t m_PadTop
Padding top value in the height dimension.
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
A StandInDescriptor for the StandIn layer.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:33
bool m_UseRegularNms
Use Regular NMS.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< unsigned int > m_BlockShape
Block shape value.
std::vector< int > m_Stride
Stride values for the input that will be sliced.
bool IsActivationSupported(const BackendId &backend, const TensorInfo &input, const TensorInfo &output, const ActivationDescriptor &descriptor, char *reasonIfUnsupported=nullptr, size_t reasonIfUnsupportedMaxLength=1024)
Deprecated in favor of IBackend and ILayerSupport interfaces.
An ActivationDescriptor for the ActivationLayer.
Definition: Descriptors.hpp:20
#define CHECK_LOCATION()
Definition: Exceptions.hpp:197
uint32_t m_NumInputs
Number of input tensors.
A SliceDescriptor for the SliceLayer.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
static std::vector< int32_t > & GetInputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
#define CHECK_TENSOR_PTR(TENSOR_PTR)
float m_ScaleH
Center size encoding scale height.
std::vector< int > m_End
End values for the input that will be sliced.
A SpaceToBatchNdDescriptor for the SpaceToBatchNdLayer.
Struct for the users to pass backend specific options.
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
Definition: Descriptors.hpp:45
uint32_t m_DilationX
Dilation along x axis.
uint32_t m_PadLeft
Padding left value in the width dimension.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
static armnn::TensorInfo OutputShapeOfSqueeze(const std::vector< uint32_t > &squeezeDims, const armnn::TensorInfo &inputTensorInfo)
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX)
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
Definition: Descriptors.hpp:90
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
std::vector< std::pair< unsigned int, unsigned int > > m_Crops
The values to crop from the input dimension.
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:175
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
void SetConcatAxis(unsigned int concatAxis)
Set the concatenation axis value.
const tflite::BufferT * BufferRawPtr
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
ResizeMethod
Definition: Types.hpp:103
A MeanDescriptor for the MeanLayer.
virtual std::vector< std::string > GetSubgraphInputTensorNames(size_t subgraphId) const override
Return the input tensor names for a given subgraph.
armnn::BindingPointInfo BindingPointInfo
uint32_t m_PadRight
Padding right value in the width dimension.
A TransposeDescriptor for the TransposeLayer.
A StridedSliceDescriptor for the StridedSliceLayer.
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
virtual const char * GetName() const =0
Returns the name of the layer.
virtual std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const override
Return the output tensor names for a given subgraph.
float m_ScaleY
Center size encoding scale y.
float m_NmsScoreThreshold
NMS score threshold.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:101
virtual int Connect(IInputSlot &destination)=0
const char * m_Function
Definition: Exceptions.hpp:16
A Pooling2dDescriptor for the Pooling2dLayer.
std::unique_ptr< tflite::ModelT > ModelPtr
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:191
#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID)
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
Definition: Descriptors.hpp:47
bool IsQuantized() const
Definition: Tensor.cpp:496
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
Definition: Permute.cpp:98
A SoftmaxDescriptor for the SoftmaxLayer.
const tflite::TensorT * TensorRawPtr
DataLayout::NCHW DataLayout::NCHW DataLayout::NHWC DataLayout::NHWC true
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
Definition: Descriptors.hpp:43
std::unique_ptr< tflite::OperatorT > OperatorPtr
An input connection slot for a layer.
Definition: INetwork.hpp:24
TfLiteParser(const armnn::Optional< ITfLiteParser::TfLiteParserOptions > &options=armnn::EmptyOptional())
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
constexpr unsigned int MaxNumOfTensorDimensions
Definition: Types.hpp:18
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const
Definition: Tensor.hpp:192
ActivationFunction
Definition: Types.hpp:55
std::vector< TensorIdRawPtr > TensorIdRawPtrVector
uint32_t m_PadRight
Padding right value in the width dimension.