blob: 0aad048970fd7dfeabc0b3e50756665e9d380a2e [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
Mike Kellyc5789ca2020-07-06 19:24:15 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
Matteo Martincighe011d202019-11-28 11:35:47 +00005
telsoa01c577f2c2018-08-31 09:22:23 +01006#include "TfLiteParser.hpp"
7
Sadik Armagand109a4d2020-07-28 10:42:13 +01008#include <armnn/BackendOptions.hpp>
Matthew Bentham39ef3e52020-01-20 10:09:09 +00009#include <armnn/Descriptors.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010010#include <armnn/Exceptions.hpp>
Derek Lamberti08446972019-11-26 16:38:31 +000011#include <armnn/Logging.hpp>
James Conroy05102392020-06-24 15:39:55 +010012#include <armnn/Tensor.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010013#include <armnn/TypesUtils.hpp>
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010014#include <armnn/utility/Assert.hpp>
Jan Eilers8eb25602020-03-09 12:13:48 +000015#include <armnn/utility/IgnoreUnused.hpp>
Derek Lambertif0176992020-04-28 13:37:49 +010016#include <armnn/utility/NumericCast.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010017
18// armnnUtils:
Matteo Martincighe011d202019-11-28 11:35:47 +000019#include <armnnUtils/Permute.hpp>
Francis Murtagh532a29d2020-06-29 11:50:01 +010020#include <Filesystem.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000021
Sadik Armagan479045b2018-10-01 11:51:37 +010022#include <ParserHelper.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010023#include <VerificationHelpers.hpp>
24
25// The generated code based on the Tf Lite schema:
26#include <schema_generated.h>
27
Matteo Martincighe011d202019-11-28 11:35:47 +000028#include <flatbuffers/flexbuffers.h>
29
telsoa01c577f2c2018-08-31 09:22:23 +010030#include <boost/format.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010031
32#include <fstream>
33#include <algorithm>
34#include <limits>
Sadikb94967b2018-09-19 15:30:00 +010035#include <numeric>
Derek Lambertic9e52792020-03-11 11:42:26 +000036#include <sstream>
37
38#define ARMNN_THROW_PARSE_EXCEPTION(msg) \
39 { \
40 throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
41 << ": " \
42 << CHECK_LOCATION().AsString()).str()); \
43 }
telsoa01c577f2c2018-08-31 09:22:23 +010044
45using namespace armnn;
46using armnn::CheckLocation;
47namespace armnnTfLiteParser
48{
49namespace
50{
jimfly01c25411c2018-11-14 17:47:22 +000051
telsoa01c577f2c2018-08-31 09:22:23 +010052const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
53
54void CheckSubgraph(const TfLiteParser::ModelPtr & model,
55 size_t subgraphIndex,
56 const CheckLocation & location)
57{
58 if (model.get() == nullptr)
59 {
60 throw ParseException(
61 boost::str(
62 boost::format("%1% was called with invalid (null) model. "
63 "Possible reason is that the model is not yet loaded and Unpack(ed). "
64 "subgraph:%2% at %3%") %
65 location.m_Function %
66 subgraphIndex %
67 location.FileLine()));
68 }
69 else if (subgraphIndex >= model->subgraphs.size())
70 {
71 throw ParseException(
72 boost::str(
73 boost::format("%1% was called with an invalid subgraph index. "
74 "subgraph:%2% at %3%") %
75 location.m_Function %
76 subgraphIndex %
77 location.FileLine()));
78 }
79}
80
81#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
82 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
83
84void CheckModel(const TfLiteParser::ModelPtr & model,
85 size_t subgraphIndex,
86 size_t operatorIndex,
87 const CheckLocation & location)
88{
89 if (model.get() == nullptr)
90 {
91 throw ParseException(
92 boost::str(
93 boost::format("%1% was called with invalid (null) model. "
94 "Possible reason is that the model is not yet loaded and Unpack(ed). "
95 "subgraph:%2% operator:%3% at %4%") %
96 location.m_Function %
97 subgraphIndex %
98 operatorIndex %
99 location.FileLine()));
100 }
101 else if (subgraphIndex >= model->subgraphs.size())
102 {
103 throw ParseException(
104 boost::str(
105 boost::format("%1% was called with an invalid subgraph index. "
106 "subgraph:%2% operator:%3% at %4%") %
107 location.m_Function %
108 subgraphIndex %
109 operatorIndex %
110 location.FileLine()));
111 }
112 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
113 operatorIndex != VIRTUAL_OPERATOR_ID)
114 {
115 throw ParseException(
116 boost::str(
117 boost::format("%1% was called with an invalid operator index. "
118 "subgraph:%2% operator:%3% at %4%") %
119 location.m_Function %
120 subgraphIndex %
121 operatorIndex %
122 location.FileLine()));
123 }
124}
125
126#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
127 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
128
129void CheckTensor(const TfLiteParser::ModelPtr & model,
130 size_t subgraphIndex,
131 size_t tensorIndex,
132 const CheckLocation & location)
133{
134 // not checking model, because I assume CHECK_MODEL already run
135 // and checked that. An assert would do.
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100136 ARMNN_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
telsoa01c577f2c2018-08-31 09:22:23 +0100137
138 // also subgraph index should be checked by CHECK_MODEL so
139 // I only add an assert here
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100140 ARMNN_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
telsoa01c577f2c2018-08-31 09:22:23 +0100141
142 // the tensor index is the only one to check here
143 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
144 {
145 throw ParseException(
146 boost::str(
147 boost::format("%1% was called with an invalid tensor index. "
148 "subgraph:%2% tensor:%3% at %4%") %
149 location.m_Function %
150 subgraphIndex %
151 tensorIndex %
152 location.FileLine()));
153 }
154}
155
156#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
157 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
158
159void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
160 const CheckLocation & location)
161{
162 if (rawPtr == nullptr)
163 {
164 throw ParseException(
165 boost::str(
166 boost::format("%1% was called with a null tensor pointer. "
167 "at %2%") %
168 location.m_Function %
169 location.FileLine()));
170
171 }
172}
173
174#define CHECK_TENSOR_PTR(TENSOR_PTR) \
175 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
176
177void CheckBuffer(const TfLiteParser::ModelPtr & model,
178 size_t bufferIndex,
179 const CheckLocation & location)
180{
181 if (model.get() == nullptr)
182 {
183 throw ParseException(
184 boost::str(
185 boost::format("%1% was called with invalid (null) model. "
186 "Possible reason is that the model is not yet loaded and Unpack(ed). "
187 "buffer:%2% at %3%") %
188 location.m_Function %
189 bufferIndex %
190 location.FileLine()));
191 }
192 else if (bufferIndex >= model->buffers.size())
193 {
194 throw ParseException(
195 boost::str(
196 boost::format("%1% was called with an invalid buffer index. "
197 "buffer index:%2% at %3%") %
198 location.m_Function %
199 bufferIndex %
200 location.FileLine()));
201 }
202 else if (model->buffers[bufferIndex].get() == nullptr)
203 {
204 throw ParseException(
205 boost::str(
206 boost::format("The buffer #%1% is null. %3%") %
207 bufferIndex %
208 location.AsString()));
209 }
210}
211
212#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
213 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
214
215void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
216 const armnn::TensorInfo & tensorInfo,
217 uint32_t bufferId,
218 const CheckLocation & location)
219{
220 if (bufferPtr == nullptr)
221 {
222 throw ParseException(
223 boost::str(
224 boost::format("BufferPtr is null for buffer:%1%. %2%") %
225 bufferId %
226 location.AsString()));
227 }
228 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
229 tensorInfo.GetNumBytes() > bufferPtr->data.size())
230 {
231 std::stringstream ss;
232 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
233 << "For tensor: " << tensorInfo.GetShape()
234 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
235 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
236 throw ParseException(ss.str());
237 }
238}
239
240#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
241 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
242
243bool IsActivationSupported(tflite::ActivationFunctionType activationType)
244{
245 switch(activationType)
246 {
247 case tflite::ActivationFunctionType_NONE:
248 case tflite::ActivationFunctionType_RELU:
249 case tflite::ActivationFunctionType_RELU6:
250 case tflite::ActivationFunctionType_TANH:
251 {
252 return true;
253 }
254 default:
255 {
256 return false;
257 }
258 }
259}
260
261#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
262 do { \
263 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
264 { \
265 throw ParseException( \
266 boost::str( \
267 boost::format("TfLite parser doesn't suppport fused activation: " \
268 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
269 OPTION->fused_activation_function % \
270 tflite::EnumNameActivationFunctionType(\
271 OPTION->fused_activation_function) % \
272 __func__ % \
273 SUBGRAPH_INDEX % \
274 OPERATOR_INDEX % \
275 CHECK_LOCATION().FileLine())); \
276 } \
277 } while(false)
278
279
280std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
281{
282 std::vector<unsigned int> result;
283 result.reserve(in.size());
284 for (auto & i : in)
285 {
286 result.push_back(CHECKED_NON_NEGATIVE(i));
287 }
288 return result;
289}
290
291void CalcPadding(uint32_t inputSize,
292 uint32_t filterSize,
293 uint32_t stride,
Pablo Tellof0bd6832019-04-26 17:58:13 +0100294 uint32_t dilation,
telsoa01c577f2c2018-08-31 09:22:23 +0100295 uint32_t& paddingFront,
296 uint32_t& paddingBack,
297 tflite::Padding padding)
298{
299 paddingFront = 0;
300 paddingBack = 0;
301 if (padding == tflite::Padding_SAME)
302 {
303 uint32_t outputSize = (inputSize + stride - 1) / stride;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100304 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
305 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
telsoa01c577f2c2018-08-31 09:22:23 +0100306 if (temp > inputSize)
307 {
308 paddingFront = (temp - inputSize) / 2;
309 paddingBack = (temp - inputSize) - paddingFront;
310 }
311 }
312}
313
Sadik Armagand109a4d2020-07-28 10:42:13 +0100314armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
315 const std::vector<unsigned int>& shapes,
316 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3},
317 const bool outputTensor = false)
telsoa01c577f2c2018-08-31 09:22:23 +0100318{
319 armnn::DataType type;
320 CHECK_TENSOR_PTR(tensorPtr);
321
322 switch (tensorPtr->type)
323 {
324 case tflite::TensorType_UINT8:
Derek Lambertif90c56d2020-01-10 17:14:08 +0000325 type = armnn::DataType::QAsymmU8;
telsoa01c577f2c2018-08-31 09:22:23 +0100326 break;
327 case tflite::TensorType_FLOAT32:
328 type = armnn::DataType::Float32;
329 break;
Finn Williamsed66d142019-12-06 09:55:55 +0000330 case tflite::TensorType_INT8:
Keith Davis67e6c542020-02-19 10:08:33 +0000331 if (tensorPtr->quantization->zero_point.size() == 1)
Ryan OShea03181ff2020-02-07 17:22:22 +0000332 {
Keith Davis0c2eeac2020-02-11 16:51:50 +0000333 // Per-tensor
Ryan OShea03181ff2020-02-07 17:22:22 +0000334 type = armnn::DataType::QAsymmS8;
335 }
336 else
337 {
Keith Davis0c2eeac2020-02-11 16:51:50 +0000338 // Per-channel
Ryan OShea03181ff2020-02-07 17:22:22 +0000339 type = armnn::DataType::QSymmS8;
340 }
Finn Williamsed66d142019-12-06 09:55:55 +0000341 break;
342 case tflite::TensorType_INT16:
Derek Lambertif90c56d2020-01-10 17:14:08 +0000343 type = armnn::DataType::QSymmS16;
Finn Williamsed66d142019-12-06 09:55:55 +0000344 break;
telsoa01c577f2c2018-08-31 09:22:23 +0100345 case tflite::TensorType_INT32:
346 type = armnn::DataType::Signed32;
347 break;
Inki Daed4619e22020-09-10 15:33:54 +0900348 case tflite::TensorType_INT64:
349 type = armnn::DataType::Signed64;
350 break;
telsoa01c577f2c2018-08-31 09:22:23 +0100351 default:
352 {
353 CheckLocation location = CHECK_LOCATION();
354 throw ParseException(
355 boost::str(
356 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
357 tensorPtr->type %
358 tflite::EnumNameTensorType(tensorPtr->type) %
359 tensorPtr->name %
360 location.AsString()));
361 }
362 }
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100363 std::vector<unsigned int> safeShape = shapes;
Sadik Armagand109a4d2020-07-28 10:42:13 +0100364 bool isDynamic = false;
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100365 if (safeShape.size() == 0)
366 {
367 safeShape.push_back(1);
Sadik Armagand109a4d2020-07-28 10:42:13 +0100368 if (outputTensor)
369 {
370 isDynamic = true;
371 }
Narumol Prangnawarat4818d462019-04-17 11:22:38 +0100372 }
373
Keith Davisd305e1a2020-01-22 11:57:54 +0000374 float quantizationScale = 0.0f;
375 int32_t quantizationOffset = 0;
376
377 if (tensorPtr->quantization.get())
378 {
379 if (tensorPtr->quantization->scale.size() <= 1)
380 {
381 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
382 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
383
384 if (tensorPtr->quantization->scale.size() == 1)
385 {
386 quantizationScale = tensorPtr->quantization->scale[0];
387 }
388 if (tensorPtr->quantization->zero_point.size() == 1)
389 {
390 // NOTE: we lose precision here when converting from 64 bit to 32
Ryan OShea03181ff2020-02-07 17:22:22 +0000391 // but this is what we support at the moment in ArmNN
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100392 quantizationOffset = armnn::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
Keith Davisd305e1a2020-01-22 11:57:54 +0000393 }
394
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100395 TensorShape tensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()),
Sadik Armagand109a4d2020-07-28 10:42:13 +0100396 safeShape.data());
397 if (isDynamic)
398 {
399 tensorShape = TensorShape(1, false);
400 }
401 armnn::TensorInfo result(tensorShape,
402 type,
403 quantizationScale,
404 quantizationOffset);
Keith Davisd305e1a2020-01-22 11:57:54 +0000405 return result;
406 }
407 else
408 {
409 std::vector<float> quantizationScales;
410 std::vector<int32_t> quantizationOffsets;
411
412 // Scale
413 std::copy(tensorPtr->quantization->scale.begin(),
414 tensorPtr->quantization->scale.end(),
415 std::back_inserter(quantizationScales));
416
Keith Davis0c2eeac2020-02-11 16:51:50 +0000417 // QSymmS8 Per-axis
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100418 TensorShape tensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()),
Sadik Armagand109a4d2020-07-28 10:42:13 +0100419 safeShape.data());
420 if (isDynamic)
421 {
422 tensorShape = TensorShape(1, false);
423 }
424 armnn::TensorInfo result(tensorShape,
425 type,
426 quantizationScales,
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100427 dimensionMappings[armnn::numeric_cast<unsigned int>(
Sadik Armagand109a4d2020-07-28 10:42:13 +0100428 tensorPtr->quantization->quantized_dimension)]);
Keith Davisd305e1a2020-01-22 11:57:54 +0000429 return result;
430 }
431 }
432 else
433 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100434 TensorShape tensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()),
Sadik Armagand109a4d2020-07-28 10:42:13 +0100435 safeShape.data());
436 if (isDynamic)
437 {
438 tensorShape = TensorShape(1, false);
439 }
440 armnn::TensorInfo result(tensorShape,
Keith Davisd305e1a2020-01-22 11:57:54 +0000441 type,
442 quantizationScale,
443 quantizationOffset);
444 return result;
445 }
telsoa01c577f2c2018-08-31 09:22:23 +0100446}
447
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +0100448armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
Keith Davis0c2eeac2020-02-11 16:51:50 +0000449 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3})
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000450{
451 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
Keith Davis0c2eeac2020-02-11 16:51:50 +0000452 return ToTensorInfo(tensorPtr, dimensions, dimensionMappings);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +0000453}
454
Sadik Armagand109a4d2020-07-28 10:42:13 +0100455armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr,
456 const bool outputTensor)
457{
458 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
459 const armnn::PermutationVector& dimensionMappings = {0, 1, 2, 3};
460 return ToTensorInfo(tensorPtr, dimensions, dimensionMappings, outputTensor);
461}
462
telsoa01c577f2c2018-08-31 09:22:23 +0100463template<typename T>
464std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
465CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
466 TfLiteParser::TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +0000467 armnn::TensorInfo& tensorInfo,
468 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +0100469{
Jan Eilers8eb25602020-03-09 12:13:48 +0000470 IgnoreUnused(tensorPtr);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100471 ARMNN_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
472 ARMNN_ASSERT_MSG(bufferPtr != nullptr,
telsoa01c577f2c2018-08-31 09:22:23 +0100473 boost::str(
474 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
475
476 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000477
478 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
479 {
480 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
Matteo Martincighd5b9e642019-01-04 18:01:21 +0000481 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
482 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
Matteo Martincigh747ef822018-12-18 09:26:39 +0000483 }
484 else
485 {
486 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
487 }
488
telsoa01c577f2c2018-08-31 09:22:23 +0100489 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
490}
491
telsoa01c577f2c2018-08-31 09:22:23 +0100492armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
493{
494 // generate the binding id by shifting the tensor id by 8 bit
495 // and add the subgraph id, which allows 256 subgraphs
496 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
497}
498
Aron Virginas-Tar70672f62019-01-23 14:00:00 +0000499bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
500{
501 const unsigned int actualSize = actual.GetNumDimensions();
502 if (actualSize != expected.size())
503 {
504 return false;
505 }
506
507 for (unsigned int i = 0u; i < actualSize; i++)
508 {
509 if (expected[i] < 0 ||
510 actual[i] != static_cast<unsigned int>(expected[i]))
511 {
512 return false;
513 }
514 }
515
516 return true;
517}
518
James Conroy05102392020-06-24 15:39:55 +0100519void CheckMatchingQuantization(const TensorInfo& first,
520 const TensorInfo& second,
521 const std::string& descName,
522 std::string const& firstName,
523 std::string const& secondName)
524{
525 if (!first.IsQuantized() ||
526 !second.IsQuantized())
527 {
528 // Not a quantized type, ignore the validation
529 return;
530 }
531
532 DataType firstDataType = first.GetDataType();
533 DataType secondDataType = second.GetDataType();
534
535 if (firstDataType != secondDataType)
536 {
537 throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
538 " must be of the same quantized type, " +
539 firstName + " is " + GetDataTypeName(firstDataType) + ", " +
540 secondName + " is " + GetDataTypeName(secondDataType));
541 }
542
543 if (!first.IsTypeSpaceMatch(second))
544 {
545 throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
546 " must have the same quantization space, " +
547 firstName + " has offset " + std::to_string(first.GetQuantizationOffset()) +
548 " and scale " + std::to_string(first.GetQuantizationScale()) + ", " +
549 secondName + " has offset " + std::to_string(second.GetQuantizationOffset()) +
550 " and scale " + std::to_string(second.GetQuantizationScale()));
551 }
552}
553
telsoa01c577f2c2018-08-31 09:22:23 +0100554} // <anonymous>
555
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100556TfLiteParser::TfLiteParser(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
557: m_Options(options)
558, m_Network(nullptr, nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +0100559, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
560{
561 // register supported operators
Sadik Armagan66dedc72019-12-10 16:32:07 +0000562 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParser::ParseAdd;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000563 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
564 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParser::ParseBatchToSpaceND;
565 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParser::ParseConcatenation;
566 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000567 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParser::ParseCustomOperator;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000568 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
Finn Williamsed66d142019-12-06 09:55:55 +0000569 m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParser::ParseDequantize;
Derek Lambertif0176992020-04-28 13:37:49 +0100570 m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParser::ParseExp;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000571 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParser::ParseFullyConnected;
Jan Eilers2f746b32020-07-28 14:00:06 +0100572 m_ParserFunctions[tflite::BuiltinOperator_HARD_SWISH] = &TfLiteParser::ParseHardSwish;
Sadik Armagan12239e72020-05-27 11:06:17 +0100573 m_ParserFunctions[tflite::BuiltinOperator_LEAKY_RELU] = &TfLiteParser::ParseLeakyRelu;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000574 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParser::ParseLogistic;
575 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParser::ParseL2Normalization;
576 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParser::ParseMaxPool2D;
577 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParser::ParseMaximum;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000578 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParser::ParseMean;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000579 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParser::ParseMinimum;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000580 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParser::ParseMul;
Darshan Patel83fcf982020-05-26 22:22:42 +0530581 m_ParserFunctions[tflite::BuiltinOperator_NEG] = &TfLiteParser::ParseNeg;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000582 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParser::ParsePack;
583 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParser::ParsePad;
584 m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParser::ParseQuantize;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000585 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
586 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
587 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParser::ParseReshape;
588 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParser::ParseResizeBilinear;
589 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParser::ParseResizeNearestNeighbor;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000590 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParser::ParseSlice;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000591 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
592 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParser::ParseSpaceToBatchND;
Sadik Armagan66dedc72019-12-10 16:32:07 +0000593 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParser::ParseSplit;
Derek Lambertif0176992020-04-28 13:37:49 +0100594 m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParser::ParseSplitV;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000595 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
596 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice;
597 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub;
Sadik Armagana3b31f02019-12-05 09:08:53 +0000598 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH;
599 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose;
600 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv;
601 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParser::ParseUnpack;
Darshan Patel42b3d7d2020-05-25 22:30:07 +0530602 m_ParserFunctions[tflite::BuiltinOperator_DIV] = &TfLiteParser::ParseDiv;
Inki Daed4619e22020-09-10 15:33:54 +0900603 m_ParserFunctions[tflite::BuiltinOperator_ARG_MAX] = &TfLiteParser::ParseArgMax;
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100604 // register supported custom operators
605 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParser::ParseDetectionPostProcess;
telsoa01c577f2c2018-08-31 09:22:23 +0100606}
607
608void TfLiteParser::ResetParser()
609{
610 m_Network = armnn::INetworkPtr(nullptr, nullptr);
611 m_Model = nullptr;
612 m_SubgraphConnections.clear();
613}
614
615INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
616{
617 ResetParser();
618 m_Model = LoadModelFromFile(graphFile);
619 return CreateNetworkFromModel();
620}
621
622INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
623{
624 ResetParser();
625 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
626 return CreateNetworkFromModel();
627}
628
629INetworkPtr TfLiteParser::CreateNetworkFromModel()
630{
Sadik Armagand109a4d2020-07-28 10:42:13 +0100631
632 using NetworkOptions = std::vector<BackendOptions>;
633 NetworkOptions networkOptions = {};
634 if (m_Options && m_Options.value().m_InferAndValidate)
635 {
636 BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod",
637 {
638 { "InferAndValidate", true }
639 });
640
641 networkOptions.push_back(shapeInferenceMethodOption);
642 }
643
644 m_Network = INetwork::Create(networkOptions);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100645 ARMNN_ASSERT(m_Model.get() != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +0100646
telsoa01c577f2c2018-08-31 09:22:23 +0100647 if (m_Model->subgraphs.size() != 1)
648 {
649 throw ParseException(
650 boost::str(
651 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
652 m_Model->subgraphs.size() %
653 CHECK_LOCATION().AsString()));
654 }
655
656 size_t subgraphIndex = 0;
Colm Donelan6350d272020-06-09 16:56:25 +0100657 size_t operatorIndex = 0;
658 try
telsoa01c577f2c2018-08-31 09:22:23 +0100659 {
Colm Donelan6350d272020-06-09 16:56:25 +0100660 for (SubgraphPtr const& subgraph : m_Model->subgraphs)
telsoa01c577f2c2018-08-31 09:22:23 +0100661 {
Colm Donelan6350d272020-06-09 16:56:25 +0100662 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
663 for (OperatorPtr const& op : subgraph->operators)
telsoa01c577f2c2018-08-31 09:22:23 +0100664 {
Colm Donelan6350d272020-06-09 16:56:25 +0100665 auto const& opCodePtr = m_Model->operator_codes[op->opcode_index];
telsoa01c577f2c2018-08-31 09:22:23 +0100666 auto builtinCode = opCodePtr->builtin_code;
667
668 if (builtinCode > tflite::BuiltinOperator_MAX)
669 {
Colm Donelan6350d272020-06-09 16:56:25 +0100670 throw ParseException(boost::str(boost::format("Operator code %1% is out of range 0-%2%. "
671 "subgraph:%3% operator idx:%4%. %5%") %
672 builtinCode % tflite::BuiltinOperator_MAX % subgraphIndex %
673 operatorIndex % CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +0100674 }
675
676 // lookup and call the parser function
Colm Donelan6350d272020-06-09 16:56:25 +0100677 auto& parserFunction = m_ParserFunctions[builtinCode];
telsoa01c577f2c2018-08-31 09:22:23 +0100678 (this->*parserFunction)(subgraphIndex, operatorIndex);
Colm Donelan6350d272020-06-09 16:56:25 +0100679 ++operatorIndex;
telsoa01c577f2c2018-08-31 09:22:23 +0100680 }
telsoa01c577f2c2018-08-31 09:22:23 +0100681
Colm Donelan6350d272020-06-09 16:56:25 +0100682 SetupInputLayers(subgraphIndex);
683 SetupOutputLayers(subgraphIndex);
684 SetupConstantLayers(subgraphIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100685
Colm Donelan6350d272020-06-09 16:56:25 +0100686 ++subgraphIndex;
687 operatorIndex = 0;
telsoa01c577f2c2018-08-31 09:22:23 +0100688 }
telsoa01c577f2c2018-08-31 09:22:23 +0100689 }
Colm Donelan6350d272020-06-09 16:56:25 +0100690 catch (const ParseException& e)
telsoa01c577f2c2018-08-31 09:22:23 +0100691 {
Colm Donelan6350d272020-06-09 16:56:25 +0100692 std::stringstream errorString;
693 errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
694 << subgraphIndex << " error: " << e.what();
695 ARMNN_LOG(error) << errorString.str();
696 std::stringstream errors;
697 errors << errorString.str() << "\n";
telsoa01c577f2c2018-08-31 09:22:23 +0100698 throw ParseException(errors.str());
699 }
700
701 // establish the connections from the layer outputs to the inputs of the subsequent layers
Colm Donelan6350d272020-06-09 16:56:25 +0100702 for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
telsoa01c577f2c2018-08-31 09:22:23 +0100703 {
704 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
705 {
706 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
707 {
708 for (size_t inputSlotIdx = 0;
709 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
710 ++inputSlotIdx)
711 {
712 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
713 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
714 }
715 }
716 }
717 }
718
719 return std::move(m_Network);
720}
721
722void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
723 size_t tensorIndex,
724 armnn::IOutputSlot* slot)
725{
726 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100727 ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
728 ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100729
730 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
731
732 // assuming there is only one producer for that tensor
733 if (tensorSlots.outputSlot != nullptr)
734 {
735 throw ParseException(boost::str(
736 boost::format("Another layer has already registered itself as the producer of "
737 "subgraph:%1% tensor:%2% %3%") %
738 subgraphIndex %
739 tensorIndex %
740 CHECK_LOCATION().AsString()));
741 }
742
743 tensorSlots.outputSlot = slot;
744}
745
746void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
747 size_t tensorIndex,
748 armnn::IInputSlot* slot)
749{
750 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100751 ARMNN_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
752 ARMNN_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100753
754 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
755 tensorSlots.inputSlots.push_back(slot);
756}
757
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100758void TfLiteParser::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
759{
760 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
761
762 // NOTE: By default we presume the custom operator is not supported
763 auto customParserFunction = &TfLiteParser::ParseUnsupportedOperator;
764
765 // Identify custom code defined for custom operator
766 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
767 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
768
769 // Find parser function that correspondes to custom code (if any)
770 auto iterator = m_CustomParserFunctions.find(customCode);
771 if (iterator != m_CustomParserFunctions.end())
772 {
773 customParserFunction = iterator->second;
774 }
775
776 // Run parser function
777 (this->*customParserFunction)(subgraphIndex, operatorIndex);
778}
779
telsoa01c577f2c2018-08-31 09:22:23 +0100780void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
781{
782 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +0100783
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100784 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
785
786 auto opcodeIndex = operatorPtr->opcode_index;
787 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
788
789 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
790 {
791 // Do not add StandInLayer, throw ParseException instead
792 throw ParseException(
793 boost::str(
794 boost::format("Operator not supported. "
795 "subgraph:%1% operator:%2% "
796 "opcode_index:%3% opcode:%4% / %5% %6%") %
797 subgraphIndex %
798 operatorIndex %
799 opcodeIndex %
800 opcode %
801 tflite::EnumNameBuiltinOperator(opcode) %
802 CHECK_LOCATION().AsString()));
803 }
804
805 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
806 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
807
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100808 const unsigned int numInputs = armnn::numeric_cast<unsigned int>(inputs.size());
809 const unsigned int numOutputs = armnn::numeric_cast<unsigned int>(outputs.size());
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100810
811 StandInDescriptor descriptor(numInputs, numOutputs);
812 auto layerName = boost::str(boost::format("StandIn:%1%:%2%:%3%") % subgraphIndex % operatorIndex % opcode);
813
814 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
815 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +0100816 ARMNN_ASSERT(layer != nullptr);
817
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100818 for (unsigned int i = 0u; i < numOutputs; ++i)
819 {
Sadik Armagand109a4d2020-07-28 10:42:13 +0100820 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[i], true));
Aron Virginas-Tarc975f922019-10-23 17:38:17 +0100821 }
822
823 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
824 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
825
826 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
827 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
telsoa01c577f2c2018-08-31 09:22:23 +0100828}
829
telsoa01c577f2c2018-08-31 09:22:23 +0100830void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
831{
832 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
833
834 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
835 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
836
837 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
838
839 Convolution2dDescriptor desc;
840 desc.m_BiasEnabled = false;
841 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
842 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000843 desc.m_DataLayout = armnn::DataLayout::NHWC;
Pablo Tellof0bd6832019-04-26 17:58:13 +0100844 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
845 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000846
telsoa01c577f2c2018-08-31 09:22:23 +0100847 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
848 CHECK_VALID_SIZE(inputs.size(), 2, 3);
849
850 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
851 CHECK_VALID_SIZE(outputs.size(), 1);
852
853 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
854 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
855
856 // assuming input is NHWC
857 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
858 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
859
860 // assuming the filter is OHWI : Output, H, W, Input
861 // which is essentially the same as NHWC
862 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
863 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
864
Pablo Tellof0bd6832019-04-26 17:58:13 +0100865 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
866 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
867 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
868 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100869
Matteo Martincigh747ef822018-12-18 09:26:39 +0000870 auto filterTensorAndData = CreateConstTensor(inputs[1],
871 filterTensorInfo,
872 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100873 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100874
875 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
876
877 if (inputs.size() == 3)
878 {
879 desc.m_BiasEnabled = true;
880 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000881 auto biasTensorAndData = CreateConstTensor(inputs[2],
882 biasTensorInfo,
883 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100884 layer = m_Network->AddConvolution2dLayer(desc,
885 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100886 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100887 layerName.c_str());
888 }
889 else
890 {
891 layer = m_Network->AddConvolution2dLayer(desc,
892 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100893 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100894 layerName.c_str());
895 }
896
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100897 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +0100898
Sadik Armagand109a4d2020-07-28 10:42:13 +0100899 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
jimfly01c25411c2018-11-14 17:47:22 +0000900 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100901
902 // register the input connection slots for the layer, connections are made after all layers have been created
903 // only the tensors for the inputs are relevant, exclude the const tensors
904 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000905 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100906
jimfly01c25411c2018-11-14 17:47:22 +0000907 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100908 // 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
913void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
914{
915 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
916
917 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
918 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
919
920 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
921
922 DepthwiseConvolution2dDescriptor desc;
923 desc.m_BiasEnabled = false;
924 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
925 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
jimfly01c25411c2018-11-14 17:47:22 +0000926 desc.m_DataLayout = armnn::DataLayout::NHWC;
Matthew Jacksond6a9dee2019-07-22 13:53:24 +0100927 CHECKED_NON_NEGATIVE(options->depth_multiplier);
telsoa01c577f2c2018-08-31 09:22:23 +0100928
929 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
930 CHECK_VALID_SIZE(inputs.size(), 2, 3);
931 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
932 CHECK_VALID_SIZE(outputs.size(), 1);
Pablo Tellof0bd6832019-04-26 17:58:13 +0100933 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
934 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
Kevin May83add212019-03-26 11:39:19 +0000935
Keith Davis0c2eeac2020-02-11 16:51:50 +0000936 // Mappings from TensorflowLite filter tensors to the ArmNN filter tensors (ArmNN weights have to be [M, I, H, W])
937 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +0100938
telsoa01c577f2c2018-08-31 09:22:23 +0100939 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Keith Davis0c2eeac2020-02-11 16:51:50 +0000940 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1], permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +0100941
Matteo Martincigh747ef822018-12-18 09:26:39 +0000942 // Assuming input is NHWC
telsoa01c577f2c2018-08-31 09:22:23 +0100943 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
944 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
Matteo Martincigh747ef822018-12-18 09:26:39 +0000945
946 // TensorflowLite weights come in the format [1, H, W, I * M]
telsoa01c577f2c2018-08-31 09:22:23 +0100947 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
948 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
949
Matteo Martincigh747ef822018-12-18 09:26:39 +0000950 // Reshape weights as [ H, W, I, M ]
951 filterTensorInfo.SetShape({ filterHeight,
952 filterWidth,
953 inputTensorInfo.GetShape()[3],
954 filterTensorInfo.GetShape()[3] / inputTensorInfo.GetShape()[3] });
955
Pablo Tellof0bd6832019-04-26 17:58:13 +0100956 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
957 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
958 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
959 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
telsoa01c577f2c2018-08-31 09:22:23 +0100960
Matteo Martincigh747ef822018-12-18 09:26:39 +0000961 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, permutationVector);
Matthew Jackson74bf7da2019-08-16 16:51:42 +0100962 armnn::IConnectableLayer* layer = nullptr;
telsoa01c577f2c2018-08-31 09:22:23 +0100963 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
964
965 if (inputs.size() == 3)
966 {
967 desc.m_BiasEnabled = true;
968 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +0000969 auto biasTensorAndData = CreateConstTensor(inputs[2],
970 biasTensorInfo,
971 armnn::Optional<armnn::PermutationVector&>());
telsoa01c577f2c2018-08-31 09:22:23 +0100972 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
973 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100974 Optional<ConstTensor>(biasTensorAndData.first),
telsoa01c577f2c2018-08-31 09:22:23 +0100975 layerName.c_str());
976 }
977 else
978 {
979 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
980 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +0100981 EmptyOptional(),
telsoa01c577f2c2018-08-31 09:22:23 +0100982 layerName.c_str());
983 }
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100984 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +0100985
Sadik Armagand109a4d2020-07-28 10:42:13 +0100986 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
jimfly01c25411c2018-11-14 17:47:22 +0000987 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
telsoa01c577f2c2018-08-31 09:22:23 +0100988
989 // register the input connection slots for the layer, connections are made after all layers have been created
990 // only the tensors for the inputs are relevant, exclude the const tensors
991 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +0000992 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
telsoa01c577f2c2018-08-31 09:22:23 +0100993
jimfly01c25411c2018-11-14 17:47:22 +0000994 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100995 // register the output connection slots for the layer, connections are made after all layers have been created
996 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
997 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
998}
999
Finn Williamsed66d142019-12-06 09:55:55 +00001000void TfLiteParser::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
1001{
1002 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1003
1004 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1005 CHECK_VALID_SIZE(inputs.size(), 1);
1006
1007 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1008 CHECK_VALID_SIZE(outputs.size(), 1);
1009
1010 auto layerName = boost::str(boost::format("Dequantize:%1%:%2%") % subgraphIndex % operatorIndex);
1011
1012 IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001013 ARMNN_ASSERT(layer != nullptr);
Finn Williamsed66d142019-12-06 09:55:55 +00001014
Sadik Armagand109a4d2020-07-28 10:42:13 +01001015 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Finn Williamsed66d142019-12-06 09:55:55 +00001016 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1017
1018 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1019 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1020
1021 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1022 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1023}
1024
Derek Lambertif0176992020-04-28 13:37:49 +01001025void TfLiteParser::ParseExp(size_t subgraphIndex, size_t operatorIndex)
1026{
1027 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1028
1029 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1030 CHECK_VALID_SIZE(inputs.size(), 1);
1031
1032 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1033 CHECK_VALID_SIZE(outputs.size(), 1);
1034
1035 auto layerName = boost::str(boost::format("Exp:%1%:%2%") % subgraphIndex % operatorIndex);
1036
1037 ElementwiseUnaryDescriptor desc;
1038 desc.m_Operation = UnaryOperation::Exp;
1039 IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerName.c_str());
1040 ARMNN_ASSERT(layer != nullptr);
1041
Sadik Armagand109a4d2020-07-28 10:42:13 +01001042 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Derek Lambertif0176992020-04-28 13:37:49 +01001043 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1044
1045 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1046 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1047
1048 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1049 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1050}
1051
Keith Davis4cd29a02019-09-09 14:49:20 +01001052void TfLiteParser::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1053{
1054 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1055
1056 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Kevin May85d92602019-09-27 17:21:06 +01001057 CHECK_VALID_SIZE(inputs.size(), 1, 2);
Keith Davis4cd29a02019-09-09 14:49:20 +01001058
1059 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1060 CHECK_VALID_SIZE(outputs.size(), 1);
1061
Keith Davis4cd29a02019-09-09 14:49:20 +01001062 auto layerName = boost::str(boost::format("Transpose:%1%:%2%") % subgraphIndex % operatorIndex);
Mike Kelly08759e22020-03-02 11:41:31 +00001063 TransposeDescriptor desc;
Keith Davis4cd29a02019-09-09 14:49:20 +01001064
josh minorba424d22019-11-13 10:55:17 -06001065 if (inputs.size() == 2)
Kevin May85d92602019-09-27 17:21:06 +01001066 {
1067 armnn::TensorInfo permuteTensorInfo = ToTensorInfo(inputs[1]);
1068 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
josh minorba424d22019-11-13 10:55:17 -06001069 auto numPermVecElements = permuteTensorInfo.GetNumElements();
1070 std::vector<unsigned int> permuteShape(numPermVecElements);
Kevin May85d92602019-09-27 17:21:06 +01001071 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
Mike Kelly08759e22020-03-02 11:41:31 +00001072 PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
Kevin May85d92602019-09-27 17:21:06 +01001073
Mike Kelly08759e22020-03-02 11:41:31 +00001074 desc = TransposeDescriptor(permutationVector);
Kevin May85d92602019-09-27 17:21:06 +01001075 }
1076
James Conroy05102392020-06-24 15:39:55 +01001077 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Sadik Armagand109a4d2020-07-28 10:42:13 +01001078 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001079 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
Keith Davis4cd29a02019-09-09 14:49:20 +01001080
James Conroy05102392020-06-24 15:39:55 +01001081 IConnectableLayer* layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001082 ARMNN_ASSERT(layer != nullptr);
Keith Davis4cd29a02019-09-09 14:49:20 +01001083 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1084
1085 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1086 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1087
1088 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1089 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1090}
1091
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001092void TfLiteParser::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1093{
1094 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1095
1096 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1097 const auto * options = operatorPtr->builtin_options.AsTransposeConvOptions();
1098
1099 TransposeConvolution2dDescriptor desc;
1100 desc.m_BiasEnabled = false;
1101 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1102 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1103 desc.m_DataLayout = armnn::DataLayout::NHWC;
1104
1105 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001106 CHECK_VALID_SIZE(inputs.size(), 3);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001107
1108 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1109 CHECK_VALID_SIZE(outputs.size(), 1);
1110
Colm Donelan0ad3ef12020-07-03 15:54:28 +01001111 if (inputs[0])
1112 {
1113 armnn::TensorInfo tensorInfo = ToTensorInfo(inputs[0]);
1114 std::vector<int> output_shape(tensorInfo.GetNumElements());
1115 if (tensorInfo.GetDataType() == DataType::Signed32)
1116 {
1117 ::memcpy(output_shape.data(), GetBuffer(m_Model, inputs[0]->buffer)->data.data(), tensorInfo.GetNumBytes());
1118 }
1119 if (tensorInfo.GetDataType() == DataType::QAsymmU8)
1120 {
1121 for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
1122 {
1123 output_shape[i] = GetBuffer(m_Model, inputs[0]->buffer)->data.data()[i];
1124 }
1125 }
1126 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
1127 for (int dimension : output_shape)
1128 {
1129 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
1130 }
1131 desc.m_OutputShapeEnabled = true;
1132 }
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001133 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[2]);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001134 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
1135
1136 // TfLite uses NHWC tensors
1137 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1138 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1139
1140 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1141 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1142
1143 CalcPadding(inputHeight,
1144 filterHeight,
1145 desc.m_StrideY,
1146 1, // DilationY
1147 desc.m_PadTop,
1148 desc.m_PadBottom,
1149 options->padding);
1150
1151 CalcPadding(inputWidth,
1152 filterWidth,
1153 desc.m_StrideX,
1154 1, // DilationX
1155 desc.m_PadLeft,
1156 desc.m_PadRight,
1157 options->padding);
1158
1159 auto filterTensorAndData = CreateConstTensor(inputs[1],
1160 filterTensorInfo,
1161 armnn::Optional<armnn::PermutationVector&>());
1162
1163 armnn::IConnectableLayer* layer = nullptr;
1164 auto layerName = boost::str(boost::format("TransposeConv:%1%:%2%") % subgraphIndex % operatorIndex);
1165
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001166 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1167 filterTensorAndData.first,
1168 EmptyOptional(),
1169 layerName.c_str());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001170
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001171 ARMNN_ASSERT(layer != nullptr);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001172
Sadik Armagand109a4d2020-07-28 10:42:13 +01001173 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001174 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1175
1176 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1177 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Matthew Jacksonccb25ea2019-08-20 17:18:33 +01001178 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
Matthew Jackson74bf7da2019-08-16 16:51:42 +01001179
1180 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1181 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1182}
1183
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001184void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1185{
1186 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1187}
1188
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001189void TfLiteParser::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1190{
1191 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1192
1193 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1194 CHECK_VALID_SIZE(inputs.size(), 3);
1195
1196 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1197 CHECK_VALID_SIZE(outputs.size(), 1);
1198
1199 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1200 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1201
1202 armnn::TensorInfo cropsTensorInfo = ToTensorInfo(inputs[2]);
1203 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1204
1205 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1206 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1207
1208 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1209 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1210
1211 size_t step = 2;
1212 std::vector<std::pair<unsigned int, unsigned int>> crops;
1213 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1214 {
1215 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1216 }
1217
1218 armnn::BatchToSpaceNdDescriptor desc;
1219 desc.m_BlockShape = blockShape;
1220 desc.m_Crops = crops;
1221 desc.m_DataLayout = armnn::DataLayout::NHWC;
1222
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001223 auto layerName = boost::str(boost::format("BatchToSpaceND:%1%:%2%") % subgraphIndex % operatorIndex);
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001224
James Conroy05102392020-06-24 15:39:55 +01001225 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Sadik Armagand109a4d2020-07-28 10:42:13 +01001226 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001227 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1228
1229 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1230 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalvesdb947e22019-02-08 18:52:21 -02001231 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1232
1233 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1234 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1235
1236 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1237 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1238}
1239
Matthew Jackson28c94572019-07-18 10:47:03 +01001240void TfLiteParser::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
1241{
1242 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1243
1244 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1245 CHECK_VALID_SIZE(inputs.size(), 1);
1246
1247 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1248 CHECK_VALID_SIZE(outputs.size(), 1);
1249
1250 L2NormalizationDescriptor desc;
1251 desc.m_DataLayout = armnn::DataLayout::NHWC;
1252 auto layerName = boost::str(boost::format("L2Normalization:%1%:%2%") % subgraphIndex % operatorIndex);
1253 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
1254
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001255 ARMNN_ASSERT(layer != nullptr);
Matthew Jackson28c94572019-07-18 10:47:03 +01001256
Sadik Armagand109a4d2020-07-28 10:42:13 +01001257 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Matthew Jackson28c94572019-07-18 10:47:03 +01001258 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1259
1260 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1261 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1262
1263 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1264 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1265}
1266
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001267void TfLiteParser::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
1268{
1269 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
1270}
1271
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001272void TfLiteParser::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
1273{
1274 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1275
1276 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1277 CHECK_VALID_SIZE(inputs.size(), 2);
1278
1279 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1280 CHECK_VALID_SIZE(outputs.size(), 1);
1281
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001282 auto layerName = boost::str(boost::format("Maximum:%1%:%2%") % subgraphIndex % operatorIndex);
James Conroy05102392020-06-24 15:39:55 +01001283
1284 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1285 TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1286 CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001287
Sadik Armagand109a4d2020-07-28 10:42:13 +01001288 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001289 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1290
1291 IConnectableLayer* layer = m_Network->AddMaximumLayer(layerName.c_str());
1292 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001293 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1294
1295 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +01001296 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
Bruno Goncalvesb8d805e2019-02-12 22:57:13 -02001297
1298 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1299 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1300}
1301
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001302void TfLiteParser::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
1303{
1304 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1305
1306 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1307 CHECK_VALID_SIZE(inputs.size(), 2);
1308
1309 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1310 CHECK_VALID_SIZE(outputs.size(), 1);
1311
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001312 auto layerName = boost::str(boost::format("Minimum:%1%:%2%") % subgraphIndex % operatorIndex);
James Conroy05102392020-06-24 15:39:55 +01001313
1314 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1315 TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1316 CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001317
Sadik Armagand109a4d2020-07-28 10:42:13 +01001318 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001319 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1320
1321 IConnectableLayer* layer = m_Network->AddMinimumLayer(layerName.c_str());
1322 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001323 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1324
1325 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +01001326 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
Bruno Goncalves8f6d7a72019-02-12 22:58:18 -02001327
1328 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1329 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1330}
1331
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001332void TfLiteParser::ParsePool(size_t subgraphIndex,
1333 size_t operatorIndex,
1334 PoolingAlgorithm algorithm)
1335{
1336 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1337
1338 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1339 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
1340
1341 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1342
1343 std::string layerName;
1344
1345 switch (algorithm)
1346 {
1347 case PoolingAlgorithm::Average:
1348 layerName =
1349 boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1350 break;
1351 case PoolingAlgorithm::Max:
1352 layerName =
1353 boost::str(boost::format("MaxPool2D:%1%:%2%") % subgraphIndex % operatorIndex);
1354 break;
1355 default:
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001356 ARMNN_ASSERT_MSG(false, "Unsupported Pooling Algorithm");
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001357 }
1358
1359 Pooling2dDescriptor desc;
1360
1361 desc.m_PoolType = algorithm;
1362 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1363 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1364 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
1365 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
1366 desc.m_PaddingMethod = PaddingMethod::Exclude;
1367 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
jimfly01c25411c2018-11-14 17:47:22 +00001368 desc.m_DataLayout = armnn::DataLayout::NHWC;
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001369
1370 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1371 CHECK_VALID_SIZE(inputs.size(), 1);
1372 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1373
1374 // assuming input is NHWC
1375 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1376 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1377
Pablo Tellof0bd6832019-04-26 17:58:13 +01001378 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
1379 desc.m_PadTop, desc.m_PadBottom, options->padding);
1380 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
1381 desc.m_PadLeft, desc.m_PadRight, options->padding);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001382
1383 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1384 CHECK_VALID_SIZE(outputs.size(), 1);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001385
Sadik Armagand109a4d2020-07-28 10:42:13 +01001386 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001387 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1388
1389 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
1390 ARMNN_ASSERT(layer != nullptr);
jimfly01c25411c2018-11-14 17:47:22 +00001391 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001392
1393 // register the input connection slots for the layer, connections are made after all layers have been created
1394 // only the tensors for the inputs are relevant, exclude the const tensors
1395 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
jimfly01c25411c2018-11-14 17:47:22 +00001396 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001397
jimfly01c25411c2018-11-14 17:47:22 +00001398 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Nattapat Chaimanowongb66504b2018-10-17 15:19:14 +01001399 // register the output connection slots for the layer, connections are made after all layers have been created
1400 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1401 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1402}
1403
josh minorba424d22019-11-13 10:55:17 -06001404void TfLiteParser::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
1405{
1406 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1407
1408 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1409 CHECK_VALID_SIZE(inputs.size(), 3);
1410 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1411 CHECK_VALID_SIZE(outputs.size(), 1);
1412
1413 SliceDescriptor desc;
1414
1415 // set begin tensor info for slice descriptor
1416 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1417 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1418
1419 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
1420 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1421
1422 // set size tensor info for slice descriptor
1423 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[2]);
1424 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1425
1426 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
1427 ::memcpy(size.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
1428 desc = SliceDescriptor(begin, size);
1429
1430 auto layerName = boost::str(boost::format("Slice:%1%:%2%") % subgraphIndex % operatorIndex);
josh minorba424d22019-11-13 10:55:17 -06001431
James Conroy05102392020-06-24 15:39:55 +01001432 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Sadik Armagand109a4d2020-07-28 10:42:13 +01001433 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001434 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1435
1436 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
josh minorba424d22019-11-13 10:55:17 -06001437 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1438
1439 // register the input connection slots for the layer, connections are made after all layers have been created
1440 // only the tensors for the inputs are relevant, exclude the const tensors
1441 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1442 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1443
1444 // register the output connection slots for the layer, connections are made after all layers have been created
1445 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1446 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1447}
1448
telsoa01c577f2c2018-08-31 09:22:23 +01001449void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
1450{
1451 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1452 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1453 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
1454
1455 SoftmaxDescriptor desc;
1456 desc.m_Beta = options->beta;
1457
1458 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1459 CHECK_VALID_SIZE(inputs.size(), 1);
1460 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1461 CHECK_VALID_SIZE(outputs.size(), 1);
1462
1463 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
1464 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
1465
Sadik Armagand109a4d2020-07-28 10:42:13 +01001466 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
telsoa01c577f2c2018-08-31 09:22:23 +01001467 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1468
1469 // register the input connection slots for the layer, connections are made after all layers have been created
1470 // only the tensors for the inputs are relevant, exclude the const tensors
1471 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1472 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1473
1474 // register the output connection slots for the layer, connections are made after all layers have been created
1475 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1476 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1477}
1478
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001479void TfLiteParser::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
1480{
1481 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1482
1483 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1484 CHECK_VALID_SIZE(inputs.size(), 3);
1485
1486 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1487 CHECK_VALID_SIZE(outputs.size(), 1);
1488
1489 armnn::TensorInfo blockShapeTensorInfo = ToTensorInfo(inputs[1]);
1490 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1491
1492 armnn::TensorInfo padListTensorInfo = ToTensorInfo(inputs[2]);
1493 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1494
1495 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1496 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1497
1498 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
1499 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
1500
1501 size_t step = 2;
1502 std::vector<std::pair<unsigned int, unsigned int>> padList;
1503 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
1504 {
1505 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
1506 }
1507
1508 armnn::SpaceToBatchNdDescriptor desc;
1509 desc.m_BlockShape = blockShape;
1510 desc.m_PadList = padList;
1511 desc.m_DataLayout = armnn::DataLayout::NHWC;
1512
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001513 auto layerName = boost::str(boost::format("SpaceToBatchND:%1%:%2%") % subgraphIndex % operatorIndex);
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001514
James Conroy05102392020-06-24 15:39:55 +01001515 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Sadik Armagand109a4d2020-07-28 10:42:13 +01001516 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001517 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1518
1519 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
1520 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalvesbaded142019-02-08 19:02:48 -02001521 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1522
1523 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1524 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1525
1526 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1527 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1528}
1529
telsoa01c577f2c2018-08-31 09:22:23 +01001530armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
1531 const armnn::TensorInfo & inputTensorInfo)
1532{
1533 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
1534 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
1535 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
1536
1537 if (inputTensorInfo.GetNumDimensions() > 4)
1538 {
1539 std::stringstream ss;
1540 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1541 << " shape:" << inputTensorInfo.GetShape() << " "
1542 << CHECK_LOCATION().AsString();
1543 throw ParseException(ss.str());
1544 }
1545
1546 if (squeezeDims.empty())
1547 {
1548 squeezeDims.assign(dimensionSequence,
1549 dimensionSequence+inputTensorInfo.GetNumDimensions());
1550 }
1551
1552 std::vector<uint32_t> outputDims;
1553 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
1554 {
1555 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
1556 auto currentDimension = inputTensorInfo.GetShape()[i];
1557 if (skipSqueeze || currentDimension != 1)
1558 {
1559 outputDims.push_back(currentDimension);
1560 }
1561 }
1562
1563 if (outputDims.size() > 4)
1564 {
1565 std::stringstream ss;
1566 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
1567 << " shape:" << inputTensorInfo.GetShape() << " "
1568 << CHECK_LOCATION().AsString();
1569 throw ParseException(ss.str());
1570 }
1571
1572 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1573 outputDims.data());
1574
1575 // we need to preserve the tensor type and the quantization data as well
1576 TensorInfo outTensorInfo = inputTensorInfo;
1577 outTensorInfo.SetShape(outShape);
1578
1579 return outTensorInfo;
1580}
1581
1582void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
1583{
1584 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1585
1586 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1587 CHECK_VALID_SIZE(inputs.size(), 1);
1588
1589 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1590 CHECK_VALID_SIZE(outputs.size(), 1);
1591
1592 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1593 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
James Conroy05102392020-06-24 15:39:55 +01001594 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
telsoa01c577f2c2018-08-31 09:22:23 +01001595
1596 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1597 armnn::TensorInfo outputTensorInfo =
1598 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
1599 inputTensorInfo);
James Conroy05102392020-06-24 15:39:55 +01001600 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
telsoa01c577f2c2018-08-31 09:22:23 +01001601
1602 ReshapeDescriptor reshapeDesc;
1603 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
1604
telsoa01c577f2c2018-08-31 09:22:23 +01001605 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001606 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01001607 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1608
1609 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1610 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1611
1612 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1613 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1614}
1615
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001616void TfLiteParser::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
1617{
1618 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1619
1620 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1621 CHECK_VALID_SIZE(inputs.size(), 4);
1622
1623 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1624 CHECK_VALID_SIZE(outputs.size(), 1);
1625
1626 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1627 const auto * options = operatorPtr->builtin_options.AsStridedSliceOptions();
1628
1629 StridedSliceDescriptor desc;
1630 desc.m_BeginMask = options->begin_mask;
1631 desc.m_EllipsisMask = options->ellipsis_mask;
1632 desc.m_EndMask = options->end_mask;
1633 desc.m_NewAxisMask = options->new_axis_mask;
1634 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
1635 desc.m_DataLayout = armnn::DataLayout::NHWC;
1636
1637 armnn::TensorInfo beginTensorInfo = ToTensorInfo(inputs[1]);
1638 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1639
1640 std::vector<int> begin(beginTensorInfo.GetNumElements());
1641 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
1642
1643 armnn::TensorInfo endTensorInfo = ToTensorInfo(inputs[2]);
1644 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1645
1646 std::vector<int> end(endTensorInfo.GetNumElements());
1647 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
1648
1649 armnn::TensorInfo strideTensorInfo = ToTensorInfo(inputs[3]);
1650 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
1651
1652 std::vector<int> stride(strideTensorInfo.GetNumElements());
1653 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
1654
1655 desc.m_Begin = begin;
1656 desc.m_End = end;
1657 desc.m_Stride = stride;
1658
1659 auto layerName = boost::str(boost::format("StridedSlice:%1%:%2%") % subgraphIndex % operatorIndex);
1660 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001661 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001662
Sadik Armagand109a4d2020-07-28 10:42:13 +01001663 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Bruno Goncalves451d95b2019-02-12 22:59:22 -02001664 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1665
1666 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1667 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1668
1669 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1670 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1671}
1672
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001673void TfLiteParser::ParseSub(size_t subgraphIndex, size_t operatorIndex)
1674{
1675 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1676
1677 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1678 const auto * options = operatorPtr->builtin_options.AsSubOptions();
1679
1680 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1681 CHECK_VALID_SIZE(inputs.size(), 2);
1682
1683 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1684 CHECK_VALID_SIZE(outputs.size(), 1);
1685
1686 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1687 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1688
1689 auto layerName = boost::str(boost::format("Sub:%1%:%2%") % subgraphIndex % operatorIndex);
1690 IConnectableLayer* layer = m_Network->AddSubtractionLayer(layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001691 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001692
Sadik Armagand109a4d2020-07-28 10:42:13 +01001693 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001694 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1695
1696 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +01001697 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
Bruno Goncalvesbbeae262019-02-07 18:37:39 -02001698
1699 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1700
1701 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1702 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1703}
1704
Darshan Patel42b3d7d2020-05-25 22:30:07 +05301705void TfLiteParser::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
1706{
1707 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1708
1709 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1710 const auto * options = operatorPtr->builtin_options.AsDivOptions();
1711
1712 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1713 CHECK_VALID_SIZE(inputs.size(), 2);
1714
1715 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1716 CHECK_VALID_SIZE(outputs.size(), 1);
1717
1718 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1719 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1720
1721 auto layerName = boost::str(boost::format("Div:%1%:%2%") % subgraphIndex % operatorIndex);
1722 IConnectableLayer* layer = m_Network->AddDivisionLayer(layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001723 ARMNN_ASSERT(layer != nullptr);
Darshan Patel42b3d7d2020-05-25 22:30:07 +05301724
Sadik Armagand109a4d2020-07-28 10:42:13 +01001725 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Darshan Patel42b3d7d2020-05-25 22:30:07 +05301726 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1727
1728 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +01001729 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
Darshan Patel42b3d7d2020-05-25 22:30:07 +05301730 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1731
1732 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1733 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1734}
1735
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001736void TfLiteParser::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
1737{
1738 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1739
1740 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1741 const auto * options = operatorPtr->builtin_options.AsAddOptions();
1742
1743 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1744 CHECK_VALID_SIZE(inputs.size(), 2);
1745
1746 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1747 CHECK_VALID_SIZE(outputs.size(), 1);
1748
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001749 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1750 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1751
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001752 auto layerName = boost::str(boost::format("Add:%1%:%2%") % subgraphIndex % operatorIndex);
1753 IConnectableLayer* layer = m_Network->AddAdditionLayer(layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001754 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001755
Sadik Armagand109a4d2020-07-28 10:42:13 +01001756 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001757 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1758
1759 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +01001760 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
Bruno Goncalvesd4ac6a42018-12-18 12:56:22 -02001761 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1762
1763 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1764 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1765}
1766
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001767void TfLiteParser::ParseMul(size_t subgraphIndex, size_t operatorIndex)
1768{
1769 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1770
1771 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1772 const auto * options = operatorPtr->builtin_options.AsMulOptions();
1773
1774 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1775 CHECK_VALID_SIZE(inputs.size(), 2);
1776
1777 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1778 CHECK_VALID_SIZE(outputs.size(), 1);
1779
Bruno Goncalves9c761a62018-12-27 14:20:35 -02001780 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1781 armnn::TensorInfo input1TensorInfo = ToTensorInfo(inputs[1]);
1782
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001783 auto layerName = boost::str(boost::format("Mul:%1%:%2%") % subgraphIndex % operatorIndex);
1784 IConnectableLayer* layer = m_Network->AddMultiplicationLayer(layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001785 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001786
Sadik Armagand109a4d2020-07-28 10:42:13 +01001787 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001788 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1789
1790 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat16f82f92020-09-14 16:12:44 +01001791 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
Bruno Goncalvesf803f782018-12-18 13:40:30 -02001792 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1793
1794 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1795 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1796}
1797
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001798void TfLiteParser::ParseMean(size_t subgraphIndex, size_t operatorIndex)
1799{
1800 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1801
1802 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1803
1804 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1805 CHECK_VALID_SIZE(outputs.size(), 1);
1806
1807 armnn::TensorInfo dimTensorInfo = ToTensorInfo(inputs[1]);
1808 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1809
1810 armnn::MeanDescriptor desc;
1811 std::vector<unsigned int> axis(dimTensorInfo.GetNumElements());
1812 ::memcpy(axis.data(), bufferPtr->data.data(), dimTensorInfo.GetNumBytes());
1813 desc.m_Axis = axis;
1814
1815 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Sadik Armagand109a4d2020-07-28 10:42:13 +01001816 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001817
1818 desc.m_KeepDims =
1819 inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ?
1820 true : false;
1821
1822 auto layerName = boost::str(boost::format("Mean:%1%:%2%") % subgraphIndex % operatorIndex);
1823 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01001824 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalves2235cee2018-12-19 12:51:45 -02001825
1826 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1827
1828 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1829 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1830
1831 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1832 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1833}
1834
Darshan Patel83fcf982020-05-26 22:22:42 +05301835void TfLiteParser::ParseNeg(size_t subgraphIndex, size_t operatorIndex)
1836{
1837 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1838
1839 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1840 CHECK_VALID_SIZE(inputs.size(), 1);
1841
1842 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1843 CHECK_VALID_SIZE(outputs.size(), 1);
1844
1845 auto layerName = boost::str(boost::format("Neg:%1%:%2%") % subgraphIndex % operatorIndex);
1846 armnn::ElementwiseUnaryDescriptor descriptor(armnn::UnaryOperation::Neg);
1847 IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(descriptor, layerName.c_str());
1848 ARMNN_ASSERT(layer != nullptr);
1849
Sadik Armagand109a4d2020-07-28 10:42:13 +01001850 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Darshan Patel83fcf982020-05-26 22:22:42 +05301851 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1852
1853 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1854 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1855
1856 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1857 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1858}
1859
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001860void TfLiteParser::ParsePad(size_t subgraphIndex, size_t operatorIndex)
1861{
1862 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1863
1864 TfLiteParser::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1865
1866 TfLiteParser::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1867 CHECK_VALID_SIZE(outputs.size(), 1);
1868
1869 armnn::TensorInfo padTensorInfo = ToTensorInfo(inputs[1]);
1870 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1871
1872 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
1873 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
1874
1875 size_t step = 2;
1876 armnn::PadDescriptor desc;
1877 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
1878 {
1879 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
1880 }
1881
1882 auto layerName = boost::str(boost::format("Pad:%1%:%2%") % subgraphIndex % operatorIndex);
Sadik Armagand109a4d2020-07-28 10:42:13 +01001883 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01001884
1885 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
1886 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalves6c2355b2018-12-19 12:52:01 -02001887 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1888
1889 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1890 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1891
1892 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1893 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1894}
1895
Sadik Armagan66dedc72019-12-10 16:32:07 +00001896void TfLiteParser::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
1897{
1898 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1899
1900 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1901 CHECK_VALID_SIZE(inputs.size(), 1);
1902
1903 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1904 CHECK_VALID_SIZE(outputs.size(), 1);
1905
1906 auto layerName = boost::str(boost::format("Quantize:%1%:%2%") % subgraphIndex % operatorIndex);
1907
1908 IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001909 ARMNN_ASSERT(layer != nullptr);
Sadik Armagan66dedc72019-12-10 16:32:07 +00001910
Sadik Armagand109a4d2020-07-28 10:42:13 +01001911 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Sadik Armagan66dedc72019-12-10 16:32:07 +00001912 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1913
1914 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1915 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1916
1917 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1918 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1919}
Finn Williamsc42c3842019-01-22 14:18:11 +00001920
Sadik Armagan58f39192018-09-17 14:14:39 +01001921void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
1922{
Finn Williamsc42c3842019-01-22 14:18:11 +00001923 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
Sadik Armagan58f39192018-09-17 14:14:39 +01001924}
1925
1926void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1927{
Finn Williamsc42c3842019-01-22 14:18:11 +00001928 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
1929}
Sadik Armagan58f39192018-09-17 14:14:39 +01001930
Sadik Armagan12239e72020-05-27 11:06:17 +01001931void TfLiteParser::ParseLeakyRelu(size_t subgraphIndex, size_t operatorIndex)
1932{
Jan Eilers2f746b32020-07-28 14:00:06 +01001933 ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::LeakyReLu);
Sadik Armagan12239e72020-05-27 11:06:17 +01001934}
1935
Finn Williamsc42c3842019-01-22 14:18:11 +00001936void TfLiteParser::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
1937{
1938 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
1939}
1940
Nina Drozd99851762019-04-09 09:37:38 +01001941void TfLiteParser::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
1942{
1943 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
1944}
1945
Jan Eilers2f746b32020-07-28 14:00:06 +01001946void TfLiteParser::ParseHardSwish(size_t subgraphIndex, size_t operatorIndex)
1947{
1948 ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::HardSwish);
1949}
Finn Williamsc42c3842019-01-22 14:18:11 +00001950
1951void TfLiteParser::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
1952{
1953 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Sadik Armagan58f39192018-09-17 14:14:39 +01001954 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
Jan Eilers8eb25602020-03-09 12:13:48 +00001955 IgnoreUnused(operatorPtr);
Sadik Armagan58f39192018-09-17 14:14:39 +01001956
1957 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1958 CHECK_VALID_SIZE(inputs.size(), 1);
1959
1960 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1961 CHECK_VALID_SIZE(outputs.size(), 1);
1962
Finn Williamsc42c3842019-01-22 14:18:11 +00001963 auto layerName = str(boost::format("Activation:"));
Sadik Armagan58f39192018-09-17 14:14:39 +01001964 ActivationDescriptor activationDesc;
Finn Williamsc42c3842019-01-22 14:18:11 +00001965 activationDesc.m_Function = activationType;
1966
1967 switch (activationType)
1968 {
1969 case ActivationFunction::ReLu:
1970 {
1971 layerName += str(boost::format("RELU:%1%:%2%") % subgraphIndex % operatorIndex);
1972 break;
1973 }
1974 case ActivationFunction::BoundedReLu:
1975 {
1976 layerName += str(boost::format("RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1977 activationDesc.m_A = 6.0f;
1978 activationDesc.m_B = 0.0f;
1979 break;
1980 }
1981 case ActivationFunction::Sigmoid:
1982 {
1983 layerName += str(boost::format("SIGMOID:%1%:%2%") % subgraphIndex % operatorIndex);
1984 break;
1985 }
Nina Drozd99851762019-04-09 09:37:38 +01001986 case ActivationFunction::TanH:
1987 {
1988 layerName += str(boost::format("TANH:%1%:%2%") % subgraphIndex % operatorIndex);
1989 activationDesc.m_A = 1.0f;
1990 activationDesc.m_B = 1.0f;
1991 break;
1992 }
Sadik Armagan12239e72020-05-27 11:06:17 +01001993 case ActivationFunction::LeakyReLu:
1994 {
1995 layerName += str(boost::format("LEAKYRELU:%1%:%2%") % subgraphIndex % operatorIndex);
1996 const auto * options = operatorPtr->builtin_options.AsLeakyReluOptions();
1997 activationDesc.m_A = options->alpha;
1998 break;
1999 }
Jan Eilers2f746b32020-07-28 14:00:06 +01002000 case ActivationFunction::HardSwish:
2001 layerName += str(boost::format("HARDSWISH:%1%:%2%") % subgraphIndex % operatorIndex);
2002 break;
Finn Williamsc42c3842019-01-22 14:18:11 +00002003 default:
2004 {
2005 throw ParseException(
2006 boost::str(boost::format("Unexpected ActivationFunction[%1%] when creating layerName "
2007 " %2% ") %static_cast<int>(activationType)% CHECK_LOCATION().AsString()));
2008 }
2009 }
2010
2011 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
Sadik Armagan58f39192018-09-17 14:14:39 +01002012
Sadik Armagand109a4d2020-07-28 10:42:13 +01002013 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Sadik Armagan58f39192018-09-17 14:14:39 +01002014 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2015
2016 // register the input connection slots for the layer, connections are made after all layers have been created
2017 // only the tensors for the inputs are relevant, exclude the const tensors
2018 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2019 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2020
2021 // register the output connection slots for the layer, connections are made after all layers have been created
2022 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2023 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2024}
Sadikb94967b2018-09-19 15:30:00 +01002025armnn::TensorInfo TfLiteParser::OutputShapeOfReshape(const armnn::TensorInfo & inputTensorInfo,
2026 const std::vector<int32_t> & targetDimsIn)
2027{
2028 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
2029 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
2030
2031 if (stretchDim != targetDimsIn.end())
2032 {
2033 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
2034 {
2035 throw ParseException(
2036 boost::str(
2037 boost::format("At most one component of shape can be -1 %1%") % CHECK_LOCATION().AsString()));
2038 }
2039
2040 auto targetNumElements =
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002041 armnn::numeric_cast<unsigned int>(
Sadikb94967b2018-09-19 15:30:00 +01002042 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
2043
2044 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
2045 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
2046 }
2047
2048 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
2049
2050 TensorInfo reshapeInfo = inputTensorInfo;
2051 reshapeInfo.SetShape(outputShape);
2052
2053 return reshapeInfo;
2054}
2055
2056void TfLiteParser::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
2057{
2058 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2059
2060 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01002061
2062 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2063 CHECK_VALID_SIZE(outputs.size(), 1);
2064
2065 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2066 const auto * options = operatorPtr->builtin_options.AsReshapeOptions();
James Conroy05102392020-06-24 15:39:55 +01002067 auto layerName = boost::str(boost::format("Reshape:%1%:%2%") % subgraphIndex % operatorIndex);
Sadikb94967b2018-09-19 15:30:00 +01002068
2069 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
kevmay0171972a82018-12-17 14:28:03 +00002070 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
James Conroy05102392020-06-24 15:39:55 +01002071 CheckMatchingQuantization(inputTensorInfo, actualOutputTensorInfo, layerName, "Input 0", "Output 0");
Derek Lambertic9e52792020-03-11 11:42:26 +00002072
Jan Eilersbac9b352020-07-13 13:40:24 +01002073 // Extracting new shape for the output
2074 // There are two ways it can be passed
2075 // * First is to define the target shape in the operator built-in options
2076 // * Second is to pass it as a second input tensor
Derek Lambertic9e52792020-03-11 11:42:26 +00002077 std::vector<int32_t> targetShape;
Jan Eilersbac9b352020-07-13 13:40:24 +01002078 bool targetShapeFound = false;
2079 // Check if built-in options were given
2080 if (options != nullptr)
Derek Lambertic9e52792020-03-11 11:42:26 +00002081 {
Jan Eilersbac9b352020-07-13 13:40:24 +01002082 // make sure the parameter is given
2083 if (options->new_shape.empty() == false)
Derek Lambertic9e52792020-03-11 11:42:26 +00002084 {
Jan Eilersbac9b352020-07-13 13:40:24 +01002085 targetShape = options->new_shape;
2086 targetShapeFound = true;
Derek Lambertif4a953f2020-03-17 14:25:57 +00002087 }
Derek Lambertic9e52792020-03-11 11:42:26 +00002088 }
Jan Eilersbac9b352020-07-13 13:40:24 +01002089
2090 // If there is no built-in option given or if the built-in new_shape parameter was empty
2091 if (!targetShapeFound)
Derek Lambertic9e52792020-03-11 11:42:26 +00002092 {
Jan Eilersbac9b352020-07-13 13:40:24 +01002093 // Check for a second input tensor
2094 if (inputs.size() > 1 && inputs[1] != nullptr)
2095 {
2096 if (inputs[1]->is_variable)
2097 {
2098 ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
2099 }
2100
2101 if (inputs[1]->shape.size() != 1)
2102 {
2103 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
2104 }
2105
2106 if (inputs[1]->type != tflite::TensorType_INT32)
2107 {
2108 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
2109 }
2110
2111 // Extract target shape from input
2112 auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2113 auto values = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
2114 for (int i=0; i < inputs[1]->shape[0]; ++i)
2115 {
2116 targetShape.push_back(values[i]);
2117 }
2118 }
2119 else
Derek Lambertic9e52792020-03-11 11:42:26 +00002120 {
2121 ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
2122 "At least one method required");
2123 }
Derek Lambertic9e52792020-03-11 11:42:26 +00002124 }
2125
kevmay0171972a82018-12-17 14:28:03 +00002126 armnn::TensorInfo reshapeOutputTensorInfo =
Derek Lambertic9e52792020-03-11 11:42:26 +00002127 TfLiteParser::OutputShapeOfReshape(inputTensorInfo, targetShape);
Sadikb94967b2018-09-19 15:30:00 +01002128
kevmay0171972a82018-12-17 14:28:03 +00002129 // Check for valid input size and that reshape parameters equal output shape
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00002130 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
2131 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape))
kevmay0171972a82018-12-17 14:28:03 +00002132 {
2133 std::stringstream ss;
2134 ss << "New shape defined in reshape parameters "
Aron Virginas-Tar70672f62019-01-23 14:00:00 +00002135 << reshapeOutputTensorShape
kevmay0171972a82018-12-17 14:28:03 +00002136 << " does not equal output shape "
2137 << actualOutputTensorInfo.GetShape()
2138 << ": "
2139 << CHECK_LOCATION().AsString();
2140 throw ParseException(ss.str());
2141 }
2142
Sadikb94967b2018-09-19 15:30:00 +01002143 ReshapeDescriptor reshapeDesc;
kevmay0171972a82018-12-17 14:28:03 +00002144 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
Sadikb94967b2018-09-19 15:30:00 +01002145
Sadikb94967b2018-09-19 15:30:00 +01002146 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01002147 ARMNN_ASSERT(layer != nullptr);
kevmay0171972a82018-12-17 14:28:03 +00002148 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
Sadikb94967b2018-09-19 15:30:00 +01002149
2150 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2151 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2152
2153 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2154 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2155}
2156
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002157void TfLiteParser::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
2158{
Sadik Armagana3b31f02019-12-05 09:08:53 +00002159 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
2160}
2161
2162void TfLiteParser::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
2163{
2164 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
2165}
2166
2167void TfLiteParser::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
2168{
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002169 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2170
2171 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2172 CHECK_VALID_SIZE(inputs.size(), 2);
2173
2174 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2175 CHECK_VALID_SIZE(outputs.size(), 1);
2176
2177 armnn::TensorInfo sizeTensorInfo = ToTensorInfo(inputs[1]);
2178
2179 // Data for the parsed tensor args (size) must be stored locally.
2180 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
2181
2182 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2183 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2184
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002185 ResizeDescriptor desc;
Sadik Armagana3b31f02019-12-05 09:08:53 +00002186 desc.m_Method = resizeMethod;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002187 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002188 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2189 desc.m_DataLayout = armnn::DataLayout::NHWC;
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002190
Sadik Armagana3b31f02019-12-05 09:08:53 +00002191 auto layerName = str(boost::format("Resize:"));
2192
2193 switch (resizeMethod)
2194 {
2195 case ResizeMethod::Bilinear:
2196 {
2197 layerName += str(boost::format("BILINEAR:%1%:%2%") % subgraphIndex % operatorIndex);
Sang-Hoon Park820eb142020-01-08 10:25:24 +00002198
2199 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2200 const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
2201
David Monahan4a0c9b92020-05-30 09:48:39 +01002202 desc.m_AlignCorners = options->align_corners;
Sadik Armagana3b31f02019-12-05 09:08:53 +00002203 break;
2204 }
2205 case ResizeMethod::NearestNeighbor:
2206 {
2207 layerName += str(boost::format("NEARESTNEIGHBOR:%1%:%2%") % subgraphIndex % operatorIndex);
2208 break;
2209 }
2210 default:
2211 {
2212 throw ParseException(
2213 boost::str(boost::format("Unexpected ResizeMethod[%1%] when creating layerName "
2214 " %2% ") %static_cast<int>(resizeMethod)% CHECK_LOCATION().AsString()));
2215 }
2216 }
2217
James Conroy05102392020-06-24 15:39:55 +01002218 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Sadik Armagand109a4d2020-07-28 10:42:13 +01002219 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01002220 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2221
2222 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
2223 ARMNN_ASSERT(layer != nullptr);
Bruno Goncalves3f58ddb2019-02-07 18:40:11 -02002224 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2225
2226 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2227 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2228
2229 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2230 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2231}
2232
Sadik Armagan479045b2018-10-01 11:51:37 +01002233void TfLiteParser::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
2234{
2235 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2236
2237 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2238 const auto * options = operatorPtr->builtin_options.AsConcatenationOptions();
2239
2240 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2241
2242 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2243 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2244 CHECK_VALID_SIZE(outputs.size(), 1);
2245
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002246 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
2247 uint32_t inputRank = ToTensorInfo(inputs[0]).GetNumDimensions();
Sadik Armagan479045b2018-10-01 11:51:37 +01002248
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002249 const unsigned int concatDimInput = static_cast<unsigned int>(
2250 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
Sadik Armagan479045b2018-10-01 11:51:37 +01002251
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002252 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
2253 concatDescriptor.SetConcatAxis(concatDimInput);
Sadik Armagan479045b2018-10-01 11:51:37 +01002254
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002255 unsigned int mergeDimOrigin = 0;
Sadik Armagan479045b2018-10-01 11:51:37 +01002256
2257 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
2258 {
2259 TensorInfo inputTensorInfo = ToTensorInfo(inputs[viewIndex]);
2260
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002261 // This set up concatDescriptor view origin
2262 armnnUtils::ProcessConcatInputTensorInfo(
2263 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
Sadik Armagan479045b2018-10-01 11:51:37 +01002264 }
2265
2266 auto layerName = boost::str(boost::format("Concatenation:%1%:%2%") % subgraphIndex % operatorIndex);
Sadik Armagand109a4d2020-07-28 10:42:13 +01002267 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
James Conroy05102392020-06-24 15:39:55 +01002268
Jim Flynn906f9462019-05-10 13:55:21 +01002269 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002270 ARMNN_ASSERT(layer != nullptr);
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002271 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Sadik Armagan479045b2018-10-01 11:51:37 +01002272
James Conroy05102392020-06-24 15:39:55 +01002273 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002274 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
Sadik Armagan479045b2018-10-01 11:51:37 +01002275
Nattapat Chaimanowong5e9d2982019-01-25 13:20:39 +00002276 // add fused activation layer
2277 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
Sadik Armagan479045b2018-10-01 11:51:37 +01002278
Sadik Armagan479045b2018-10-01 11:51:37 +01002279 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2280 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2281}
2282
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002283void TfLiteParser::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
2284{
2285 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2286
2287 const auto & operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2288 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
2289
2290 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2291
2292 FullyConnectedDescriptor desc;
2293 desc.m_BiasEnabled = false;
Nattapat Chaimanowongd8eee592018-10-26 10:24:14 +01002294 desc.m_TransposeWeightMatrix = true;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002295
2296 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2297 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2298 CHECK_VALID_SIZE(outputs.size(), 1);
2299
2300 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
2301
2302 // Fully Connected Layer accepts two dimensional weights input
2303 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
2304 if (weightsDimension != 2)
2305 {
2306 throw ParseException(
2307 boost::str(
2308 boost::format(
2309 "Dimension %1% for Fully Connected weights is not supported by Armnn. "
2310 "Node %2%")
2311 % weightsDimension
2312 % CHECK_LOCATION().AsString()));
2313 }
2314
Matteo Martincigh747ef822018-12-18 09:26:39 +00002315 auto filterTensorAndData = CreateConstTensor(inputs[1],
2316 filterTensorInfo,
2317 armnn::Optional<armnn::PermutationVector&>());
Matthew Jackson74bf7da2019-08-16 16:51:42 +01002318 armnn::IConnectableLayer* layer = nullptr;
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002319 auto layerName = boost::str(boost::format("FullyConnected:%1%:%2%") % subgraphIndex % operatorIndex);
2320
2321 if (inputs.size() == 3)
2322 {
2323 desc.m_BiasEnabled = true;
2324 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
Matteo Martincigh747ef822018-12-18 09:26:39 +00002325 auto biasTensorAndData = CreateConstTensor(inputs[2],
2326 biasTensorInfo,
2327 armnn::Optional<armnn::PermutationVector&>());
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002328 layer = m_Network->AddFullyConnectedLayer(desc,
2329 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002330 Optional<ConstTensor>(biasTensorAndData.first),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002331 layerName.c_str());
2332 }
2333 else
2334 {
2335 layer = m_Network->AddFullyConnectedLayer(desc,
2336 filterTensorAndData.first,
Matteo Martincighfc598e12019-05-14 10:36:13 +01002337 EmptyOptional(),
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002338 layerName.c_str());
2339 }
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002340 ARMNN_ASSERT(layer != nullptr);
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002341
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002342 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2343
2344 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2345
2346 if (inputTensorInfo.GetNumDimensions() > 2)
2347 {
2348 // Add reshape to flatten to 2D [batch_size, input_size],
2349 // where "input_size" corresponds to the number of inputs to the layer,
2350 // matching the second dimension of weights,
2351 // and "batch_size" is calculated by dividing the number of elements by "input_size".
2352 std::vector<unsigned int> reshapedDimensions(2);
2353 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
2354 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
2355
2356 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
2357 {
2358 throw ParseException(
2359 boost::str(
2360 boost::format(
2361 "Failed to deduce input tensor shape from filter size %1%")
2362 % reshapedDimensions[1]
2363 % CHECK_LOCATION().AsString()));
2364 }
2365
2366 armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(inputs[0]);
2367 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
2368
2369 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2370 armnn::ReshapeDescriptor desc;
2371 desc.m_TargetShape = reshapedTensorInfo.GetShape();
2372 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2373
2374 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
2375 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
2376
2377 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
2378 }
2379 else
2380 {
2381 // register the input connection slot for the layer
2382 // only the tensors for the inputs are relevant, exclude the const tensors
2383 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2384 }
2385
Sadik Armagand109a4d2020-07-28 10:42:13 +01002386 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002387 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2388
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002389 // we need to add the activation layer and fortunately we don't need to care about the data layout
2390 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
2391 options->fused_activation_function);
Narumol Prangnawarat501f4d42019-04-24 15:52:20 +01002392
Sadik Armagan8853c1f2018-10-22 09:04:18 +01002393 // register the output connection slots for the layer, connections are made after all layers have been created
2394 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2395 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
2396}
2397
keidav011b3e2ea2019-02-21 10:07:37 +00002398void TfLiteParser::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
2399{
2400 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2401
2402 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2403
2404 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2405 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2406 CHECK_VALID_SIZE(outputs.size(), 4);
2407
2408 // Obtain custom options from flexbuffers
2409 auto custom_options = operatorPtr->custom_options;
2410 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
2411
2412 // Obtain descriptor information from tf lite
2413 DetectionPostProcessDescriptor desc;
2414 desc.m_MaxDetections = m["max_detections"].AsUInt32();
2415 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
2416 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
2417 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
2418 desc.m_NumClasses = m["num_classes"].AsUInt32();
2419 desc.m_ScaleH = m["h_scale"].AsFloat();
2420 desc.m_ScaleW = m["w_scale"].AsFloat();
2421 desc.m_ScaleX = m["x_scale"].AsFloat();
2422 desc.m_ScaleY = m["y_scale"].AsFloat();
2423
keidav0107d58c72019-02-26 11:57:39 +00002424 if (!(m["use_regular_nms"].IsNull()))
keidav011b3e2ea2019-02-21 10:07:37 +00002425 {
keidav0107d58c72019-02-26 11:57:39 +00002426 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
keidav011b3e2ea2019-02-21 10:07:37 +00002427 }
2428 if (!(m["detections_per_class"].IsNull()))
2429 {
2430 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
2431 }
2432
2433 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
2434 {
2435 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
2436 "must be positive and less than or equal to 1.");
2437 }
2438
2439 armnn::TensorInfo anchorTensorInfo = ToTensorInfo(inputs[2]);
2440 auto anchorTensorAndData = CreateConstTensor(inputs[2], anchorTensorInfo,
2441 armnn::Optional<armnn::PermutationVector&>());
2442
2443 auto layerName = boost::str(boost::format("DetectionPostProcess:%1%:%2%") % subgraphIndex % operatorIndex);
2444 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData.first,
2445 layerName.c_str());
2446
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002447 ARMNN_ASSERT(layer != nullptr);
keidav011b3e2ea2019-02-21 10:07:37 +00002448
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002449 // The model does not specify the output shapes.
2450 // The output shapes are calculated from the max_detection and max_classes_per_detection.
2451 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
2452 m_OverridenOutputShapes.push_back({ 1, numDetectedBox, 4 });
2453 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2454 m_OverridenOutputShapes.push_back({ 1, numDetectedBox });
2455 m_OverridenOutputShapes.push_back({ 1 });
2456
keidav011b3e2ea2019-02-21 10:07:37 +00002457 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
2458 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00002459 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverridenOutputShapes[i]);
keidav011b3e2ea2019-02-21 10:07:37 +00002460 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
2461 }
2462
2463 // Register the input connection slots for the layer, connections are made after all layers have been created
2464 // only the tensors for the inputs are relevant, exclude the const tensors
2465 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2466 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2467
2468 // Register the output connection slots for the layer, connections are made after all layers have been created
2469 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2470 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
2471 outputTensorIndexes[1],
2472 outputTensorIndexes[2],
2473 outputTensorIndexes[3]});
2474}
2475
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002476/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
2477void TfLiteParser::ParsePack(size_t subgraphIndex, size_t operatorIndex)
2478{
2479 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2480
2481 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2482 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2483 CHECK_VALID_SIZE(outputs.size(), 1);
2484
2485 if (inputs.size() < 1)
2486 {
2487 throw ParseException("Pack must have at least one input.");
2488 }
2489
2490 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2491 const auto* options = operatorPtr->builtin_options.AsPackOptions();
2492
2493 StackDescriptor desc;
2494 desc.m_Axis = static_cast<uint32_t>(options->axis);
2495 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
2496
2497 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2498 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
2499 desc.m_InputShape = inputTensorInfo.GetShape();
2500
2501 auto layerName = boost::str(boost::format("Pack:%1%:%2%") % subgraphIndex % operatorIndex);
2502 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
2503
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002504 ARMNN_ASSERT(layer != nullptr);
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002505
Sadik Armagand109a4d2020-07-28 10:42:13 +01002506 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
Matthew Jacksonbcca1f42019-07-16 11:39:21 +01002507 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2508
2509 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2510 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
2511
2512 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2513 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2514}
2515
Nina Drozd200e3802019-04-15 09:47:39 +01002516void TfLiteParser::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
2517{
2518 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2519
2520 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2521 const auto * options = operatorPtr->builtin_options.AsUnpackOptions();
2522
2523 // This unpackAxis indicates the axis to unpack
2524 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
2525
2526 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2527 CHECK_VALID_SIZE(inputs.size(), 1);
2528
2529 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002530
2531 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
2532 {
2533 throw ParseException(
2534 boost::str(
2535 boost::format(
2536 "The unpack axis: %1% cannot be greater than or equal to "
2537 "the number of input dimension %2% %3%")
2538 % unpackAxis
2539 % inputTensorInfo.GetNumDimensions()
2540 % CHECK_LOCATION().AsString()));
2541 }
2542
Nina Drozd200e3802019-04-15 09:47:39 +01002543 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
2544 // If num is not defined, automatically infer from the length of the dimension axis.
2545 if(unpackNum == 0)
2546 {
2547 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
2548 }
2549
2550 // If unpack number cannot be inferred and is still zero, throw ParseException.
2551 if(unpackNum == 0)
2552 {
2553 throw ParseException("Number to unpack must greater than zero.");
2554 }
2555
2556 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2557 CHECK_VALID_SIZE(outputs.size(), unpackNum);
2558
2559 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2560 std::vector<unsigned int> unpackDimSizes(inputDimSize);
2561
2562 // Add current input shape to unpackDimSizes
2563 for (unsigned int i = 0; i < inputDimSize; ++i)
2564 {
2565 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
2566 }
2567
2568 if (unpackDimSizes[unpackAxis] != unpackNum)
2569 {
2570 throw ParseException("Number to unpack must be the same as length of the dimension to "
2571 "unpack along.");
2572 }
2573
2574 unpackDimSizes[unpackAxis] /= unpackNum;
2575
2576 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
2577 for (unsigned int j = 0; j < unpackNum; ++j)
2578 {
2579 // Set the size of the views.
2580 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
2581 {
2582 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
2583 }
2584 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
2585 }
2586
2587 auto layerName = boost::str(boost::format("Unpack:%1%:%2%") % subgraphIndex % operatorIndex);
2588 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01002589 ARMNN_ASSERT(layer != nullptr);
Nina Drozd200e3802019-04-15 09:47:39 +01002590
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002591 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
2592 unpackDimSizes.data());
2593
Nina Drozd200e3802019-04-15 09:47:39 +01002594 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2595 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2596
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002597 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
2598 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2599 {
Sadik Armagand109a4d2020-07-28 10:42:13 +01002600 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k], true);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002601 std::string reshapeLayerName = boost::str(boost::format("Reshape_for:%1%") % layer->GetName());
2602 armnn::ReshapeDescriptor desc;
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002603 desc.m_TargetShape = outputTensorInfo.GetShape();
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002604 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
2605
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002606 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
2607 outputTensorInfo.GetDataType(),
2608 outputTensorInfo.GetQuantizationScale(),
2609 outputTensorInfo.GetQuantizationOffset()));
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002610 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
2611
Narumol Prangnawarat2c526462019-10-21 14:58:26 +01002612 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
Narumol Prangnawarat672de572019-04-23 15:28:06 +01002613
2614 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
2615 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
2616 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
2617 }
Nina Drozd200e3802019-04-15 09:47:39 +01002618}
2619
Nina Drozd0324f482019-04-08 10:52:10 +01002620void TfLiteParser::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
2621{
2622 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2623
2624 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2625 const auto * options = operatorPtr->builtin_options.AsSplitOptions();
2626
2627 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
2628
Nina Drozd200e3802019-04-15 09:47:39 +01002629 // If number of splits cannot be inferred and is zero, throw ParseException.
2630 if(numSplits == 0)
2631 {
2632 throw ParseException("Number to splits must greater than zero.");
2633 }
2634
Nina Drozd0324f482019-04-08 10:52:10 +01002635 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2636 CHECK_VALID_SIZE(inputs.size(), 2);
2637 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2638 CHECK_VALID_SIZE(outputs.size(), numSplits);
2639
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002640 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[1]);
2641 armnn::TensorInfo axisTensorInfo = ToTensorInfo(inputs[0]);
Nina Drozd0324f482019-04-08 10:52:10 +01002642
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002643 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
2644 std::vector<unsigned int> axisData(axisTensorInfo.GetNumElements());
2645 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2646
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002647 ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002648 const unsigned int splitDim = axisData[0];
Nina Drozd0324f482019-04-08 10:52:10 +01002649
Nina Drozd0324f482019-04-08 10:52:10 +01002650 auto inputDimSize = inputTensorInfo.GetNumDimensions();
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002651 if (inputDimSize > MaxNumOfTensorDimensions)
Nina Drozd0324f482019-04-08 10:52:10 +01002652 {
2653 throw ParseException(
2654 boost::str(
2655 boost::format(
2656 "The number of dimensions: %1% for input tensors of the "
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002657 "split op cannot be greater than %2% %3%")
Nina Drozd0324f482019-04-08 10:52:10 +01002658 % inputTensorInfo.GetNumDimensions()
2659 % MaxNumOfTensorDimensions
2660 % CHECK_LOCATION().AsString()));
2661 }
2662
2663 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2664
2665 // Add current input shape to splitterDimSizes
2666 for (unsigned int i = 0; i < inputDimSize; ++i)
2667 {
2668 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2669 }
2670
2671 if (splitterDimSizes[splitDim] % numSplits != 0)
2672 {
2673 throw ParseException("Number of splits must evenly divide the dimension");
2674 }
2675 splitterDimSizes[splitDim] /= numSplits;
2676
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002677 SplitterDescriptor splitDesc(numSplits, inputDimSize);
Nina Drozd0324f482019-04-08 10:52:10 +01002678 for (unsigned int j = 0; j < numSplits; ++j)
2679 {
2680 // Set the size of the views.
2681 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2682 {
2683 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
2684 }
2685 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
2686 }
2687
2688 auto layerName = boost::str(boost::format("Split:%1%:%2%") % subgraphIndex % operatorIndex);
2689 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01002690 ARMNN_ASSERT(layer != nullptr);
Nina Drozd0324f482019-04-08 10:52:10 +01002691
2692 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
Narumol Prangnawarat17660e62019-04-18 16:56:19 +01002693 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
Nina Drozd0324f482019-04-08 10:52:10 +01002694
Nina Drozd0324f482019-04-08 10:52:10 +01002695 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2696 {
Sadik Armagand109a4d2020-07-28 10:42:13 +01002697 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
Francis Murtagh98d6b3d2019-10-21 10:52:54 +01002698 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
Nina Drozd0324f482019-04-08 10:52:10 +01002699 }
2700
2701 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2702 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2703}
2704
Derek Lambertif0176992020-04-28 13:37:49 +01002705unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
2706{
2707 int numDims = armnn::numeric_cast<int>(numDimsIn);
2708 int v = idx < 0 ? numDims + idx : idx;
2709 ARMNN_ASSERT(v >= 0);
2710 ARMNN_ASSERT(v < numDims);
2711
2712 return static_cast<unsigned int>(v);
2713}
2714
2715void TfLiteParser::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
2716{
2717 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2718
2719 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
Ryan OShea86704732020-05-26 11:41:04 +01002720 const auto * options = operatorPtr->builtin_options.AsSplitVOptions();
Derek Lambertif0176992020-04-28 13:37:49 +01002721
2722 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2723 CHECK_VALID_SIZE(inputs.size(), 3);
2724
2725 auto& inputTensor = inputs[0];
2726 auto& splitsTensor = inputs[1];
2727 auto& axisTensor = inputs[2];
2728
2729 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
2730 armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
2731 armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
2732 ARMNN_ASSERT(axisTensorInfo.GetNumElements() == 1);
2733
2734 // Inputs
2735 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2736 if (inputDimSize > MaxNumOfTensorDimensions)
2737 {
2738 throw ParseException(
2739 boost::str(
2740 boost::format(
2741 "The number of dimensions: %1% for input tensors of the "
Jan Eilersc0761e92020-06-29 16:48:44 +01002742 "SplitV op cannot be greater than %2% %3%")
Derek Lambertif0176992020-04-28 13:37:49 +01002743 % inputTensorInfo.GetNumDimensions()
2744 % MaxNumOfTensorDimensions
2745 % CHECK_LOCATION().AsString()));
2746 }
2747
2748 // Get split axis
2749 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
2750 std::vector<int> axisData(axisTensorInfo.GetNumElements());
2751 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
2752 const unsigned int splitDim = ComputeWrappedIndex(axisData[0], inputTensorInfo.GetNumDimensions());
2753
Derek Lambertif0176992020-04-28 13:37:49 +01002754 // Set split sizes
Derek Lambertif0176992020-04-28 13:37:49 +01002755 CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
Ryan OShea86704732020-05-26 11:41:04 +01002756 unsigned int numSplits{0};
2757
2758 if(options)
Derek Lambertif0176992020-04-28 13:37:49 +01002759 {
2760 numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
Derek Lambertif0176992020-04-28 13:37:49 +01002761 }
2762 else
2763 {
Ryan OShea86704732020-05-26 11:41:04 +01002764 numSplits = splitsInfo.GetNumElements();
Derek Lambertif0176992020-04-28 13:37:49 +01002765 }
2766
2767 if (numSplits <=0)
2768 {
2769 throw ParseException("SplitV has invalid number of splits");
2770 }
2771
Jan Eilersc0761e92020-06-29 16:48:44 +01002772 std::vector<int> splitsData(numSplits);
Ryan OShea86704732020-05-26 11:41:04 +01002773 BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
Jan Eilersc0761e92020-06-29 16:48:44 +01002774 ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
Ryan OShea86704732020-05-26 11:41:04 +01002775
Jan Eilersc0761e92020-06-29 16:48:44 +01002776 unsigned int idx = 0;
Ryan OShea86704732020-05-26 11:41:04 +01002777 int numInferred{0};
2778 unsigned int inferIdx{0};
2779 int splitSum{0};
2780 for (auto split : splitsData)
2781 {
2782 if (split < 0)
2783 {
2784 numInferred++;
2785 inferIdx = idx;
2786 }
2787 else
2788 {
2789 splitSum += split;
2790 }
2791 idx++;
2792 }
2793 // Check for inferred Axis
2794 if (numInferred == 0)
2795 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002796 if (splitSum != armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]))
Ryan OShea86704732020-05-26 11:41:04 +01002797 {
2798 throw ParseException("SplitV split_sizes does not sum to the dimension of value along split_dim.");
2799 }
2800 }
2801 else if (numInferred == 1)
2802 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002803 splitsData[inferIdx] = armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - splitSum;
Ryan OShea86704732020-05-26 11:41:04 +01002804 }
2805 else
2806 {
2807 throw ParseException("Cannot infer split size for more than one split");
2808 }
2809
Derek Lambertif0176992020-04-28 13:37:49 +01002810 //Ouput size validation
2811 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2812 CHECK_VALID_SIZE(outputs.size(), numSplits);
2813
2814 // Setup Armnn descriptor
2815 SplitterDescriptor splitDesc(numSplits, inputDimSize);
2816 unsigned int accumSplit = 0;
2817 for (unsigned int j = 0; j < numSplits; ++j)
2818 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002819 unsigned int splitSize = armnn::numeric_cast<unsigned int>(splitsData[j]);
Derek Lambertif0176992020-04-28 13:37:49 +01002820
2821 // Set the size of the views.
2822 for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
2823 {
2824 unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
2825 if (dimIdx == splitDim)
2826 {
2827 dimSize = splitSize;
2828 }
2829 splitDesc.SetViewSize(j, dimIdx, dimSize);
2830 }
2831
2832 splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
2833 accumSplit += splitSize;
2834 }
2835
Ryan OShea86704732020-05-26 11:41:04 +01002836 auto layerName = boost::str(boost::format("SplitV:%1%:%2%") % subgraphIndex % operatorIndex);
Derek Lambertif0176992020-04-28 13:37:49 +01002837 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
James Conroy05102392020-06-24 15:39:55 +01002838 ARMNN_ASSERT(layer != nullptr);
Derek Lambertif0176992020-04-28 13:37:49 +01002839
2840 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2841 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2842
2843 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
2844 {
Sadik Armagand109a4d2020-07-28 10:42:13 +01002845 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
Derek Lambertif0176992020-04-28 13:37:49 +01002846 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
2847 }
2848
2849 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2850 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2851}
2852
Inki Daed4619e22020-09-10 15:33:54 +09002853void TfLiteParser::ParseArgMax(size_t subgraphIndex, size_t operatorIndex)
2854{
2855 const auto &operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2856 const auto *options = operatorPtr->builtin_options.AsArgMaxOptions();
2857
2858 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2859 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2860 CHECK_VALID_SIZE(inputs.size(), 2);
2861
2862 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2863 CHECK_VALID_SIZE(outputs.size(), 1);
2864
2865 auto layerName = boost::str(boost::format("ArgMax:%1%:%2%") % subgraphIndex % operatorIndex);
2866
2867 armnn::TensorInfo sizeTensorInfo0 = ToTensorInfo(inputs[0]);
2868 armnn::TensorInfo sizeTensorInfo1 = ToTensorInfo(inputs[1]);
2869
2870 // Get const axis value from model and set it to descriptor.
2871 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2872
2873 ArgMinMaxDescriptor desc;
2874 desc.m_Axis = axisBufferPtr->data.data()[0];
2875 // If output_type is int32 then set Signed32 else Signed64. Default type is Signed64.
2876 desc.m_Output_Type = options->output_type == 3 ? armnn::DataType::Signed32 : armnn::DataType::Signed64;
2877 desc.m_Function = ArgMinMaxFunction::Max;
2878
2879 // Register a ArgMax layer.
2880 IConnectableLayer *layer = m_Network->AddArgMinMaxLayer(desc, layerName.c_str());
2881
2882 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2883 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2884
2885 // Register input tensor to the layer.
2886 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2887 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2888
2889 // Register output tensor to the layer.
2890 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2891 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2892}
2893
Sadik Armagan58f39192018-09-17 14:14:39 +01002894armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
2895 unsigned int outputSlot,
2896 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01002897{
2898 ActivationDescriptor activationDesc;
2899 std::string layerName = prevLayer->GetName();
2900
2901 switch(activationType)
2902 {
2903 case tflite::ActivationFunctionType_NONE:
2904 {
2905 // this is a no-op: return previous layer
2906 return prevLayer;
2907 }
2908 case tflite::ActivationFunctionType_RELU:
2909 {
2910 activationDesc.m_Function = ActivationFunction::ReLu;
2911 layerName += ":RELU";
2912 break;
2913 }
2914 case tflite::ActivationFunctionType_RELU6:
2915 {
2916 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2917 activationDesc.m_A = 6.0f;
2918 activationDesc.m_B = 0.0f;
2919 layerName += ":RELU6";
2920 break;
2921 }
2922 case tflite::ActivationFunctionType_TANH:
2923 {
2924 activationDesc.m_Function = ActivationFunction::TanH;
2925 activationDesc.m_A = 1.0f;
2926 activationDesc.m_B = 1.0f;
2927 layerName += ":TANH";
2928 break;
2929 }
2930
2931 // I only put these here as a reminder what others we could support
2932 case tflite::ActivationFunctionType_RELU_N1_TO_1:
2933 case tflite::ActivationFunctionType_SIGN_BIT:
2934 default:
2935 {
2936 throw ParseException(
2937 boost::str(
2938 boost::format("TfLite parser doesn't suppport fused activation: "
2939 "%1%/%2% %3% ") %
2940 activationType %
2941 tflite::EnumNameActivationFunctionType(activationType) %
2942 CHECK_LOCATION().AsString()));
2943
2944 }
2945 }
2946
2947 IConnectableLayer* activationLayer =
2948 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
2949
2950 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
2951 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
2952 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
2953 return activationLayer;
2954}
2955
2956TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
2957{
2958 if (fileName == nullptr)
2959 {
2960 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
2961 CHECK_LOCATION().AsString()));
2962 }
Francis Murtagh532a29d2020-06-29 11:50:01 +01002963 std::error_code errorCode;
2964 fs::path pathToFile(fileName);
2965 if (!fs::exists(pathToFile, errorCode))
telsoa01c577f2c2018-08-31 09:22:23 +01002966 {
Derek Lambertic9e52792020-03-11 11:42:26 +00002967 std::string locationString = CHECK_LOCATION().AsString();
2968 std::string msg = boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
2969 fileName %
2970 errorCode %
2971 locationString);
2972 throw FileNotFoundException(msg);
telsoa01c577f2c2018-08-31 09:22:23 +01002973 }
2974 std::ifstream file(fileName, std::ios::binary);
2975 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
2976 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
2977 fileContent.size());
2978}
2979
2980TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
2981{
2982 if (binaryContent == nullptr)
2983 {
2984 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
2985 CHECK_LOCATION().AsString()));
2986 }
2987 flatbuffers::Verifier verifier(binaryContent, len);
2988 if (verifier.VerifyBuffer<tflite::Model>() == false)
2989 {
2990 throw ParseException(
2991 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
2992 "flatbuffers format. size:%1% %2%") %
2993 len %
2994 CHECK_LOCATION().AsString()));
2995 }
2996 return tflite::UnPackModel(binaryContent);
2997}
2998
2999TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
3000 size_t subgraphIndex,
3001 size_t operatorIndex)
3002{
3003 CHECK_MODEL(model, subgraphIndex, operatorIndex);
3004
Derek Lambertiff05cc52019-04-26 13:05:17 +01003005 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3006 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01003007
3008 size_t inputCount = operatorPtr->inputs.size();
3009 TensorRawPtrVector result(inputCount);
3010 for (size_t i=0; i<inputCount; ++i)
3011 {
3012 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003013 result[i] = subgraphPtr->tensors[inputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01003014 }
3015 return result;
3016}
3017
3018TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
3019 size_t subgraphIndex,
3020 size_t operatorIndex)
3021{
3022 CHECK_MODEL(model, subgraphIndex, operatorIndex);
3023
Derek Lambertiff05cc52019-04-26 13:05:17 +01003024 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3025 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01003026
3027 size_t outputCount = operatorPtr->outputs.size();
3028 TensorRawPtrVector result(outputCount);
3029 for (size_t i=0; i<outputCount; ++i)
3030 {
3031 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
3032 CHECK_TENSOR(model, subgraphIndex, outputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003033 result[i] = subgraphPtr->tensors[outputId].get();
telsoa01c577f2c2018-08-31 09:22:23 +01003034 }
3035 return result;
3036}
3037
3038TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
3039 size_t subgraphIndex)
3040{
3041 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003042 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01003043
Derek Lambertiff05cc52019-04-26 13:05:17 +01003044 size_t inputCount = subgraphPtr->inputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01003045 TensorIdRawPtrVector result(inputCount);
3046 for (size_t i=0; i<inputCount; ++i)
3047 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01003048 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
telsoa01c577f2c2018-08-31 09:22:23 +01003049 CHECK_TENSOR(model, subgraphIndex, inputId);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003050 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01003051 }
3052 return result;
3053}
3054
3055TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
3056 size_t subgraphIndex)
3057{
3058 CHECK_SUBGRAPH(model, subgraphIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003059 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01003060
Derek Lambertiff05cc52019-04-26 13:05:17 +01003061 size_t outputCount = subgraphPtr->outputs.size();
telsoa01c577f2c2018-08-31 09:22:23 +01003062 TensorIdRawPtrVector result(outputCount);
3063 for (size_t i=0; i<outputCount; ++i)
3064 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01003065 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
3066 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
telsoa01c577f2c2018-08-31 09:22:23 +01003067 }
3068 return result;
3069}
3070
3071std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
3072 size_t subgraphIndex,
3073 size_t operatorIndex)
3074{
3075 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003076 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3077 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01003078 return operatorPtr->inputs;
3079}
3080
3081std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
3082 size_t subgraphIndex,
3083 size_t operatorIndex)
3084{
3085 CHECK_MODEL(model, subgraphIndex, operatorIndex);
Derek Lambertiff05cc52019-04-26 13:05:17 +01003086 const auto & subgraphPtr = model->subgraphs[subgraphIndex];
3087 const auto & operatorPtr = subgraphPtr->operators[operatorIndex];
telsoa01c577f2c2018-08-31 09:22:23 +01003088 return operatorPtr->outputs;
3089}
3090
3091void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
3092 size_t operatorIndex,
3093 IConnectableLayer* layer,
3094 const std::vector<unsigned int>& tensorIndexes)
3095{
3096 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01003097 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01003098 if (tensorIndexes.size() != layer->GetNumInputSlots())
3099 {
3100 throw ParseException(
3101 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
3102 " for subgraph:%3% operator index:%4% %5%") %
3103 tensorIndexes.size() %
3104 layer->GetNumInputSlots() %
3105 subgraphIndex %
3106 operatorIndex %
3107 CHECK_LOCATION().AsString()));
3108 }
3109
3110 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
3111 {
3112 unsigned int tensorIndex = tensorIndexes[slotIndex];
3113 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
3114 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
3115 }
3116}
3117
3118void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
3119 size_t operatorIndex,
3120 IConnectableLayer* layer,
3121 const std::vector<unsigned int>& tensorIndexes)
3122{
3123 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01003124 ARMNN_ASSERT(layer != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01003125 if (tensorIndexes.size() != layer->GetNumOutputSlots())
3126 {
3127 throw ParseException(
3128 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
3129 " for subgraph:%3% operator index:%4% %5%") %
3130 tensorIndexes.size() %
3131 layer->GetNumOutputSlots() %
3132 subgraphIndex %
3133 operatorIndex %
3134 CHECK_LOCATION().AsString()));
3135 }
3136
3137 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
3138 {
3139 unsigned int tensorIndex = tensorIndexes[slotIndex];
3140 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
3141 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
3142 }
3143}
3144
3145void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
3146{
3147 CHECK_SUBGRAPH(m_Model, subgraphIndex);
3148
3149 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
3150 for (auto const & tensorIdAndPtr : inputs)
3151 {
3152 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3153 IConnectableLayer* layer =
3154 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3155
3156 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
3157 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3158
3159 RegisterOutputSlots(subgraphIndex,
3160 VIRTUAL_OPERATOR_ID,
3161 layer,
3162 { static_cast<uint32_t>(tensorIdAndPtr.first) });
3163 }
3164}
3165
3166void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
3167{
3168 CHECK_SUBGRAPH(m_Model, subgraphIndex);
3169
3170 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
3171 for (auto const & tensorIdAndPtr : outputs)
3172 {
3173 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
3174 IConnectableLayer* layer =
3175 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
3176
3177 RegisterInputSlots(subgraphIndex,
3178 VIRTUAL_OPERATOR_ID,
3179 layer,
3180 { static_cast<uint32_t>(tensorIdAndPtr.first) });
3181 }
3182}
3183
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02003184void TfLiteParser::SetupConstantLayers(size_t subgraphIndex)
3185{
3186 CHECK_SUBGRAPH(m_Model, subgraphIndex);
3187
Derek Lambertiff05cc52019-04-26 13:05:17 +01003188 const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex];
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02003189 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
3190 {
3191 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
3192 {
3193 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
3194 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
3195 {
Derek Lambertiff05cc52019-04-26 13:05:17 +01003196 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
Bruno Goncalves3d7efe92018-12-27 14:21:43 -02003197 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
3198 auto tensorAndData = CreateConstTensor(tensorPtr,
3199 tensorInfo,
3200 armnn::Optional<armnn::PermutationVector&>());
3201
3202 std::string layerName = boost::str(boost::format("Constant:%1%") % tensorPtr->name);
3203 IConnectableLayer *layer =
3204 m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
3205
3206 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
3207 RegisterOutputSlots(subgraphIndex,
3208 VIRTUAL_OPERATOR_ID,
3209 layer,
3210 { tensorIndex });
3211
3212 }
3213 }
3214 }
3215}
3216
telsoa01c577f2c2018-08-31 09:22:23 +01003217// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
3218TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
3219{
3220 CHECK_BUFFER(model, bufferIndex);
3221 return model->buffers[bufferIndex].get();
3222}
3223
Matteo Martincigh747ef822018-12-18 09:26:39 +00003224template<typename T>
3225std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3226TfLiteParser::CreateConstTensorAndStoreData(TfLiteParser::BufferRawPtr bufferPtr,
3227 TfLiteParser::TensorRawPtr tensorPtr,
3228 armnn::TensorInfo& tensorInfo,
3229 armnn::Optional<armnn::PermutationVector&> permutationVector)
3230{
3231 auto constData = CreateConstTensorImpl<T>(bufferPtr,
3232 tensorPtr,
3233 tensorInfo,
3234 permutationVector);
3235 TfLiteParser::SupportedDataStorage storage(std::move(constData.second));
3236 return std::make_pair(constData.first, std::move(storage));
3237}
3238
telsoa01c577f2c2018-08-31 09:22:23 +01003239std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
3240TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
Matteo Martincigh747ef822018-12-18 09:26:39 +00003241 armnn::TensorInfo& tensorInfo,
3242 armnn::Optional<armnn::PermutationVector&> permutationVector)
telsoa01c577f2c2018-08-31 09:22:23 +01003243{
3244 CHECK_TENSOR_PTR(tensorPtr);
3245 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
3246 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
3247
3248 switch (tensorInfo.GetDataType())
3249 {
3250 case armnn::DataType::Float32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00003251 return CreateConstTensorAndStoreData<float>(bufferPtr,
3252 tensorPtr,
3253 tensorInfo,
3254 permutationVector);
Derek Lambertif90c56d2020-01-10 17:14:08 +00003255 case armnn::DataType::QAsymmU8:
Matteo Martincigh747ef822018-12-18 09:26:39 +00003256 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
3257 tensorPtr,
3258 tensorInfo,
3259 permutationVector);
Keith Davisd305e1a2020-01-22 11:57:54 +00003260 case armnn::DataType::QSymmS8:
3261 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3262 tensorPtr,
3263 tensorInfo,
3264 permutationVector);
Keith Davis67e6c542020-02-19 10:08:33 +00003265 case armnn::DataType::QAsymmS8:
3266 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
3267 tensorPtr,
3268 tensorInfo,
3269 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01003270 case armnn::DataType::Signed32:
Matteo Martincigh747ef822018-12-18 09:26:39 +00003271 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
3272 tensorPtr,
3273 tensorInfo,
3274 permutationVector);
telsoa01c577f2c2018-08-31 09:22:23 +01003275 default:
3276 {
3277 std::stringstream errString;
3278 errString << "Unexpected datatype when creating const tensor: "
3279 << armnn::GetDataTypeName(tensorInfo.GetDataType())
3280 << " shape:" << tensorInfo.GetShape()
3281 << CHECK_LOCATION().AsString();
3282 throw ParseException(errString.str());
3283 }
3284 }
3285}
3286
3287BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
3288 const std::string& name) const
3289{
3290 CHECK_SUBGRAPH(m_Model, subgraphId);
3291 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3292 for (auto const & input : inputs)
3293 {
3294 if (input.second->name == name)
3295 {
3296 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
3297 return std::make_pair(bindingId, ToTensorInfo(input.second));
3298 }
3299 }
3300
3301 std::stringstream bindings;
3302 for (auto const & input : inputs)
3303 {
3304 bindings << "'" << input.second->name << "' ";
3305 }
3306
3307 throw ParseException(
3308 boost::str(
3309 boost::format("No input binding found for subgraph:%1% and name:%2%. "
3310 "Possible inputs are: [%3%] %4%") %
3311 subgraphId %
3312 name %
3313 bindings.str() %
3314 CHECK_LOCATION().AsString()));
3315}
3316
3317BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
3318 const std::string& name) const
3319{
3320 CHECK_SUBGRAPH(m_Model, subgraphId);
3321 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00003322 for (unsigned int i = 0; i < outputs.size(); ++i)
telsoa01c577f2c2018-08-31 09:22:23 +01003323 {
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00003324 auto const output = outputs[i];
telsoa01c577f2c2018-08-31 09:22:23 +01003325 if (output.second->name == name)
3326 {
3327 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
Narumol Prangnawarat4628d052019-02-25 17:26:05 +00003328 std::vector<unsigned int> shape = m_OverridenOutputShapes.size() > 0 ?
3329 m_OverridenOutputShapes[i] : AsUnsignedVector(output.second->shape);
3330 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
telsoa01c577f2c2018-08-31 09:22:23 +01003331 }
3332 }
3333
3334 std::stringstream bindings;
3335 for (auto const & output : outputs)
3336 {
3337 bindings << "'" << output.second->name << "' ";
3338 }
3339
3340 throw ParseException(
3341 boost::str(
3342 boost::format("No output binding found for subgraph:%1% and name:%2%. "
3343 "Possible outputs are: [%3%] %4%") %
3344 subgraphId %
3345 name %
3346 bindings.str() %
3347 CHECK_LOCATION().AsString()));
3348}
3349
3350size_t TfLiteParser::GetSubgraphCount() const
3351{
3352 return m_Model->subgraphs.size();
3353}
3354
3355std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
3356{
3357 CHECK_SUBGRAPH(m_Model, subgraphId);
3358 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
3359 std::vector<std::string> result;
3360 result.reserve(inputs.size());
3361 for (auto const & input : inputs)
3362 {
3363 result.push_back(input.second->name);
3364 }
3365 return result;
3366}
3367
3368std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
3369{
3370 CHECK_SUBGRAPH(m_Model, subgraphId);
3371 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
3372 std::vector<std::string> result;
3373 result.reserve(outputs.size());
3374 for (auto const & output : outputs)
3375 {
3376 result.push_back(output.second->name);
3377 }
3378 return result;
3379}
3380
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003381ITfLiteParser* ITfLiteParser::CreateRaw(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01003382{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003383 return new TfLiteParser(options);
telsoa01c577f2c2018-08-31 09:22:23 +01003384}
3385
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003386ITfLiteParserPtr ITfLiteParser::Create(const Optional<ITfLiteParser::TfLiteParserOptions>& options)
telsoa01c577f2c2018-08-31 09:22:23 +01003387{
Aron Virginas-Tarc975f922019-10-23 17:38:17 +01003388 return ITfLiteParserPtr(CreateRaw(options), &ITfLiteParser::Destroy);
telsoa01c577f2c2018-08-31 09:22:23 +01003389}
3390
3391void ITfLiteParser::Destroy(ITfLiteParser* parser)
3392{
3393 delete parser;
3394}
3395
3396TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
3397: m_FloatData(std::move(data))
3398, m_Uint8Data(nullptr)
Keith Davisd305e1a2020-01-22 11:57:54 +00003399, m_Int8Data(nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +01003400, m_Int32Data(nullptr)
3401{
3402}
3403
3404TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
3405: m_FloatData(nullptr)
3406, m_Uint8Data(std::move(data))
Keith Davisd305e1a2020-01-22 11:57:54 +00003407, m_Int8Data(nullptr)
3408, m_Int32Data(nullptr)
3409{
3410}
3411
3412TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]> && data)
3413: m_FloatData(nullptr)
3414, m_Uint8Data(nullptr)
3415, m_Int8Data(std::move(data))
telsoa01c577f2c2018-08-31 09:22:23 +01003416, m_Int32Data(nullptr)
3417{
3418}
3419
3420TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
3421: m_FloatData(nullptr)
3422, m_Uint8Data(nullptr)
Keith Davisd305e1a2020-01-22 11:57:54 +00003423, m_Int8Data(nullptr)
telsoa01c577f2c2018-08-31 09:22:23 +01003424, m_Int32Data(std::move(data))
3425{
3426}
3427
3428} // armnnTfLiteParser