blob: e3306b317fe6b690f28069b53afdc8c593b2a04d [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{
36const PermutationVector NHWCToArmNN = { 0, 2, 3, 1 };
37const PermutationVector ArmNNToNHWC = { 0, 3, 1, 2 };
38
jimfly01c25411c2018-11-14 17:47:22 +000039IConnectableLayer* SwizzleIn(INetwork& network,
40 IConnectableLayer* layer,
41 unsigned int inputSlotIndex,
42 const TensorInfo & inputInfo)
43{
44 BOOST_ASSERT(layer != nullptr);
45 // Add swizzle layer
46 std::stringstream name;
47 name << "swizzle_for-" << layer->GetName() << ":in" << inputSlotIndex;
48 IConnectableLayer* const swizzleLayer = network.AddPermuteLayer(NHWCToArmNN, name.str().c_str());
49 // Set swizzled output shape
50 const TensorInfo swizzleOutInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
51 swizzleLayer->GetOutputSlot(0).SetTensorInfo(swizzleOutInfo);
52 // Connect the swizzle layer to the actual layer
53 swizzleLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(inputSlotIndex));
54
55 return swizzleLayer;
56}
57
58IConnectableLayer* DeswizzleOut(INetwork& network,
59 IConnectableLayer* layer,
60 unsigned int outputSlotIndex,
61 const TensorInfo & outputInfo)
62{
63 BOOST_ASSERT(layer != nullptr);
64 // Add deswizzle layer
65 std::stringstream name;
66 name << "deswizzle_for-" << layer->GetName() << ":out" << outputSlotIndex;
67 IConnectableLayer* const deswizzleLayer = network.AddPermuteLayer(ArmNNToNHWC, name.str().c_str());
68 // Set deswizzled output shape
69 deswizzleLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
70 // Set original layer output shape
71 const TensorInfo deswizzleOutInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
72 layer->GetOutputSlot(outputSlotIndex).SetTensorInfo(deswizzleOutInfo);
73 // Connect the actual layer to the deswizzle layer
74 layer->GetOutputSlot(outputSlotIndex).Connect(deswizzleLayer->GetInputSlot(0));
75
76 return deswizzleLayer;
77}
78
telsoa01c577f2c2018-08-31 09:22:23 +010079const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
80
81void CheckSubgraph(const TfLiteParser::ModelPtr & model,
82 size_t subgraphIndex,
83 const CheckLocation & location)
84{
85 if (model.get() == nullptr)
86 {
87 throw ParseException(
88 boost::str(
89 boost::format("%1% was called with invalid (null) model. "
90 "Possible reason is that the model is not yet loaded and Unpack(ed). "
91 "subgraph:%2% at %3%") %
92 location.m_Function %
93 subgraphIndex %
94 location.FileLine()));
95 }
96 else if (subgraphIndex >= model->subgraphs.size())
97 {
98 throw ParseException(
99 boost::str(
100 boost::format("%1% was called with an invalid subgraph index. "
101 "subgraph:%2% at %3%") %
102 location.m_Function %
103 subgraphIndex %
104 location.FileLine()));
105 }
106}
107
108#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
109 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
110
111void CheckModel(const TfLiteParser::ModelPtr & model,
112 size_t subgraphIndex,
113 size_t operatorIndex,
114 const CheckLocation & location)
115{
116 if (model.get() == nullptr)
117 {
118 throw ParseException(
119 boost::str(
120 boost::format("%1% was called with invalid (null) model. "
121 "Possible reason is that the model is not yet loaded and Unpack(ed). "
122 "subgraph:%2% operator:%3% at %4%") %
123 location.m_Function %
124 subgraphIndex %
125 operatorIndex %
126 location.FileLine()));
127 }
128 else if (subgraphIndex >= model->subgraphs.size())
129 {
130 throw ParseException(
131 boost::str(
132 boost::format("%1% was called with an invalid subgraph index. "
133 "subgraph:%2% operator:%3% at %4%") %
134 location.m_Function %
135 subgraphIndex %
136 operatorIndex %
137 location.FileLine()));
138 }
139 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
140 operatorIndex != VIRTUAL_OPERATOR_ID)
141 {
142 throw ParseException(
143 boost::str(
144 boost::format("%1% was called with an invalid operator index. "
145 "subgraph:%2% operator:%3% at %4%") %
146 location.m_Function %
147 subgraphIndex %
148 operatorIndex %
149 location.FileLine()));
150 }
151}
152
153#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
154 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
155
156void CheckTensor(const TfLiteParser::ModelPtr & model,
157 size_t subgraphIndex,
158 size_t tensorIndex,
159 const CheckLocation & location)
160{
161 // not checking model, because I assume CHECK_MODEL already run
162 // and checked that. An assert would do.
163 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
164
165 // also subgraph index should be checked by CHECK_MODEL so
166 // I only add an assert here
167 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
168
169 // the tensor index is the only one to check here
170 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
171 {
172 throw ParseException(
173 boost::str(
174 boost::format("%1% was called with an invalid tensor index. "
175 "subgraph:%2% tensor:%3% at %4%") %
176 location.m_Function %
177 subgraphIndex %
178 tensorIndex %
179 location.FileLine()));
180 }
181}
182
183#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
184 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
185
186void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
187 const CheckLocation & location)
188{
189 if (rawPtr == nullptr)
190 {
191 throw ParseException(
192 boost::str(
193 boost::format("%1% was called with a null tensor pointer. "
194 "at %2%") %
195 location.m_Function %
196 location.FileLine()));
197
198 }
199}
200
201#define CHECK_TENSOR_PTR(TENSOR_PTR) \
202 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
203
204void CheckBuffer(const TfLiteParser::ModelPtr & model,
205 size_t bufferIndex,
206 const CheckLocation & location)
207{
208 if (model.get() == nullptr)
209 {
210 throw ParseException(
211 boost::str(
212 boost::format("%1% was called with invalid (null) model. "
213 "Possible reason is that the model is not yet loaded and Unpack(ed). "
214 "buffer:%2% at %3%") %
215 location.m_Function %
216 bufferIndex %
217 location.FileLine()));
218 }
219 else if (bufferIndex >= model->buffers.size())
220 {
221 throw ParseException(
222 boost::str(
223 boost::format("%1% was called with an invalid buffer index. "
224 "buffer index:%2% at %3%") %
225 location.m_Function %
226 bufferIndex %
227 location.FileLine()));
228 }
229 else if (model->buffers[bufferIndex].get() == nullptr)
230 {
231 throw ParseException(
232 boost::str(
233 boost::format("The buffer #%1% is null. %3%") %
234 bufferIndex %
235 location.AsString()));
236 }
237}
238
239#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
240 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
241
242void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
243 const armnn::TensorInfo & tensorInfo,
244 uint32_t bufferId,
245 const CheckLocation & location)
246{
247 if (bufferPtr == nullptr)
248 {
249 throw ParseException(
250 boost::str(
251 boost::format("BufferPtr is null for buffer:%1%. %2%") %
252 bufferId %
253 location.AsString()));
254 }
255 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
256 tensorInfo.GetNumBytes() > bufferPtr->data.size())
257 {
258 std::stringstream ss;
259 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
260 << "For tensor: " << tensorInfo.GetShape()
261 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
262 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
263 throw ParseException(ss.str());
264 }
265}
266
267#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
268 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
269
270bool IsActivationSupported(tflite::ActivationFunctionType activationType)
271{
272 switch(activationType)
273 {
274 case tflite::ActivationFunctionType_NONE:
275 case tflite::ActivationFunctionType_RELU:
276 case tflite::ActivationFunctionType_RELU6:
277 case tflite::ActivationFunctionType_TANH:
278 {
279 return true;
280 }
281 default:
282 {
283 return false;
284 }
285 }
286}
287
288#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
289 do { \
290 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
291 { \
292 throw ParseException( \
293 boost::str( \
294 boost::format("TfLite parser doesn't suppport fused activation: " \
295 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
296 OPTION->fused_activation_function % \
297 tflite::EnumNameActivationFunctionType(\
298 OPTION->fused_activation_function) % \
299 __func__ % \
300 SUBGRAPH_INDEX % \
301 OPERATOR_INDEX % \
302 CHECK_LOCATION().FileLine())); \
303 } \
304 } while(false)
305
306
307std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
308{
309 std::vector<unsigned int> result;
310 result.reserve(in.size());
311 for (auto & i : in)
312 {
313 result.push_back(CHECKED_NON_NEGATIVE(i));
314 }
315 return result;
316}
317
318void CalcPadding(uint32_t inputSize,
319 uint32_t filterSize,
320 uint32_t stride,
321 uint32_t& paddingFront,
322 uint32_t& paddingBack,
323 tflite::Padding padding)
324{
325 paddingFront = 0;
326 paddingBack = 0;
327 if (padding == tflite::Padding_SAME)
328 {
329 uint32_t outputSize = (inputSize + stride - 1) / stride;
330 uint32_t temp = (outputSize - 1) * stride + filterSize;
331 if (temp > inputSize)
332 {
333 paddingFront = (temp - inputSize) / 2;
334 paddingBack = (temp - inputSize) - paddingFront;
335 }
336 }
337}
338
339armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
340{
341 armnn::DataType type;
342 CHECK_TENSOR_PTR(tensorPtr);
343
344 switch (tensorPtr->type)
345 {
346 case tflite::TensorType_UINT8:
347 type = armnn::DataType::QuantisedAsymm8;
348 break;
349 case tflite::TensorType_FLOAT32:
350 type = armnn::DataType::Float32;
351 break;
352 case tflite::TensorType_INT32:
353 type = armnn::DataType::Signed32;
354 break;
355
356 default:
357 {
358 CheckLocation location = CHECK_LOCATION();
359 throw ParseException(
360 boost::str(
361 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
362 tensorPtr->type %
363 tflite::EnumNameTensorType(tensorPtr->type) %
364 tensorPtr->name %
365 location.AsString()));
366 }
367 }
368
369 float quantizationScale = 0.0f;
370 int32_t quantizationOffset = 0;
371
372 if (tensorPtr->quantization.get())
373 {
374 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
375 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
376
377 if (tensorPtr->quantization->scale.size() == 1)
378 {
379 quantizationScale = tensorPtr->quantization->scale[0];
380 }
381 if (tensorPtr->quantization->zero_point.size() == 1)
382 {
383 // NOTE: we lose precision here when converting from 64 bit to 32
384 // but this is what we support at the monent in ArmNN
385 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
386 }
387 }
388
389 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
390
391 // two statements (on purpose) for easier debugging:
392 armnn::TensorInfo result(static_cast<unsigned int>(tensorPtr->shape.size()),
393 dimensions.data(),
394 type,
395 quantizationScale,
396 quantizationOffset);
397 return result;
398}
399
400template<typename T>
401std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
402CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
403 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000404 armnn::TensorInfo& tensorInfo,
405 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100406{
407 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
408 BOOST_ASSERT_MSG(bufferPtr != nullptr,
409 boost::str(
410 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
411
412 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000413
414 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
415 {
416 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000417 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
418 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000419 }
420 else
421 {
422 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
423 }
424
telsoa01c577f2c2018-08-31 09:22:23 +0100425 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
426}
427
telsoa01c577f2c2018-08-31 09:22:23 +0100428armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
429{
430 // generate the binding id by shifting the tensor id by 8 bit
431 // and add the subgraph id, which allows 256 subgraphs
432 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
433}
434
435} // <anonymous>
436
437TfLiteParser::TfLiteParser()
438: m_Network(nullptr, nullptr)
439, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
440{
441 // register supported operators
442 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
Sadik Armagan479045b2018-10-01 11:51:37 +0100443 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
telsoa01c577f2c2018-08-31 09:22:23 +0100444 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
445 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Sadik Armagan8853c1f2018-10-22 09:04:18 +0100446 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100447 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
Sadik Armagan58f39192018-09-17 14:14:39 +0100448 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
449 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
Sadikb94967b2018-09-19 15:30:00 +0100450 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
Sadik Armagan479045b2018-10-01 11:51:37 +0100451 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
452 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -0200453 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Bruno Goncalvesf803f782018-12-18 13:40:30 -0200454 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
Bruno Goncalves2235cee2018-12-19 12:51:45 -0200455 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
telsoa01c577f2c2018-08-31 09:22:23 +0100456}
457
458void TfLiteParser::ResetParser()
459{
460 m_Network = armnn::INetworkPtr(nullptr, nullptr);
461 m_Model = nullptr;
462 m_SubgraphConnections.clear();
463}
464
465INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
466{
467 ResetParser();
468 m_Model = LoadModelFromFile(graphFile);
469 return CreateNetworkFromModel();
470}
471
472INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
473{
474 ResetParser();
475 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
476 return CreateNetworkFromModel();
477}
478
479INetworkPtr TfLiteParser::CreateNetworkFromModel()
480{
481 m_Network = INetwork::Create();
482 BOOST_ASSERT(m_Model.get() != nullptr);
483
484 bool failedToCreate = false;
485 std::stringstream errors;
486
487 if (m_Model->subgraphs.size() != 1)
488 {
489 throw ParseException(
490 boost::str(
491 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
492 m_Model->subgraphs.size() %
493 CHECK_LOCATION().AsString()));
494 }
495
496 size_t subgraphIndex = 0;
497 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
498 {
499 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
500
501 size_t operatorIndex = 0;
502 for (OperatorPtr const & op : subgraph->operators)
503 {
504 try
505 {
506 if (op->custom_options.size() > 0)
507 {
508 throw ParseException(
509 boost::str(
510 boost::format("Custom options for op: %1% is not supported. "
511 "It has %2% bytes of custom options. %3%") %
512 op->opcode_index %
513 op->custom_options.size() %
514 CHECK_LOCATION().AsString()));
515 }
516
517 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
518 auto builtinCode = opCodePtr->builtin_code;
519
520 if (builtinCode > tflite::BuiltinOperator_MAX)
521 {
522 throw ParseException(
523 boost::str(
524 boost::format("Operator code %1% is out of range 0-%2%. "
525 "subgraph:%3% operator idx:%4%. %5%") %
526 builtinCode %
527 tflite::BuiltinOperator_MAX %
528 subgraphIndex %
529 operatorIndex %
530 CHECK_LOCATION().AsString()));
531 }
532
533 // lookup and call the parser function
534 auto & parserFunction = m_ParserFunctions[builtinCode];
535 (this->*parserFunction)(subgraphIndex, operatorIndex);
536 }
537 catch (const ParseException& e)
538 {
539 failedToCreate = true;
540 std::stringstream errorString;
541
542 errorString << "Failed to parse operator #" << operatorIndex
543 << " within subgraph #" << subgraphIndex
544 << " error: " << e.what();
545 BOOST_LOG_TRIVIAL(error) << errorString.str();
546
547 errors << errorString.str() << "\n";
548 }
549 ++operatorIndex;
550 }
551
552 SetupInputLayers(subgraphIndex);
553 SetupOutputLayers(subgraphIndex);
554
555 ++subgraphIndex;
556 }
557
558 if (failedToCreate)
559 {
560 // we can skip everything and let the outer exception handler deal with the error
561 throw ParseException(errors.str());
562 }
563
564 // establish the connections from the layer outputs to the inputs of the subsequent layers
565 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
566 {
567 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
568 {
569 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
570 {
571 for (size_t inputSlotIdx = 0;
572 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
573 ++inputSlotIdx)
574 {
575 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
576 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
577 }
578 }
579 }
580 }
581
582 return std::move(m_Network);
583}
584
585void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
586 size_t tensorIndex,
587 armnn::IOutputSlot* slot)
588{
589 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
590 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
591 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
592
593 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
594
595 // assuming there is only one producer for that tensor
596 if (tensorSlots.outputSlot != nullptr)
597 {
598 throw ParseException(boost::str(
599 boost::format("Another layer has already registered itself as the producer of "
600 "subgraph:%1% tensor:%2% %3%") %
601 subgraphIndex %
602 tensorIndex %
603 CHECK_LOCATION().AsString()));
604 }
605
606 tensorSlots.outputSlot = slot;
607}
608
609void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
610 size_t tensorIndex,
611 armnn::IInputSlot* slot)
612{
613 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
614 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
615 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
616
617 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
618 tensorSlots.inputSlots.push_back(slot);
619}
620
621void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
622{
623 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
624 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
625 //
626 auto opcodeIndex = operatorPtr->opcode_index;
627 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
628
629 throw ParseException(
630 boost::str(
631 boost::format("Operator not supported. "
632 "subgraph:%1% operator:%2% "
633 "opcode_index:%3% opcode:%4% / %5% %6%") %
634 subgraphIndex %
635 operatorIndex %
636 opcodeIndex %
637 opcode %
638 tflite::EnumNameBuiltinOperator(opcode) %
639 CHECK_LOCATION().AsString()));
640}
641
telsoa01c577f2c2018-08-31 09:22:23 +0100642void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
643{
644 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
645
646 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
647 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
648
649 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
650
651 Convolution2dDescriptor desc;
652 desc.m_BiasEnabled = false;
653 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
654 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000655 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100656
657 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
658 CHECK_VALID_SIZE(inputs.size(), 2, 3);
659
660 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
661 CHECK_VALID_SIZE(outputs.size(), 1);
662
663 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
664 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
665
666 // assuming input is NHWC
667 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
668 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
669
670 // assuming the filter is OHWI : Output, H, W, Input
671 // which is essentially the same as NHWC
672 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
673 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
674
675 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
676 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
677
Matteo Martincigh747ef822018-12-18 09:26:39 +0000678 auto filterTensorAndData = CreateConstTensor(inputs[1],
679 filterTensorInfo,
680 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100681 armnn::IConnectableLayer* layer;
682
683 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
684
685 if (inputs.size() == 3)
686 {
687 desc.m_BiasEnabled = true;
688 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000689 auto biasTensorAndData = CreateConstTensor(inputs[2],
690 biasTensorInfo,
691 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100692 layer = m_Network->AddConvolution2dLayer(desc,
693 filterTensorAndData.first,
694 biasTensorAndData.first,
695 layerName.c_str());
696 }
697 else
698 {
699 layer = m_Network->AddConvolution2dLayer(desc,
700 filterTensorAndData.first,
701 layerName.c_str());
702 }
703
704 BOOST_ASSERT(layer != nullptr);
705
telsoa01c577f2c2018-08-31 09:22:23 +0100706 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000707 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100708
709 // register the input connection slots for the layer, connections are made after all layers have been created
710 // only the tensors for the inputs are relevant, exclude the const tensors
711 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000712 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100713
jimfly01c25411c2018-11-14 17:47:22 +0000714 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100715 // register the output connection slots for the layer, connections are made after all layers have been created
716 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
717 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
718}
719
720void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
721{
722 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
723
724 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
725 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
726
727 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
728
729 DepthwiseConvolution2dDescriptor desc;
730 desc.m_BiasEnabled = false;
731 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
732 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000733 desc.m_DataLayout = armnn::DataLayout::NHWC;
telsoa01c577f2c2018-08-31 09:22:23 +0100734 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
735 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
736
737 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
738 CHECK_VALID_SIZE(inputs.size(), 2, 3);
739 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
740 CHECK_VALID_SIZE(outputs.size(), 1);
741
742 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
743 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
744
Matteo Martincigh747ef822018-12-18 09:26:39 +0000745 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100746 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
747 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000748
749 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100750 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
751 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
752
Matteo Martincigh747ef822018-12-18 09:26:39 +0000753 // Reshape weights as [ H, W, I, M ]
754 filterTensorInfo.SetShape({ filterHeight,
755 filterWidth,
756 inputTensorInfo.GetShape()[3],
757 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
758
759 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
760 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
761
telsoa01c577f2c2018-08-31 09:22:23 +0100762 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
763 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
764
Matteo Martincigh747ef822018-12-18 09:26:39 +0000765 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100766 armnn::IConnectableLayer* layer;
767 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
768
769 if (inputs.size() == 3)
770 {
771 desc.m_BiasEnabled = true;
772 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000773 auto biasTensorAndData = CreateConstTensor(inputs[2],
774 biasTensorInfo,
775 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100776 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
777 filterTensorAndData.first,
778 biasTensorAndData.first,
779 layerName.c_str());
780 }
781 else
782 {
783 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
784 filterTensorAndData.first,
785 layerName.c_str());
786 }
787 BOOST_ASSERT(layer != nullptr);
788
telsoa01c577f2c2018-08-31 09:22:23 +0100789 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
jimfly01c25411c2018-11-14 17:47:22 +0000790 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100791
792 // register the input connection slots for the layer, connections are made after all layers have been created
793 // only the tensors for the inputs are relevant, exclude the const tensors
794 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000795 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100796
jimfly01c25411c2018-11-14 17:47:22 +0000797 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100798 // register the output connection slots for the layer, connections are made after all layers have been created
799 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
800 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
801}
802
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100803void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
804{
805 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
806}
807
808void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
809{
810 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
811}
812
813void TfLiteParser::ParsePool(size_t subgraphIndex,
814 size_t operatorIndex,
815 PoolingAlgorithm algorithm)
816{
817 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
818
819 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
820 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
821
822 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
823
824 std::string layerName;
825
826 switch (algorithm)
827 {
828 case PoolingAlgorithm::Average:
829 layerName =
830 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
831 break;
832 case PoolingAlgorithm::Max:
833 layerName =
834 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
835 break;
836 default:
837 BOOST_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
838 }
839
840 Pooling2dDescriptor desc;
841
842 desc.m_PoolType = algorithm;
843 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
844 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
845 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
846 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
847 desc.m_PaddingMethod = PaddingMethod::Exclude;
848 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +0000849 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100850
851 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
852 CHECK_VALID_SIZE(inputs.size(), 1);
853 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
854
855 // assuming input is NHWC
856 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
857 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
858
859 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
860 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
861
862 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
863 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100864
865 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
866
867 BOOST_ASSERT(layer != nullptr);
868
jimfly01c25411c2018-11-14 17:47:22 +0000869 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
870 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100871
872 // register the input connection slots for the layer, connections are made after all layers have been created
873 // only the tensors for the inputs are relevant, exclude the const tensors
874 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000875 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100876
jimfly01c25411c2018-11-14 17:47:22 +0000877 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +0100878 // register the output connection slots for the layer, connections are made after all layers have been created
879 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
880 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
881}
882
telsoa01c577f2c2018-08-31 09:22:23 +0100883void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
884{
885 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
886 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
887 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
888
889 SoftmaxDescriptor desc;
890 desc.m_Beta = options->beta;
891
892 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
893 CHECK_VALID_SIZE(inputs.size(), 1);
894 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
895 CHECK_VALID_SIZE(outputs.size(), 1);
896
897 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
898 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
899
900 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
901 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
902
903 // register the input connection slots for the layer, connections are made after all layers have been created
904 // only the tensors for the inputs are relevant, exclude the const tensors
905 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
906 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
907
908 // register the output connection slots for the layer, connections are made after all layers have been created
909 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
910 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
911}
912
913armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
914 const armnn::TensorInfo & inputTensorInfo)
915{
916 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
917 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
918 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
919
920 if (inputTensorInfo.GetNumDimensions() > 4)
921 {
922 std::stringstream ss;
923 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
924 << " shape:" << inputTensorInfo.GetShape() << " "
925 << CHECK_LOCATION().AsString();
926 throw ParseException(ss.str());
927 }
928
929 if (squeezeDims.empty())
930 {
931 squeezeDims.assign(dimensionSequence,
932 dimensionSequence+inputTensorInfo.GetNumDimensions());
933 }
934
935 std::vector<uint32_t> outputDims;
936 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
937 {
938 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
939 auto currentDimension = inputTensorInfo.GetShape()[i];
940 if (skipSqueeze || currentDimension != 1)
941 {
942 outputDims.push_back(currentDimension);
943 }
944 }
945
946 if (outputDims.size() > 4)
947 {
948 std::stringstream ss;
949 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
950 << " shape:" << inputTensorInfo.GetShape() << " "
951 << CHECK_LOCATION().AsString();
952 throw ParseException(ss.str());
953 }
954
955 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
956 outputDims.data());
957
958 // we need to preserve the tensor type and the quantization data as well
959 TensorInfo outTensorInfo = inputTensorInfo;
960 outTensorInfo.SetShape(outShape);
961
962 return outTensorInfo;
963}
964
965void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
966{
967 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
968
969 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
970 CHECK_VALID_SIZE(inputs.size(), 1);
971
972 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
973 CHECK_VALID_SIZE(outputs.size(), 1);
974
975 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
976 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
977
978 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
979 armnn::TensorInfo outputTensorInfo =
980 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
981 inputTensorInfo);
982
983 ReshapeDescriptor reshapeDesc;
984 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
985
986 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
987 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
988 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
989
990 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
991 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
992
993 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
994 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
995}
996
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -0200997void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
998{
999 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1000
1001 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1002 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1003
1004 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1005 CHECK_VALID_SIZE(inputs.size(), 2);
1006
1007 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1008 CHECK_VALID_SIZE(outputs.size(), 1);
1009
1010 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1011 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
1012
1013 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1014 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1015
1016 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1017 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1018
1019 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1020
1021 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1022 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1023}
1024
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001025void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1026{
1027 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1028
1029 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1030 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1031
1032 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1033 CHECK_VALID_SIZE(inputs.size(), 2);
1034
1035 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1036 CHECK_VALID_SIZE(outputs.size(), 1);
1037
1038 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1039 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
1040
1041 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1042 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1043
1044 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1045 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1046
1047 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1048
1049 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1050 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1051}
1052
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001053void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1054{
1055 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1056
1057 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1058
1059 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1060 CHECK_VALID_SIZE(outputs.size(), 1);
1061
1062 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1063 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1064
1065 armnn::MeanDescriptor desc;
1066 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1067 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1068 desc.m_Axis = axis;
1069
1070 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1071 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1072
1073 desc.m_KeepDims =
1074 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1075 true : false;
1076
1077 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1078 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
1079
1080 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1081
1082 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1083 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1084
1085 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1086 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1087}
1088
Sadik Armagan58f39192018-09-17 14:14:39 +01001089void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1090{
1091 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1092
1093 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1094 boost::ignore_unused(operatorPtr);
1095
1096 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1097 CHECK_VALID_SIZE(inputs.size(), 1);
1098
1099 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1100 CHECK_VALID_SIZE(outputs.size(), 1);
1101
1102 auto layerName = str(boost::format("Activation:RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1103 ActivationDescriptor activationDesc;
1104 activationDesc.m_Function = ActivationFunction::ReLu;
1105 IConnectableLayer* const layer =
1106 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1107
1108 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1109 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1110
1111 // register the input connection slots for the layer, connections are made after all layers have been created
1112 // only the tensors for the inputs are relevant, exclude the const tensors
1113 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1114 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1115
1116 // register the output connection slots for the layer, connections are made after all layers have been created
1117 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1118 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1119}
1120
1121void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1122{
1123 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1124
1125 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1126 boost::ignore_unused(operatorPtr);
1127
1128 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1129 CHECK_VALID_SIZE(inputs.size(), 1);
1130
1131 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1132 CHECK_VALID_SIZE(outputs.size(), 1);
1133
1134 auto layerName = str(boost::format("Activation:RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1135 ActivationDescriptor activationDesc;
1136 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1137 activationDesc.m_A = 6.0f;
1138 activationDesc.m_B = 0.0f;
1139 IConnectableLayer* const layer =
1140 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1141
1142 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1143 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1144
1145 // register the input connection slots for the layer, connections are made after all layers have been created
1146 // only the tensors for the inputs are relevant, exclude the const tensors
1147 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1148 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1149
1150 // register the output connection slots for the layer, connections are made after all layers have been created
1151 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1152 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1153}
1154
Sadikb94967b2018-09-19 15:30:00 +01001155armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
1156 const std::vector<int32_t> & targetDimsIn)
1157{
1158 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
1159 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
1160
1161 if (stretchDim != targetDimsIn.end())
1162 {
1163 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
1164 {
1165 throw ParseException(
1166 boost::str(
1167 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
1168 }
1169
1170 auto targetNumElements =
1171 boost::numeric_cast<unsigned int>(
1172 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
1173
1174 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
1175 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
1176 }
1177
1178 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
1179
1180 TensorInfo reshapeInfo = inputTensorInfo;
1181 reshapeInfo.SetShape(outputShape);
1182
1183 return reshapeInfo;
1184}
1185
1186void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
1187{
1188 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1189
1190 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01001191
1192 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1193 CHECK_VALID_SIZE(outputs.size(), 1);
1194
1195 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1196 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
1197
1198 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00001199 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
1200 armnn::TensorInfo reshapeOutputTensorInfo =
Sadikb94967b2018-09-19 15:30:00 +01001201 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, options->new_shape);
1202
kevmay0171972a82018-12-17 14:28:03 +00001203 // Check for valid input size and that reshape parameters equal output shape
1204 if (inputs.size() > 1 && (options->new_shape != outputs[0]->shape))
1205 {
1206 std::stringstream ss;
1207 ss << "New shape defined in reshape parameters "
1208 << reshapeOutputTensorInfo.GetShape()
1209 << " does not equal output shape "
1210 << actualOutputTensorInfo.GetShape()
1211 << ": "
1212 << CHECK_LOCATION().AsString();
1213 throw ParseException(ss.str());
1214 }
1215
Sadikb94967b2018-09-19 15:30:00 +01001216 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00001217 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01001218
1219 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
1220 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
kevmay0171972a82018-12-17 14:28:03 +00001221 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01001222
1223 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1224 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1225
1226 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1227 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1228}
1229
Sadik Armagan479045b2018-10-01 11:51:37 +01001230void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
1231{
1232 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1233
1234 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1235 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
1236
1237 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1238
1239 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1240 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1241 CHECK_VALID_SIZE(outputs.size(), 1);
1242
1243 unsigned int numInputs = static_cast<unsigned int>(inputs.size());
1244 unsigned int numConcatView = numInputs;
1245
1246 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), MaxNumOfTensorDimensions);
1247 std::vector<unsigned int>mergeDimSizes(MaxNumOfTensorDimensions, 0u);
1248
1249 unsigned int mergeDim = 0;
1250
1251 // This concatDim indicates the data format: 3 is the NHWC, 1 is the NCHW.
1252 // axis could also be negative numbers. Negative axis are interpreted as counting from the end of the rank,
1253 // i.e., axis + rank(values)-th dimension.
1254 int32_t inputRank = static_cast<int32_t>(ToTensorInfo(inputs[0]).GetNumDimensions());
1255 const unsigned int concatDimInput = static_cast<unsigned int>((inputRank + options->axis) % inputRank);
1256
1257 // ArmNN supports concatenation along the channel dimension for data formats NHWC and NCHW.
1258 if (concatDimInput == 0 || concatDimInput == 2)
1259 {
1260 throw ParseException(
1261 boost::str(
1262 boost::format(
1263 "Dimension %1% for concatenation is not supported by Armnn. "
1264 "Node %2%")
1265 % concatDimInput
1266 % CHECK_LOCATION().AsString()));
1267 }
1268
1269 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1270 {
1271 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1272
1273 // process the input tensor info
1274 armnnUtils::ProcessConcatInputTensorInfo(inputTensorInfo, concatDescriptor,
1275 concatDimInput, viewIndex, mergeDimSizes, mergeDim);
1276 }
1277
1278 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
1279 IConnectableLayer* layer = m_Network->AddMergerLayer(concatDescriptor, layerName.c_str());
1280
1281 BOOST_ASSERT(layer != nullptr);
1282
1283 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1284 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1285 if (concatDimInput == 3)
1286 {
1287 // Adding Fused Activation Layer after this moment....
1288 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
1289 {
1290 // add permute layers to swizzle the inputs
1291 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
1292 IConnectableLayer* const swizzleLayer = SwizzleIn(*m_Network, layer, viewIndex, inputTensorInfo);
1293
1294 BOOST_ASSERT(swizzleLayer != nullptr);
1295
1296 // register the input connection slots for the layer
1297 // only the tensors for the inputs are relevant, exclude the const tensors
1298 RegisterInputSlots(subgraphIndex, operatorIndex, swizzleLayer, {inputTensorIndexes[viewIndex]});
1299 }
1300
1301 // add permute layer to deswizzle the output
1302 IConnectableLayer* const deswizzleLayer = DeswizzleOut(*m_Network, layer, 0, outputTensorInfo);
1303
1304 // add fused activation layer after the trailing swizzle layer
1305 layer = AddFusedActivationLayer(deswizzleLayer, 0, options->fused_activation_function);
1306 }
1307 else
1308 {
1309 // set the layer output tensor info
1310 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1311
1312 // register the input connection slots for the layer, connections are made after all layers have been created
1313 // only the tensors for the inputs are relevant, exclude the const tensors
1314 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
1315 }
1316
1317 // register the output connection slots for the layer, connections are made after all layers have been created
1318 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1319 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1320}
1321
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001322void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
1323{
1324 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1325
1326 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1327 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
1328
1329 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1330
1331 FullyConnectedDescriptor desc;
1332 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01001333 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001334
1335 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1336 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1337 CHECK_VALID_SIZE(outputs.size(), 1);
1338
1339 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1340
1341 // Fully Connected Layer accepts two dimensional weights input
1342 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
1343 if (weightsDimension != 2)
1344 {
1345 throw ParseException(
1346 boost::str(
1347 boost::format(
1348 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
1349 "Node %2%")
1350 % weightsDimension
1351 % CHECK_LOCATION().AsString()));
1352 }
1353
Matteo Martincigh747ef822018-12-18 09:26:39 +00001354 auto filterTensorAndData = CreateConstTensor(inputs[1],
1355 filterTensorInfo,
1356 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001357 armnn::IConnectableLayer* layer;
1358 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
1359
1360 if (inputs.size() == 3)
1361 {
1362 desc.m_BiasEnabled = true;
1363 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00001364 auto biasTensorAndData = CreateConstTensor(inputs[2],
1365 biasTensorInfo,
1366 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01001367 layer = m_Network->AddFullyConnectedLayer(desc,
1368 filterTensorAndData.first,
1369 biasTensorAndData.first,
1370 layerName.c_str());
1371 }
1372 else
1373 {
1374 layer = m_Network->AddFullyConnectedLayer(desc,
1375 filterTensorAndData.first,
1376 layerName.c_str());
1377 }
1378 BOOST_ASSERT(layer != nullptr);
1379
1380 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1381 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1382
1383 // register the input connection slot for the layer
1384 // only the tensors for the inputs are relevant, exclude the const tensors
1385 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1386 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1387
1388 // we need to add the activation layer and fortunately we don't need to care about the data layout
1389 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
1390 options->fused_activation_function);
1391 // register the output connection slots for the layer, connections are made after all layers have been created
1392 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1393 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
1394}
1395
Sadik Armagan58f39192018-09-17 14:14:39 +01001396armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1397 unsigned int outputSlot,
1398 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001399{
1400 ActivationDescriptor activationDesc;
1401 std::string layerName = prevLayer->GetName();
1402
1403 switch(activationType)
1404 {
1405 case tflite::ActivationFunctionType_NONE:
1406 {
1407 // this is a no-op: return previous layer
1408 return prevLayer;
1409 }
1410 case tflite::ActivationFunctionType_RELU:
1411 {
1412 activationDesc.m_Function = ActivationFunction::ReLu;
1413 layerName += ":RELU";
1414 break;
1415 }
1416 case tflite::ActivationFunctionType_RELU6:
1417 {
1418 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1419 activationDesc.m_A = 6.0f;
1420 activationDesc.m_B = 0.0f;
1421 layerName += ":RELU6";
1422 break;
1423 }
1424 case tflite::ActivationFunctionType_TANH:
1425 {
1426 activationDesc.m_Function = ActivationFunction::TanH;
1427 activationDesc.m_A = 1.0f;
1428 activationDesc.m_B = 1.0f;
1429 layerName += ":TANH";
1430 break;
1431 }
1432
1433 // I only put these here as a reminder what others we could support
1434 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1435 case tflite::ActivationFunctionType_SIGN_BIT:
1436 default:
1437 {
1438 throw ParseException(
1439 boost::str(
1440 boost::format("TfLite parser doesn't suppport fused activation: "
1441 "%1%/%2% %3% ") %
1442 activationType %
1443 tflite::EnumNameActivationFunctionType(activationType) %
1444 CHECK_LOCATION().AsString()));
1445
1446 }
1447 }
1448
1449 IConnectableLayer* activationLayer =
1450 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1451
1452 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1453 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1454 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1455 return activationLayer;
1456}
1457
1458TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1459{
1460 if (fileName == nullptr)
1461 {
1462 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1463 CHECK_LOCATION().AsString()));
1464 }
1465 boost::system::error_code errorCode;
1466 boost::filesystem::path pathToFile(fileName);
1467 if (!boost::filesystem::exists(pathToFile, errorCode))
1468 {
1469 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1470 fileName %
1471 errorCode %
1472 CHECK_LOCATION().AsString()));
1473 }
1474 std::ifstream file(fileName, std::ios::binary);
1475 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1476 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1477 fileContent.size());
1478}
1479
1480TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1481{
1482 if (binaryContent == nullptr)
1483 {
1484 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1485 CHECK_LOCATION().AsString()));
1486 }
1487 flatbuffers::Verifier verifier(binaryContent, len);
1488 if (verifier.VerifyBuffer<tflite::Model>() == false)
1489 {
1490 throw ParseException(
1491 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1492 "flatbuffers format. size:%1% %2%") %
1493 len %
1494 CHECK_LOCATION().AsString()));
1495 }
1496 return tflite::UnPackModel(binaryContent);
1497}
1498
1499TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1500 size_t subgraphIndex,
1501 size_t operatorIndex)
1502{
1503 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1504
1505 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1506 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1507
1508 size_t inputCount = operatorPtr->inputs.size();
1509 TensorRawPtrVector result(inputCount);
1510 for (size_t i=0; i<inputCount; ++i)
1511 {
1512 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1513 result[i] = subGraphPtr->tensors[inputId].get();
1514 }
1515 return result;
1516}
1517
1518TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1519 size_t subgraphIndex,
1520 size_t operatorIndex)
1521{
1522 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1523
1524 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1525 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1526
1527 size_t outputCount = operatorPtr->outputs.size();
1528 TensorRawPtrVector result(outputCount);
1529 for (size_t i=0; i<outputCount; ++i)
1530 {
1531 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1532 CHECK_TENSOR(model, subgraphIndex, outputId);
1533 result[i] = subGraphPtr->tensors[outputId].get();
1534 }
1535 return result;
1536}
1537
1538TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1539 size_t subgraphIndex)
1540{
1541 CHECK_SUBGRAPH(model, subgraphIndex);
1542 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1543
1544 size_t inputCount = subGraphPtr->inputs.size();
1545 TensorIdRawPtrVector result(inputCount);
1546 for (size_t i=0; i<inputCount; ++i)
1547 {
1548 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1549 CHECK_TENSOR(model, subgraphIndex, inputId);
1550 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1551 }
1552 return result;
1553}
1554
1555TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1556 size_t subgraphIndex)
1557{
1558 CHECK_SUBGRAPH(model, subgraphIndex);
1559 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1560
1561 size_t outputCount = subGraphPtr->outputs.size();
1562 TensorIdRawPtrVector result(outputCount);
1563 for (size_t i=0; i<outputCount; ++i)
1564 {
1565 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1566 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1567 }
1568 return result;
1569}
1570
1571std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1572 size_t subgraphIndex,
1573 size_t operatorIndex)
1574{
1575 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1576 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1577 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1578 return operatorPtr->inputs;
1579}
1580
1581std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1582 size_t subgraphIndex,
1583 size_t operatorIndex)
1584{
1585 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1586 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1587 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1588 return operatorPtr->outputs;
1589}
1590
1591void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1592 size_t operatorIndex,
1593 IConnectableLayer* layer,
1594 const std::vector<unsigned int>& tensorIndexes)
1595{
1596 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1597 BOOST_ASSERT(layer != nullptr);
1598 if (tensorIndexes.size() != layer->GetNumInputSlots())
1599 {
1600 throw ParseException(
1601 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1602 " for subgraph:%3% operator index:%4% %5%") %
1603 tensorIndexes.size() %
1604 layer->GetNumInputSlots() %
1605 subgraphIndex %
1606 operatorIndex %
1607 CHECK_LOCATION().AsString()));
1608 }
1609
1610 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1611 {
1612 unsigned int tensorIndex = tensorIndexes[slotIndex];
1613 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1614 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1615 }
1616}
1617
1618void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1619 size_t operatorIndex,
1620 IConnectableLayer* layer,
1621 const std::vector<unsigned int>& tensorIndexes)
1622{
1623 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1624 BOOST_ASSERT(layer != nullptr);
1625 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1626 {
1627 throw ParseException(
1628 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1629 " for subgraph:%3% operator index:%4% %5%") %
1630 tensorIndexes.size() %
1631 layer->GetNumOutputSlots() %
1632 subgraphIndex %
1633 operatorIndex %
1634 CHECK_LOCATION().AsString()));
1635 }
1636
1637 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1638 {
1639 unsigned int tensorIndex = tensorIndexes[slotIndex];
1640 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1641 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1642 }
1643}
1644
1645void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1646{
1647 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1648
1649 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1650 for (auto const & tensorIdAndPtr : inputs)
1651 {
1652 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1653 IConnectableLayer* layer =
1654 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1655
1656 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1657 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1658
1659 RegisterOutputSlots(subgraphIndex,
1660 VIRTUAL_OPERATOR_ID,
1661 layer,
1662 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1663 }
1664}
1665
1666void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1667{
1668 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1669
1670 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1671 for (auto const & tensorIdAndPtr : outputs)
1672 {
1673 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1674 IConnectableLayer* layer =
1675 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1676
1677 RegisterInputSlots(subgraphIndex,
1678 VIRTUAL_OPERATOR_ID,
1679 layer,
1680 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1681 }
1682}
1683
1684// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1685TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1686{
1687 CHECK_BUFFER(model, bufferIndex);
1688 return model->buffers[bufferIndex].get();
1689}
1690
Matteo Martincigh747ef822018-12-18 09:26:39 +00001691template<typename T>
1692std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1693TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
1694 TfLiteParser::TensorRawPtr tensorPtr,
1695 armnn::TensorInfo& tensorInfo,
1696 armnn::Optional<armnn::PermutationVector&> permutationVector)
1697{
1698 auto constData = CreateConstTensorImpl<T>(bufferPtr,
1699 tensorPtr,
1700 tensorInfo,
1701 permutationVector);
1702 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
1703 return std::make_pair(constData.first, std::move(storage));
1704}
1705
telsoa01c577f2c2018-08-31 09:22:23 +01001706std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1707TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00001708 armnn::TensorInfo& tensorInfo,
1709 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01001710{
1711 CHECK_TENSOR_PTR(tensorPtr);
1712 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1713 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1714
1715 switch (tensorInfo.GetDataType())
1716 {
1717 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001718 return CreateConstTensorAndStoreData<float>(bufferPtr,
1719 tensorPtr,
1720 tensorInfo,
1721 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001722 case armnn::DataType::QuantisedAsymm8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001723 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
1724 tensorPtr,
1725 tensorInfo,
1726 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001727 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00001728 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
1729 tensorPtr,
1730 tensorInfo,
1731 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01001732 default:
1733 {
1734 std::stringstream errString;
1735 errString << "Unexpected datatype when creating const tensor: "
1736 << armnn::GetDataTypeName(tensorInfo.GetDataType())
1737 << " shape:" << tensorInfo.GetShape()
1738 << CHECK_LOCATION().AsString();
1739 throw ParseException(errString.str());
1740 }
1741 }
1742}
1743
1744BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
1745 const std::string& name) const
1746{
1747 CHECK_SUBGRAPH(m_Model, subgraphId);
1748 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1749 for (auto const & input : inputs)
1750 {
1751 if (input.second->name == name)
1752 {
1753 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
1754 return std::make_pair(bindingId, ToTensorInfo(input.second));
1755 }
1756 }
1757
1758 std::stringstream bindings;
1759 for (auto const & input : inputs)
1760 {
1761 bindings << "'" << input.second->name << "' ";
1762 }
1763
1764 throw ParseException(
1765 boost::str(
1766 boost::format("No input binding found for subgraph:%1% and name:%2%. "
1767 "Possible inputs are: [%3%] %4%") %
1768 subgraphId %
1769 name %
1770 bindings.str() %
1771 CHECK_LOCATION().AsString()));
1772}
1773
1774BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
1775 const std::string& name) const
1776{
1777 CHECK_SUBGRAPH(m_Model, subgraphId);
1778 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1779 for (auto const & output : outputs)
1780 {
1781 if (output.second->name == name)
1782 {
1783 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
1784 return std::make_pair(bindingId, ToTensorInfo(output.second));
1785 }
1786 }
1787
1788 std::stringstream bindings;
1789 for (auto const & output : outputs)
1790 {
1791 bindings << "'" << output.second->name << "' ";
1792 }
1793
1794 throw ParseException(
1795 boost::str(
1796 boost::format("No output binding found for subgraph:%1% and name:%2%. "
1797 "Possible outputs are: [%3%] %4%") %
1798 subgraphId %
1799 name %
1800 bindings.str() %
1801 CHECK_LOCATION().AsString()));
1802}
1803
1804size_t TfLiteParser::GetSubgraphCount() const
1805{
1806 return m_Model->subgraphs.size();
1807}
1808
1809std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
1810{
1811 CHECK_SUBGRAPH(m_Model, subgraphId);
1812 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1813 std::vector<std::string> result;
1814 result.reserve(inputs.size());
1815 for (auto const & input : inputs)
1816 {
1817 result.push_back(input.second->name);
1818 }
1819 return result;
1820}
1821
1822std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
1823{
1824 CHECK_SUBGRAPH(m_Model, subgraphId);
1825 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1826 std::vector<std::string> result;
1827 result.reserve(outputs.size());
1828 for (auto const & output : outputs)
1829 {
1830 result.push_back(output.second->name);
1831 }
1832 return result;
1833}
1834
1835ITfLiteParser* ITfLiteParser::CreateRaw()
1836{
1837 return new TfLiteParser();
1838}
1839
1840ITfLiteParserPtr ITfLiteParser::Create()
1841{
1842 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
1843}
1844
1845void ITfLiteParser::Destroy(ITfLiteParser* parser)
1846{
1847 delete parser;
1848}
1849
1850TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
1851: m_FloatData(std::move(data))
1852, m_Uint8Data(nullptr)
1853, m_Int32Data(nullptr)
1854{
1855}
1856
1857TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
1858: m_FloatData(nullptr)
1859, m_Uint8Data(std::move(data))
1860, m_Int32Data(nullptr)
1861{
1862}
1863
1864TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
1865: m_FloatData(nullptr)
1866, m_Uint8Data(nullptr)
1867, m_Int32Data(std::move(data))
1868{
1869}
1870
1871} // armnnTfLiteParser