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