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