blob: 8b2a818e6df3f27302b7406a5a9b3ff6a8dbb5dd [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
5#include "TfLiteParser.hpp"
6
7#include <armnn/ArmNN.hpp>
8#include <armnn/Exceptions.hpp>
9#include <armnn/TypesUtils.hpp>
10#include <boost/filesystem.hpp>
11
12// armnnUtils:
Sadik Armagan479045b2018-10-01 11:51:37 +010013#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010014#include <Permute.hpp>
15#include <VerificationHelpers.hpp>
16
17// The generated code based on the Tf Lite schema:
18#include <schema_generated.h>
19
20#include <boost/core/ignore_unused.hpp>
21#include <boost/assert.hpp>
22#include <boost/format.hpp>
23#include <boost/log/trivial.hpp>
24
25#include <fstream>
26#include <algorithm>
27#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010028#include <numeric>
telsoa01c577f2c2018-08-31 09:22:23 +010029
30using namespace armnn;
31using armnn::CheckLocation;
32namespace armnnTfLiteParser
33{
34namespace
35{
jimfly01c25411c2018-11-14 17:47:22 +000036
telsoa01c577f2c2018-08-31 09:22:23 +010037const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
38
39void CheckSubgraph(const TfLiteParser::ModelPtr & model,
40 size_t subgraphIndex,
41 const CheckLocation & location)
42{
43 if (model.get() == nullptr)
44 {
45 throw ParseException(
46 boost::str(
47 boost::format("%1% was called with invalid (null) model. "
48 "Possible reason is that the model is not yet loaded and Unpack(ed). "
49 "subgraph:%2% at %3%") %
50 location.m_Function %
51 subgraphIndex %
52 location.FileLine()));
53 }
54 else if (subgraphIndex >= model->subgraphs.size())
55 {
56 throw ParseException(
57 boost::str(
58 boost::format("%1% was called with an invalid subgraph index. "
59 "subgraph:%2% at %3%") %
60 location.m_Function %
61 subgraphIndex %
62 location.FileLine()));
63 }
64}
65
66#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
67 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
68
69void CheckModel(const TfLiteParser::ModelPtr & model,
70 size_t subgraphIndex,
71 size_t operatorIndex,
72 const CheckLocation & location)
73{
74 if (model.get() == nullptr)
75 {
76 throw ParseException(
77 boost::str(
78 boost::format("%1% was called with invalid (null) model. "
79 "Possible reason is that the model is not yet loaded and Unpack(ed). "
80 "subgraph:%2% operator:%3% at %4%") %
81 location.m_Function %
82 subgraphIndex %
83 operatorIndex %
84 location.FileLine()));
85 }
86 else if (subgraphIndex >= model->subgraphs.size())
87 {
88 throw ParseException(
89 boost::str(
90 boost::format("%1% was called with an invalid subgraph index. "
91 "subgraph:%2% operator:%3% at %4%") %
92 location.m_Function %
93 subgraphIndex %
94 operatorIndex %
95 location.FileLine()));
96 }
97 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
98 operatorIndex != VIRTUAL_OPERATOR_ID)
99 {
100 throw ParseException(
101 boost::str(
102 boost::format("%1% was called with an invalid operator index. "
103 "subgraph:%2% operator:%3% at %4%") %
104 location.m_Function %
105 subgraphIndex %
106 operatorIndex %
107 location.FileLine()));
108 }
109}
110
111#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
112 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
113
114void CheckTensor(const TfLiteParser::ModelPtr & model,
115 size_t subgraphIndex,
116 size_t tensorIndex,
117 const CheckLocation & location)
118{
119 // not checking model, because I assume CHECK_MODEL already run
120 // and checked that. An assert would do.
121 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
122
123 // also subgraph index should be checked by CHECK_MODEL so
124 // I only add an assert here
125 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
126
127 // the tensor index is the only one to check here
128 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
129 {
130 throw ParseException(
131 boost::str(
132 boost::format("%1% was called with an invalid tensor index. "
133 "subgraph:%2% tensor:%3% at %4%") %
134 location.m_Function %
135 subgraphIndex %
136 tensorIndex %
137 location.FileLine()));
138 }
139}
140
141#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
142 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
143
144void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
145 const CheckLocation & location)
146{
147 if (rawPtr == nullptr)
148 {
149 throw ParseException(
150 boost::str(
151 boost::format("%1% was called with a null tensor pointer. "
152 "at %2%") %
153 location.m_Function %
154 location.FileLine()));
155
156 }
157}
158
159#define CHECK_TENSOR_PTR(TENSOR_PTR) \
160 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
161
162void CheckBuffer(const TfLiteParser::ModelPtr & model,
163 size_t bufferIndex,
164 const CheckLocation & location)
165{
166 if (model.get() == nullptr)
167 {
168 throw ParseException(
169 boost::str(
170 boost::format("%1% was called with invalid (null) model. "
171 "Possible reason is that the model is not yet loaded and Unpack(ed). "
172 "buffer:%2% at %3%") %
173 location.m_Function %
174 bufferIndex %
175 location.FileLine()));
176 }
177 else if (bufferIndex >= model->buffers.size())
178 {
179 throw ParseException(
180 boost::str(
181 boost::format("%1% was called with an invalid buffer index. "
182 "buffer index:%2% at %3%") %
183 location.m_Function %
184 bufferIndex %
185 location.FileLine()));
186 }
187 else if (model->buffers[bufferIndex].get() == nullptr)
188 {
189 throw ParseException(
190 boost::str(
191 boost::format("The buffer #%1% is null. %3%") %
192 bufferIndex %
193 location.AsString()));
194 }
195}
196
197#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
198 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
199
200void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
201 const armnn::TensorInfo & tensorInfo,
202 uint32_t bufferId,
203 const CheckLocation & location)
204{
205 if (bufferPtr == nullptr)
206 {
207 throw ParseException(
208 boost::str(
209 boost::format("BufferPtr is null for buffer:%1%. %2%") %
210 bufferId %
211 location.AsString()));
212 }
213 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
214 tensorInfo.GetNumBytes() > bufferPtr->data.size())
215 {
216 std::stringstream ss;
217 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
218 << "For tensor: " << tensorInfo.GetShape()
219 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
220 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
221 throw ParseException(ss.str());
222 }
223}
224
225#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
226 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
227
228bool IsActivationSupported(tflite::ActivationFunctionType activationType)
229{
230 switch(activationType)
231 {
232 case tflite::ActivationFunctionType_NONE:
233 case tflite::ActivationFunctionType_RELU:
234 case tflite::ActivationFunctionType_RELU6:
235 case tflite::ActivationFunctionType_TANH:
236 {
237 return true;
238 }
239 default:
240 {
241 return false;
242 }
243 }
244}
245
246#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
247 do { \
248 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
249 { \
250 throw ParseException( \
251 boost::str( \
252 boost::format("TfLite parser doesn't suppport fused activation: " \
253 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
254 OPTION->fused_activation_function % \
255 tflite::EnumNameActivationFunctionType(\
256 OPTION->fused_activation_function) % \
257 __func__ % \
258 SUBGRAPH_INDEX % \
259 OPERATOR_INDEX % \
260 CHECK_LOCATION().FileLine())); \
261 } \
262 } while(false)
263
264
265std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
266{
267 std::vector<unsigned int> result;
268 result.reserve(in.size());
269 for (auto & i : in)
270 {
271 result.push_back(CHECKED_NON_NEGATIVE(i));
272 }
273 return result;
274}
275
276void CalcPadding(uint32_t inputSize,
277 uint32_t filterSize,
278 uint32_t stride,
279 uint32_t& paddingFront,
280 uint32_t& paddingBack,
281 tflite::Padding padding)
282{
283 paddingFront = 0;
284 paddingBack = 0;
285 if (padding == tflite::Padding_SAME)
286 {
287 uint32_t outputSize = (inputSize + stride - 1) / stride;
288 uint32_t temp = (outputSize - 1) * stride + filterSize;
289 if (temp > inputSize)
290 {
291 paddingFront = (temp - inputSize) / 2;
292 paddingBack = (temp - inputSize) - paddingFront;
293 }
294 }
295}
296
297armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
298{
299 armnn::DataType type;
300 CHECK_TENSOR_PTR(tensorPtr);
301
302 switch (tensorPtr->type)
303 {
304 case tflite::TensorType_UINT8:
305 type = armnn::DataType::QuantisedAsymm8;
306 break;
307 case tflite::TensorType_FLOAT32:
308 type = armnn::DataType::Float32;
309 break;
310 case tflite::TensorType_INT32:
311 type = armnn::DataType::Signed32;
312 break;
313
314 default:
315 {
316 CheckLocation location = CHECK_LOCATION();
317 throw ParseException(
318 boost::str(
319 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
320 tensorPtr->type %
321 tflite::EnumNameTensorType(tensorPtr->type) %
322 tensorPtr->name %
323 location.AsString()));
324 }
325 }
326
327 float quantizationScale = 0.0f;
328 int32_t quantizationOffset = 0;
329
330 if (tensorPtr->quantization.get())
331 {
332 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
333 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
334
335 if (tensorPtr->quantization->scale.size() == 1)
336 {
337 quantizationScale = tensorPtr->quantization->scale[0];
338 }
339 if (tensorPtr->quantization->zero_point.size() == 1)
340 {
341 // NOTE: we lose precision here when converting from 64 bit to 32
342 // but this is what we support at the monent in ArmNN
343 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
344 }
345 }
346
347 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
348
349 // two statements (on purpose) for easier debugging:
350 armnn::TensorInfo result(static_cast<unsigned int>(tensorPtr->shape.size()),
351 dimensions.data(),
352 type,
353 quantizationScale,
354 quantizationOffset);
355 return result;
356}
357
358template<typename T>
359std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
360CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
361 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000362 armnn::TensorInfo& tensorInfo,
363 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100364{
365 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
366 BOOST_ASSERT_MSG(bufferPtr != nullptr,
367 boost::str(
368 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
369
370 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000371
372 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
373 {
374 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000375 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
376 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000377 }
378 else
379 {
380 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
381 }
382
telsoa01c577f2c2018-08-31 09:22:23 +0100383 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
384}
385
telsoa01c577f2c2018-08-31 09:22:23 +0100386armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
387{
388 // generate the binding id by shifting the tensor id by 8 bit
389 // and add the subgraph id, which allows 256 subgraphs
390 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
391}
392
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000393bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
394{
395 const unsigned int actualSize = actual.GetNumDimensions();
396 if (actualSize != expected.size())
397 {
398 return false;
399 }
400
401 for (unsigned int i = 0u; i < actualSize; i++)
402 {
403 if (expected[i] < 0 ||
404 actual[i] != static_cast<unsigned int>(expected[i]))
405 {
406 return false;
407 }
408 }
409
410 return true;
411}
412
telsoa01c577f2c2018-08-31 09:22:23 +0100413} // <anonymous>
414
415TfLiteParser::TfLiteParser()
416: m_Network(nullptr, nullptr)
417, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
418{
419 // register supported operators
420 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
Sadik Armagan479045b2018-10-01 11:51:37 +0100421 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
telsoa01c577f2c2018-08-31 09:22:23 +0100422 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
423 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Sadik Armagan8853c1f2018-10-22 09:04:18 +0100424 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Finn Williamsc42c3842019-01-22 14:18:11 +0000425 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100426 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
Sadik Armagan58f39192018-09-17 14:14:39 +0100427 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
428 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100429 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Sadik Armagan479045b2018-10-01 11:51:37 +0100430 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
431 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -0200432 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Bruno Goncalvesf803f782018-12-18 13:40:30 -0200433 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
Bruno Goncalves2235cee2018-12-19 12:51:45 -0200434 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Bruno Goncalves6c2355b2018-12-19 12:52:01 -0200435 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
telsoa01c577f2c2018-08-31 09:22:23 +0100436}
437
438void TfLiteParser::ResetParser()
439{
440 m_Network = armnn::INetworkPtr(nullptr, nullptr);
441 m_Model = nullptr;
442 m_SubgraphConnections.clear();
443}
444
Bruno Goncalves9c761a62018-12-27 14:20:35 -0200445void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex,
446 size_t operatorIndex,
447 IConnectableLayer *layer)
448{
449 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
450 BOOST_ASSERT(layer != nullptr);
451
452 const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex];
453 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
454
455 BOOST_ASSERT(operatorPtr->inputs.size() > 1);
456
457 uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]);
458 TensorRawPtr tensorPtr = subGraphPtr->tensors[reshapedInputId].get();
459 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]);
460 TensorRawPtr tensorPtr1 = subGraphPtr->tensors[inputId].get();
461
462 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr);
463 armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1);
464
465 if (inputTensorInfo.GetNumDimensions() < reshapedTensorInfo.GetNumDimensions())
466 {
467 uint32_t id = reshapedInputId;
468 reshapedInputId = inputId;
469 inputId = id;
470
471 reshapedTensorInfo = ToTensorInfo(tensorPtr1);
472 inputTensorInfo = ToTensorInfo(tensorPtr);
473 }
474
475 uint32_t numDimensions = inputTensorInfo.GetNumDimensions();
476
477 std::vector<unsigned> reshapedDim;
478 for (unsigned int i = 0; i < reshapedTensorInfo.GetNumDimensions(); ++i)
479 {
480 reshapedDim.push_back(reshapedTensorInfo.GetShape()[i]);
481 }
482
483 std::vector<unsigned int> reshapedDimensions(numDimensions, 1);
484 std::copy_backward (reshapedDim.begin(), reshapedDim.end(), reshapedDimensions.end());
485
486 reshapedTensorInfo.SetShape(armnn::TensorShape{ numDimensions, reshapedDimensions.data() });
487
488 std::string layerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
489 armnn::ReshapeDescriptor desc;
490 desc.m_TargetShape = reshapedTensorInfo.GetShape();
491 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
492
493 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
494 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
495
496 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {reshapedInputId});
497
498 armnn::IInputSlot* input1Slot = &(layer->GetInputSlot(1));
499 RegisterConsumerOfTensor(subgraphIndex, inputId, input1Slot);
500}
501
telsoa01c577f2c2018-08-31 09:22:23 +0100502INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
503{
504 ResetParser();
505 m_Model = LoadModelFromFile(graphFile);
506 return CreateNetworkFromModel();
507}
508
509INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
510{
511 ResetParser();
512 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
513 return CreateNetworkFromModel();
514}
515
516INetworkPtr TfLiteParser::CreateNetworkFromModel()
517{
518 m_Network = INetwork::Create();
519 BOOST_ASSERT(m_Model.get() != nullptr);
520
521 bool failedToCreate = false;
522 std::stringstream errors;
523
524 if (m_Model->subgraphs.size() != 1)
525 {
526 throw ParseException(
527 boost::str(
528 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
529 m_Model->subgraphs.size() %
530 CHECK_LOCATION().AsString()));
531 }
532
533 size_t subgraphIndex = 0;
534 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
535 {
536 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
537
538 size_t operatorIndex = 0;
539 for (OperatorPtr const & op : subgraph->operators)
540 {
541 try
542 {
543 if (op->custom_options.size() > 0)
544 {
545 throw ParseException(
546 boost::str(
547 boost::format("Custom options for op: %1% is not supported. "
548 "It has %2% bytes of custom options. %3%") %
549 op->opcode_index %
550 op->custom_options.size() %
551 CHECK_LOCATION().AsString()));
552 }
553
554 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
555 auto builtinCode = opCodePtr->builtin_code;
556
557 if (builtinCode > tflite::BuiltinOperator_MAX)
558 {
559 throw ParseException(
560 boost::str(
561 boost::format("Operator code %1% is out of range 0-%2%. "
562 "subgraph:%3% operator idx:%4%. %5%") %
563 builtinCode %
564 tflite::BuiltinOperator_MAX %
565 subgraphIndex %
566 operatorIndex %
567 CHECK_LOCATION().AsString()));
568 }
569
570 // lookup and call the parser function
571 auto & parserFunction = m_ParserFunctions[builtinCode];
572 (this->*parserFunction)(subgraphIndex, operatorIndex);
573 }
574 catch (const ParseException& e)
575 {
576 failedToCreate = true;
577 std::stringstream errorString;
578
579 errorString << "Failed to parse operator #" << operatorIndex
580 << " within subgraph #" << subgraphIndex
581 << " error: " << e.what();
582 BOOST_LOG_TRIVIAL(error) << errorString.str();
583
584 errors << errorString.str() << "\n";
585 }
586 ++operatorIndex;
587 }
588
589 SetupInputLayers(subgraphIndex);
590 SetupOutputLayers(subgraphIndex);
591
592 ++subgraphIndex;
593 }
594
595 if (failedToCreate)
596 {
597 // we can skip everything and let the outer exception handler deal with the error
598 throw ParseException(errors.str());
599 }
600
601 // establish the connections from the layer outputs to the inputs of the subsequent layers
602 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
603 {
604 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
605 {
606 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
607 {
608 for (size_t inputSlotIdx = 0;
609 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
610 ++inputSlotIdx)
611 {
612 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
613 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
614 }
615 }
616 }
617 }
618
619 return std::move(m_Network);
620}
621
622void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
623 size_t tensorIndex,
624 armnn::IOutputSlot* slot)
625{
626 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
627 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
628 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
629
630 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
631
632 // assuming there is only one producer for that tensor
633 if (tensorSlots.outputSlot != nullptr)
634 {
635 throw ParseException(boost::str(
636 boost::format("Another layer has already registered itself as the producer of "
637 "subgraph:%1% tensor:%2% %3%") %
638 subgraphIndex %
639 tensorIndex %
640 CHECK_LOCATION().AsString()));
641 }
642
643 tensorSlots.outputSlot = slot;
644}
645
646void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
647 size_t tensorIndex,
648 armnn::IInputSlot* slot)
649{
650 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
651 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
652 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
653
654 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
655 tensorSlots.inputSlots.push_back(slot);
656}
657
658void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
659{
660 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
661 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
662 //
663 auto opcodeIndex = operatorPtr->opcode_index;
664 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
665
666 throw ParseException(
667 boost::str(
668 boost::format("Operator not supported. "
669 "subgraph:%1% operator:%2% "
670 "opcode_index:%3% opcode:%4% / %5% %6%") %
671 subgraphIndex %
672 operatorIndex %
673 opcodeIndex %
674 opcode %
675 tflite::EnumNameBuiltinOperator(opcode) %
676 CHECK_LOCATION().AsString()));
677}
678
telsoa01c577f2c2018-08-31 09:22:23 +0100679void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
680{
681 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
682
683 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
684 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
685
686 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
687
688 Convolution2dDescriptor desc;
689 desc.m_BiasEnabled = false;
690 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
691 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000692 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100693
694 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
695 CHECK_VALID_SIZE(inputs.size(), 2, 3);
696
697 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
698 CHECK_VALID_SIZE(outputs.size(), 1);
699
700 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
701 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
702
703 // assuming input is NHWC
704 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
705 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
706
707 // assuming the filter is OHWI : Output, H, W, Input
708 // which is essentially the same as NHWC
709 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
710 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
711
712 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
713 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
714
Matteo Martincigh747ef822018-12-18 09:26:39 +0000715 auto filterTensorAndData = CreateConstTensor(inputs[1],
716 filterTensorInfo,
717 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100718 armnn::IConnectableLayer* layer;
719
720 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
721
722 if (inputs.size() == 3)
723 {
724 desc.m_BiasEnabled = true;
725 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000726 auto biasTensorAndData = CreateConstTensor(inputs[2],
727 biasTensorInfo,
728 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100729 layer = m_Network->AddConvolution2dLayer(desc,
730 filterTensorAndData.first,
731 biasTensorAndData.first,
732 layerName.c_str());
733 }
734 else
735 {
736 layer = m_Network->AddConvolution2dLayer(desc,
737 filterTensorAndData.first,
738 layerName.c_str());
739 }
740
741 BOOST_ASSERT(layer != nullptr);
742
telsoa01c577f2c2018-08-31 09:22:23 +0100743 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000744 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100745
746 // register the input connection slots for the layer, connections are made after all layers have been created
747 // only the tensors for the inputs are relevant, exclude the const tensors
748 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000749 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100750
jimfly01c25411c2018-11-14 17:47:22 +0000751 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100752 // register the output connection slots for the layer, connections are made after all layers have been created
753 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
754 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
755}
756
757void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
758{
759 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
760
761 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
762 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
763
764 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
765
766 DepthwiseConvolution2dDescriptor desc;
767 desc.m_BiasEnabled = false;
768 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
769 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000770 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100771 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
772 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
773
774 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
775 CHECK_VALID_SIZE(inputs.size(), 2, 3);
776 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
777 CHECK_VALID_SIZE(outputs.size(), 1);
778
779 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
780 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
781
Matteo Martincigh747ef822018-12-18 09:26:39 +0000782 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100783 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
784 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000785
786 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100787 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
788 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
789
Matteo Martincigh747ef822018-12-18 09:26:39 +0000790 // Reshape weights as [ H, W, I, M ]
791 filterTensorInfo.SetShape({ filterHeight,
792 filterWidth,
793 inputTensorInfo.GetShape()[3],
794 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
795
796 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
797 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
798
telsoa01c577f2c2018-08-31 09:22:23 +0100799 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
800 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
801
Matteo Martincigh747ef822018-12-18 09:26:39 +0000802 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100803 armnn::IConnectableLayer* layer;
804 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
805
806 if (inputs.size() == 3)
807 {
808 desc.m_BiasEnabled = true;
809 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000810 auto biasTensorAndData = CreateConstTensor(inputs[2],
811 biasTensorInfo,
812 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100813 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
814 filterTensorAndData.first,
815 biasTensorAndData.first,
816 layerName.c_str());
817 }
818 else
819 {
820 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
821 filterTensorAndData.first,
822 layerName.c_str());
823 }
824 BOOST_ASSERT(layer != nullptr);
825
telsoa01c577f2c2018-08-31 09:22:23 +0100826 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000827 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100828
829 // register the input connection slots for the layer, connections are made after all layers have been created
830 // only the tensors for the inputs are relevant, exclude the const tensors
831 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000832 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100833
jimfly01c25411c2018-11-14 17:47:22 +0000834 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100835 // register the output connection slots for the layer, connections are made after all layers have been created
836 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
837 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
838}
839
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100840void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
841{
842 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
843}
844
845void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
846{
847 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
848}
849
850void TfLiteParser::ParsePool(size_t subgraphIndex,
851 size_t operatorIndex,
852 PoolingAlgorithm algorithm)
853{
854 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
855
856 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
857 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
858
859 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
860
861 std::string layerName;
862
863 switch (algorithm)
864 {
865 case PoolingAlgorithm::Average:
866 layerName =
867 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
868 break;
869 case PoolingAlgorithm::Max:
870 layerName =
871 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
872 break;
873 default:
874 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
875 }
876
877 Pooling2dDescriptor desc;
878
879 desc.m_PoolType = algorithm;
880 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
881 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
882 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
883 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
884 desc.m_PaddingMethod = PaddingMethod::Exclude;
885 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +0000886 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100887
888 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
889 CHECK_VALID_SIZE(inputs.size(), 1);
890 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
891
892 // assuming input is NHWC
893 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
894 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
895
896 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
897 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
898
899 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
900 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100901
902 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
903
904 BOOST_ASSERT(layer != nullptr);
905
jimfly01c25411c2018-11-14 17:47:22 +0000906 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
907 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100908
909 // register the input connection slots for the layer, connections are made after all layers have been created
910 // only the tensors for the inputs are relevant, exclude the const tensors
911 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000912 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100913
jimfly01c25411c2018-11-14 17:47:22 +0000914 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100915 // register the output connection slots for the layer, connections are made after all layers have been created
916 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
917 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
918}
919
telsoa01c577f2c2018-08-31 09:22:23 +0100920void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
921{
922 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
923 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
924 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
925
926 SoftmaxDescriptor desc;
927 desc.m_Beta = options->beta;
928
929 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
930 CHECK_VALID_SIZE(inputs.size(), 1);
931 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
932 CHECK_VALID_SIZE(outputs.size(), 1);
933
934 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
935 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
936
937 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
938 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
939
940 // register the input connection slots for the layer, connections are made after all layers have been created
941 // only the tensors for the inputs are relevant, exclude the const tensors
942 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
943 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
944
945 // register the output connection slots for the layer, connections are made after all layers have been created
946 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
947 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
948}
949
950armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
951 const armnn::TensorInfo & inputTensorInfo)
952{
953 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
954 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
955 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
956
957 if (inputTensorInfo.GetNumDimensions() > 4)
958 {
959 std::stringstream ss;
960 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
961 << " shape:" << inputTensorInfo.GetShape() << " "
962 << CHECK_LOCATION().AsString();
963 throw ParseException(ss.str());
964 }
965
966 if (squeezeDims.empty())
967 {
968 squeezeDims.assign(dimensionSequence,
969 dimensionSequence+inputTensorInfo.GetNumDimensions());
970 }
971
972 std::vector<uint32_t> outputDims;
973 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
974 {
975 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
976 auto currentDimension = inputTensorInfo.GetShape()[i];
977 if (skipSqueeze || currentDimension != 1)
978 {
979 outputDims.push_back(currentDimension);
980 }
981 }
982
983 if (outputDims.size() > 4)
984 {
985 std::stringstream ss;
986 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
987 << " shape:" << inputTensorInfo.GetShape() << " "
988 << CHECK_LOCATION().AsString();
989 throw ParseException(ss.str());
990 }
991
992 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
993 outputDims.data());
994
995 // we need to preserve the tensor type and the quantization data as well
996 TensorInfo outTensorInfo = inputTensorInfo;
997 outTensorInfo.SetShape(outShape);
998
999 return outTensorInfo;
1000}
1001
1002void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1003{
1004 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1005
1006 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1007 CHECK_VALID_SIZE(inputs.size(), 1);
1008
1009 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1010 CHECK_VALID_SIZE(outputs.size(), 1);
1011
1012 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1013 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
1014
1015 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1016 armnn::TensorInfo outputTensorInfo =
1017 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1018 inputTensorInfo);
1019
1020 ReshapeDescriptor reshapeDesc;
1021 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1022
1023 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
1024 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1025 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1026
1027 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1028 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1029
1030 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1031 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1032}
1033
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001034void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1035{
1036 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1037
1038 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1039 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1040
1041 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1042 CHECK_VALID_SIZE(inputs.size(), 2);
1043
1044 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1045 CHECK_VALID_SIZE(outputs.size(), 1);
1046
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001047 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1048 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1049
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001050 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1051 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1052
1053 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1054 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1055
1056 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001057 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1058 {
1059 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1060 }
1061 else
1062 {
1063 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1064 }
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001065
1066 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1067
1068 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1069 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1070}
1071
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001072void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1073{
1074 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1075
1076 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1077 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1078
1079 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1080 CHECK_VALID_SIZE(inputs.size(), 2);
1081
1082 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1083 CHECK_VALID_SIZE(outputs.size(), 1);
1084
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001085 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1086 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1087
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001088 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1089 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1090
1091 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1092 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1093
1094 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001095 if (inputTensorInfo.GetNumDimensions() != input1TensorInfo.GetNumDimensions())
1096 {
1097 AddBroadcastReshapeLayer(subgraphIndex, operatorIndex, layer);
1098 }
1099 else
1100 {
1101 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1102 }
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001103
1104 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1105
1106 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1107 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1108}
1109
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001110void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1111{
1112 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1113
1114 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1115
1116 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1117 CHECK_VALID_SIZE(outputs.size(), 1);
1118
1119 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1120 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1121
1122 armnn::MeanDescriptor desc;
1123 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1124 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1125 desc.m_Axis = axis;
1126
1127 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1128 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1129
1130 desc.m_KeepDims =
1131 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1132 true : false;
1133
1134 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1135 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1136
1137 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1138
1139 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1140 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1141
1142 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1143 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1144}
1145
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001146void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1147{
1148 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1149
1150 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1151
1152 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1153 CHECK_VALID_SIZE(outputs.size(), 1);
1154
1155 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1156 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1157
1158 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1159 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1160
1161 size_t step = 2;
1162 armnn::PadDescriptor desc;
1163 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1164 {
1165 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1166 }
1167
1168 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
1169 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1170
1171 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1172 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1173
1174 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1175 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1176
1177 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1178 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1179}
1180
Finn Williamsc42c3842019-01-22 14:18:11 +00001181
Sadik Armagan58f39192018-09-17 14:14:39 +01001182void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1183{
Finn Williamsc42c3842019-01-22 14:18:11 +00001184 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001185}
1186
1187void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1188{
Finn Williamsc42c3842019-01-22 14:18:11 +00001189 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1190}
Sadik Armagan58f39192018-09-17 14:14:39 +01001191
Finn Williamsc42c3842019-01-22 14:18:11 +00001192void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1193{
1194 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1195}
1196
1197
1198void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1199{
1200 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001201 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1202 boost::ignore_unused(operatorPtr);
1203
1204 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1205 CHECK_VALID_SIZE(inputs.size(), 1);
1206
1207 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1208 CHECK_VALID_SIZE(outputs.size(), 1);
1209
Finn Williamsc42c3842019-01-22 14:18:11 +00001210 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001211 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001212 activationDesc.m_Function = activationType;
1213
1214 switch (activationType)
1215 {
1216 case ActivationFunction::ReLu:
1217 {
1218 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1219 break;
1220 }
1221 case ActivationFunction::BoundedReLu:
1222 {
1223 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1224 activationDesc.m_A = 6.0f;
1225 activationDesc.m_B = 0.0f;
1226 break;
1227 }
1228 case ActivationFunction::Sigmoid:
1229 {
1230 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1231 break;
1232 }
1233 default:
1234 {
1235 throw ParseException(
1236 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
1237 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
1238 }
1239 }
1240
1241 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01001242
1243 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1244 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1245
1246 // register the input connection slots for the layer, connections are made after all layers have been created
1247 // only the tensors for the inputs are relevant, exclude the const tensors
1248 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1249 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1250
1251 // register the output connection slots for the layer, connections are made after all layers have been created
1252 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1253 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1254}
Sadikb94967b2018-09-19 15:30:00 +01001255armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1256 const std::vector<int32_t> & targetDimsIn)
1257{
1258 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1259 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1260
1261 if (stretchDim != targetDimsIn.end())
1262 {
1263 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1264 {
1265 throw ParseException(
1266 boost::str(
1267 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1268 }
1269
1270 auto targetNumElements =
1271 boost::numeric_cast<unsigned int>(
1272 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1273
1274 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1275 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1276 }
1277
1278 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1279
1280 TensorInfo reshapeInfo = inputTensorInfo;
1281 reshapeInfo.SetShape(outputShape);
1282
1283 return reshapeInfo;
1284}
1285
1286void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1287{
1288 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1289
1290 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001291
1292 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1293 CHECK_VALID_SIZE(outputs.size(), 1);
1294
1295 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1296 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1297
1298 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001299 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1300 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001301 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1302
kevmay0171972a82018-12-17 14:28:03 +00001303 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001304 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
1305 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00001306 {
1307 std::stringstream ss;
1308 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00001309 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00001310 << " does not equal output shape "
1311 << actualOutputTensorInfo.GetShape()
1312 << ": "
1313 << CHECK_LOCATION().AsString();
1314 throw ParseException(ss.str());
1315 }
1316
Sadikb94967b2018-09-19 15:30:00 +01001317 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001318 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001319
1320 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1321 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001322 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001323
1324 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1325 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1326
1327 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1328 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1329}
1330
Sadik Armagan479045b2018-10-01 11:51:37 +01001331void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1332{
1333 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1334
1335 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1336 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1337
1338 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1339
1340 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1341 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1342 CHECK_VALID_SIZE(outputs.size(), 1);
1343
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001344 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
1345 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01001346
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001347 const unsigned int concatDimInput = static_cast<unsigned int>(
1348 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01001349
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001350 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
1351 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01001352
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001353 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01001354
1355 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1356 {
1357 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1358
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001359 // This set up concatDescriptor view origin
1360 armnnUtils::ProcessConcatInputTensorInfo(
1361 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01001362 }
1363
1364 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1365 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1366
1367 BOOST_ASSERT(layer != nullptr);
1368
1369 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1370 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Sadik Armagan479045b2018-10-01 11:51:37 +01001371
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001372 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01001373
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001374 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01001375
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00001376 // add fused activation layer
1377 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01001378
Sadik Armagan479045b2018-10-01 11:51:37 +01001379 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1380 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1381}
1382
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001383void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1384{
1385 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1386
1387 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1388 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1389
1390 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1391
1392 FullyConnectedDescriptor desc;
1393 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001394 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001395
1396 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1397 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1398 CHECK_VALID_SIZE(outputs.size(), 1);
1399
1400 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1401
1402 // Fully Connected Layer accepts two dimensional weights input
1403 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1404 if (weightsDimension != 2)
1405 {
1406 throw ParseException(
1407 boost::str(
1408 boost::format(
1409 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1410 "Node %2%")
1411 % weightsDimension
1412 % CHECK_LOCATION().AsString()));
1413 }
1414
Matteo Martincigh747ef822018-12-18 09:26:39 +00001415 auto filterTensorAndData = CreateConstTensor(inputs[1],
1416 filterTensorInfo,
1417 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001418 armnn::IConnectableLayer* layer;
1419 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1420
1421 if (inputs.size() == 3)
1422 {
1423 desc.m_BiasEnabled = true;
1424 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00001425 auto biasTensorAndData = CreateConstTensor(inputs[2],
1426 biasTensorInfo,
1427 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001428 layer = m_Network->AddFullyConnectedLayer(desc,
1429 filterTensorAndData.first,
1430 biasTensorAndData.first,
1431 layerName.c_str());
1432 }
1433 else
1434 {
1435 layer = m_Network->AddFullyConnectedLayer(desc,
1436 filterTensorAndData.first,
1437 layerName.c_str());
1438 }
1439 BOOST_ASSERT(layer != nullptr);
1440
1441 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1442 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1443
1444 // register the input connection slot for the layer
1445 // only the tensors for the inputs are relevant, exclude the const tensors
1446 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1447 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1448
1449 // we need to add the activation layer and fortunately we don't need to care about the data layout
1450 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1451 options->fused_activation_function);
1452 // register the output connection slots for the layer, connections are made after all layers have been created
1453 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1454 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1455}
1456
Sadik Armagan58f39192018-09-17 14:14:39 +01001457armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1458 unsigned int outputSlot,
1459 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001460{
1461 ActivationDescriptor activationDesc;
1462 std::string layerName = prevLayer->GetName();
1463
1464 switch(activationType)
1465 {
1466 case tflite::ActivationFunctionType_NONE:
1467 {
1468 // this is a no-op: return previous layer
1469 return prevLayer;
1470 }
1471 case tflite::ActivationFunctionType_RELU:
1472 {
1473 activationDesc.m_Function = ActivationFunction::ReLu;
1474 layerName += ":RELU";
1475 break;
1476 }
1477 case tflite::ActivationFunctionType_RELU6:
1478 {
1479 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1480 activationDesc.m_A = 6.0f;
1481 activationDesc.m_B = 0.0f;
1482 layerName += ":RELU6";
1483 break;
1484 }
1485 case tflite::ActivationFunctionType_TANH:
1486 {
1487 activationDesc.m_Function = ActivationFunction::TanH;
1488 activationDesc.m_A = 1.0f;
1489 activationDesc.m_B = 1.0f;
1490 layerName += ":TANH";
1491 break;
1492 }
1493
1494 // I only put these here as a reminder what others we could support
1495 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1496 case tflite::ActivationFunctionType_SIGN_BIT:
1497 default:
1498 {
1499 throw ParseException(
1500 boost::str(
1501 boost::format("TfLite parser doesn't suppport fused activation: "
1502 "%1%/%2% %3% ") %
1503 activationType %
1504 tflite::EnumNameActivationFunctionType(activationType) %
1505 CHECK_LOCATION().AsString()));
1506
1507 }
1508 }
1509
1510 IConnectableLayer* activationLayer =
1511 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1512
1513 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1514 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1515 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1516 return activationLayer;
1517}
1518
1519TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1520{
1521 if (fileName == nullptr)
1522 {
1523 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1524 CHECK_LOCATION().AsString()));
1525 }
1526 boost::system::error_code errorCode;
1527 boost::filesystem::path pathToFile(fileName);
1528 if (!boost::filesystem::exists(pathToFile, errorCode))
1529 {
1530 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1531 fileName %
1532 errorCode %
1533 CHECK_LOCATION().AsString()));
1534 }
1535 std::ifstream file(fileName, std::ios::binary);
1536 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1537 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1538 fileContent.size());
1539}
1540
1541TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1542{
1543 if (binaryContent == nullptr)
1544 {
1545 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1546 CHECK_LOCATION().AsString()));
1547 }
1548 flatbuffers::Verifier verifier(binaryContent, len);
1549 if (verifier.VerifyBuffer<tflite::Model>() == false)
1550 {
1551 throw ParseException(
1552 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1553 "flatbuffers format. size:%1% %2%") %
1554 len %
1555 CHECK_LOCATION().AsString()));
1556 }
1557 return tflite::UnPackModel(binaryContent);
1558}
1559
1560TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1561 size_t subgraphIndex,
1562 size_t operatorIndex)
1563{
1564 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1565
1566 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1567 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1568
1569 size_t inputCount = operatorPtr->inputs.size();
1570 TensorRawPtrVector result(inputCount);
1571 for (size_t i=0; i<inputCount; ++i)
1572 {
1573 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1574 result[i] = subGraphPtr->tensors[inputId].get();
1575 }
1576 return result;
1577}
1578
1579TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1580 size_t subgraphIndex,
1581 size_t operatorIndex)
1582{
1583 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1584
1585 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1586 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1587
1588 size_t outputCount = operatorPtr->outputs.size();
1589 TensorRawPtrVector result(outputCount);
1590 for (size_t i=0; i<outputCount; ++i)
1591 {
1592 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1593 CHECK_TENSOR(model, subgraphIndex, outputId);
1594 result[i] = subGraphPtr->tensors[outputId].get();
1595 }
1596 return result;
1597}
1598
1599TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1600 size_t subgraphIndex)
1601{
1602 CHECK_SUBGRAPH(model, subgraphIndex);
1603 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1604
1605 size_t inputCount = subGraphPtr->inputs.size();
1606 TensorIdRawPtrVector result(inputCount);
1607 for (size_t i=0; i<inputCount; ++i)
1608 {
1609 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1610 CHECK_TENSOR(model, subgraphIndex, inputId);
1611 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1612 }
1613 return result;
1614}
1615
1616TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1617 size_t subgraphIndex)
1618{
1619 CHECK_SUBGRAPH(model, subgraphIndex);
1620 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1621
1622 size_t outputCount = subGraphPtr->outputs.size();
1623 TensorIdRawPtrVector result(outputCount);
1624 for (size_t i=0; i<outputCount; ++i)
1625 {
1626 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1627 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1628 }
1629 return result;
1630}
1631
1632std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1633 size_t subgraphIndex,
1634 size_t operatorIndex)
1635{
1636 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1637 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1638 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1639 return operatorPtr->inputs;
1640}
1641
1642std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1643 size_t subgraphIndex,
1644 size_t operatorIndex)
1645{
1646 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1647 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1648 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1649 return operatorPtr->outputs;
1650}
1651
1652void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1653 size_t operatorIndex,
1654 IConnectableLayer* layer,
1655 const std::vector<unsigned int>& tensorIndexes)
1656{
1657 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1658 BOOST_ASSERT(layer != nullptr);
1659 if (tensorIndexes.size() != layer->GetNumInputSlots())
1660 {
1661 throw ParseException(
1662 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1663 " for subgraph:%3% operator index:%4% %5%") %
1664 tensorIndexes.size() %
1665 layer->GetNumInputSlots() %
1666 subgraphIndex %
1667 operatorIndex %
1668 CHECK_LOCATION().AsString()));
1669 }
1670
1671 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1672 {
1673 unsigned int tensorIndex = tensorIndexes[slotIndex];
1674 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1675 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1676 }
1677}
1678
1679void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1680 size_t operatorIndex,
1681 IConnectableLayer* layer,
1682 const std::vector<unsigned int>& tensorIndexes)
1683{
1684 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1685 BOOST_ASSERT(layer != nullptr);
1686 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1687 {
1688 throw ParseException(
1689 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1690 " for subgraph:%3% operator index:%4% %5%") %
1691 tensorIndexes.size() %
1692 layer->GetNumOutputSlots() %
1693 subgraphIndex %
1694 operatorIndex %
1695 CHECK_LOCATION().AsString()));
1696 }
1697
1698 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1699 {
1700 unsigned int tensorIndex = tensorIndexes[slotIndex];
1701 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1702 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1703 }
1704}
1705
1706void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1707{
1708 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1709
1710 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1711 for (auto const & tensorIdAndPtr : inputs)
1712 {
1713 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1714 IConnectableLayer* layer =
1715 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1716
1717 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1718 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1719
1720 RegisterOutputSlots(subgraphIndex,
1721 VIRTUAL_OPERATOR_ID,
1722 layer,
1723 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1724 }
1725}
1726
1727void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1728{
1729 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1730
1731 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1732 for (auto const & tensorIdAndPtr : outputs)
1733 {
1734 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1735 IConnectableLayer* layer =
1736 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1737
1738 RegisterInputSlots(subgraphIndex,
1739 VIRTUAL_OPERATOR_ID,
1740 layer,
1741 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1742 }
1743}
1744
1745// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1746TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1747{
1748 CHECK_BUFFER(model, bufferIndex);
1749 return model->buffers[bufferIndex].get();
1750}
1751
Matteo Martincigh747ef822018-12-18 09:26:39 +00001752template<typename T>
1753std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1754TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
1755 TfLiteParser::TensorRawPtr tensorPtr,
1756 armnn::TensorInfo& tensorInfo,
1757 armnn::Optional<armnn::PermutationVector&> permutationVector)
1758{
1759 auto constData = CreateConstTensorImpl<T>(bufferPtr,
1760 tensorPtr,
1761 tensorInfo,
1762 permutationVector);
1763 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
1764 return std::make_pair(constData.first, std::move(storage));
1765}
1766
telsoa01c577f2c2018-08-31 09:22:23 +01001767std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1768TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00001769 armnn::TensorInfo& tensorInfo,
1770 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01001771{
1772 CHECK_TENSOR_PTR(tensorPtr);
1773 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1774 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1775
1776 switch (tensorInfo.GetDataType())
1777 {
1778 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001779 return CreateConstTensorAndStoreData<float>(bufferPtr,
1780 tensorPtr,
1781 tensorInfo,
1782 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001783 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001784 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
1785 tensorPtr,
1786 tensorInfo,
1787 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001788 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001789 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
1790 tensorPtr,
1791 tensorInfo,
1792 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001793 default:
1794 {
1795 std::stringstream errString;
1796 errString << "Unexpected datatype when creating const tensor: "
1797 << armnn::GetDataTypeName(tensorInfo.GetDataType())
1798 << " shape:" << tensorInfo.GetShape()
1799 << CHECK_LOCATION().AsString();
1800 throw ParseException(errString.str());
1801 }
1802 }
1803}
1804
1805BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
1806 const std::string& name) const
1807{
1808 CHECK_SUBGRAPH(m_Model, subgraphId);
1809 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1810 for (auto const & input : inputs)
1811 {
1812 if (input.second->name == name)
1813 {
1814 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
1815 return std::make_pair(bindingId, ToTensorInfo(input.second));
1816 }
1817 }
1818
1819 std::stringstream bindings;
1820 for (auto const & input : inputs)
1821 {
1822 bindings << "'" << input.second->name << "' ";
1823 }
1824
1825 throw ParseException(
1826 boost::str(
1827 boost::format("No input binding found for subgraph:%1% and name:%2%. "
1828 "Possible inputs are: [%3%] %4%") %
1829 subgraphId %
1830 name %
1831 bindings.str() %
1832 CHECK_LOCATION().AsString()));
1833}
1834
1835BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
1836 const std::string& name) const
1837{
1838 CHECK_SUBGRAPH(m_Model, subgraphId);
1839 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1840 for (auto const & output : outputs)
1841 {
1842 if (output.second->name == name)
1843 {
1844 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
1845 return std::make_pair(bindingId, ToTensorInfo(output.second));
1846 }
1847 }
1848
1849 std::stringstream bindings;
1850 for (auto const & output : outputs)
1851 {
1852 bindings << "'" << output.second->name << "' ";
1853 }
1854
1855 throw ParseException(
1856 boost::str(
1857 boost::format("No output binding found for subgraph:%1% and name:%2%. "
1858 "Possible outputs are: [%3%] %4%") %
1859 subgraphId %
1860 name %
1861 bindings.str() %
1862 CHECK_LOCATION().AsString()));
1863}
1864
1865size_t TfLiteParser::GetSubgraphCount() const
1866{
1867 return m_Model->subgraphs.size();
1868}
1869
1870std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
1871{
1872 CHECK_SUBGRAPH(m_Model, subgraphId);
1873 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1874 std::vector<std::string> result;
1875 result.reserve(inputs.size());
1876 for (auto const & input : inputs)
1877 {
1878 result.push_back(input.second->name);
1879 }
1880 return result;
1881}
1882
1883std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
1884{
1885 CHECK_SUBGRAPH(m_Model, subgraphId);
1886 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1887 std::vector<std::string> result;
1888 result.reserve(outputs.size());
1889 for (auto const & output : outputs)
1890 {
1891 result.push_back(output.second->name);
1892 }
1893 return result;
1894}
1895
1896ITfLiteParser* ITfLiteParser::CreateRaw()
1897{
1898 return new TfLiteParser();
1899}
1900
1901ITfLiteParserPtr ITfLiteParser::Create()
1902{
1903 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
1904}
1905
1906void ITfLiteParser::Destroy(ITfLiteParser* parser)
1907{
1908 delete parser;
1909}
1910
1911TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
1912: m_FloatData(std::move(data))
1913, m_Uint8Data(nullptr)
1914, m_Int32Data(nullptr)
1915{
1916}
1917
1918TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
1919: m_FloatData(nullptr)
1920, m_Uint8Data(std::move(data))
1921, m_Int32Data(nullptr)
1922{
1923}
1924
1925TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
1926: m_FloatData(nullptr)
1927, m_Uint8Data(nullptr)
1928, m_Int32Data(std::move(data))
1929{
1930}
1931
1932} // armnnTfLiteParser