ArmNN
 25.11
Loading...
Searching...
No Matches
TfLiteParser.cpp
Go to the documentation of this file.
1//
2// Copyright © 2017-2025 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6// Do not include flatbuffers::ClassicLocale which can cause abort when destroyed
7// This define must be added before the include or it causes a macro redefine error
8#define FLATBUFFERS_LOCALE_INDEPENDENT 0
9
10#include "TfLiteParser.hpp"
11
13#include "armnn/LstmParams.hpp"
14
16#include <armnn/Descriptors.hpp>
17#include <armnn/Exceptions.hpp>
18#include <armnn/Logging.hpp>
19#include <armnn/Tensor.hpp>
21#include <armnn/TypesUtils.hpp>
25
26// armnnUtils:
29
30#include <ParserHelper.hpp>
32
33// The generated code based on the Tf Lite schema:
34#include <schema_generated.h>
35
36#include <flatbuffers/flexbuffers.h>
37
38#include <fmt/format.h>
39
40#include <algorithm>
41#include <iostream>
42#include <limits>
43#include <numeric>
44
45#define ARMNN_THROW_PARSE_EXCEPTION(msg) \
46 { \
47 throw armnn::ParseException( static_cast<const std::stringstream&>( std::stringstream() << msg \
48 << ": " \
49 << CHECK_LOCATION().AsString()).str()); \
50 }
51
52using namespace armnn;
54namespace armnnTfLiteParser
55{
56
57ITfLiteParser::ITfLiteParser(const armnn::Optional<TfLiteParserOptions>& options) :
58 pTfLiteParserImpl(new TfLiteParserImpl(options)) {}
59
60ITfLiteParser::~ITfLiteParser() = default;
61
63{
64 return new ITfLiteParser(options);
65}
66
71
72void ITfLiteParser::Destroy(ITfLiteParser* parser)
73{
74 delete parser;
75}
76
78{
79 return pTfLiteParserImpl->CreateNetworkFromBinaryFile(graphFile);
80}
81
82armnn::INetworkPtr ITfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t>& binaryContent)
83{
84 return pTfLiteParserImpl->CreateNetworkFromBinary(binaryContent);
85}
86
88 const std::string& name) const
89{
90 return pTfLiteParserImpl->GetNetworkInputBindingInfo(subgraphId, name);
91}
92
94 const std::string& name) const
95{
96 return pTfLiteParserImpl->GetNetworkOutputBindingInfo(subgraphId, name);
97}
98
100{
101 return pTfLiteParserImpl->GetSubgraphCount();
102}
103
104std::vector<std::string> ITfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
105{
106 return pTfLiteParserImpl->GetSubgraphInputTensorNames(subgraphId);
107}
108
109std::vector<std::string> ITfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
110{
111 return pTfLiteParserImpl->GetSubgraphOutputTensorNames(subgraphId);
112}
113
114namespace
115{
116
117const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
118
119void CheckSubgraph(const TfLiteParserImpl::ModelPtr& model,
120 size_t subgraphIndex,
121 const CheckLocation& location)
122{
123 if (model.get() == nullptr)
124 {
125 throw ParseException(
126 fmt::format("{} was called with invalid (null) model. "
127 "Possible reason is that the model is not yet loaded and Unpack(ed). "
128 "subgraph:{} at {}",
129 location.m_Function,
130 subgraphIndex,
131 location.FileLine()));
132 }
133 else if (subgraphIndex >= model->subgraphs.size())
134 {
135 throw ParseException(
136 fmt::format("{} was called with an invalid subgraph index. "
137 "subgraph:{} at {}",
138 location.m_Function,
139 subgraphIndex,
140 location.FileLine()));
141 }
142}
143
144#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
145 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
146
147void CheckModel(const TfLiteParserImpl::ModelPtr& model,
148 size_t subgraphIndex,
149 size_t operatorIndex,
150 const CheckLocation& location)
151{
152 if (model.get() == nullptr)
153 {
154 throw ParseException(
155 fmt::format("{} was called with invalid (null) model. "
156 "Possible reason is that the model is not yet loaded and Unpack(ed). "
157 "subgraph:{} operator:{} at {}",
158 location.m_Function,
159 subgraphIndex,
160 operatorIndex,
161 location.FileLine()));
162 }
163 else if (subgraphIndex >= model->subgraphs.size())
164 {
165 throw ParseException(
166 fmt::format("{} was called with an invalid subgraph index. "
167 "subgraph:{} operator:{} at {}",
168 location.m_Function,
169 subgraphIndex,
170 operatorIndex,
171 location.FileLine()));
172 }
173 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
174 operatorIndex != VIRTUAL_OPERATOR_ID)
175 {
176 throw ParseException(
177 fmt::format("{} was called with an invalid operator index. "
178 "subgraph:{} operator:{} at {}",
179 location.m_Function,
180 subgraphIndex,
181 operatorIndex,
182 location.FileLine()));
183 }
184}
185
186#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
187 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
188
189void CheckTensor(const TfLiteParserImpl::ModelPtr& model,
190 size_t subgraphIndex,
191 size_t tensorIndex,
192 const CheckLocation& location)
193{
194 // the tensor index is the only one to check here
195 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
196 {
197 throw ParseException(
198 fmt::format("{} was called with an invalid tensor index. "
199 "subgraph:{} tensor:{} at {}",
200 location.m_Function,
201 subgraphIndex,
202 tensorIndex,
203 location.FileLine()));
204 }
205}
206
207#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
208 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
209
210void CheckTensorPtr(TfLiteParserImpl::TensorRawPtr rawPtr,
211 const CheckLocation& location)
212{
213 if (rawPtr == nullptr)
214 {
215 throw ParseException(
216 fmt::format("{} was called with a null tensor pointer at {}", location.m_Function, location.FileLine()));
217 }
218}
219
220#define CHECK_TENSOR_PTR(TENSOR_PTR) \
221 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
222
223void CheckBuffer(const TfLiteParserImpl::ModelPtr& model,
224 size_t bufferIndex,
225 const CheckLocation& location)
226{
227 if (model.get() == nullptr)
228 {
229 throw ParseException(
230 fmt::format("{} was called with invalid (null) model. "
231 "Possible reason is that the model is not yet loaded and Unpack(ed). "
232 "buffer:{} at {}",
233 location.m_Function,
234 bufferIndex,
235 location.FileLine()));
236 }
237 else if (bufferIndex >= model->buffers.size())
238 {
239 throw ParseException(
240 fmt::format("{} was called with an invalid buffer index. "
241 "buffer index:{} at {}",
242 location.m_Function,
243 bufferIndex,
244 location.FileLine()));
245 }
246 else if (model->buffers[bufferIndex].get() == nullptr)
247 {
248 throw ParseException(
249 fmt::format("The buffer #{} is null. {}",
250 bufferIndex,
251 location.AsString()));
252 }
253}
254
255#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
256 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
257
258void CheckBufferSize(TfLiteParserImpl::BufferRawPtr bufferPtr,
259 const armnn::TensorInfo& tensorInfo,
260 uint32_t bufferId,
261 const CheckLocation& location)
262{
263 if (bufferPtr == nullptr)
264 {
265 throw ParseException(
266 fmt::format("BufferPtr is null for buffer:{}. {}",
267 bufferId,
268 location.AsString()));
269 }
270 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
271 tensorInfo.GetNumBytes() > bufferPtr->data.size())
272 {
273 std::stringstream ss;
274 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
275 << "For tensor: " << tensorInfo.GetShape()
276 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
277 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
278 throw ParseException(ss.str());
279 }
280}
281
282
283tflite::BuiltinOperator GetOpCode(const TfLiteParserImpl::ModelPtr& model, size_t subgraphIndex, size_t operatorIndex)
284{
285 const auto& operatorPtr = model->subgraphs[subgraphIndex]->operators[operatorIndex];
286 auto opcodeIndex = operatorPtr->opcode_index;
287
288// work around the introduction of the deprecated_builtin_code introduced in 2.4 in a backwards compatible manner
289#if defined(ARMNN_POST_TFLITE_2_3)
290 auto opcode = std::max(model->operator_codes[opcodeIndex]->builtin_code,
291 static_cast<tflite::BuiltinOperator>(model->operator_codes[opcodeIndex]->deprecated_builtin_code));
292#else
293 auto opcode = model->operator_codes[opcodeIndex]->builtin_code;
294#endif
295 return opcode;
296}
297
298std::vector<unsigned int> GetUIntBuffer(armnn::TensorInfo info,
299 const TfLiteParserImpl::ModelPtr& model,
300 size_t bufferIndex)
301{
302 TfLiteParserImpl::BufferRawPtr bufferPtr = TfLiteParserImpl::GetBuffer(model, bufferIndex);
303
304 if (!bufferPtr || bufferPtr->data.empty())
305 {
306 throw ParseException("UInt buffer is null or empty.");
307 }
308
309 const size_t numElements = info.GetNumElements();
310 std::vector<unsigned int> buffer(info.GetNumElements());
311 buffer.reserve(numElements);
312
313 if (info.GetDataType() == DataType::Signed32)
314 {
315 size_t requiredBytes = numElements * sizeof(int32_t);
316 if (bufferPtr->data.size() < requiredBytes)
317 {
318 throw ParseException("UInt buffer too small for Signed32 tensor.");
319 }
320
321 ::memcpy(buffer.data(), bufferPtr->data.data(), requiredBytes);
322 }
323 else if (info.GetDataType() == DataType::Signed64)
324 {
325 size_t requiredBytes = numElements * sizeof(int64_t);
326 if (bufferPtr->data.size() < requiredBytes)
327 {
328 throw ParseException("UInt buffer too small for Signed64 tensor.");
329 }
330
331 std::vector<uint64_t> uint64Buffer(numElements);
332 ::memcpy(uint64Buffer.data(), bufferPtr->data.data(), requiredBytes);
333 buffer.assign(std::begin(uint64Buffer), std::end(uint64Buffer));
334 }
335 else
336 {
337 CheckLocation location = CHECK_LOCATION();
338 throw ParseException(
339 fmt::format("Unsupported data type for uint buffer {}, only Signed 32 or Signed 64 are supported. {}",
340 GetDataTypeName(info.GetDataType()),
341 location.AsString()));
342 }
343 return buffer;
344}
345
346#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
347 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
348
349bool IsActivationSupported(tflite::ActivationFunctionType activationType)
350{
351 switch(activationType)
352 {
353 case tflite::ActivationFunctionType_NONE:
354 case tflite::ActivationFunctionType_RELU:
355 case tflite::ActivationFunctionType_RELU6:
356 case tflite::ActivationFunctionType_TANH:
357 {
358 return true;
359 }
360 default:
361 {
362 return false;
363 }
364 }
365}
366
367#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
368 do { \
369 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
370 { \
371 throw ParseException( \
372 fmt::format("TfLite parser doesn't support fused activation: " \
373 "{}/{} in {} subgraph:{} operator:{} at {}", \
374 OPTION->fused_activation_function, \
375 tflite::EnumNameActivationFunctionType(\
376 OPTION->fused_activation_function), \
377 __func__, \
378 SUBGRAPH_INDEX, \
379 OPERATOR_INDEX, \
380 CHECK_LOCATION().FileLine())); \
381 } \
382 } while(false)
383
384
385std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t>& in)
386{
387 std::vector<unsigned int> result;
388 result.reserve(in.size());
389 for (auto& i : in)
390 {
391 // If the location of the input data is -1 then the input should be ignored.
392 if (i == -1)
393 {
394 continue;
395 }
396 result.push_back(CHECKED_NON_NEGATIVE(i));
397 }
398 return result;
399}
400
401bool IsOptionalOperandPresent(int input)
402{
403 return (input >= 0);
404}
405
406void CalcPadding(uint32_t inputSize,
407 uint32_t filterSize,
408 uint32_t stride,
409 uint32_t dilation,
410 uint32_t& paddingFront,
411 uint32_t& paddingBack,
412 tflite::Padding padding)
413{
414 paddingFront = 0;
415 paddingBack = 0;
416 if (padding == tflite::Padding_SAME)
417 {
418 uint32_t outputSize = (inputSize + stride - 1) / stride;
419 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
420 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
421 if (temp > inputSize)
422 {
423 paddingFront = (temp - inputSize) / 2;
424 paddingBack = (temp - inputSize) - paddingFront;
425 }
426 }
427}
428
429// Function that calculates explicit padding when the output shape is known.
430// At the moment the output is only given as an input parameter in Transpose Convolution,
431// not in Convolution and Depthwise Convolution
432void CalcPadding(uint32_t inputSize,
433 uint32_t filterSize,
434 uint32_t stride,
435 uint32_t dilation,
436 uint32_t& paddingFront,
437 uint32_t& paddingBack,
438 tflite::Padding padding,
439 uint32_t outputSize)
440{
441 IgnoreUnused(dilation);
442 paddingFront = 0;
443 paddingBack = 0;
444 if (padding == tflite::Padding_SAME)
445 {
446 uint32_t totalPadding = (inputSize - 1) * stride + filterSize - outputSize;
447 paddingFront = totalPadding / 2;
448 paddingBack = totalPadding - paddingFront;
449 }
450}
451
452armnn::TensorInfo ToTensorInfo(TfLiteParserImpl::TensorRawPtr tensorPtr,
453 const std::vector<unsigned int>& shape,
454 const bool outputTensor = false)
455{
456 armnn::DataType type;
457 CHECK_TENSOR_PTR(tensorPtr);
458
459 switch (tensorPtr->type)
460 {
461 case tflite::TensorType_UINT8:
463 break;
464 case tflite::TensorType_FLOAT32:
466 break;
467 case tflite::TensorType_FLOAT16:
469 break;
470 case tflite::TensorType_INT8:
471 if (tensorPtr->quantization->zero_point.size() == 1)
472 {
473 // Per-tensor
475 }
476 else
477 {
478 // Per-channel
480 }
481 break;
482 case tflite::TensorType_INT16:
484 break;
485 case tflite::TensorType_INT32:
487 break;
488 case tflite::TensorType_INT64:
490 break;
491 case tflite::TensorType_BOOL:
493 break;
494 default:
495 {
496 CheckLocation location = CHECK_LOCATION();
497 throw ParseException(
498 fmt::format("Unsupported data type {} = {} for tensor: {}. {}",
499 tensorPtr->type,
500 tflite::EnumNameTensorType(tensorPtr->type),
501 tensorPtr->name,
502 location.AsString()));
503 }
504 }
505 TensorShape tensorShape;
506
507 std::vector<unsigned int> safeShape = shape;
508 if (shape.size() == 0)
509 {
510 safeShape.push_back(1);
511 }
512
513 if (!outputTensor)
514 {
515 tensorShape = TensorShape(armnn::numeric_cast<unsigned int>(safeShape.size()), safeShape.data());
516 }
517 else
518 {
519 size_t shapeSignatureSize = tensorPtr->shape_signature.size();
520
521 // If a shape signature exists we will use that to infer dynamic tensors
522 if (shapeSignatureSize != 0)
523 {
524 // If the shape is incompatible with the shape signature override the shape
525 if (shapeSignatureSize != shape.size())
526 {
527 safeShape = {};
528
529 for (unsigned int i = 0; i < shapeSignatureSize; ++i)
530 {
531 unsigned int dim = tensorPtr->shape_signature[i] > -1 ?
532 static_cast<unsigned int>(tensorPtr->shape_signature[i]) : 0;
533 safeShape.push_back(dim);
534 }
535 }
536
537 std::unique_ptr<bool[]> dimMask = std::make_unique<bool[]>(tensorPtr->shape_signature.size());
538 bool batchOnly = true;
539 for (unsigned int i = 0; i < tensorPtr->shape_signature.size(); ++i)
540 {
541 dimMask[i] = tensorPtr->shape_signature[i] != -1;
542
543 if (i > 0 && !dimMask[i])
544 {
545 batchOnly = false;
546 }
547 }
548 if (batchOnly)
549 {
550 dimMask[0] = true;
551 }
552 tensorShape = TensorShape(static_cast<unsigned int>(safeShape.size()), safeShape.data(), dimMask.get());
553 }
554 // If there is no shape signature treat the tensor as dynamic if the shape has a size of zero
555 else if (shape.size() == 0)
556 {
557 tensorShape = TensorShape(1, false);
558 }
559 else
560 {
561 tensorShape = TensorShape(armnn::numeric_cast<unsigned int>(shape.size()), shape.data());
562 }
563 }
564
565 float quantizationScale = 1.0f;
566 int32_t quantizationOffset = 0;
567
568 if (tensorPtr->quantization.get())
569 {
570 if (tensorPtr->quantization->scale.size() <= 1)
571 {
572 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
573 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
574
575 if (tensorPtr->quantization->scale.size() == 1)
576 {
577 quantizationScale = tensorPtr->quantization->scale[0];
578 }
579 if (tensorPtr->quantization->zero_point.size() == 1)
580 {
581 // NOTE: we lose precision here when converting from 64 bit to 32
582 // but this is what we support at the moment in ArmNN
583 quantizationOffset = armnn::numeric_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
584 }
585
586 armnn::TensorInfo result(tensorShape,
587 type,
588 quantizationScale,
589 quantizationOffset);
590 return result;
591 }
592 else
593 {
594 std::vector<float> quantizationScales;
595 std::vector<int32_t> quantizationOffsets;
596
597 // Scale
598 std::copy(tensorPtr->quantization->scale.begin(),
599 tensorPtr->quantization->scale.end(),
600 std::back_inserter(quantizationScales));
601
602 // QSymmS8 Per-axis
603 armnn::TensorInfo result(tensorShape,
604 type,
605 quantizationScales,
606 armnn::numeric_cast<unsigned int>(tensorPtr->quantization->quantized_dimension));
607 return result;
608 }
609 }
610 else
611 {
612 armnn::TensorInfo result(tensorShape,
613 type,
614 quantizationScale,
615 quantizationOffset);
616 return result;
617 }
618}
619
620armnn::TensorInfo ToTensorInfo(TfLiteParserImpl::TensorRawPtr tensorPtr,
621 const bool outputTensor = false)
622{
623 auto const& dimensions = AsUnsignedVector(tensorPtr->shape);
624 return ToTensorInfo(tensorPtr, dimensions, outputTensor);
625}
626
627template<typename T>
628std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
631 armnn::TensorInfo& tensorInfo,
632 armnn::Optional<armnn::PermutationVector&> permutationVector)
633{
634 IgnoreUnused(tensorPtr);
635
636 if (!tensorPtr)
637 {
638 throw armnn::ParseException(fmt::format("Tensor pointer is null {}", CHECK_LOCATION().AsString()));
639 }
640
641 if (!bufferPtr)
642 {
643 throw armnn::ParseException(fmt::format("Buffer for buffer:{} is null", tensorPtr->buffer).c_str());
644 }
645
646 size_t requiredBytes = tensorInfo.GetNumBytes();
647 if (bufferPtr->data.size() < requiredBytes)
648 {
649 throw ParseException(fmt::format("Buffer for buffer:{} too small. Expected at least {} bytes, got {}.",
650 tensorPtr->buffer, requiredBytes, bufferPtr->data.size()));
651 }
652
653 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
654
655 if (permutationVector.has_value() && permutationVector.value().GetSize() > 0)
656 {
657 tensorInfo = armnnUtils::Permuted(tensorInfo, permutationVector.value());
658 armnnUtils::Permute(tensorInfo.GetShape(), permutationVector.value(),
659 reinterpret_cast<const T*>(bufferPtr->data.data()), data.get(), sizeof(T));
660 }
661 else
662 {
663 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
664 }
665
666 // Make sure isConstant flag is set.
667 tensorInfo.SetConstant();
668
669 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
670}
671
672armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
673{
674 // generate the binding id by shifting the tensor id by 8 bit
675 // and add the subgraph id, which allows 256 subgraphs
676 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
677}
678
679bool CheckShape(const armnn::TensorShape& actual, const std::vector<int32_t>& expected)
680{
681 const unsigned int actualSize = actual.GetNumDimensions();
682 if (actualSize != expected.size())
683 {
684 return false;
685 }
686
687 for (unsigned int i = 0u; i < actualSize; i++)
688 {
689 if (expected[i] < 0 ||
690 actual[i] != static_cast<unsigned int>(expected[i]))
691 {
692 return false;
693 }
694 }
695
696 return true;
697}
698
699bool CheckShape(const armnn::TensorShape& actual, const armnn::TensorShape& expected)
700{
701 std::vector<int32_t> expectedVec;
702 for (uint32_t i = 0; i < expected.GetNumDimensions(); i++)
703 {
704 expectedVec.push_back(expected[i]);
705 }
706 return CheckShape(actual, expectedVec);
707}
708
709void CheckMatchingQuantization(const TensorInfo& first,
710 const TensorInfo& second,
711 const std::string& descName,
712 std::string const& firstName,
713 std::string const& secondName)
714{
715 if (!first.IsQuantized() ||
716 !second.IsQuantized())
717 {
718 // Not a quantized type, ignore the validation
719 return;
720 }
721
722 DataType firstDataType = first.GetDataType();
723 DataType secondDataType = second.GetDataType();
724
725 if (firstDataType != secondDataType)
726 {
727 throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
728 " must be of the same quantized type, " +
729 firstName + " is " + GetDataTypeName(firstDataType) + ", " +
730 secondName + " is " + GetDataTypeName(secondDataType));
731 }
732
733 if (!first.IsTypeSpaceMatch(second))
734 {
735 throw InvalidArgumentException(descName + ": " + firstName + " and " + secondName +
736 " must have the same quantization space, " +
737 firstName + " has offset " + std::to_string(first.GetQuantizationOffset()) +
738 " and scale " + std::to_string(first.GetQuantizationScale()) + ", " +
739 secondName + " has offset " + std::to_string(second.GetQuantizationOffset()) +
740 " and scale " + std::to_string(second.GetQuantizationScale()));
741 }
742}
743
744bool IsDynamic(TfLiteParserImpl::TensorRawPtr tensorPtr)
745{
746 auto shape = tensorPtr->shape;
747
748 if (shape.empty())
749 {
750 return true;
751 }
752 auto shapeSig = tensorPtr->shape_signature;
753
754 if (shapeSig.empty())
755 {
756 return false;
757 }
758
759 for (unsigned int i = 0; i < shapeSig.size() ; ++i)
760 {
761 if (shapeSig[i] == -1)
762 {
763 return true;
764 }
765 }
766 return false;
767}
768
769} // <anonymous>
770
772: m_Options(options)
773, m_Network(nullptr, nullptr)
774, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParserImpl::ParseUnsupportedOperator)
775{
776 // register supported operators
777 m_ParserFunctions[tflite::BuiltinOperator_ABS] = &TfLiteParserImpl::ParseAbs;
778 m_ParserFunctions[tflite::BuiltinOperator_ADD] = &TfLiteParserImpl::ParseAdd;
779 m_ParserFunctions[tflite::BuiltinOperator_ARG_MIN] = &TfLiteParserImpl::ParseArgMin;
780 m_ParserFunctions[tflite::BuiltinOperator_ARG_MAX] = &TfLiteParserImpl::ParseArgMax;
781 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParserImpl::ParseAveragePool2D;
782 m_ParserFunctions[tflite::BuiltinOperator_BATCH_TO_SPACE_ND] = &TfLiteParserImpl::ParseBatchToSpaceND;
783 m_ParserFunctions[tflite::BuiltinOperator_BATCH_MATMUL] = &TfLiteParserImpl::ParseBatchMatMul;
784 m_ParserFunctions[tflite::BuiltinOperator_BROADCAST_TO] = &TfLiteParserImpl::ParseBroadcastTo;
785 m_ParserFunctions[tflite::BuiltinOperator_CEIL] = &TfLiteParserImpl::ParseCeil;
786 m_ParserFunctions[tflite::BuiltinOperator_CAST] = &TfLiteParserImpl::ParseCast;
787 m_ParserFunctions[tflite::BuiltinOperator_CONCATENATION] = &TfLiteParserImpl::ParseConcatenation;
788 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParserImpl::ParseConv2D;
789 // Conv3D support was added in TF 2.5, so for backwards compatibility a hash define is needed.
790 #if defined(ARMNN_POST_TFLITE_2_4)
791 m_ParserFunctions[tflite::BuiltinOperator_CONV_3D] = &TfLiteParserImpl::ParseConv3D;
792 #endif
793 m_ParserFunctions[tflite::BuiltinOperator_CUSTOM] = &TfLiteParserImpl::ParseCustomOperator;
794 m_ParserFunctions[tflite::BuiltinOperator_DEPTH_TO_SPACE] = &TfLiteParserImpl::ParseDepthToSpace;
795 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParserImpl::ParseDepthwiseConv2D;
796 m_ParserFunctions[tflite::BuiltinOperator_DEQUANTIZE] = &TfLiteParserImpl::ParseDequantize;
797 m_ParserFunctions[tflite::BuiltinOperator_DIV] = &TfLiteParserImpl::ParseDiv;
798 m_ParserFunctions[tflite::BuiltinOperator_ELU] = &TfLiteParserImpl::ParseElu;
799 m_ParserFunctions[tflite::BuiltinOperator_EQUAL] = &TfLiteParserImpl::ParseEqual;
800 m_ParserFunctions[tflite::BuiltinOperator_EXP] = &TfLiteParserImpl::ParseExp;
801 m_ParserFunctions[tflite::BuiltinOperator_EXPAND_DIMS] = &TfLiteParserImpl::ParseExpandDims;
802 m_ParserFunctions[tflite::BuiltinOperator_FLOOR_DIV] = &TfLiteParserImpl::ParseFloorDiv;
803 m_ParserFunctions[tflite::BuiltinOperator_FULLY_CONNECTED] = &TfLiteParserImpl::ParseFullyConnected;
804 m_ParserFunctions[tflite::BuiltinOperator_GATHER] = &TfLiteParserImpl::ParseGather;
805 m_ParserFunctions[tflite::BuiltinOperator_GELU] = &TfLiteParserImpl::ParseGelu;
806 m_ParserFunctions[tflite::BuiltinOperator_GATHER_ND] = &TfLiteParserImpl::ParseGatherNd;
807 m_ParserFunctions[tflite::BuiltinOperator_GREATER] = &TfLiteParserImpl::ParseGreater;
808 m_ParserFunctions[tflite::BuiltinOperator_GREATER_EQUAL] = &TfLiteParserImpl::ParseGreaterOrEqual;
809 m_ParserFunctions[tflite::BuiltinOperator_HARD_SWISH] = &TfLiteParserImpl::ParseHardSwish;
810 m_ParserFunctions[tflite::BuiltinOperator_LEAKY_RELU] = &TfLiteParserImpl::ParseLeakyRelu;
811 m_ParserFunctions[tflite::BuiltinOperator_LESS] = &TfLiteParserImpl::ParseLess;
812 m_ParserFunctions[tflite::BuiltinOperator_LESS_EQUAL] = &TfLiteParserImpl::ParseLessOrEqual;
813 m_ParserFunctions[tflite::BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION]
814 = &TfLiteParserImpl::ParseLocalResponseNormalization;
815 m_ParserFunctions[tflite::BuiltinOperator_LOG] = &TfLiteParserImpl::ParseLog;
816 m_ParserFunctions[tflite::BuiltinOperator_LOGICAL_NOT] = &TfLiteParserImpl::ParseLogicalNot;
817 m_ParserFunctions[tflite::BuiltinOperator_LOGISTIC] = &TfLiteParserImpl::ParseLogistic;
818 m_ParserFunctions[tflite::BuiltinOperator_LOG_SOFTMAX] = &TfLiteParserImpl::ParseLogSoftmax;
819 m_ParserFunctions[tflite::BuiltinOperator_L2_NORMALIZATION] = &TfLiteParserImpl::ParseL2Normalization;
820 m_ParserFunctions[tflite::BuiltinOperator_MAX_POOL_2D] = &TfLiteParserImpl::ParseMaxPool2D;
821 m_ParserFunctions[tflite::BuiltinOperator_MAXIMUM] = &TfLiteParserImpl::ParseMaximum;
822 m_ParserFunctions[tflite::BuiltinOperator_MEAN] = &TfLiteParserImpl::ParseMean;
823 m_ParserFunctions[tflite::BuiltinOperator_MINIMUM] = &TfLiteParserImpl::ParseMinimum;
824 m_ParserFunctions[tflite::BuiltinOperator_MIRROR_PAD] = &TfLiteParserImpl::ParseMirrorPad;
825 m_ParserFunctions[tflite::BuiltinOperator_MUL] = &TfLiteParserImpl::ParseMul;
826 m_ParserFunctions[tflite::BuiltinOperator_NEG] = &TfLiteParserImpl::ParseNeg;
827 m_ParserFunctions[tflite::BuiltinOperator_NOT_EQUAL] = &TfLiteParserImpl::ParseNotEqual;
828 m_ParserFunctions[tflite::BuiltinOperator_PACK] = &TfLiteParserImpl::ParsePack;
829 m_ParserFunctions[tflite::BuiltinOperator_PAD] = &TfLiteParserImpl::ParsePad;
830 m_ParserFunctions[tflite::BuiltinOperator_PADV2] = &TfLiteParserImpl::ParsePad;
831 m_ParserFunctions[tflite::BuiltinOperator_POW] = &TfLiteParserImpl::ParsePower;
832 m_ParserFunctions[tflite::BuiltinOperator_PRELU] = &TfLiteParserImpl::ParsePrelu;
833 m_ParserFunctions[tflite::BuiltinOperator_QUANTIZE] = &TfLiteParserImpl::ParseQuantize;
834 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParserImpl::ParseRelu;
835 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParserImpl::ParseRelu6;
836 m_ParserFunctions[tflite::BuiltinOperator_REDUCE_MAX] = &TfLiteParserImpl::ParseReduceMax;
837 m_ParserFunctions[tflite::BuiltinOperator_REDUCE_MIN] = &TfLiteParserImpl::ParseReduceMin;
838 m_ParserFunctions[tflite::BuiltinOperator_REDUCE_PROD] = &TfLiteParserImpl::ParseReduceProd;
839 m_ParserFunctions[tflite::BuiltinOperator_RESHAPE] = &TfLiteParserImpl::ParseReshape;
840 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_BILINEAR] = &TfLiteParserImpl::ParseResizeBilinear;
841 m_ParserFunctions[tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR] = &TfLiteParserImpl::ParseResizeNearestNeighbor;
842 m_ParserFunctions[tflite::BuiltinOperator_REVERSE_V2] = &TfLiteParserImpl::ParseReverseV2;
843 m_ParserFunctions[tflite::BuiltinOperator_RSQRT] = &TfLiteParserImpl::ParseRsqrt;
844 m_ParserFunctions[tflite::BuiltinOperator_SCATTER_ND] = &TfLiteParserImpl::ParseScatterNd;
845 m_ParserFunctions[tflite::BuiltinOperator_SQRT] = &TfLiteParserImpl::ParseSqrt;
846 m_ParserFunctions[tflite::BuiltinOperator_SHAPE] = &TfLiteParserImpl::ParseShape;
847 m_ParserFunctions[tflite::BuiltinOperator_SIN] = &TfLiteParserImpl::ParseSin;
848 m_ParserFunctions[tflite::BuiltinOperator_SLICE] = &TfLiteParserImpl::ParseSlice;
849 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParserImpl::ParseSoftmax;
850 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_BATCH_ND] = &TfLiteParserImpl::ParseSpaceToBatchND;
851 m_ParserFunctions[tflite::BuiltinOperator_SPACE_TO_DEPTH] = &TfLiteParserImpl::ParseSpaceToDepth;
852 m_ParserFunctions[tflite::BuiltinOperator_SPLIT] = &TfLiteParserImpl::ParseSplit;
853 m_ParserFunctions[tflite::BuiltinOperator_SPLIT_V] = &TfLiteParserImpl::ParseSplitV;
854 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParserImpl::ParseSqueeze;
855 m_ParserFunctions[tflite::BuiltinOperator_SQUARE] = &TfLiteParserImpl::ParseSquare;
856 m_ParserFunctions[tflite::BuiltinOperator_SQUARED_DIFFERENCE] = &TfLiteParserImpl::ParseSquaredDifference;
857 m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParserImpl::ParseStridedSlice;
858 m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParserImpl::ParseSub;
859 m_ParserFunctions[tflite::BuiltinOperator_SUM] = &TfLiteParserImpl::ParseSum;
860 m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParserImpl::ParseTanH;
861 m_ParserFunctions[tflite::BuiltinOperator_TILE] = &TfLiteParserImpl::ParseTile;
862 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParserImpl::ParseTranspose;
863 m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParserImpl::ParseTransposeConv;
864 m_ParserFunctions[tflite::BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM]
865 = &TfLiteParserImpl::ParseUnidirectionalSequenceLSTM;
866 m_ParserFunctions[tflite::BuiltinOperator_UNPACK] = &TfLiteParserImpl::ParseUnpack;
867
868 // register supported custom operators
869 m_CustomParserFunctions["TFLite_Detection_PostProcess"] = &TfLiteParserImpl::ParseDetectionPostProcess;
870}
871
872armnn::TensorInfo TfLiteParserImpl::InputTensorInfo(size_t subgraphIndex,
873 size_t operatorIndex,
874 int input)
875{
876 const auto& subgraphPtr = m_Model->subgraphs[subgraphIndex];
877 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
878
879 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[input]);
880 auto search = armnnTfLiteParser::TfLiteParserImpl::m_TensorInfos.find(inputId);
881
882 if (search != m_TensorInfos.end())
883 {
884 return m_TensorInfos[inputId];
885 }
886 else
887 {
888 auto tensorInfo = ::armnnTfLiteParser::ToTensorInfo(subgraphPtr->tensors[inputId].get());
889 m_TensorInfos.insert({ inputId, tensorInfo });
890 return tensorInfo;
891 }
892}
893
894armnn::TensorInfo TfLiteParserImpl::OutputTensorInfoFromInputs(size_t subgraphIndex,
895 size_t operatorIndex,
896 armnn::IConnectableLayer* layer,
897 int output,
898 std::vector<int> inputs)
899{
900 const auto& subgraphPtr = m_Model->subgraphs[subgraphIndex];
901 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
902
903 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[output]);
904
905 auto outputSearch = armnnTfLiteParser::TfLiteParserImpl::m_TensorInfos.find(outputId);
906
907 if (outputSearch != m_TensorInfos.end())
908 {
909 return m_TensorInfos[outputId];
910 }
911
912 const auto& outputTensorPtr = subgraphPtr->tensors[outputId].get();
913 TensorInfo tensor = ::armnnTfLiteParser::ToTensorInfo(outputTensorPtr, true);
914
915 if (IsDynamic(outputTensorPtr))
916 {
917 if (inputs.empty())
918 {
919 for (unsigned int i = 0; i < layer->GetNumInputSlots(); ++i)
920 {
921 inputs.emplace_back(i);
922 }
923 }
924 auto inputTensorIds = GetInputTensorIds(m_Model, subgraphIndex, operatorIndex);
925 std::vector<armnn::TensorShape> inputShapes;
926
927 for (unsigned int i = 0; i < inputs.size(); ++i)
928 {
929 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[inputs[i]]);
930 auto search = armnnTfLiteParser::TfLiteParserImpl::m_TensorInfos.find(inputId);
931
932 if (search != m_TensorInfos.end())
933 {
934 auto &inputTensorInfo = m_TensorInfos[inputId];
935 inputShapes.push_back(inputTensorInfo.GetShape());
936 }
937 else
938 {
939 auto inputTensorInfo = ::armnnTfLiteParser::ToTensorInfo(subgraphPtr->tensors[inputId].get());
940 m_TensorInfos.insert({ inputId, inputTensorInfo});
941 inputShapes.push_back(inputTensorInfo.GetShape());
942 }
943 }
944 const auto outputShape = layer->InferOutputShapes(inputShapes)[output];
945 tensor.SetShape(outputShape);
946 }
947 m_TensorInfos.insert({ outputId, tensor});
948 return tensor;
949}
950
951armnn::TensorInfo TfLiteParserImpl::OutputTensorInfoFromShapes(size_t subgraphIndex,
952 size_t operatorIndex,
953 armnn::IConnectableLayer* layer,
954 int output,
955 std::vector<armnn::TensorShape> inputShapes)
956{
957 const auto& subgraphPtr = m_Model->subgraphs[subgraphIndex];
958 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
959
960 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[output]);
961 const auto& outputTensorPtr = subgraphPtr->tensors[outputId].get();
962 TensorInfo tensor = ::armnnTfLiteParser::ToTensorInfo(outputTensorPtr, true);
963
964 if (IsDynamic(outputTensorPtr))
965 {
966 const auto outputShape = layer->InferOutputShapes(inputShapes)[output];
967 tensor.SetShape(outputShape);
968 }
969 m_TensorInfos.insert({ outputId, tensor});
970 return tensor;
971}
972
973void TfLiteParserImpl::ResetParser()
974{
975 m_Network = armnn::INetworkPtr(nullptr, nullptr);
976 m_Model = nullptr;
977 m_SubgraphConnections.clear();
978 m_OverriddenOutputShapes.clear();
979 m_ConstantsToDequantize.clear();
980 m_ConstantsToBeCreated.clear();
981 m_TensorInfos.clear();
982}
983
985{
986 ResetParser();
987 m_Model = LoadModelFromFile(graphFile);
988 return CreateNetworkFromModel();
989}
990
991INetworkPtr TfLiteParserImpl::CreateNetworkFromBinary(const std::vector<uint8_t>& binaryContent)
992{
993 ResetParser();
994 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
995 return CreateNetworkFromModel();
996}
997
998
999armnn::INetworkPtr TfLiteParserImpl::LoadModel(std::unique_ptr<tflite::ModelT> model)
1000{
1001 ResetParser();
1002 m_Model = std::move(model);
1003
1004 return CreateNetworkFromModel();
1005}
1006
1007INetworkPtr TfLiteParserImpl::CreateNetworkFromModel()
1008{
1009
1010 using NetworkOptions = std::vector<BackendOptions>;
1011 NetworkOptions networkOptions = {};
1012 if (m_Options)
1013 {
1014 if (m_Options.value().m_InferAndValidate)
1015 {
1016 BackendOptions shapeInferenceMethodOption("ShapeInferenceMethod",
1017 {
1018 { "InferAndValidate", true }
1019 });
1020
1021 networkOptions.push_back(shapeInferenceMethodOption);
1022 }
1023 if (m_Options.value().m_AllowExpandedDims)
1024 {
1025 BackendOptions shapeInferenceMethodOption("AllowExpandedDims",
1026 {
1027 { "AllowExpandedDims", true }
1028 });
1029
1030 networkOptions.push_back(shapeInferenceMethodOption);
1031 }
1032 }
1033 m_Network = INetwork::Create(networkOptions);
1034
1035 if (m_Model.get() == nullptr)
1036 {
1037 throw ParseException(fmt::format("Tflite Model pointer is null {}", CHECK_LOCATION().AsString()));
1038 }
1039
1040 // Identify which subgraph we are going to parse. We only support one subgraph but there may be validation
1041 // subgraphs still stored in the model. We'll ignore these. In the tflite code base they are identified by
1042 // their name beginning with "VALIDATION:".
1043 size_t subgraphIndex = 0;
1044 uint8_t usableSubgraphs = 0;
1045 for (size_t i = 0; i < m_Model->subgraphs.size(); i++)
1046 {
1047 if (m_Model->subgraphs[i]->name.rfind("VALIDATION:", 0) != 0)
1048 {
1049 usableSubgraphs++;
1050 subgraphIndex = i;
1051 }
1052 }
1053
1054 if (usableSubgraphs > 1)
1055 {
1056 throw ParseException(
1057 fmt::format("Current TfLite parser only supports 1 non validation subgraph. This model has: {} {}",
1058 usableSubgraphs, CHECK_LOCATION().AsString()));
1059 }
1060
1061 size_t operatorIndex = 0;
1062 try
1063 {
1064 const SubgraphPtr& subgraph = m_Model->subgraphs[subgraphIndex];
1065 SetupInputLayerTensorInfos(subgraphIndex);
1066 SetupConstantLayerTensorInfos(subgraphIndex);
1067
1068 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
1069 for (const OperatorPtr& op : subgraph->operators)
1070 {
1071 const auto& opCodePtr = m_Model->operator_codes[op->opcode_index];
1072
1073// work around the introduction of the deprecated_builtin_code introduced in 2.4 in a backwards compatible manner
1074#if defined(ARMNN_POST_TFLITE_2_3)
1075 auto builtinCode = std::max(opCodePtr->builtin_code,
1076 static_cast<tflite::BuiltinOperator>(opCodePtr->deprecated_builtin_code));
1077#else
1078 auto builtinCode = opCodePtr->builtin_code;
1079#endif
1080
1081 if (builtinCode > tflite::BuiltinOperator_MAX)
1082 {
1083 throw ParseException(fmt::format("Operator code {} is out of range 0-{}. "
1084 "subgraph:{} operator idx:{}. {}",
1085 builtinCode, tflite::BuiltinOperator_MAX, subgraphIndex,
1086 operatorIndex, CHECK_LOCATION().AsString()));
1087 }
1088
1089 // lookup and call the parser function
1090 auto& parserFunction = m_ParserFunctions[builtinCode];
1091 (this->*parserFunction)(subgraphIndex, operatorIndex);
1092 ++operatorIndex;
1093 }
1094
1095 SetupInputLayers(subgraphIndex);
1096 SetupOutputLayers(subgraphIndex);
1097 SetupConstantLayers(subgraphIndex);
1098 }
1099 catch (const ParseException& e)
1100 {
1101 std::stringstream errorString;
1102 errorString << "Failed to parse operator #" << operatorIndex << " within subgraph #"
1103 << subgraphIndex << " error: " << e.what();
1104 ARMNN_LOG(error) << errorString.str();
1105 std::stringstream errors;
1106 errors << errorString.str() << "\n";
1107 throw ParseException(errors.str());
1108 }
1109
1110 // establish the connections from the layer outputs to the inputs of the subsequent layers
1111 for (subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
1112 {
1113 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
1114 {
1115 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
1116 {
1117 for (size_t inputSlotIdx = 0;
1118 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
1119 ++inputSlotIdx)
1120 {
1121 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
1122 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
1123 }
1124 }
1125 }
1126 }
1127 return std::move(m_Network);
1128}
1129
1130bool TfLiteParserImpl::ShouldConstantTensorBeConverted(TfLiteParserImpl::TensorRawPtr tensorPtr,
1131 armnn::DataType inputDataType,
1132 armnn::DataType tensorDataType)
1133{
1134 return (TfLiteParserImpl::IsConstTensor(tensorPtr) && inputDataType == DataType::Float32 &&
1135 (tensorDataType == DataType::QAsymmU8 ||
1136 tensorDataType == DataType::QAsymmS8 ||
1137 tensorDataType == DataType::QSymmS8 ||
1138 tensorDataType == DataType::Signed32 ||
1139 tensorDataType == DataType::Signed64));
1140}
1141
1142void TfLiteParserImpl::RegisterProducerOfTensor(size_t subgraphIndex,
1143 size_t tensorIndex,
1144 armnn::IOutputSlot* slot)
1145{
1146 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
1147
1148 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
1149
1150 if (slot->GetOwningIConnectableLayer().GetType() != LayerType::Constant)
1151 {
1152
1153 // assuming there is only one producer for that tensor
1154 if (tensorSlots.outputSlot != nullptr)
1155 {
1156 throw ParseException(fmt::format("Another layer has already registered itself as the producer of "
1157 "subgraph:{} tensor:{} {}",
1158 subgraphIndex,
1159 tensorIndex,
1160 CHECK_LOCATION().AsString()));
1161 }
1162 }
1163 tensorSlots.outputSlot = slot;
1164}
1165
1166void TfLiteParserImpl::RegisterConsumerOfTensor(size_t subgraphIndex,
1167 size_t tensorIndex,
1168 armnn::IInputSlot* slot)
1169{
1170 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
1171
1172 TensorSlots& tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
1173 tensorSlots.inputSlots.push_back(slot);
1174}
1175
1176void TfLiteParserImpl::ParseCustomOperator(size_t subgraphIndex, size_t operatorIndex)
1177{
1178 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1179
1180 // NOTE: By default we presume the custom operator is not supported
1181 auto customParserFunction = &TfLiteParserImpl::ParseUnsupportedOperator;
1182
1183 // Identify custom code defined for custom operator
1184 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1185 const auto& customCode = m_Model->operator_codes[operatorPtr->opcode_index]->custom_code;
1186
1187 // Find parser function that corresponds to custom code (if any)
1188 auto iterator = m_CustomParserFunctions.find(customCode);
1189 if (iterator != m_CustomParserFunctions.end())
1190 {
1191 customParserFunction = iterator->second;
1192 }
1193
1194 // Run parser function
1195 (this->*customParserFunction)(subgraphIndex, operatorIndex);
1196}
1197
1198void TfLiteParserImpl::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
1199{
1200 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1201
1202 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1203
1204 auto opcodeIndex = operatorPtr->opcode_index;
1205
1206// work around the introduction of the deprecated_builtin_code introduced in 2.4 in a backwards compatible manner
1207#if defined(ARMNN_POST_TFLITE_2_3)
1208 auto opcode = std::max(m_Model->operator_codes[opcodeIndex]->builtin_code,
1209 static_cast<tflite::BuiltinOperator>(m_Model->operator_codes[opcodeIndex]->deprecated_builtin_code));
1210#else
1211 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
1212#endif
1213
1214 if (!m_Options || !m_Options.value().m_StandInLayerForUnsupported)
1215 {
1216 // Do not add StandInLayer, throw ParseException instead
1217 throw ParseException(
1218 fmt::format("Operator not supported. "
1219 "subgraph:{} operator:{} "
1220 "opcode_index:{} opcode:{} / {} {}",
1221 subgraphIndex,
1222 operatorIndex,
1223 opcodeIndex,
1224 opcode,
1225 tflite::EnumNameBuiltinOperator(opcode),
1226 CHECK_LOCATION().AsString()));
1227 }
1228
1229 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1230 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1231
1232 const unsigned int numInputs = armnn::numeric_cast<unsigned int>(inputs.size());
1233 const unsigned int numOutputs = armnn::numeric_cast<unsigned int>(outputs.size());
1234
1235 StandInDescriptor descriptor(numInputs, numOutputs);
1236 auto layerName = fmt::format("StandIn:{}:{}:{}", subgraphIndex, operatorIndex, opcode);
1237
1238 // Add a non-executable StandInLayer as a placeholder for any unsupported operator
1239 IConnectableLayer* layer = m_Network->AddStandInLayer(descriptor, layerName.c_str());
1240
1241 if (!layer)
1242 {
1243 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1244 operatorIndex, CHECK_LOCATION().AsString()));
1245 }
1246
1247 for (unsigned int i = 0u; i < numOutputs; ++i)
1248 {
1249 layer->GetOutputSlot(i).SetTensorInfo(ToTensorInfo(outputs[0], true));
1250 }
1251
1252 auto inputTensorIds = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1253 auto outputTensorIds = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1254
1255 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIds);
1256 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIds);
1257}
1258
1259void TfLiteParserImpl::ParseCast(size_t subgraphIndex, size_t operatorIndex)
1260{
1261 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1262
1263 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1264 CHECK_VALID_SIZE(inputs.size(), 1);
1265 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1266 CHECK_VALID_SIZE(outputs.size(), 1);
1267
1268 auto layerName = fmt::format("Cast:{}:{}", subgraphIndex, operatorIndex);
1269
1270 IConnectableLayer* layer = m_Network->AddCastLayer(layerName.c_str());
1271
1272 if (!layer)
1273 {
1274 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1275 operatorIndex, CHECK_LOCATION().AsString()));
1276 }
1277
1278 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
1279 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1280
1281 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1282 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1283
1284 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1285 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1286}
1287
1288void TfLiteParserImpl::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
1289{
1290 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1291
1292 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1293 const auto* options = operatorPtr->builtin_options.AsConv2DOptions();
1294
1295 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1296
1297 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1298 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1299 CHECK_VALID_SIZE(outputs.size(), 1);
1300
1301 Convolution2dDescriptor desc;
1302 inputs.size() == 3 ?
1303 desc.m_BiasEnabled = true : desc.m_BiasEnabled = false;
1304 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1305 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1307 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1308 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1309
1310 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1311 armnn::TensorInfo filterTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1312
1313 // assuming input is NHWC
1314 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1315 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1316
1317 // assuming the filter is OHWI : Output, H, W, Input
1318 // which is essentially the same as NHWC
1319 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1320 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1321
1322 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1323 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1324 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1325 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1326
1327 // Add the first input and weights tensor to the registration list.
1328 // The constant weights will be added by SetupConstantLayers.
1329 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1330 std::vector<unsigned int> tensorIndexesToRegister = { inputTensorIndexes[0], inputTensorIndexes[1] };
1331
1332 auto layerName = fmt::format("Conv2D:{}:{}", subgraphIndex, operatorIndex);
1333 armnn::IConnectableLayer* layer = m_Network->AddConvolution2dLayer(desc, layerName.c_str());
1334
1335 if (ShouldConstantTensorBeConverted(inputs[1], inputTensorInfo.GetDataType(), filterTensorInfo.GetDataType()))
1336 {
1337 m_ConstantsToDequantize.emplace_back(inputs[1]->buffer);
1338 }
1339
1340 if (desc.m_BiasEnabled)
1341 {
1342 armnn::TensorInfo biasTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
1343
1344 // Add the biases input to the registration list, a constant layer will be added by SetupConstantLayers.
1345 tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
1346
1347 if (ShouldConstantTensorBeConverted(inputs[2], inputTensorInfo.GetDataType(), biasTensorInfo.GetDataType()))
1348 {
1349 m_ConstantsToDequantize.emplace_back(inputs[2]->buffer);
1350 }
1351 }
1352
1353 if (!layer)
1354 {
1355 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1356 operatorIndex, CHECK_LOCATION().AsString()));
1357 }
1358
1359 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
1360 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1361
1362 // register the input connection slots for the layer, connections are made after all layers have been created
1363 // only the tensors for the inputs are relevant, exclude the const tensors
1364 RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister);
1365
1366 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1367 // register the output connection slots for the layer, connections are made after all layers have been created
1368 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1369 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, { outputTensorIndexes[0] });
1370}
1371
1372// Conv3D support was added in TF 2.5, so for backwards compatibility a hash define is needed.
1373#if defined(ARMNN_POST_TFLITE_2_4)
1374void TfLiteParserImpl::ParseConv3D(size_t subgraphIndex, size_t operatorIndex)
1375{
1376 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1377
1378 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1379 const auto* options = operatorPtr->builtin_options.AsConv3DOptions();
1380
1381 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1382
1383 Convolution3dDescriptor desc;
1384 desc.m_BiasEnabled = false;
1386 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1387 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1388 desc.m_StrideZ = CHECKED_NON_NEGATIVE(options->stride_d);
1389 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1390 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1391 desc.m_DilationZ = CHECKED_NON_NEGATIVE(options->dilation_d_factor);
1392
1393 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1394 CHECK_VALID_SIZE(inputs.size(), 2, 3);
1395
1396 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1397 CHECK_VALID_SIZE(outputs.size(), 1);
1398
1399 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1400 armnn::TensorInfo filterTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1401
1402 // Assuming input is NDHWC
1403 unsigned int inputDepth = inputTensorInfo.GetShape()[1];
1404 unsigned int inputHeight = inputTensorInfo.GetShape()[2];
1405 unsigned int inputWidth = inputTensorInfo.GetShape()[3];
1406
1407 // Assuming the filter is DHWIO : Depth, Height, Width, OutputChannels, InputChannels
1408 unsigned int filterDepth = filterTensorInfo.GetShape()[0];
1409 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1410 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1411
1412 CalcPadding(inputDepth, filterDepth, desc.m_StrideZ,
1413 desc.m_DilationZ, desc.m_PadFront, desc.m_PadBack, options->padding);
1414 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1415 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1416 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1417 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1418
1419 auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo, inputTensorInfo.GetDataType());
1420
1421 auto layerName = fmt::format("Conv3D:{}:{}", subgraphIndex, operatorIndex);
1422
1423 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1424 // Add the first input and weights tensor to the registration list.
1425 // The constant weights will be added by SetupConstantLayers.
1426 std::vector<unsigned int> tensorIndexesToRegister = {inputTensorIndexes[0], inputTensorIndexes[1]};
1427
1428 if (inputs.size() == 3)
1429 {
1430 desc.m_BiasEnabled = true;
1431
1432 // Add the biases input to the registration list, a constant layer will be added by SetupConstantLayers.
1433 tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
1434 }
1435
1436 armnn::IConnectableLayer* layer = m_Network->AddConvolution3dLayer(desc, layerName.c_str());
1437
1438 if (!layer)
1439 {
1440 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1441 operatorIndex, CHECK_LOCATION().AsString()));
1442 }
1443
1444 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
1445 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1446
1447 // Register the input connection slots for the layer, connections are made after all layers have been created
1448 RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister);
1449
1450 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1451 // Register the output connection slots for the layer, connections are made after all layers have been created
1452 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1453 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1454}
1455#endif
1456
1457void TfLiteParserImpl::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
1458{
1459 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1460
1461 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1462 const auto* options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
1463
1464 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
1465
1466 DepthwiseConvolution2dDescriptor desc;
1467 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1468 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1470 CHECKED_NON_NEGATIVE(options->depth_multiplier);
1471
1472 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1473 CHECK_VALID_SIZE(inputs.size(), 2, 3);
1474 if (inputs.size() == 3)
1475 {
1476 desc.m_BiasEnabled = true;
1477 }
1478
1479 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1480 CHECK_VALID_SIZE(outputs.size(), 1);
1481 desc.m_DilationX = CHECKED_NON_NEGATIVE(options->dilation_w_factor);
1482 desc.m_DilationY = CHECKED_NON_NEGATIVE(options->dilation_h_factor);
1483
1484 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1485 armnn::TensorInfo filterTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1486
1487 // Assuming input is NHWC
1488 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1489 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1490
1491 // TensorflowLite weights come in the format [1, H, W, I * M]
1492 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1493 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1494
1495 CalcPadding(inputHeight, filterHeight, desc.m_StrideY,
1496 desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, options->padding);
1497 CalcPadding(inputWidth, filterWidth, desc.m_StrideX,
1498 desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, options->padding);
1499
1500 // ArmNN uses the same filter tensor layout at TfLite [1, H, W, O] no need for any permutation
1501 auto layerName = fmt::format("DepthwiseConv2D:{}:{}", subgraphIndex, operatorIndex);
1502
1503 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1504 // Add the first input and weights tensor to the registration list.
1505 // The constant weights will be added by SetupConstantLayers.
1506 std::vector<unsigned int> tensorIndexesToRegister = {inputTensorIndexes[0], inputTensorIndexes[1]};
1507
1508 armnn::IConnectableLayer* layer = m_Network->AddDepthwiseConvolution2dLayer(desc, layerName.c_str());
1509
1510 if (desc.m_BiasEnabled)
1511 {
1512 desc.m_BiasEnabled = true;
1513 TensorInfo biasTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
1514
1515 // Add the biases input to the registration list, a constant layer will be added by SetupConstantLayers.
1516 tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
1517 }
1518
1519 if (!layer)
1520 {
1521 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1522 operatorIndex, CHECK_LOCATION().AsString()));
1523 }
1524
1525 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
1526 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1527
1528 // register the input connection slots for the layer, connections are made after all layers have been created
1529 // only the tensors for the inputs are relevant, exclude the const tensors
1530 RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister);
1531
1532 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
1533 // register the output connection slots for the layer, connections are made after all layers have been created
1534 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1535 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1536}
1537
1538void TfLiteParserImpl::ParseDequantize(size_t subgraphIndex, size_t operatorIndex)
1539{
1540 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1541
1542 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1543 CHECK_VALID_SIZE(inputs.size(), 1);
1544
1545 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1546 CHECK_VALID_SIZE(outputs.size(), 1);
1547
1548 auto layerName = fmt::format("Dequantize:{}:{}", subgraphIndex, operatorIndex);
1549
1550 IConnectableLayer* layer = m_Network->AddDequantizeLayer(layerName.c_str());
1551
1552 if (!layer)
1553 {
1554 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1555 operatorIndex, CHECK_LOCATION().AsString()));
1556 }
1557
1558 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
1559 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1560
1561 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1562 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1563
1564 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1565 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
1566}
1567
1568void TfLiteParserImpl::ParseExpandDims(size_t subgraphIndex, size_t operatorIndex)
1569{
1570 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1571
1572 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1573 CHECK_VALID_SIZE(inputs.size(), 2);
1574
1575 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1576 CHECK_VALID_SIZE(outputs.size(), 1);
1577
1578 auto layerName = fmt::format("ExpandDims:{}:{}", subgraphIndex, operatorIndex);
1579
1580 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1581 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
1582 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1583
1584 armnn::TensorInfo axisTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1585
1586 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1587 ValidateBuffer(axisBufferPtr, axisTensorInfo, "axis");
1588
1589 if (axisBufferPtr == nullptr)
1590 {
1591 throw ParseException(fmt::format("{}: Operation has invalid inputs. Failed to read axis.",
1592 CHECK_LOCATION().AsString()));
1593 }
1594
1595 std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
1596 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
1597 int32_t axis = axisData[0];
1598
1599 auto inputRank = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions());
1600 auto outputRank = inputRank + 1;
1601 if((axis < -1 * outputRank) || (outputRank <= axis))
1602 {
1603 throw ParseException(fmt::format("{}: Axis {} is not within [-{}, {}) range.",
1604 CHECK_LOCATION().AsString(), axis, outputRank, outputRank));
1605 }
1606
1607 axis = axis < 0 ? (axis + outputRank) : axis;
1608
1609 std::vector<unsigned int> shape(static_cast<unsigned int>(outputRank));
1610 unsigned int inputShapeIndex = 0;
1611 for (unsigned int i = 0; i < static_cast<unsigned int>(outputRank); ++i)
1612 {
1613 if (i == static_cast<unsigned int>(axis))
1614 {
1615 shape[i] = 1;
1616 }
1617 else
1618 {
1619 shape[i] = inputTensorInfo.GetShape()[inputShapeIndex];
1620 ++inputShapeIndex;
1621 }
1622 }
1623
1624 ReshapeDescriptor reshapeDesc;
1625 reshapeDesc.m_TargetShape = TensorShape(static_cast<unsigned int>(outputRank), shape.data());
1626 outputTensorInfo.SetShape(reshapeDesc.m_TargetShape);
1627
1628 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
1629
1630 if (!layer)
1631 {
1632 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1633 operatorIndex, CHECK_LOCATION().AsString()));
1634 } layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1635
1636 auto outputTensorIds = GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex);
1637 m_TensorInfos[outputTensorIds[0]] = outputTensorInfo;
1638
1639 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1640 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1641
1642 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1643 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1644}
1645
1646void TfLiteParserImpl::ParseTranspose(size_t subgraphIndex, size_t operatorIndex)
1647{
1648 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1649
1650 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1651 CHECK_VALID_SIZE(inputs.size(), 1, 2);
1652
1653 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1654 CHECK_VALID_SIZE(outputs.size(), 1);
1655
1656 auto layerName = fmt::format("Transpose:{}:{}", subgraphIndex, operatorIndex);
1657 TransposeDescriptor desc;
1658
1659 if (inputs.size() == 2)
1660 {
1661 armnn::TensorInfo permuteTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1662 BufferRawPtr permuteBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1663 ValidateBuffer(permuteBufferPtr, permuteTensorInfo, "permute");
1664
1665 auto numPermVecElements = permuteTensorInfo.GetNumElements();
1666 std::vector<unsigned int> permuteShape(numPermVecElements);
1667 ::memcpy(permuteShape.data(), permuteBufferPtr->data.data(), permuteTensorInfo.GetNumBytes());
1668 PermutationVector permutationVector(permuteShape.data(), permuteTensorInfo.GetNumElements());
1669
1670 desc = TransposeDescriptor(permutationVector);
1671 }
1672 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1673
1674 IConnectableLayer* layer = m_Network->AddTransposeLayer(desc, layerName.c_str());
1675
1676 if (!layer)
1677 {
1678 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1679 operatorIndex, CHECK_LOCATION().AsString()));
1680 }
1681
1682 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
1683 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1684 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1685
1686 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1687 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1688
1689 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1690 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1691}
1692
1693void TfLiteParserImpl::ParseTransposeConv(size_t subgraphIndex, size_t operatorIndex)
1694{
1695 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1696
1697 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1698 const auto* options = operatorPtr->builtin_options.AsTransposeConvOptions();
1699
1700 TransposeConvolution2dDescriptor desc;
1701 desc.m_BiasEnabled = false;
1702 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
1703 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
1705
1706 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1707 if (inputs.size() == 4)
1708 {
1709 desc.m_BiasEnabled = true;
1710 }
1711 else
1712 {
1713 CHECK_VALID_SIZE(inputs.size(), 3);
1714 }
1715
1716 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1717 CHECK_VALID_SIZE(outputs.size(), 1);
1718
1719
1720 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
1721 armnn::TensorInfo filterTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1722
1723 // TfLite uses NHWC tensors
1724 const unsigned int inputHeight = inputTensorInfo.GetShape()[1];
1725 const unsigned int inputWidth = inputTensorInfo.GetShape()[2];
1726
1727 const unsigned int filterHeight = filterTensorInfo.GetShape()[1];
1728 const unsigned int filterWidth = filterTensorInfo.GetShape()[2];
1729
1730 // This block determines the output shape of the transpose convolution. If the output shape tensor ptr is not null
1731 // And the tensor is a constant, we can access the data at load time and set the output shape of the
1732 // layer. If this is not constant, We do not have access to the shape data, so we have to use
1733 // infer output shape and skip this code block.
1734 if (inputs[0] && IsConstTensor(inputs[0]))
1735 {
1736 armnn::TensorInfo tensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1737 std::vector<int> output_shape(tensorInfo.GetNumElements());
1738
1739 BufferRawPtr outputBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1740 ValidateBuffer(outputBufferPtr, tensorInfo, "output");
1741
1742 if (tensorInfo.GetDataType() == DataType::Signed32)
1743 {
1744 ::memcpy(output_shape.data(), outputBufferPtr->data.data(), tensorInfo.GetNumBytes());
1745 }
1746 if (tensorInfo.GetDataType() == DataType::QAsymmU8)
1747 {
1748 for(unsigned int i=0; i < tensorInfo.GetNumElements(); i++)
1749 {
1750 output_shape[i] = GetBuffer(m_Model, inputs[0]->buffer)->data.data()[i];
1751 }
1752 }
1753 // Change from signed to unsigned int to store in TransposeConvolution2dDescriptor.
1754 for (int dimension : output_shape)
1755 {
1756 desc.m_OutputShape.push_back(static_cast<unsigned int>(dimension));
1757 }
1758 desc.m_OutputShapeEnabled = true;
1759
1760 // TfLite uses NHWC tensors
1761 const unsigned int outputHeight = desc.m_OutputShape[1];
1762 const unsigned int outputWidth = desc.m_OutputShape[2];
1763
1764 CalcPadding(inputHeight,
1765 filterHeight,
1766 desc.m_StrideY,
1767 1, // DilationY
1768 desc.m_PadTop,
1769 desc.m_PadBottom,
1770 options->padding,
1771 outputHeight);
1772
1773 CalcPadding(inputWidth,
1774 filterWidth,
1775 desc.m_StrideX,
1776 1, // DilationX
1777 desc.m_PadLeft,
1778 desc.m_PadRight,
1779 options->padding,
1780 outputWidth);
1781 }
1782 else
1783 {
1784 CalcPadding(inputHeight,
1785 filterHeight,
1786 desc.m_StrideY,
1787 1, // DilationY
1788 desc.m_PadTop,
1789 desc.m_PadBottom,
1790 options->padding);
1791
1792 CalcPadding(inputWidth,
1793 filterWidth,
1794 desc.m_StrideX,
1795 1, // DilationX
1796 desc.m_PadLeft,
1797 desc.m_PadRight,
1798 options->padding);
1799 }
1800
1801 auto filterTensorAndData = CreateConstTensorNonPermuted(inputs[1], filterTensorInfo, inputTensorInfo.GetDataType());
1802
1803 armnn::IConnectableLayer* layer = nullptr;
1804 auto layerName = fmt::format("TransposeConv:{}:{}", subgraphIndex, operatorIndex);
1805
1806 if (desc.m_BiasEnabled)
1807 {
1808 auto biasTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 3);
1809 auto biasConstTensor = CreateConstTensorNonPermuted(inputs[3], biasTensorInfo, inputTensorInfo.GetDataType());
1810 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1811 filterTensorAndData.first,
1812 biasConstTensor.first,
1813 layerName.c_str());
1814 }
1815 else
1816 {
1817 layer = m_Network->AddTransposeConvolution2dLayer(desc,
1818 filterTensorAndData.first,
1819 EmptyOptional(),
1820 layerName.c_str());
1821 }
1822
1823 if (!layer)
1824 {
1825 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1826 operatorIndex, CHECK_LOCATION().AsString()));
1827 }
1828
1829 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0 , { 2, 1 });
1830 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1831
1832 // only the tensors for the inputs are relevant, exclude the const (filter) tensor
1833 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1834 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[2]});
1835
1836 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1837 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1838}
1839
1840void TfLiteParserImpl::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
1841{
1842 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Average);
1843}
1844
1845void TfLiteParserImpl::ParseBatchMatMul(size_t subgraphIndex, size_t operatorIndex)
1846{
1847 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1848
1849 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1850 CHECK_VALID_SIZE(inputs.size(), 2);
1851
1852 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1853 CHECK_VALID_SIZE(outputs.size(), 1);
1854
1855 auto layerName = fmt::format("BatchMatMul:{}:{}", subgraphIndex, operatorIndex);
1856
1857 TensorInfo inputXTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1858 TensorInfo inputYTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1859
1860 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1861 const auto* options = operatorPtr->builtin_options.AsBatchMatMulOptions();
1862
1863 // Adjoint in tensorflow lite performs transpose operation
1864 BatchMatMulDescriptor descriptor(options->adj_x,
1865 options->adj_y,
1866 false,
1867 false);
1868 // Arbitrary DataLayout
1869
1870 IConnectableLayer* layer = m_Network->AddBatchMatMulLayer(descriptor, layerName.c_str());
1871
1872 if (!layer)
1873 {
1874 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1875 operatorIndex, CHECK_LOCATION().AsString()));
1876 }
1877
1878 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
1879 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1880
1881 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1882 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
1883
1884 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1885 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1886}
1887
1888void TfLiteParserImpl::ParseBatchToSpaceND(size_t subgraphIndex, size_t operatorIndex)
1889{
1890 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1891
1892 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1893 CHECK_VALID_SIZE(inputs.size(), 3);
1894
1895 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1896 CHECK_VALID_SIZE(outputs.size(), 1);
1897
1898 armnn::TensorInfo blockShapeTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
1899 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1900 ValidateBuffer(blockShapeBufferPtr, blockShapeTensorInfo, "blockShape");
1901
1902 armnn::TensorInfo cropsTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
1903 BufferRawPtr cropsBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
1904 ValidateBuffer(cropsBufferPtr, cropsTensorInfo, "crops");
1905
1906 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
1907 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
1908
1909 std::vector<unsigned int> cropsVector(cropsTensorInfo.GetNumElements());
1910 ::memcpy(cropsVector.data(), cropsBufferPtr->data.data(), cropsTensorInfo.GetNumBytes());
1911
1912 size_t step = 2;
1913 std::vector<std::pair<unsigned int, unsigned int>> crops;
1914 for (unsigned int i = 0; i < cropsTensorInfo.GetNumElements() / step; ++i)
1915 {
1916 crops.emplace_back(cropsVector[i * step], cropsVector[i * step + 1]);
1917 }
1918
1919 armnn::BatchToSpaceNdDescriptor desc;
1920 desc.m_BlockShape = blockShape;
1921 desc.m_Crops = crops;
1923
1924 auto layerName = fmt::format("BatchToSpaceND:{}:{}", subgraphIndex, operatorIndex);
1925
1926 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
1927
1928 IConnectableLayer* layer = m_Network->AddBatchToSpaceNdLayer(desc, layerName.c_str());
1929
1930 if (!layer)
1931 {
1932 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
1933 operatorIndex, CHECK_LOCATION().AsString()));
1934 }
1935
1936 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
1937 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
1938 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1939
1940 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1941 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1942
1943 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1944 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1945}
1946
1947void TfLiteParserImpl::ParseBroadcastTo(size_t subgraphIndex, size_t operatorIndex)
1948{
1949 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1950
1951 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1952 CHECK_VALID_SIZE(inputs.size(), 2);
1953
1954 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1955 CHECK_VALID_SIZE(outputs.size(), 1);
1956
1957 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
1958 TensorInfo shapeTensorInfo = ToTensorInfo(inputs[1]);
1959 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1960
1961 auto layerName = fmt::format("Broadcast_to:{}:{}", subgraphIndex, operatorIndex);
1962
1963 BroadcastToDescriptor descriptor;
1964
1965 auto shapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
1966 if (shapeBufferPtr != nullptr)
1967 {
1968 std::vector<unsigned int> targetShape;
1969 unsigned int numElement = shapeTensorInfo.GetNumElements();
1970 auto shapeData = reinterpret_cast<const int32_t*>(shapeBufferPtr->data.data());
1971 if (shapeData)
1972 {
1973 for (unsigned int i = 0; i < numElement; ++i)
1974 {
1975 targetShape.push_back(armnn::numeric_cast<unsigned int>(shapeData[i]));
1976 }
1977 descriptor.m_BroadcastToShape = TensorShape(numElement, targetShape.data());
1978 }
1979 /// get dataShape from outputShape if missing
1980 else
1981 {
1982 if(outputTensorInfo.GetShape().GetNumElements() <= 1)
1983 {
1984 ARMNN_THROW_PARSE_EXCEPTION("For Broadcast_to layer, "
1985 "data and output shape are not found in the buffer.");
1986 }
1987 descriptor.m_BroadcastToShape = outputTensorInfo.GetShape();
1988 }
1989 }
1990 else
1991 {
1992 ARMNN_THROW_PARSE_EXCEPTION("For Broadcast_to layer, Shape data was not found in the buffer.");
1993 }
1994
1995 IConnectableLayer* layer = m_Network->AddBroadcastToLayer(descriptor, layerName.c_str());
1996 ARMNN_ASSERT(layer != nullptr);
1997
1998 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1999
2000 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2001 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2002
2003 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2004 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2005}
2006
2007void TfLiteParserImpl::ParseL2Normalization(size_t subgraphIndex, size_t operatorIndex)
2008{
2009 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2010
2011 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2012 CHECK_VALID_SIZE(inputs.size(), 1);
2013
2014 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2015 CHECK_VALID_SIZE(outputs.size(), 1);
2016
2017 L2NormalizationDescriptor desc;
2019 auto layerName = fmt::format("L2Normalization:{}:{}", subgraphIndex, operatorIndex);
2020 IConnectableLayer* layer = m_Network->AddL2NormalizationLayer(desc, layerName.c_str());
2021
2022 if (!layer)
2023 {
2024 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2025 operatorIndex, CHECK_LOCATION().AsString()));
2026 }
2027
2028 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2029 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2030
2031 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2032 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2033
2034 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2035 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2036}
2037
2038void TfLiteParserImpl::ParseMaxPool2D(size_t subgraphIndex, size_t operatorIndex)
2039{
2040 ParsePool(subgraphIndex, operatorIndex, PoolingAlgorithm::Max);
2041}
2042
2043void TfLiteParserImpl::ParseMaximum(size_t subgraphIndex, size_t operatorIndex)
2044{
2045 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2046
2047 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2048 CHECK_VALID_SIZE(inputs.size(), 2);
2049
2050 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2051 CHECK_VALID_SIZE(outputs.size(), 1);
2052
2053 auto layerName = fmt::format("Maximum:{}:{}", subgraphIndex, operatorIndex);
2054
2055 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2056 TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2057 CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
2058
2059 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Maximum, layerName.c_str());
2060
2061 if (!layer)
2062 {
2063 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2064 operatorIndex, CHECK_LOCATION().AsString()));
2065 }
2066
2067 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2068 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2069 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2070
2071 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2072 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2073
2074 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2075 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2076}
2077
2078void TfLiteParserImpl::ParseMinimum(size_t subgraphIndex, size_t operatorIndex)
2079{
2080 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2081
2082 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2083 CHECK_VALID_SIZE(inputs.size(), 2);
2084
2085 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2086 CHECK_VALID_SIZE(outputs.size(), 1);
2087
2088 auto layerName = fmt::format("Minimum:{}:{}", subgraphIndex, operatorIndex);
2089
2090 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2091 TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2092 CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
2093
2094 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Minimum, layerName.c_str());
2095
2096 if (!layer)
2097 {
2098 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2099 operatorIndex, CHECK_LOCATION().AsString()));
2100 }
2101
2102 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2103 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2104 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2105
2106 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2107 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2108
2109 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2110 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2111}
2112
2113void TfLiteParserImpl::ParsePool(size_t subgraphIndex,
2114 size_t operatorIndex,
2115 PoolingAlgorithm algorithm)
2116{
2117 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2118
2119 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2120 const auto* options = operatorPtr->builtin_options.AsPool2DOptions();
2121
2122 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
2123
2124 std::string layerName;
2125
2126 switch (algorithm)
2127 {
2128 case PoolingAlgorithm::Average:
2129 layerName =
2130 fmt::format("AveragePool2D:{}:{}", subgraphIndex, operatorIndex);
2131 break;
2132 case PoolingAlgorithm::Max:
2133 layerName =
2134 fmt::format("MaxPool2D:{}:{}", subgraphIndex, operatorIndex);
2135 break;
2136 default:
2137 throw ParseException(fmt::format("Unsupported Pooling Algorithm {}", CHECK_LOCATION().AsString()));
2138 }
2139
2141
2142 desc.m_PoolType = algorithm;
2143 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
2144 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
2145 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
2146 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
2147 desc.m_PaddingMethod = PaddingMethod::Exclude;
2148 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
2150
2151 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2152 CHECK_VALID_SIZE(inputs.size(), 1);
2153 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2154
2155 // assuming input is NHWC
2156 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
2157 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
2158
2159 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, 1u,
2160 desc.m_PadTop, desc.m_PadBottom, options->padding);
2161 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, 1u,
2162 desc.m_PadLeft, desc.m_PadRight, options->padding);
2163
2164 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2165 CHECK_VALID_SIZE(outputs.size(), 1);
2166
2167 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
2168
2169 if (!layer)
2170 {
2171 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2172 operatorIndex, CHECK_LOCATION().AsString()));
2173 }
2174
2175 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2176 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2177 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2178
2179 // register the input connection slots for the layer, connections are made after all layers have been created
2180 // only the tensors for the inputs are relevant, exclude the const tensors
2181 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2182 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2183
2184 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2185 // register the output connection slots for the layer, connections are made after all layers have been created
2186 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2187 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2188}
2189
2190void TfLiteParserImpl::ParseSlice(size_t subgraphIndex, size_t operatorIndex)
2191{
2192 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2193
2194 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2195 CHECK_VALID_SIZE(inputs.size(), 3);
2196 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2197 CHECK_VALID_SIZE(outputs.size(), 1);
2198
2199 SliceDescriptor desc;
2200
2201 // set begin tensor info for slice descriptor
2202 armnn::TensorInfo beginTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2203 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2204 ValidateBuffer(beginBufferPtr, beginTensorInfo, "begin");
2205
2206 std::vector<unsigned int> begin(beginTensorInfo.GetNumElements());
2207 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
2208
2209 // set size tensor info for slice descriptor
2210 armnn::TensorInfo sizeTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
2211 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
2212 ValidateBuffer(sizeBufferPtr, sizeTensorInfo, "size");
2213
2214 std::vector<int> signedSize(sizeTensorInfo.GetNumElements(), 1);
2215
2216 // if size buffer data is not specified, all contents of size vector remain as values of 1
2217 if (sizeBufferPtr->data.data())
2218 {
2219 ::memcpy(signedSize.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
2220 }
2221
2222 std::vector<unsigned int> size(sizeTensorInfo.GetNumElements());
2223 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2224
2225 for (unsigned int i = 0; i < signedSize.size(); ++i)
2226 {
2227 int signedValue = signedSize[i];
2228
2229 if (signedValue < -1 || signedValue > static_cast<int>(inputTensorInfo.GetShape()[i] - begin[i]))
2230 {
2231 throw ParseException(fmt::format("Invalid value for size {} size must be in range "
2232 "[-1, inputDimSize - begin] [-1, {}] inclusive {}",
2233 signedValue,
2234 inputTensorInfo.GetShape()[i] - begin[i],
2235 CHECK_LOCATION().AsString()));
2236 }
2237
2238 if (signedValue == -1)
2239 {
2240 size[i] = inputTensorInfo.GetShape()[i] - begin[i];
2241 }
2242 else
2243 {
2244 size[i] = static_cast<unsigned int>(signedValue);
2245 }
2246 }
2247
2248 desc = SliceDescriptor(begin, size);
2249
2250 auto layerName = fmt::format("Slice:{}:{}", subgraphIndex, operatorIndex);
2251
2252 IConnectableLayer* const layer = m_Network->AddSliceLayer(desc, layerName.c_str());
2253
2254 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2255 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2256 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2257
2258 // register the input connection slots for the layer, connections are made after all layers have been created
2259 // only the tensors for the inputs are relevant, exclude the const tensors
2260 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2261 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2262
2263 // register the output connection slots for the layer, connections are made after all layers have been created
2264 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2265 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2266}
2267
2268void TfLiteParserImpl::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
2269{
2270 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2271 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2272 const auto* options = operatorPtr->builtin_options.AsSoftmaxOptions();
2273
2274 SoftmaxDescriptor desc;
2275 desc.m_Beta = options->beta;
2276
2277 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2278 CHECK_VALID_SIZE(inputs.size(), 1);
2279 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2280 CHECK_VALID_SIZE(outputs.size(), 1);
2281
2282 auto layerName = fmt::format("Softmax:{}:{}", subgraphIndex, operatorIndex);
2283 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
2284
2285 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2286 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2287
2288 // register the input connection slots for the layer, connections are made after all layers have been created
2289 // only the tensors for the inputs are relevant, exclude the const tensors
2290 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2291 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2292
2293 // register the output connection slots for the layer, connections are made after all layers have been created
2294 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2295 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2296}
2297
2298void TfLiteParserImpl::ParseLogSoftmax(size_t subgraphIndex, size_t operatorIndex)
2299{
2300 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2301
2303
2304 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2305 CHECK_VALID_SIZE(inputs.size(), 1);
2306 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2307 CHECK_VALID_SIZE(outputs.size(), 1);
2308
2309 auto layerName = fmt::format("LogSoftmax:{}:{}", subgraphIndex, operatorIndex);
2310 IConnectableLayer* const layer = m_Network->AddLogSoftmaxLayer(desc, layerName.c_str());
2311
2312 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2313 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2314
2315 // register the input connection slots for the layer, connections are made after all layers have been created
2316 // only the tensors for the inputs are relevant, exclude the const tensors
2317 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2318 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2319
2320 // register the output connection slots for the layer, connections are made after all layers have been created
2321 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2322 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2323}
2324
2325void TfLiteParserImpl::ParseScatterNd(size_t subgraphIndex, size_t operatorIndex)
2326{
2327 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2328
2329 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2330 CHECK_VALID_SIZE(inputs.size(), 3);
2331 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2332 CHECK_VALID_SIZE(outputs.size(), 1);
2333
2334 armnn::TensorInfo indicesTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2335 armnn::TensorInfo updatesTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2336 armnn::TensorInfo shapeTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
2337 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
2338
2339 // TFLite currently only have these options: update and no input given, just shape.
2340 armnn::ScatterNdDescriptor descriptor(armnn::ScatterNdFunction::Update, false);
2341
2342 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2343 const auto* options = operatorPtr->builtin_options.AsScatterNdOptions();
2344 IgnoreUnused(options);
2345
2346 auto layerName = fmt::format("ScatterND:{}:{}", subgraphIndex, operatorIndex);
2347
2348 IConnectableLayer* layer = m_Network->AddScatterNdLayer(descriptor, layerName.c_str());
2349
2350 if (!layer)
2351 {
2352 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2353 operatorIndex, CHECK_LOCATION().AsString()));
2354 }
2355
2356 outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1, 2});
2357 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2358
2359 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2360 RegisterInputSlots(subgraphIndex,
2361 operatorIndex,
2362 layer,
2363 {inputTensorIndexes[2], inputTensorIndexes[0], inputTensorIndexes[1]});
2364
2365 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2366 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2367}
2368
2369void TfLiteParserImpl::ParseSpaceToBatchND(size_t subgraphIndex, size_t operatorIndex)
2370{
2371 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2372
2373 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2374 CHECK_VALID_SIZE(inputs.size(), 3);
2375
2376 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2377 CHECK_VALID_SIZE(outputs.size(), 1);
2378
2379 armnn::TensorInfo blockShapeTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2380 BufferRawPtr blockShapeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2381 ValidateBuffer(blockShapeBufferPtr, blockShapeTensorInfo, "block");
2382
2383 armnn::TensorInfo padListTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
2384 BufferRawPtr padListBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
2385 ValidateBuffer(padListBufferPtr, padListTensorInfo, "padList");
2386
2387 // Both block_shape and paddings are supposed to be INT32.
2388 if (blockShapeTensorInfo.GetDataType() != armnn::DataType::Signed32 ||
2389 padListTensorInfo.GetDataType() != armnn::DataType::Signed32)
2390 {
2391 throw ParseException(
2392 fmt::format("In SpaceToBatchND block_shape and paddings can only be of type INT32. {}",
2393 CHECK_LOCATION().AsString()));
2394 }
2395
2396 std::vector<unsigned int> blockShape(blockShapeTensorInfo.GetNumElements());
2397 ::memcpy(blockShape.data(), blockShapeBufferPtr->data.data(), blockShapeTensorInfo.GetNumBytes());
2398
2399 std::vector<unsigned int> padListVector(padListTensorInfo.GetNumElements());
2400 ::memcpy(padListVector.data(), padListBufferPtr->data.data(), padListTensorInfo.GetNumBytes());
2401
2402 size_t step = 2;
2403 std::vector<std::pair<unsigned int, unsigned int>> padList;
2404 for (unsigned int i = 0; i < padListTensorInfo.GetNumElements() / step; ++i)
2405 {
2406 padList.emplace_back(padListVector[i * step], padListVector[i * step + 1]);
2407 }
2408
2409 armnn::SpaceToBatchNdDescriptor desc;
2410 desc.m_BlockShape = blockShape;
2411 desc.m_PadList = padList;
2413
2414 auto layerName = fmt::format("SpaceToBatchND:{}:{}", subgraphIndex, operatorIndex);
2415
2416 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2417
2418 IConnectableLayer* layer = m_Network->AddSpaceToBatchNdLayer(desc, layerName.c_str());
2419
2420 if (!layer)
2421 {
2422 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2423 operatorIndex, CHECK_LOCATION().AsString()));
2424 }
2425
2426 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2427 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2428 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2429
2430 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2431 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2432
2433 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2434 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2435}
2436
2437void TfLiteParserImpl::ParseSpaceToDepth(size_t subgraphIndex, size_t operatorIndex)
2438{
2439 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2440
2441 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2442 CHECK_VALID_SIZE(inputs.size(), 1);
2443 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2444 CHECK_VALID_SIZE(outputs.size(), 1);
2445
2446 armnn::SpaceToDepthDescriptor descriptor;
2447
2448 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2449 const auto* options = operatorPtr->builtin_options.AsSpaceToDepthOptions();
2450 auto blockSize = options->block_size;
2451 if (blockSize < 2)
2452 {
2453 throw ParseException(
2454 fmt::format("Operation has invalid block size: {} Block size should be >= 2 {}",
2455 blockSize,
2456 CHECK_LOCATION().AsString()));
2457 }
2458 descriptor.m_BlockSize = armnn::numeric_cast<uint32_t>(blockSize);
2459
2460 auto layerName = fmt::format("SpaceToDepth:{}:{}", subgraphIndex, operatorIndex);
2461 IConnectableLayer* layer = m_Network->AddSpaceToDepthLayer(descriptor, layerName.c_str());
2462
2463 if (!layer)
2464 {
2465 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2466 operatorIndex, CHECK_LOCATION().AsString()));
2467 }
2468
2469 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2470 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2471
2472 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2473 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2474
2475 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2476 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2477}
2478
2480 const armnn::TensorInfo& inputTensorInfo)
2481{
2482 CHECK_VALID_SIZE(squeezeDims.size(), 0, 1, 2, 3, 4);
2483 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
2484
2485 if (inputTensorInfo.GetNumDimensions() > 4)
2486 {
2487 std::stringstream ss;
2488 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
2489 << " shape:" << inputTensorInfo.GetShape() << " "
2490 << CHECK_LOCATION().AsString();
2491 throw ParseException(ss.str());
2492 }
2493
2494 if (squeezeDims.empty())
2495 {
2496 squeezeDims.assign(dimensionSequence,
2497 dimensionSequence+inputTensorInfo.GetNumDimensions());
2498 }
2499
2500 std::vector<uint32_t> outputDims;
2501 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
2502 {
2503 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
2504 auto currentDimension = inputTensorInfo.GetShape()[i];
2505 if (skipSqueeze || currentDimension != 1)
2506 {
2507 outputDims.push_back(currentDimension);
2508 }
2509 }
2510
2511 if (outputDims.size() > 4)
2512 {
2513 std::stringstream ss;
2514 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
2515 << " shape:" << inputTensorInfo.GetShape() << " "
2516 << CHECK_LOCATION().AsString();
2517 throw ParseException(ss.str());
2518 }
2519
2520 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
2521 outputDims.data());
2522
2523 // we need to preserve the tensor type and the quantization data as well
2524 TensorInfo outTensorInfo = inputTensorInfo;
2525 outTensorInfo.SetShape(outShape);
2526
2527 return outTensorInfo;
2528}
2529
2530void TfLiteParserImpl::ParseShape(size_t subgraphIndex, size_t operatorIndex)
2531{
2532 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2533
2534 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2535 CHECK_VALID_SIZE(inputs.size(), 1);
2536 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2537 CHECK_VALID_SIZE(outputs.size(), 1);
2538
2539 auto layerName = fmt::format("Shape:{}:{}", subgraphIndex, operatorIndex);
2540
2541 IConnectableLayer* layer = m_Network->AddShapeLayer(layerName.c_str());
2542
2543 if (!layer)
2544 {
2545 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2546 operatorIndex, CHECK_LOCATION().AsString()));
2547 }
2548
2549 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2550 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2551
2552 // Check if output tensor type is Signed32 or Signed64
2553 if (outputTensorInfo.GetDataType() != armnn::DataType::Signed32 &&
2554 outputTensorInfo.GetDataType() != armnn::DataType::Signed64)
2555 {
2556 throw ParseException(
2557 fmt::format(
2558 "Output tensor data type is not supported. (Supported types: Signed32 & Signed64) {}",
2559 CHECK_LOCATION().AsString()));
2560 }
2561
2562 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2563 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2564
2565 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2566 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
2567}
2568
2569void TfLiteParserImpl::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
2570{
2571 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2572
2573 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2574 CHECK_VALID_SIZE(inputs.size(), 1);
2575
2576 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2577 CHECK_VALID_SIZE(outputs.size(), 1);
2578
2579 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2580 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
2581 auto layerName = fmt::format("Squeeze:{}:{}", subgraphIndex, operatorIndex);
2582
2583 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2584
2585 std::vector<uint32_t> squeezeDim;
2586 // A single negative dim index is interpreted as a negative index in python
2587 // Meaning the index will be the shape size plus the negative index value
2588 if (options->squeeze_dims.size() == 1 && options->squeeze_dims[0] < 0)
2589 {
2590 int32_t dim = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions()) + options->squeeze_dims[0];
2591 squeezeDim.push_back(static_cast<uint32_t>(dim));
2592 }
2593 else
2594 {
2595 squeezeDim = AsUnsignedVector(options->squeeze_dims);
2596 }
2597
2598 armnn::TensorInfo outputTensorInfo = TfLiteParserImpl::OutputShapeOfSqueeze(squeezeDim, inputTensorInfo);
2599
2600 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
2601
2602 ReshapeDescriptor reshapeDesc;
2603 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
2604
2605 auto outputTensorIds = GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex);
2606 m_TensorInfos[outputTensorIds[0]] = outputTensorInfo;
2607
2608 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
2609
2610 if (!layer)
2611 {
2612 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2613 operatorIndex, CHECK_LOCATION().AsString()));
2614 }
2615
2616 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2617
2618 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2619 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2620
2621 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2622 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2623}
2624
2625void TfLiteParserImpl::ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex)
2626{
2627 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2628
2629 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2630 CHECK_VALID_SIZE(inputs.size(), 4);
2631
2632 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2633 CHECK_VALID_SIZE(outputs.size(), 1);
2634
2635 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2636 const auto* options = operatorPtr->builtin_options.AsStridedSliceOptions();
2637
2638 StridedSliceDescriptor desc;
2639 desc.m_BeginMask = options->begin_mask;
2640 desc.m_EllipsisMask = options->ellipsis_mask;
2641 desc.m_EndMask = options->end_mask;
2642 desc.m_NewAxisMask = options->new_axis_mask;
2643 desc.m_ShrinkAxisMask = options->shrink_axis_mask;
2645
2646 armnn::TensorInfo beginTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2647 BufferRawPtr beginBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2648 ValidateBuffer(beginBufferPtr, beginTensorInfo, "begin");
2649
2650 std::vector<int> begin(beginTensorInfo.GetNumElements());
2651 if (beginBufferPtr->data.data() != nullptr)
2652 {
2653 ::memcpy(begin.data(), beginBufferPtr->data.data(), beginTensorInfo.GetNumBytes());
2654 }
2655 else
2656 {
2657 throw ParseException("ParseStridedSlice: Invalid input - the begin vector is null");
2658 }
2659
2660 armnn::TensorInfo endTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
2661 BufferRawPtr endBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
2662 ValidateBuffer(endBufferPtr, endTensorInfo, "end");
2663
2664 std::vector<int> end(endTensorInfo.GetNumElements());
2665 if (endBufferPtr->data.data() != nullptr)
2666 {
2667 ::memcpy(end.data(), endBufferPtr->data.data(), endTensorInfo.GetNumBytes());
2668 }
2669 else
2670 {
2671 throw ParseException("ParseStridedSlice: Invalid input - the end vector is null");
2672 }
2673
2674 armnn::TensorInfo strideTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 3);
2675 BufferRawPtr strideBufferPtr = GetBuffer(m_Model, inputs[3]->buffer);
2676 ValidateBuffer(strideBufferPtr, strideTensorInfo, "stride");
2677
2678 std::vector<int> stride(strideTensorInfo.GetNumElements());
2679
2680 if (strideBufferPtr->data.data() != nullptr)
2681 {
2682 ::memcpy(stride.data(), strideBufferPtr->data.data(), strideTensorInfo.GetNumBytes());
2683 }
2684 else
2685 {
2686 throw ParseException("ParseStridedSlice: Invalid input - the stride vector is null");
2687 }
2688
2689 desc.m_Begin = begin;
2690 desc.m_End = end;
2691 desc.m_Stride = stride;
2692
2693 auto layerName = fmt::format("StridedSlice:{}:{}", subgraphIndex, operatorIndex);
2694 IConnectableLayer* layer = m_Network->AddStridedSliceLayer(desc, layerName.c_str());
2695
2696 if (!layer)
2697 {
2698 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2699 operatorIndex, CHECK_LOCATION().AsString()));
2700 }
2701
2702 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2703 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2704
2705 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2706 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2707
2708 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2709 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2710}
2711
2712void TfLiteParserImpl::ParseSub(size_t subgraphIndex, size_t operatorIndex)
2713{
2714 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2715
2716 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2717 const auto* options = operatorPtr->builtin_options.AsSubOptions();
2718
2719 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2720 CHECK_VALID_SIZE(inputs.size(), 2);
2721
2722 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2723 CHECK_VALID_SIZE(outputs.size(), 1);
2724
2725 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2726 armnn::TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2727
2728 auto layerName = fmt::format("Sub:{}:{}", subgraphIndex, operatorIndex);
2729 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Sub, layerName.c_str());
2730
2731 if (!layer)
2732 {
2733 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2734 operatorIndex, CHECK_LOCATION().AsString()));
2735 }
2736
2737 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2738 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2739
2740 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2741 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2742 if (options)
2743 {
2744 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2745 }
2746
2747 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2748 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2749}
2750
2751void TfLiteParserImpl::ParseDiv(size_t subgraphIndex, size_t operatorIndex)
2752{
2753 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2754
2755 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2756 const auto* options = operatorPtr->builtin_options.AsDivOptions();
2757
2758 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2759 CHECK_VALID_SIZE(inputs.size(), 2);
2760
2761 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2762 CHECK_VALID_SIZE(outputs.size(), 1);
2763
2764 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2765 armnn::TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2766
2767 auto layerName = fmt::format("Div:{}:{}", subgraphIndex, operatorIndex);
2768 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Div, layerName.c_str());
2769
2770 if (!layer)
2771 {
2772 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2773 operatorIndex, CHECK_LOCATION().AsString()));
2774 }
2775
2776 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2777 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2778
2779 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2780 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2781 if (options)
2782 {
2783 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2784 }
2785
2786 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2787 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2788}
2789
2790void TfLiteParserImpl::ParseFloorDiv(size_t subgraphIndex, size_t operatorIndex)
2791{
2792 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2793
2794 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2795 CHECK_VALID_SIZE(inputs.size(), 2);
2796
2797 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2798 CHECK_VALID_SIZE(outputs.size(), 1);
2799
2800 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2801 armnn::TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2802
2803 auto layerName = fmt::format("FloorDiv:{}:{}", subgraphIndex, operatorIndex);
2804 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::FloorDiv, layerName.c_str());
2805
2806 if (!layer)
2807 {
2808 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2809 operatorIndex, CHECK_LOCATION().AsString()));
2810 }
2811
2812 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2813 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2814
2815 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2816 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2817 layer = AddFusedFloorLayer(layer, 0);
2818
2819 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2820 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2821}
2822
2823void TfLiteParserImpl::ParseAdd(size_t subgraphIndex, size_t operatorIndex)
2824{
2825 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2826
2827 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2828 const auto* options = operatorPtr->builtin_options.AsAddOptions();
2829
2830 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2831 CHECK_VALID_SIZE(inputs.size(), 2);
2832
2833 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2834 CHECK_VALID_SIZE(outputs.size(), 1);
2835
2836 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2837 armnn::TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2838
2839 auto layerName = fmt::format("Add:{}:{}", subgraphIndex, operatorIndex);
2840 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Add, layerName.c_str());
2841
2842 if (!layer)
2843 {
2844 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2845 operatorIndex, CHECK_LOCATION().AsString()));
2846 }
2847
2848 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2849 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2850
2851 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2852 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2853 if (options)
2854 {
2855 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2856 }
2857
2858 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2859 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2860}
2861
2862void TfLiteParserImpl::ParseMul(size_t subgraphIndex, size_t operatorIndex)
2863{
2864 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2865
2866 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
2867 const auto* options = operatorPtr->builtin_options.AsMulOptions();
2868
2869 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2870 CHECK_VALID_SIZE(inputs.size(), 2);
2871
2872 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2873 CHECK_VALID_SIZE(outputs.size(), 1);
2874
2875 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2876 armnn::TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2877
2878 auto layerName = fmt::format("Mul:{}:{}", subgraphIndex, operatorIndex);
2879 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Mul, layerName.c_str());
2880
2881 if (!layer)
2882 {
2883 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2884 operatorIndex, CHECK_LOCATION().AsString()));
2885 }
2886
2887 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
2888 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2889
2890 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2891 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
2892 if (options)
2893 {
2894 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
2895 }
2896
2897 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2898 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2899}
2900
2901void TfLiteParserImpl::ParseMean(size_t subgraphIndex, size_t operatorIndex)
2902{
2903 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2904
2905 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2906
2907 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2908 CHECK_VALID_SIZE(outputs.size(), 1);
2909
2910 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2911 TensorInfo dimTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2912
2913 armnn::MeanDescriptor desc;
2914 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
2915
2916 // Get const axis value from model and set it to descriptor.
2917 if (axisBufferPtr != nullptr && !axisBufferPtr->data.empty())
2918 {
2919 ValidateBuffer(axisBufferPtr, dimTensorInfo, "axis");
2920 std::vector<int32_t> axisData(dimTensorInfo.GetNumElements());
2921 ::memcpy(axisData.data(), axisBufferPtr->data.data(), dimTensorInfo.GetNumBytes());
2922
2923 // Convert the axis to unsigned int and remove duplicates.
2924 auto rank = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
2925 std::set<unsigned int> uniqueAxis;
2926 std::transform(axisData.begin(),
2927 axisData.end(),
2928 std::inserter(uniqueAxis, uniqueAxis.begin()),
2929 [rank](int i)->unsigned int{
2930 return static_cast<uint32_t>(((i + rank) % rank)); });
2931 desc.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
2932 }
2933 else
2934 {
2935 for (uint32_t i = 0; i < inputTensorInfo.GetNumDimensions(); ++i)
2936 {
2937 desc.m_Axis.push_back(i);
2938 }
2939 }
2940
2941 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0], true);
2942
2943 desc.m_KeepDims = inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ? true : false;
2944
2945 auto layerName = fmt::format("Mean:{}:{}", subgraphIndex, operatorIndex);
2946 IConnectableLayer* layer = m_Network->AddMeanLayer(desc, layerName.c_str());
2947
2948 if (!layer)
2949 {
2950 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
2951 operatorIndex, CHECK_LOCATION().AsString()));
2952 }
2953
2954 outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
2955 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2956
2957 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
2958 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
2959
2960 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
2961 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
2962}
2963
2964void TfLiteParserImpl::ParsePad(size_t subgraphIndex, size_t operatorIndex)
2965{
2966 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
2967
2968 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
2969
2970 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
2971 CHECK_VALID_SIZE(outputs.size(), 1);
2972
2973 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
2974 armnn::TensorInfo padTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
2975
2976 std::vector<unsigned int> padBuffer = GetUIntBuffer(padTensorInfo, m_Model, inputs[1]->buffer);
2977
2978 size_t step = 2;
2979 armnn::PadDescriptor desc;
2980 auto opcode = GetOpCode(m_Model, subgraphIndex, operatorIndex);
2981
2982 if (opcode == tflite::BuiltinOperator_PAD)
2983 {
2984 CHECK_VALID_SIZE(inputs.size(), 2);
2985
2986 if (inputTensorInfo.IsQuantized())
2987 {
2988 desc.m_PadValue = static_cast<float>(inputTensorInfo.GetQuantizationOffset());
2989 }
2990 }
2991 else if (opcode == tflite::BuiltinOperator_PADV2)
2992 {
2993 CHECK_VALID_SIZE(inputs.size(), 3);
2994
2995 armnn::TensorInfo padValueTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
2996
2997 if (padValueTensorInfo.GetNumElements() != 1)
2998 {
2999 ARMNN_THROW_PARSE_EXCEPTION("Multiple padding values are not supported in PADV2");
3000 }
3001 BufferRawPtr padValueBufferPtr = GetBuffer(m_Model, inputs[2]->buffer);
3002
3003 // Get the pad value from the input tensor
3004 if (!padValueBufferPtr->data.empty())
3005 {
3006 switch (padValueTensorInfo.GetDataType())
3007 {
3009 {
3010 std::vector<float> padValueBuffer(padValueTensorInfo.GetNumElements());
3011
3012 const size_t requiredBytes = padValueTensorInfo.GetNumElements() * sizeof(float);
3013 if (padValueBufferPtr->data.size() < requiredBytes)
3014 {
3015 throw ParseException("Pad value buffer is too small for expected data.");
3016 }
3017
3018 ::memcpy(padValueBuffer.data(), padValueBufferPtr->data.data(), padValueBufferPtr->data.size());
3019 desc.m_PadValue = padValueBuffer[0];
3020 break;
3021 }
3023 {
3024 std::vector<uint8_t> padValueBuffer(padValueTensorInfo.GetNumElements());
3025
3026 const size_t requiredBytes = padValueTensorInfo.GetNumElements() * sizeof(uint8_t);
3027 if (padValueBufferPtr->data.size() < requiredBytes)
3028 {
3029 throw ParseException("Pad value buffer is too small for expected data.");
3030 }
3031
3032 ::memcpy(padValueBuffer.data(), padValueBufferPtr->data.data(), padValueBufferPtr->data.size());
3033 desc.m_PadValue = armnn::Dequantize<uint8_t>(padValueBuffer[0],
3034 padValueTensorInfo.GetQuantizationScale(),
3035 padValueTensorInfo.GetQuantizationOffset());
3036 break;
3037 }
3040 {
3041 std::vector<int8_t> padValueBuffer(padValueTensorInfo.GetNumElements());
3042
3043 const size_t requiredBytes = padValueTensorInfo.GetNumElements() * sizeof(int8_t);
3044 if (padValueBufferPtr->data.size() < requiredBytes)
3045 {
3046 throw ParseException("Pad value buffer is too small for expected data.");
3047 }
3048
3049 ::memcpy(padValueBuffer.data(), padValueBufferPtr->data.data(), padValueBufferPtr->data.size());
3050 desc.m_PadValue = armnn::Dequantize<int8_t>(padValueBuffer[0],
3051 padValueTensorInfo.GetQuantizationScale(),
3052 padValueTensorInfo.GetQuantizationOffset());
3053 break;
3054 }
3055 default: ARMNN_THROW_PARSE_EXCEPTION("Unsupported DataType");
3056 }
3057 }
3058 else if (inputTensorInfo.IsQuantized())
3059 {
3060 desc.m_PadValue = static_cast<float>(inputTensorInfo.GetQuantizationOffset());
3061 }
3062 }
3063
3064 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
3065 {
3066 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
3067 }
3068
3069 auto layerName = (opcode == tflite::BuiltinOperator_PAD) ? fmt::format("Pad:{}:{}", subgraphIndex, operatorIndex)
3070 : fmt::format("PadV2:{}:{}", subgraphIndex, operatorIndex);
3071
3072 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
3073
3074 if (!layer)
3075 {
3076 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3077 operatorIndex, CHECK_LOCATION().AsString()));
3078 }
3079
3080 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
3081 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3082
3083 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3084 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3085
3086 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3087 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3088}
3089
3090void TfLiteParserImpl::ParseMirrorPad(size_t subgraphIndex, size_t operatorIndex)
3091{
3092 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3093
3094 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3095 CHECK_VALID_SIZE(inputs.size(), 2);
3096
3097 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3098 CHECK_VALID_SIZE(outputs.size(), 1);
3099
3100 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
3101
3102 armnn::TensorInfo padTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
3103 BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3104
3105 std::vector<unsigned int> padBuffer(padTensorInfo.GetNumElements());
3106 ValidateBuffer(bufferPtr, padTensorInfo, "axis");
3107 ::memcpy(padBuffer.data(), bufferPtr->data.data(), padTensorInfo.GetNumBytes());
3108
3109 size_t step = 2;
3110 armnn::PadDescriptor desc;
3111 for (unsigned int i = 0; i < padTensorInfo.GetNumElements() / step; ++i)
3112 {
3113 desc.m_PadList.emplace_back(padBuffer[i * step], padBuffer[i * step + 1]);
3114 }
3115
3116 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3117 const auto* options = operatorPtr->builtin_options.AsMirrorPadOptions();
3118
3119 if (options->mode == tflite::MirrorPadMode_REFLECT)
3120 {
3121 desc.m_PaddingMode = PaddingMode::Reflect;
3122 }
3123 else if (options->mode == tflite::MirrorPadMode_SYMMETRIC)
3124 {
3125 desc.m_PaddingMode = PaddingMode::Symmetric;
3126 }
3127 else
3128 {
3129 ARMNN_THROW_PARSE_EXCEPTION("PaddingMode must be either REFLECT or SYMMETRIC");
3130 }
3131
3132 // If padding mode is Reflect then both paddings must be no greater than inputShape(i) - 1.
3133 // If padding mode is Symmetric then both paddings must be no greater than inputShape(i).
3134 auto inputShape = inputTensorInfo.GetShape();
3135 auto padList = desc.m_PadList;
3136
3137 const unsigned int isReflect = static_cast<unsigned int>(desc.m_PaddingMode == PaddingMode::Reflect);
3138 for(unsigned int i = 0; i < padList.size(); ++i)
3139 {
3140 if(padList.at(i).first > (inputShape[i] - isReflect) ||
3141 padList.at(i).second > (inputShape[i] - isReflect))
3142 {
3143 ARMNN_THROW_PARSE_EXCEPTION("Padding values must be less (Reflect) or "
3144 "equal (Symmetric) to the dimension size.");
3145 }
3146 }
3147
3148 auto layerName = fmt::format("MirrorPad:{}:{}", subgraphIndex, operatorIndex);
3149
3150 IConnectableLayer* layer = m_Network->AddPadLayer(desc, layerName.c_str());
3151
3152 if (!layer)
3153 {
3154 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3155 operatorIndex, CHECK_LOCATION().AsString()));
3156 }
3157
3158 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
3159 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3160
3161 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3162 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3163
3164 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3165 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3166}
3167
3168void TfLiteParserImpl::ParsePrelu(size_t subgraphIndex, size_t operatorIndex)
3169{
3170 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3171
3172 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3173 CHECK_VALID_SIZE(inputs.size(), 2);
3174
3175 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3176 CHECK_VALID_SIZE(outputs.size(), 1);
3177
3178 auto layerName = fmt::format("Prelu:{}:{}", subgraphIndex, operatorIndex);
3179
3180 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
3181 armnn::TensorInfo alphaTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
3182
3183 IConnectableLayer* layer = m_Network->AddPreluLayer(layerName.c_str());
3184
3185 if (!layer)
3186 {
3187 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3188 operatorIndex, CHECK_LOCATION().AsString()));
3189 }
3190
3191 if (IsConstTensor(inputs[1]))
3192 {
3193 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3194 armnn::IInputSlot* slot = &(layer->GetInputSlot(0));
3195 RegisterConsumerOfTensor(subgraphIndex, inputTensorIndexes[0], slot);
3196
3197 auto alphaTensorAndData = CreateConstTensorNonPermuted(inputs[1], alphaTensorInfo,
3198 inputTensorInfo.GetDataType());
3199 std::string constLayerName = fmt::format("Constant:{}", inputs[1]->name);
3200 IConnectableLayer* constLayer =
3201 m_Network->AddConstantLayer(alphaTensorAndData.first, constLayerName.c_str());
3202
3203 if (!constLayer)
3204 {
3205 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3206 operatorIndex, CHECK_LOCATION().AsString()));
3207 }
3208
3209 constLayer->GetOutputSlot(0).SetTensorInfo(alphaTensorInfo);
3210 constLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
3211 RegisterOutputSlots(subgraphIndex,
3212 VIRTUAL_OPERATOR_ID,
3213 constLayer,
3214 { inputTensorIndexes[1] });
3215 }
3216 else
3217 {
3218 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3219 RegisterInputSlots(subgraphIndex, operatorIndex, layer, inputTensorIndexes);
3220 }
3221
3222 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
3223 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3224
3225 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3226 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3227}
3228
3229void TfLiteParserImpl::ParseQuantize(size_t subgraphIndex, size_t operatorIndex)
3230{
3231 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3232
3233 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3234 CHECK_VALID_SIZE(inputs.size(), 1);
3235
3236 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3237 CHECK_VALID_SIZE(outputs.size(), 1);
3238
3239 auto layerName = fmt::format("Quantize:{}:{}", subgraphIndex, operatorIndex);
3240
3241 IConnectableLayer* layer = m_Network->AddQuantizeLayer(layerName.c_str());
3242
3243 if (!layer)
3244 {
3245 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3246 operatorIndex, CHECK_LOCATION().AsString()));
3247 }
3248
3249 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
3250 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3251
3252 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3253 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3254
3255 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3256 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3257}
3258
3259void TfLiteParserImpl::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
3260{
3261 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::ReLu);
3262}
3263
3264void TfLiteParserImpl::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
3265{
3266 ParseActivation(subgraphIndex,operatorIndex, ActivationFunction::BoundedReLu);
3267}
3268
3269void TfLiteParserImpl::ParseLeakyRelu(size_t subgraphIndex, size_t operatorIndex)
3270{
3271 ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::LeakyReLu);
3272}
3273
3274void TfLiteParserImpl::ParseLogistic(size_t subgraphIndex, size_t operatorIndex)
3275{
3276 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Sigmoid);
3277}
3278
3279void TfLiteParserImpl::ParseTanH(size_t subgraphIndex, size_t operatorIndex)
3280{
3281 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::TanH);
3282}
3283
3284void TfLiteParserImpl::ParseElu(size_t subgraphIndex, size_t operatorIndex)
3285{
3286 ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::Elu);
3287}
3288
3289void TfLiteParserImpl::ParseHardSwish(size_t subgraphIndex, size_t operatorIndex)
3290{
3291 ParseActivation(subgraphIndex, operatorIndex, ActivationFunction::HardSwish);
3292}
3293
3294void TfLiteParserImpl::ParseGelu(size_t subgraphIndex, size_t operatorIndex)
3295{
3296 ParseActivation(subgraphIndex,operatorIndex,ActivationFunction::Gelu);
3297}
3298
3299void TfLiteParserImpl::ParseActivation(size_t subgraphIndex, size_t operatorIndex, ActivationFunction activationType)
3300{
3301 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3302 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3303 IgnoreUnused(operatorPtr);
3304
3305 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3306 CHECK_VALID_SIZE(inputs.size(), 1);
3307
3308 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3309 CHECK_VALID_SIZE(outputs.size(), 1);
3310
3311 auto layerName = fmt::format("Activation:");
3312 ActivationDescriptor activationDesc;
3313 activationDesc.m_Function = activationType;
3314
3315 switch (activationType)
3316 {
3317 case ActivationFunction::ReLu:
3318 {
3319 layerName += fmt::format("RELU:{}:{}", subgraphIndex, operatorIndex);
3320 break;
3321 }
3322 case ActivationFunction::BoundedReLu:
3323 {
3324 layerName += fmt::format("RELU6:{}:{}", subgraphIndex, operatorIndex);
3325 activationDesc.m_A = 6.0f;
3326 activationDesc.m_B = 0.0f;
3327 break;
3328 }
3329 case ActivationFunction::Sigmoid:
3330 {
3331 layerName += fmt::format("SIGMOID:{}:{}", subgraphIndex, operatorIndex);
3332 break;
3333 }
3334 case ActivationFunction::TanH:
3335 {
3336 layerName += fmt::format("TANH:{}:{}", subgraphIndex, operatorIndex);
3337 activationDesc.m_A = 1.0f;
3338 activationDesc.m_B = 1.0f;
3339 break;
3340 }
3341 case ActivationFunction::LeakyReLu:
3342 {
3343 layerName += fmt::format("LEAKYRELU:{}:{}", subgraphIndex, operatorIndex);
3344 const auto* options = operatorPtr->builtin_options.AsLeakyReluOptions();
3345 activationDesc.m_A = options->alpha;
3346 break;
3347 }
3348 case ActivationFunction::Elu:
3349 {
3350 layerName += fmt::format("ELU:{}:{}", subgraphIndex, operatorIndex);
3351 activationDesc.m_A = 1.0f;
3352 break;
3353 }
3354 case ActivationFunction::HardSwish:
3355 {
3356 layerName += fmt::format("HARDSWISH:{}:{}", subgraphIndex, operatorIndex);
3357 break;
3358 }
3359 case ActivationFunction::Gelu:
3360 {
3361 layerName += fmt::format("GELU:{}:{}", subgraphIndex, operatorIndex);
3362 break;
3363 }
3364 default:
3365 {
3366 throw ParseException(
3367 fmt::format("Unexpected ActivationFunction[{}] when creating layerName {} ",
3368 static_cast<int>(activationType), CHECK_LOCATION().AsString()));
3369 }
3370 }
3371
3372 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, layerName.c_str());
3373
3374 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
3375 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3376
3377 // register the input connection slots for the layer, connections are made after all layers have been created
3378 // only the tensors for the inputs are relevant, exclude the const tensors
3379 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3380 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3381
3382 // register the output connection slots for the layer, connections are made after all layers have been created
3383 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3384 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3385}
3386
3388 const std::vector<int32_t>& targetDimsIn)
3389{
3390 std::vector<unsigned int> outputDims(targetDimsIn.begin(), targetDimsIn.end());
3391 const auto stretchDim = std::find(targetDimsIn.begin(), targetDimsIn.end(), -1);
3392
3393 if (stretchDim != targetDimsIn.end())
3394 {
3395 if (std::find(std::next(stretchDim), targetDimsIn.end(), -1) != targetDimsIn.end())
3396 {
3397 throw ParseException(
3398 fmt::format("At most one component of shape can be -1 {}", CHECK_LOCATION().AsString()));
3399 }
3400
3401 auto targetNumElements =
3403 std::accumulate(targetDimsIn.begin(), targetDimsIn.end(), -1, std::multiplies<int32_t>()));
3404
3405 auto stretchIndex = static_cast<size_t>(std::distance(targetDimsIn.begin(), stretchDim));
3406
3407 if (targetNumElements == 0)
3408 {
3409 if (inputTensorInfo.GetNumElements() == 0)
3410 {
3411 outputDims[stretchIndex] = 0;
3412 }
3413 else
3414 {
3415 throw ParseException(
3416 fmt::format("Input to reshape is a tensor with elements, but the requested shape has 0. {}",
3417 CHECK_LOCATION().AsString()));
3418 }
3419 }
3420 else
3421 {
3422 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
3423 }
3424 }
3425
3426 TensorShape outputShape = TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
3427
3428 TensorInfo reshapeInfo = inputTensorInfo;
3429 reshapeInfo.SetShape(outputShape);
3430
3431 return reshapeInfo;
3432}
3433
3434void TfLiteParserImpl::ParseReshape(size_t subgraphIndex, size_t operatorIndex)
3435{
3436 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3437
3438 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3439
3440 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3441 CHECK_VALID_SIZE(outputs.size(), 1);
3442
3443 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3444 const auto* options = operatorPtr->builtin_options.AsReshapeOptions();
3445 auto layerName = fmt::format("Reshape:{}:{}", subgraphIndex, operatorIndex);
3446
3447 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
3448 armnn::TensorInfo actualOutputTensorInfo = ToTensorInfo(outputs[0]);
3449 CheckMatchingQuantization(inputTensorInfo, actualOutputTensorInfo, layerName, "Input 0", "Output 0");
3450
3451 // Extracting new shape for the output
3452 // There are two ways it can be passed
3453 // * First is to define the target shape in the operator built-in options
3454 // * Second is to pass it as a second input tensor
3455 std::vector<int32_t> targetShape;
3456 bool targetShapeFound = false;
3457 // Check if built-in options were given
3458 if (options != nullptr)
3459 {
3460 // make sure the parameter is given
3461 if (options->new_shape.empty() == false)
3462 {
3463 targetShape = options->new_shape;
3464 targetShapeFound = true;
3465 }
3466 }
3467
3468 // If there is no built-in option given or if the built-in new_shape parameter was empty
3469 if (!targetShapeFound)
3470 {
3471 // Check for a second input tensor
3472 if (inputs.size() > 1 && inputs[1] != nullptr)
3473 {
3474 if (inputs[1]->is_variable)
3475 {
3476 ARMNN_THROW_PARSE_EXCEPTION( "Target shapes defined in non-const input tensors is not supported");
3477 }
3478
3479 if (inputs[1]->shape.size() != 1)
3480 {
3481 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not a 1D tensor");
3482 }
3483
3484 if (inputs[1]->type != tflite::TensorType_INT32)
3485 {
3486 ARMNN_THROW_PARSE_EXCEPTION("Target 'shape' input is not an int32 type");
3487 }
3488
3489 // Extract target shape from input
3490 auto bufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3491 auto values = reinterpret_cast<const int32_t*>(bufferPtr->data.data());
3492 if (values)
3493 {
3494 for (int i = 0; i < inputs[1]->shape[0]; ++i)
3495 {
3496 targetShape.push_back(values[i]);
3497 }
3498 }
3499 else
3500 {
3501 try
3502 {
3503 // We attempt to infer during Runtime.
3504 TensorShape reshapeShapes = ToTensorInfo(inputs[1]).GetShape();
3505
3506 if (reshapeShapes[0] == actualOutputTensorInfo.GetNumDimensions())
3507 {
3508 for (unsigned int i = 0; i < actualOutputTensorInfo.GetShape().GetNumDimensions(); ++i)
3509 {
3510 targetShape.push_back(actualOutputTensorInfo.GetShape()[i]);
3511 }
3512 }
3513 // The parser only supports shape (batch, -1) or (-1) for non-constant shape input.
3514 else if (reshapeShapes[0] > 2)
3515 {
3516 throw ParseException(fmt::format("Invalid input shape '{}' in Reshape layer '{}' {}. "
3517 "When inferring during runtime, the parser only supports "
3518 "shape (batch, -1) or (-1) for target shape input.",
3519 reshapeShapes[0],
3520 layerName,
3521 CHECK_LOCATION().AsString()));
3522 }
3523 else
3524 {
3525 const int32_t numInputElements = inputTensorInfo.GetNumElements();
3526 const int32_t inputTensorShape = inputTensorInfo.GetShape()[0];
3527 if (reshapeShapes[0] == 1)
3528 {
3529 targetShape = {numInputElements};
3530 }
3531 else if (reshapeShapes[0] == 2)
3532 {
3533 targetShape = {inputTensorShape, numInputElements / inputTensorShape};
3534 }
3535 }
3536 }
3537 catch (const std::exception& exc)
3538 {
3539 ARMNN_THROW_PARSE_EXCEPTION("Failed attempt to infer during runtime the target shape input for "
3540 "Reshape operation. Reshape operator target shape input buffer data "
3541 "is null. " << exc.what());
3542 }
3543 }
3544 }
3545 else
3546 {
3547 ARMNN_THROW_PARSE_EXCEPTION("Target shape not defined in reshape parameters or input tensor. "
3548 "At least one method required");
3549 }
3550 }
3551
3552 armnn::TensorInfo reshapeOutputTensorInfo = TfLiteParserImpl::OutputShapeOfReshape(inputTensorInfo, targetShape);
3553
3554 // Check for valid input size and that reshape parameters equal output shape
3555 // The output shape can be provided to us in 2 ways:
3556 // 1. through the normal 'shape' parameter given by outputs[indx]->shape
3557 // 2. through additional parameter 'shape_signature' given by outputs[indx]->buffer.
3558 // This parameter can sometimes contain -1 value not visible in the 'shape' parameter.
3559 const armnn::TensorShape& reshapeOutputTensorShape = reshapeOutputTensorInfo.GetShape();
3560 if (inputs.size() > 1 && !CheckShape(reshapeOutputTensorShape, outputs[0]->shape)
3561 && !outputs[0]->shape_signature.empty())
3562 {
3563 // Attempt to extract output shape from secondary 'shape_signature'
3564 // parameter and try to CheckShape() with this param.
3565 std::vector<int32_t> secondaryOutputTargetShape = outputs[0]->shape_signature;
3566
3567 // if outputs[0]->shape_signature contain a -1 value, we need to compute its actual value
3568 // from reshape input in order to correctly verify reshape parameters equal output shape
3569 armnn::TensorInfo secondaryReshapeOutputTensorInfo =
3570 TfLiteParserImpl::OutputShapeOfReshape(inputTensorInfo, secondaryOutputTargetShape);
3571
3572 if (!CheckShape(reshapeOutputTensorShape, secondaryReshapeOutputTensorInfo.GetShape()))
3573 {
3574 std::stringstream ss;
3575 ss << "New shape defined in reshape parameters "
3576 << reshapeOutputTensorShape
3577 << " does not equal output shape "
3578 << actualOutputTensorInfo.GetShape()
3579 << ": "
3580 << CHECK_LOCATION().AsString();
3581 throw ParseException(ss.str());
3582 }
3583 }
3584 auto outputTensorIds = GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex);
3585
3586 ReshapeDescriptor reshapeDesc;
3587 reshapeDesc.m_TargetShape = reshapeOutputTensorInfo.GetShape();
3588 m_TensorInfos[outputTensorIds[0]] = reshapeOutputTensorInfo;
3589
3590 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
3591
3592 if (!layer)
3593 {
3594 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3595 operatorIndex, CHECK_LOCATION().AsString()));
3596 }
3597
3598 layer->GetOutputSlot(0).SetTensorInfo(reshapeOutputTensorInfo);
3599
3600 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3601 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3602
3603 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3604 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3605}
3606
3607void TfLiteParserImpl::ParseResizeBilinear(size_t subgraphIndex, size_t operatorIndex)
3608{
3609 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::Bilinear);
3610}
3611
3612void TfLiteParserImpl::ParseResizeNearestNeighbor(size_t subgraphIndex, size_t operatorIndex)
3613{
3614 ParseResize(subgraphIndex, operatorIndex, ResizeMethod::NearestNeighbor);
3615}
3616
3617void TfLiteParserImpl::ParseResize(size_t subgraphIndex, size_t operatorIndex, ResizeMethod resizeMethod)
3618{
3619 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3620
3621 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3622 CHECK_VALID_SIZE(inputs.size(), 2);
3623
3624 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3625 CHECK_VALID_SIZE(outputs.size(), 1);
3626
3627 armnn::TensorInfo sizeTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
3628
3629 // Data for the parsed tensor args (size) must be stored locally.
3630 std::vector<int32_t> sizeTensorData(sizeTensorInfo.GetNumElements());
3631
3632 BufferRawPtr sizeBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3633 ValidateBuffer(sizeBufferPtr, sizeTensorInfo, "size");
3634 ::memcpy(sizeTensorData.data(), sizeBufferPtr->data.data(), sizeTensorInfo.GetNumBytes());
3635
3636 ResizeDescriptor desc;
3637 desc.m_Method = resizeMethod;
3638 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
3639 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
3641
3642 auto layerName = fmt::format("Resize:");
3643
3644 switch (resizeMethod)
3645 {
3646 case ResizeMethod::Bilinear:
3647 {
3648 layerName += fmt::format("BILINEAR:{}:{}", subgraphIndex, operatorIndex);
3649
3650 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3651 const auto * options = operatorPtr->builtin_options.AsResizeBilinearOptions();
3652
3653 desc.m_AlignCorners = options->align_corners;
3654 desc.m_HalfPixelCenters = options->half_pixel_centers;
3655 break;
3656 }
3657 case ResizeMethod::NearestNeighbor:
3658 {
3659 layerName += fmt::format("NEARESTNEIGHBOR:{}:{}", subgraphIndex, operatorIndex);
3660 break;
3661 }
3662 default:
3663 {
3664 throw ParseException(
3665 fmt::format("Unexpected ResizeMethod[{}] when creating layerName {} ",
3666 static_cast<int>(resizeMethod), CHECK_LOCATION().AsString()));
3667 }
3668 }
3669
3670 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
3671
3672 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, layerName.c_str());
3673
3674 if (!layer)
3675 {
3676 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3677 operatorIndex, CHECK_LOCATION().AsString()));
3678 }
3679
3680 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
3681 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
3682 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3683
3684 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3685 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3686
3687 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3688 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
3689}
3690
3691void TfLiteParserImpl::ParseReverseV2(size_t subgraphIndex, size_t operatorIndex)
3692{
3693 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3694
3695 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3696 CHECK_VALID_SIZE(inputs.size(), 2);
3697
3698 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3699 CHECK_VALID_SIZE(outputs.size(), 1);
3700
3701 auto layerName = fmt::format("ReverseV2:{}:{}", subgraphIndex, operatorIndex);
3702
3703 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3704 TensorInfo axisTensorInfo = ToTensorInfo(inputs[1]);
3705 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
3706
3707 IConnectableLayer* layer = m_Network->AddReverseV2Layer(layerName.c_str());
3708 ARMNN_ASSERT(layer != nullptr);
3709
3710 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3711
3712 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3713 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
3714
3715 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3716 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3717}
3718
3719void TfLiteParserImpl::ParseTile(size_t subgraphIndex, size_t operatorIndex)
3720{
3721 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3722
3723 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3724 CHECK_VALID_SIZE(inputs.size(), 2);
3725
3726 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3727 CHECK_VALID_SIZE(outputs.size(), 1);
3728
3729 TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
3730 TensorInfo multiplesTensorInfo = ToTensorInfo(inputs[1]);
3731 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
3732
3733 auto layerName = fmt::format("Tile:{}:{}", subgraphIndex, operatorIndex);
3734
3735 TileDescriptor descriptor;
3736
3737 BufferRawPtr multiplesBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
3738 if (multiplesBufferPtr != nullptr)
3739 {
3740 std::vector<int32_t> multiplesData(multiplesTensorInfo.GetNumElements());
3741 ValidateBuffer(multiplesBufferPtr, multiplesTensorInfo, "multiples");
3742 ::memcpy(multiplesData.data(), multiplesBufferPtr->data.data(), multiplesTensorInfo.GetNumBytes());
3743 descriptor.m_Multiples.assign(multiplesData.begin(), multiplesData.end());
3744 }
3745 else
3746 {
3747 ARMNN_THROW_PARSE_EXCEPTION("For Tile layer, Multiples data was not found in the buffer.");
3748 }
3749
3750 IConnectableLayer* layer = m_Network->AddTileLayer(descriptor, layerName.c_str());
3751 ARMNN_ASSERT(layer != nullptr);
3752
3753 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3754
3755 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3756 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
3757
3758 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3759 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3760}
3761
3762void TfLiteParserImpl::ParseConcatenation(size_t subgraphIndex, size_t operatorIndex)
3763{
3764 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3765
3766 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3767 const auto* options = operatorPtr->builtin_options.AsConcatenationOptions();
3768
3769 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
3770
3771 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3772 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3773 auto inputTensorIds = GetInputTensorIds(m_Model, subgraphIndex, operatorIndex);
3774
3775 CHECK_VALID_SIZE(outputs.size(), 1);
3776
3777 unsigned int numConcatView = static_cast<unsigned int>(inputs.size());
3778 uint32_t inputRank = InputTensorInfo(subgraphIndex, operatorIndex, 0).GetNumDimensions();
3779
3780 const unsigned int concatDimInput = static_cast<unsigned int>(
3781 (static_cast<int>(inputRank) + options->axis) % static_cast<int>(inputRank));
3782
3783 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
3784 concatDescriptor.SetConcatAxis(concatDimInput);
3785 unsigned int mergeDimOrigin = 0;
3786
3787 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
3788 {
3789 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, viewIndex);
3790
3791 // This set up concatDescriptor view origin
3793 inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
3794 }
3795
3796 auto layerName = fmt::format("Concatenation:{}:{}", subgraphIndex, operatorIndex);
3797
3798 IConnectableLayer* layer = m_Network->AddConcatLayer(concatDescriptor, layerName.c_str());
3799
3800 if (!layer)
3801 {
3802 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3803 operatorIndex, CHECK_LOCATION().AsString()));
3804 }
3805
3806 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {});
3807 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3808
3809 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3810 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
3811
3812 // add fused activation layer
3813 layer = AddFusedActivationLayer(layer, 0, options->fused_activation_function);
3814
3815 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3816 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
3817}
3818
3819void TfLiteParserImpl::ParseFullyConnected(size_t subgraphIndex, size_t operatorIndex)
3820{
3821 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3822
3823 const auto& operatorRfr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3824 const auto options = operatorRfr->builtin_options.AsFullyConnectedOptions();
3825
3826 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
3827
3828 FullyConnectedDescriptor desc;
3829 desc.m_BiasEnabled = false;
3830 desc.m_TransposeWeightMatrix = true;
3831
3832 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3833 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3834 CHECK_VALID_SIZE(outputs.size(), 1);
3835
3836 armnn::TensorInfo filterTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
3837
3838 // Fully Connected Layer accepts two dimensional weights input
3839 int32_t weightsDimension = static_cast<int32_t>(filterTensorInfo.GetNumDimensions());
3840 if (weightsDimension != 2)
3841 {
3842 throw ParseException(
3843 fmt::format("Dimension {} for Fully Connected weights is not supported by Armnn. "
3844 "Node {}",
3845 weightsDimension,
3846 CHECK_LOCATION().AsString()));
3847 }
3848
3849 armnn::IConnectableLayer* layer = nullptr;
3850 auto layerName = fmt::format("FullyConnected:{}:{}", subgraphIndex, operatorIndex);
3851
3852 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
3853 // Add the first input tensor to the registration list
3854 std::vector<unsigned int> tensorIndexesToRegister = {inputTensorIndexes[0]};
3855 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
3856
3857 desc.m_ConstantWeights = IsConstTensor(inputs[1]);
3858
3859 // Add the weights input to the registration list, constant layers will be added by SetupConstantLayers if constant.
3860 tensorIndexesToRegister.emplace_back(inputTensorIndexes[1]);
3861
3862 if (ShouldConstantTensorBeConverted(inputs[1], inputTensorInfo.GetDataType(), filterTensorInfo.GetDataType()))
3863 {
3864 m_ConstantsToDequantize.emplace_back(inputs[1]->buffer);
3865 }
3866
3867 if (inputs.size() == 3)
3868 {
3869 desc.m_BiasEnabled = true;
3870 armnn::TensorInfo biasTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
3871
3872 // Add the biases input to the registration list, constant layer will be added by SetupConstantLayers.
3873 tensorIndexesToRegister.emplace_back(inputTensorIndexes[2]);
3874
3875 if (ShouldConstantTensorBeConverted(inputs[2], inputTensorInfo.GetDataType(), biasTensorInfo.GetDataType()))
3876 {
3877 m_ConstantsToDequantize.emplace_back(inputs[2]->buffer);
3878 }
3879 }
3880
3881 // Filters and biases are always passed to fully connected as inputs
3882 layer = m_Network->AddFullyConnectedLayer(desc, layerName.c_str());
3883
3884 if (!layer)
3885 {
3886 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
3887 operatorIndex, CHECK_LOCATION().AsString()));
3888 }
3889
3890 unsigned int startingSlotIndex = 0;
3891 if (inputTensorInfo.GetNumDimensions() > 2)
3892 {
3893 // Add reshape to flatten to 2D [batch_size, input_size],
3894 // where "input_size" corresponds to the number of inputs to the layer,
3895 // matching the second dimension of weights,
3896 // and "batch_size" is calculated by dividing the number of elements by "input_size".
3897 std::vector<unsigned int> reshapedDimensions(2);
3898 reshapedDimensions[1] = filterTensorInfo.GetShape()[1];
3899 reshapedDimensions[0] = inputTensorInfo.GetNumElements() / reshapedDimensions[1];
3900
3901 if (inputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
3902 {
3903 throw ParseException(
3904 fmt::format("Failed to deduce input tensor shape from filter size {} {}",
3905 reshapedDimensions[1],
3906 CHECK_LOCATION().AsString()));
3907 }
3908
3909 armnn::TensorInfo reshapedTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
3910 reshapedTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
3911 inputTensorInfo = reshapedTensorInfo;
3912
3913 std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
3914 armnn::ReshapeDescriptor reshapeDescriptor;
3915 reshapeDescriptor.m_TargetShape = reshapedTensorInfo.GetShape();
3916 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(reshapeDescriptor,
3917 reshapeLayerName.c_str());
3918
3919 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedTensorInfo);
3920 reshapeLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
3921
3922 RegisterInputSlots(subgraphIndex, operatorIndex, reshapeLayer, {inputTensorIndexes[0]});
3923 // Fc layer connects to the reshape layer, so we skip the first input slot when registering fc's input slots
3924 tensorIndexesToRegister.erase(tensorIndexesToRegister.begin());
3925 startingSlotIndex = 1;
3926 }
3927
3928 RegisterInputSlots(subgraphIndex, operatorIndex, layer, tensorIndexesToRegister, startingSlotIndex);
3929
3930 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromShapes(subgraphIndex, operatorIndex, layer, 0,
3931 { inputTensorInfo.GetShape(),
3932 filterTensorInfo.GetShape() });
3933
3934 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
3935
3936 if (outputTensorInfo.GetNumDimensions() > 2)
3937 {
3938 // Calculate reshape to flatten to 2D [batch_size, input_size]
3939 std::vector<unsigned int> reshapedDimensions(2);
3940 reshapedDimensions[1] = filterTensorInfo.GetShape()[0];
3941 reshapedDimensions[0] = outputTensorInfo.GetNumElements() / reshapedDimensions[1];
3942 armnn::TensorInfo reshapedOutputTensorInfo = outputTensorInfo;
3943 if (outputTensorInfo.GetNumElements() % reshapedDimensions[1] != 0)
3944 {
3945 throw ParseException(
3946 fmt::format("Failed to deduce output tensor shape from filter size {} {}",
3947 reshapedDimensions[1],
3948 CHECK_LOCATION().AsString()));
3949 }
3950 reshapedOutputTensorInfo.SetShape(armnn::TensorShape{ 2, reshapedDimensions.data() });
3951 layer->GetOutputSlot(0).SetTensorInfo(reshapedOutputTensorInfo);
3952
3953 std::string reshapeLayerName = fmt::format("ExpandDims:{}:{}", subgraphIndex, operatorIndex);
3954 layer = AddReshapeLayer(layer, 0, reshapeLayerName, outputTensorInfo);
3955 }
3956
3957 // we need to add the activation layer and fortunately we don't need to care about the data layout
3958 armnn::IConnectableLayer* fusedActivationLayer = AddFusedActivationLayer(layer, 0,
3959 options->fused_activation_function);
3960
3961 // register the output connection slots for the layer, connections are made after all layers have been created
3962 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
3963 RegisterOutputSlots(subgraphIndex, operatorIndex, fusedActivationLayer, {outputTensorIndexes[0]});
3964
3965 m_TensorInfos[outputTensorIndexes[0]] = layer->GetOutputSlot(0).GetTensorInfo();
3966}
3967
3968void TfLiteParserImpl::ParseDetectionPostProcess(size_t subgraphIndex, size_t operatorIndex)
3969{
3970 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
3971
3972 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
3973
3974 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
3975 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
3976 CHECK_VALID_SIZE(outputs.size(), 4);
3977
3978 // Obtain custom options from flexbuffers
3979 auto custom_options = operatorPtr->custom_options;
3980 const flexbuffers::Map& m = flexbuffers::GetRoot(custom_options.data(), custom_options.size()).AsMap();
3981
3982 // Obtain descriptor information from tf lite
3983 DetectionPostProcessDescriptor desc;
3984 desc.m_MaxDetections = m["max_detections"].AsUInt32();
3985 desc.m_MaxClassesPerDetection = m["max_classes_per_detection"].AsUInt32();
3986 desc.m_NmsScoreThreshold = m["nms_score_threshold"].AsFloat();
3987 desc.m_NmsIouThreshold = m["nms_iou_threshold"].AsFloat();
3988 desc.m_NumClasses = m["num_classes"].AsUInt32();
3989 desc.m_ScaleH = m["h_scale"].AsFloat();
3990 desc.m_ScaleW = m["w_scale"].AsFloat();
3991 desc.m_ScaleX = m["x_scale"].AsFloat();
3992 desc.m_ScaleY = m["y_scale"].AsFloat();
3993
3994 if (!(m["use_regular_nms"].IsNull()))
3995 {
3996 desc.m_UseRegularNms = m["use_regular_nms"].AsBool();
3997 }
3998 if (!(m["detections_per_class"].IsNull()))
3999 {
4000 desc.m_DetectionsPerClass = m["detections_per_class"].AsUInt32();
4001 }
4002
4003 if (desc.m_NmsIouThreshold <= 0.0f || desc.m_NmsIouThreshold > 1.0f)
4004 {
4005 throw InvalidArgumentException("DetectionPostProcessTFLiteParser: Intersection over union threshold "
4006 "must be positive and less than or equal to 1.");
4007 }
4008
4009 armnn::TensorInfo anchorTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 2);
4010 auto anchorTensorAndData = CreateConstTensorNonPermuted(inputs[2], anchorTensorInfo);
4011
4012 auto layerName = fmt::format("DetectionPostProcess:{}:{}", subgraphIndex, operatorIndex);
4013 IConnectableLayer* layer = m_Network->AddDetectionPostProcessLayer(desc, anchorTensorAndData,
4014 layerName.c_str());
4015
4016 if (!layer)
4017 {
4018 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4019 operatorIndex, CHECK_LOCATION().AsString()));
4020 }
4021
4022 // The model does not specify the output shapes.
4023 // The output shapes are calculated from the max_detection and max_classes_per_detection.
4024 unsigned int numDetectedBox = desc.m_MaxDetections * desc.m_MaxClassesPerDetection;
4025 m_OverriddenOutputShapes.push_back({ 1, numDetectedBox, 4 });
4026 m_OverriddenOutputShapes.push_back({ 1, numDetectedBox });
4027 m_OverriddenOutputShapes.push_back({ 1, numDetectedBox });
4028 m_OverriddenOutputShapes.push_back({ 1 });
4029
4030 for (unsigned int i = 0 ; i < outputs.size() ; ++i)
4031 {
4032 armnn::TensorInfo detectionBoxOutputTensorInfo = ToTensorInfo(outputs[i], m_OverriddenOutputShapes[i]);
4033 layer->GetOutputSlot(i).SetTensorInfo(detectionBoxOutputTensorInfo);
4034 }
4035
4036 // Register the input connection slots for the layer, connections are made after all layers have been created
4037 // only the tensors for the inputs are relevant, exclude the const tensors
4038 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4039 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
4040
4041 // Register the output connection slots for the layer, connections are made after all layers have been created
4042 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4043 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0],
4044 outputTensorIndexes[1],
4045 outputTensorIndexes[2],
4046 outputTensorIndexes[3]});
4047}
4048
4049/// The TfLite Pack operator is equivalent to the ArmNN Stack operator
4050void TfLiteParserImpl::ParsePack(size_t subgraphIndex, size_t operatorIndex)
4051{
4052 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4053
4054 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4055 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4056 CHECK_VALID_SIZE(outputs.size(), 1);
4057
4058 if (inputs.size() < 1)
4059 {
4060 throw ParseException("Pack must have at least one input.");
4061 }
4062
4063 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4064 const auto* options = operatorPtr->builtin_options.AsPackOptions();
4065
4066 StackDescriptor desc;
4067 desc.m_Axis = static_cast<uint32_t>(options->axis);
4068 desc.m_NumInputs = static_cast<uint32_t>(inputs.size());
4069
4070 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
4071 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4072 desc.m_InputShape = inputTensorInfo.GetShape();
4073
4074 auto layerName = fmt::format("Pack:{}:{}", subgraphIndex, operatorIndex);
4075 IConnectableLayer* layer = m_Network->AddStackLayer(desc, layerName.c_str());
4076
4077 if (!layer)
4078 {
4079 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4080 operatorIndex, CHECK_LOCATION().AsString()));
4081 }
4082
4083 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {});
4084 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
4085
4086 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4087 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes});
4088
4089 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4090 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
4091}
4092
4093void TfLiteParserImpl::ParseUnidirectionalSequenceLSTM(size_t subgraphIndex, size_t operatorIndex)
4094{
4095 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4096
4097 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4098 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4099
4100 if (inputs.size() < 2)
4101 {
4102 throw ParseException("UnidirectionalSequenceLSTM must have at least 2 input.");
4103 }
4104
4105 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4106 const auto& subgraphPtr = m_Model->subgraphs[subgraphIndex];
4107 const auto nodeParams = operatorPtr->builtin_options.AsUnidirectionalSequenceLSTMOptions();
4108 CHECK_SUPPORTED_FUSED_ACTIVATION(nodeParams, subgraphIndex, operatorIndex);
4109 auto inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4110 auto outputTensorInfo = ToTensorInfo(outputs[0]);
4111
4112 // Set the params structure for the AddUnidirectionalSequenceLstmLayer call
4113 // Please refer to each operand at
4114 // https://www.tensorflow.org/mlir/tfl_ops#tflunidirectional_sequence_lstm_tflunidirectionalsequencelstmop
4115 armnn::LstmInputParams params;
4116
4117 if (IsOptionalOperandPresent(operatorPtr->inputs[1]))
4118 {
4119 params.m_InputToInputWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[1]].get(),
4120 inputTensorInfo).first;
4121 }
4122
4123 params.m_InputToForgetWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[2]].get(),
4124 inputTensorInfo).first;
4125 params.m_InputToCellWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[3]].get(),
4126 inputTensorInfo).first;
4127 params.m_InputToOutputWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[4]].get(),
4128 inputTensorInfo).first;
4129
4130 // Recurrent weight tensors of size {n_cell, n_output}
4131 if (IsOptionalOperandPresent(operatorPtr->inputs[5]))
4132 {
4133 params.m_RecurrentToInputWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[5]].get(),
4134 inputTensorInfo).first;
4135 }
4136
4137 params.m_RecurrentToForgetWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[6]].get(),
4138 inputTensorInfo).first;
4139 params.m_RecurrentToCellWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[7]].get(),
4140 inputTensorInfo).first;
4141 params.m_RecurrentToOutputWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[8]].get(),
4142 inputTensorInfo).first;
4143
4144 // Peephole weights tensors of size {n_cell}, representing a diagonal matrix.
4145 if (IsOptionalOperandPresent(operatorPtr->inputs[9]))
4146 {
4147 params.m_CellToInputWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[9]].get(),
4148 inputTensorInfo).first;
4149 }
4150
4151 if (IsOptionalOperandPresent(operatorPtr->inputs[10]))
4152 {
4153 params.m_CellToForgetWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[10]].get(),
4154 inputTensorInfo).first;
4155 }
4156
4157 if (IsOptionalOperandPresent(operatorPtr->inputs[11]))
4158 {
4159 params.m_CellToOutputWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[11]].get(),
4160 inputTensorInfo).first;
4161 }
4162
4163 // Gates bias tensors of size {n_cell}
4164 if (IsOptionalOperandPresent(operatorPtr->inputs[12]))
4165 {
4166 params.m_InputGateBias = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[12]].get(),
4167 inputTensorInfo).first;
4168 }
4169
4170 params.m_ForgetGateBias = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[13]].get(),
4171 inputTensorInfo).first;
4172 params.m_CellBias = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[14]].get(),
4173 inputTensorInfo).first;
4174 params.m_OutputGateBias = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[15]].get(),
4175 inputTensorInfo).first;
4176
4177 // Projection weight tensor of size {n_output, n_cell}
4178 if (IsOptionalOperandPresent(operatorPtr->inputs[16]))
4179 {
4180 params.m_ProjectionWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[16]].get(),
4181 inputTensorInfo).first;
4182 }
4183 // Projection bias tensor of size {n_output}
4184 if (IsOptionalOperandPresent(operatorPtr->inputs[17]))
4185 {
4186 params.m_ProjectionBias = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[17]].get(),
4187 inputTensorInfo).first;
4188 }
4189
4190 // These state tensors are defined as variable tensors, and will be modified by this op.
4191 armnn::TensorInfo outputStateInInfo = ToTensorInfo(subgraphPtr->tensors[operatorPtr->inputs[18]].get());
4192 m_ConstantsToBeCreated.push_back(operatorPtr->inputs[18]);
4193 armnn::TensorInfo cellStateInInfo = ToTensorInfo(subgraphPtr->tensors[operatorPtr->inputs[19]].get());
4194 m_ConstantsToBeCreated.push_back(operatorPtr->inputs[19]);
4195
4196 // Layer norm coefficient tensors of size {n_cell}, representing a diagonal matrix.
4197 if (inputs.size() >= 21 && IsOptionalOperandPresent(operatorPtr->inputs[20]))
4198 {
4199 params.m_InputLayerNormWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[20]].get(),
4200 inputTensorInfo).first;
4201 }
4202
4203 if (inputs.size() >= 22 && IsOptionalOperandPresent(operatorPtr->inputs[21]))
4204 {
4205 params.m_ForgetLayerNormWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[21]].get(),
4206 inputTensorInfo).first;
4207 }
4208
4209 if (inputs.size() >= 23 && IsOptionalOperandPresent(operatorPtr->inputs[22]))
4210 {
4211 params.m_CellLayerNormWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[22]].get(),
4212 inputTensorInfo).first;
4213 }
4214
4215 if (inputs.size() >= 24 && IsOptionalOperandPresent(operatorPtr->inputs[23]))
4216 {
4217 params.m_OutputLayerNormWeights = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->inputs[23]].get(),
4218 inputTensorInfo).first;
4219 }
4220
4221 // set the layer descriptor
4223 desc.m_ActivationFunc = nodeParams->fused_activation_function;
4224 desc.m_ClippingThresCell = nodeParams->cell_clip;
4225 desc.m_ClippingThresProj = nodeParams->proj_clip;
4226 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr
4227 || params.m_RecurrentToInputWeights == nullptr
4228 || params.m_InputGateBias == nullptr);
4229 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr || params.m_CellToOutputWeights != nullptr);
4230 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
4231 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr
4232 || params.m_ForgetLayerNormWeights != nullptr
4233 || params.m_CellLayerNormWeights != nullptr
4234 || params.m_OutputLayerNormWeights != nullptr);
4235 desc.m_TimeMajor = nodeParams->time_major;
4236
4237 if (operatorPtr->intermediates.size() > 3 && desc.m_LayerNormEnabled)
4238 {
4239 auto inputIntermediate = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->intermediates[0]].get(),
4240 inputTensorInfo).first;
4241 auto inputIntermediateTensorInfo = inputIntermediate->GetInfo();
4242 desc.m_InputIntermediateScale = inputIntermediateTensorInfo.GetQuantizationScale();
4243
4244 auto forgetIntermediate = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->intermediates[1]].get(),
4245 inputTensorInfo).first;
4246 auto forgetIntermediateTensorInfo = forgetIntermediate->GetInfo();
4247 desc.m_ForgetIntermediateScale = forgetIntermediateTensorInfo.GetQuantizationScale();
4248
4249 auto cellIntermediate = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->intermediates[2]].get(),
4250 inputTensorInfo).first;
4251 auto cellIntermediateTensorInfo = cellIntermediate->GetInfo();
4252 desc.m_CellIntermediateScale = cellIntermediateTensorInfo.GetQuantizationScale();
4253
4254 auto outputIntermediate = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->intermediates[3]].get(),
4255 inputTensorInfo).first;
4256 auto outputIntermediateTensorInfo = outputIntermediate->GetInfo();
4257 desc.m_OutputIntermediateScale = outputIntermediateTensorInfo.GetQuantizationScale();
4258 }
4259 else
4260 {
4261 float defaultIntermediate = std::pow(2, -12);
4262 desc.m_InputIntermediateScale = defaultIntermediate;
4263 desc.m_ForgetIntermediateScale = defaultIntermediate;
4264 desc.m_CellIntermediateScale = defaultIntermediate;
4265 desc.m_OutputIntermediateScale = defaultIntermediate;
4266 }
4267
4268 if (operatorPtr->intermediates.size() > 4)
4269 {
4270 auto hiddentensor = CreateConstTensorPtr(subgraphPtr->tensors[operatorPtr->intermediates[4]].get(),
4271 inputTensorInfo).first;
4272
4273 desc.m_HiddenStateScale = hiddentensor->GetInfo().GetQuantizationScale();
4274 desc.m_HiddenStateZeroPoint = hiddentensor->GetInfo().GetQuantizationOffset();
4275 }
4276 unsigned int batchSize = desc.m_TimeMajor ? inputTensorInfo.GetShape()[1] : inputTensorInfo.GetShape()[0];
4277 unsigned int outputSize = outputTensorInfo.GetShape()[2];
4278 unsigned int numUnits = cellStateInInfo.GetShape()[1];
4279
4280 armnn::DataType dataType = inputTensorInfo.GetDataType();
4281 float qScale = inputTensorInfo.GetQuantizationScale();
4282 float qOffset = inputTensorInfo.GetQuantizationOffset();
4283
4284 armnn::TensorInfo scratchBufferTensorInfo({batchSize, numUnits * 3}, dataType, qScale, qOffset);
4285 if (!desc.m_CifgEnabled)
4286 {
4287 scratchBufferTensorInfo = armnn::TensorInfo({batchSize, numUnits * 4}, dataType, qScale, qOffset);
4288 }
4289 armnn::TensorInfo cellStateOutTensorInfo({batchSize, numUnits},
4290 cellStateInInfo.GetDataType(),
4291 cellStateInInfo.GetQuantizationScale(),
4292 cellStateInInfo.GetQuantizationOffset());
4293 armnn::TensorInfo outputStateOutTensorInfo({batchSize, outputSize}, dataType, qScale, qOffset);
4294
4295 armnn::LstmInputParamsInfo paramsInfo;
4296 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
4297 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
4298 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
4299 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
4300 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
4301 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
4302 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
4303 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
4304 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
4305
4306 if (!desc.m_CifgEnabled)
4307 {
4308 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
4309 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
4310 if (params.m_CellToInputWeights != nullptr)
4311 {
4312 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
4313 }
4314 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
4315 }
4316
4317 if (desc.m_ProjectionEnabled)
4318 {
4319 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
4320 if (params.m_ProjectionBias != nullptr)
4321 {
4322 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
4323 }
4324 }
4325
4326 if (desc.m_PeepholeEnabled)
4327 {
4328 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
4329 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
4330 }
4331
4332 if (desc.m_LayerNormEnabled)
4333 {
4334 if(!desc.m_CifgEnabled)
4335 {
4336 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
4337 }
4338 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
4339 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
4340 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
4341 }
4342
4343 auto layerName = fmt::format("UnidirectionalSequenceLSTM:{}:{}", subgraphIndex, operatorIndex);
4344 armnn::IConnectableLayer* layer = m_Network->AddUnidirectionalSequenceLstmLayer(desc, params);
4345
4346 if (!layer)
4347 {
4348 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4349 operatorIndex, CHECK_LOCATION().AsString()));
4350 }
4351
4352 // register the input connection slots for the layer, connections are made after all layers have been created
4353 // only the tensors for the inputs are relevant, exclude the const tensors
4354 auto inputTensorIndexes = AsUnsignedVector({operatorPtr->inputs[0],
4355 operatorPtr->inputs[18],
4356 operatorPtr->inputs[19]});
4357 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0],
4358 inputTensorIndexes[1],
4359 inputTensorIndexes[2]});
4360
4361 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4362
4363 layer->GetOutputSlot(0).SetTensorInfo(outputStateOutTensorInfo);
4364 layer->GetOutputSlot(1).SetTensorInfo(cellStateOutTensorInfo);
4365 layer->GetOutputSlot(2).SetTensorInfo(outputTensorInfo);
4366
4367 unsigned int tensorIndex = outputTensorIndexes[0];
4368 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(2));
4369 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
4370}
4371
4372void TfLiteParserImpl::ParseUnpack(size_t subgraphIndex, size_t operatorIndex)
4373{
4374 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4375
4376 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4377 const auto* options = operatorPtr->builtin_options.AsUnpackOptions();
4378
4379 // This unpackAxis indicates the axis to unpack
4380 const unsigned int unpackAxis = CHECKED_NON_NEGATIVE(options->axis);
4381
4382 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4383 CHECK_VALID_SIZE(inputs.size(), 1);
4384
4385 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4386
4387 if (unpackAxis >= inputTensorInfo.GetNumDimensions())
4388 {
4389 throw ParseException(
4390 fmt::format("The unpack axis: {} cannot be greater than or equal to "
4391 "the number of input dimension {} {}",
4392 unpackAxis,
4393 inputTensorInfo.GetNumDimensions(),
4394 CHECK_LOCATION().AsString()));
4395 }
4396
4397 unsigned int unpackNum = CHECKED_NON_NEGATIVE(options->num);
4398 // If num is not defined, automatically infer from the length of the dimension axis.
4399 if(unpackNum == 0)
4400 {
4401 unpackNum = inputTensorInfo.GetShape()[unpackAxis];
4402 }
4403
4404 // If unpack number cannot be inferred and is still zero, throw ParseException.
4405 if(unpackNum == 0)
4406 {
4407 throw ParseException("Number to unpack must greater than zero.");
4408 }
4409
4410 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4411 CHECK_VALID_SIZE(outputs.size(), unpackNum);
4412
4413 auto inputDimSize = inputTensorInfo.GetNumDimensions();
4414 std::vector<unsigned int> unpackDimSizes(inputDimSize);
4415
4416 // Add current input shape to unpackDimSizes
4417 for (unsigned int i = 0; i < inputDimSize; ++i)
4418 {
4419 unpackDimSizes[i] = inputTensorInfo.GetShape()[i];
4420 }
4421
4422 if (unpackDimSizes[unpackAxis] != unpackNum)
4423 {
4424 throw ParseException("Number to unpack must be the same as length of the dimension to "
4425 "unpack along.");
4426 }
4427
4428 unpackDimSizes[unpackAxis] /= unpackNum;
4429
4430 SplitterDescriptor splitDesc(unpackNum, static_cast<unsigned int>(unpackDimSizes.size()));
4431 for (unsigned int j = 0; j < unpackNum; ++j)
4432 {
4433 // Set the size of the views.
4434 for (unsigned int dimIdx = 0; dimIdx < unpackDimSizes.size(); ++dimIdx)
4435 {
4436 splitDesc.SetViewSize(j, dimIdx, unpackDimSizes[dimIdx]);
4437 }
4438 splitDesc.SetViewOriginCoord(j, unpackAxis, unpackDimSizes[unpackAxis] * j);
4439 }
4440 splitDesc.SetAxis(unpackAxis);
4441 auto layerName = fmt::format("Unpack:{}:{}", subgraphIndex, operatorIndex);
4442 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
4443
4444 if (!layer)
4445 {
4446 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4447 operatorIndex, CHECK_LOCATION().AsString()));
4448 }
4449
4450 TensorShape splitOutShape = TensorShape(static_cast<unsigned int>(unpackDimSizes.size()),
4451 unpackDimSizes.data());
4452
4453 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4454 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
4455
4456 std::vector<unsigned int> reshapeDims;
4457 for (unsigned int axis = 0; axis < splitOutShape.GetNumDimensions(); ++axis)
4458 {
4459 if (axis != unpackAxis)
4460 {
4461 reshapeDims.push_back(splitOutShape[axis]);
4462 }
4463 }
4464
4465 TensorShape reshapeOutputShape(splitOutShape.GetNumDimensions() -1, reshapeDims.data());
4466
4467 // Create reshape to remove the unpacked dimension for unpack operator of each output from Splitter.
4468 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
4469 {
4470 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[k], true);
4471 std::string reshapeLayerName = fmt::format("Reshape_for:{}", layer->GetName());
4472 armnn::ReshapeDescriptor desc;
4473 desc.m_TargetShape = reshapeOutputShape;
4474 armnn::IConnectableLayer* reshapeLayer = m_Network->AddReshapeLayer(desc, layerName.c_str());
4475
4476 layer->GetOutputSlot(k).SetTensorInfo(armnn::TensorInfo(splitOutShape,
4477 outputTensorInfo.GetDataType(),
4478 outputTensorInfo.GetQuantizationScale(),
4479 outputTensorInfo.GetQuantizationOffset()));
4480 layer->GetOutputSlot(k).Connect(reshapeLayer->GetInputSlot(0));
4481
4482 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
4483
4484 uint32_t reshapedOutputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[k]);
4485 armnn::IOutputSlot* slot = &(reshapeLayer->GetOutputSlot(0));
4486 RegisterProducerOfTensor(subgraphIndex, reshapedOutputId, slot);
4487 }
4488}
4489
4490void TfLiteParserImpl::ParseSplit(size_t subgraphIndex, size_t operatorIndex)
4491{
4492 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4493
4494 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4495 const auto* options = operatorPtr->builtin_options.AsSplitOptions();
4496
4497 const unsigned int numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
4498
4499 // If number of splits cannot be inferred and is zero, throw ParseException.
4500 if(numSplits == 0)
4501 {
4502 throw ParseException("Number to splits must greater than zero.");
4503 }
4504
4505 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4506 CHECK_VALID_SIZE(inputs.size(), 2);
4507 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4508 CHECK_VALID_SIZE(outputs.size(), numSplits);
4509
4510 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
4511 armnn::TensorInfo axisTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4512
4513 if (axisTensorInfo.GetNumElements() != 1)
4514 {
4515 throw ParseException(fmt::format("Axis tensor can only have 1 element {}",
4516 CHECK_LOCATION().AsString()));
4517 }
4518
4519 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
4520 if (axisBufferPtr == nullptr)
4521 {
4522 throw ParseException(
4523 fmt::format("Operation has invalid inputs. Failed to read axis. {}",
4524 CHECK_LOCATION().AsString()));
4525 }
4526
4527 std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
4528 ValidateBuffer(axisBufferPtr, axisTensorInfo, "axis");
4529 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
4530 int32_t axis = axisData[0];
4531
4532 auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
4533 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
4534 {
4535 // Square bracket denotes inclusive n while parenthesis denotes exclusive n
4536 // E.g. Rank 4 tensor can have axis in range [-4, 3)
4537 // -1 == 3, -2 == 2, -3 == 1, -4 == 0
4538 throw ParseException(
4539 fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
4540 axis,
4541 CHECK_LOCATION().AsString()));
4542 }
4543
4544 const unsigned int splitDim = armnnUtils::GetUnsignedAxis(inputTensorInfo.GetNumDimensions(), axis);
4545
4546 auto inputDimSize = inputTensorInfo.GetNumDimensions();
4547 if (inputDimSize > MaxNumOfTensorDimensions)
4548 {
4549 throw ParseException(
4550 fmt::format("The number of dimensions: {} for input tensors of the split op cannot be greater than {} {}",
4551 inputTensorInfo.GetNumDimensions(),
4553 CHECK_LOCATION().AsString()));
4554 }
4555
4556 std::vector<unsigned int> splitterDimSizes(inputDimSize);
4557
4558 // Add current input shape to splitterDimSizes
4559 for (unsigned int i = 0; i < inputDimSize; ++i)
4560 {
4561 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
4562 }
4563
4564 if (splitterDimSizes[splitDim] % numSplits != 0)
4565 {
4566 throw ParseException("Number of splits must evenly divide the dimension");
4567 }
4568 splitterDimSizes[splitDim] /= numSplits;
4569
4570 SplitterDescriptor splitDesc(numSplits, inputDimSize);
4571 for (unsigned int j = 0; j < numSplits; ++j)
4572 {
4573 // Set the size of the views.
4574 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
4575 {
4576 splitDesc.SetViewSize(j, dimIdx, splitterDimSizes[dimIdx]);
4577 }
4578 splitDesc.SetViewOriginCoord(j, splitDim, splitterDimSizes[splitDim] * j);
4579 }
4580 if (axisTensorInfo.GetNumElements() == 1)
4581 {
4582 splitDesc.SetAxis(axis);
4583 }
4584 auto layerName = fmt::format("Split:{}:{}", subgraphIndex, operatorIndex);
4585 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
4586
4587 if (!layer)
4588 {
4589 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4590 operatorIndex, CHECK_LOCATION().AsString()));
4591 }
4592
4593 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4594 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[1]});
4595
4596 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
4597 {
4598 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
4599 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
4600 }
4601
4602 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4603 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
4604}
4605
4606unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
4607{
4608 int numDims = armnn::numeric_cast<int>(numDimsIn);
4609 int v = idx < 0 ? numDims + idx : idx;
4610
4611 if (v < 0 || v > numDims)
4612 {
4613 throw ParseException(fmt::format("Unable to compute index {}", CHECK_LOCATION().AsString()));
4614 }
4615
4616 return static_cast<unsigned int>(v);
4617}
4618
4619void TfLiteParserImpl::ParseSplitV(size_t subgraphIndex, size_t operatorIndex)
4620{
4621 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4622
4623 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4624 const auto* options = operatorPtr->builtin_options.AsSplitVOptions();
4625
4626 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4627 CHECK_VALID_SIZE(inputs.size(), 3);
4628
4629 auto& inputTensor = inputs[0];
4630 auto& splitsTensor = inputs[1];
4631 auto& axisTensor = inputs[2];
4632
4633 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputTensor);
4634 armnn::TensorInfo splitsInfo = ToTensorInfo(splitsTensor);
4635 armnn::TensorInfo axisTensorInfo = ToTensorInfo(axisTensor);
4636
4637 if (axisTensorInfo.GetNumElements() != 1)
4638 {
4639 throw ParseException(fmt::format("Axis tensor can only have 1 element {}",
4640 CHECK_LOCATION().AsString()));
4641 }
4642
4643 // Inputs
4644 auto inputDimSize = inputTensorInfo.GetNumDimensions();
4645 if (inputDimSize > MaxNumOfTensorDimensions)
4646 {
4647 throw ParseException(
4648 fmt::format("The number of dimensions: {} for input tensors of the "
4649 "SplitV op cannot be greater than {} {}",
4650 inputTensorInfo.GetNumDimensions(),
4652 CHECK_LOCATION().AsString()));
4653 }
4654
4655 // Get split axis
4656 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, axisTensor->buffer);
4657 if (axisBufferPtr == nullptr)
4658 {
4659 throw ParseException(
4660 fmt::format("Operation has invalid inputs. Failed to read axis. {}",
4661 CHECK_LOCATION().AsString()));
4662 }
4663
4664 std::vector<int> axisData(axisTensorInfo.GetNumElements());
4665 ValidateBuffer(axisBufferPtr, axisTensorInfo, "axis");
4666 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
4667 int32_t axis = axisData[0];
4668
4669 auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
4670 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
4671 {
4672 // Square bracket denotes inclusive n while parenthesis denotes exclusive n
4673 // E.g. Rank 4 tensor can have axis in range [-4, 3)
4674 // -1 == 3, -2 == 2, -3 == 1, -4 == 0
4675 throw ParseException(
4676 fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
4677 axis,
4678 CHECK_LOCATION().AsString()));
4679 }
4680 const unsigned int splitDim = ComputeWrappedIndex(axis, inputTensorInfo.GetNumDimensions());
4681
4682 // Set split sizes
4683 CHECK_VALID_SIZE(splitsInfo.GetNumDimensions(), 1);
4684 unsigned int numSplits{0};
4685
4686 if(options)
4687 {
4688 numSplits = CHECKED_NON_NEGATIVE(options->num_splits);
4689 }
4690 else
4691 {
4692 numSplits = splitsInfo.GetNumElements();
4693 }
4694
4695 if (numSplits <=0)
4696 {
4697 throw ParseException("SplitV has invalid number of splits");
4698 }
4699
4700 std::vector<int> splitsData(numSplits);
4701 BufferRawPtr splitsBufferPtr = GetBuffer(m_Model, splitsTensor->buffer);
4702 ValidateBuffer(splitsBufferPtr, splitsInfo, "splits");
4703 ::memcpy(splitsData.data(), splitsBufferPtr->data.data(), splitsInfo.GetNumBytes());
4704
4705 unsigned int idx = 0;
4706 int numInferred{0};
4707 unsigned int inferIdx{0};
4708 int splitSum{0};
4709 for (auto split : splitsData)
4710 {
4711 if (split < 0)
4712 {
4713 numInferred++;
4714 inferIdx = idx;
4715 }
4716 else
4717 {
4718 splitSum += split;
4719 }
4720 idx++;
4721 }
4722 // Check for inferred Axis
4723 if (numInferred == 0)
4724 {
4725 if (splitSum != armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]))
4726 {
4727 throw ParseException("SplitV split_sizes does not sum to the dimension of value along split_dim.");
4728 }
4729 }
4730 else if (numInferred == 1)
4731 {
4732 splitsData[inferIdx] = armnn::numeric_cast<int>(inputTensorInfo.GetShape()[splitDim]) - splitSum;
4733 }
4734 else
4735 {
4736 throw ParseException("Cannot infer split size for more than one split");
4737 }
4738
4739 //Ouput size validation
4740 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4741 CHECK_VALID_SIZE(outputs.size(), numSplits);
4742
4743 // Setup Armnn descriptor
4744 SplitterDescriptor splitDesc(numSplits, inputDimSize);
4745 unsigned int accumSplit = 0;
4746 for (unsigned int j = 0; j < numSplits; ++j)
4747 {
4748 unsigned int splitSize = armnn::numeric_cast<unsigned int>(splitsData[j]);
4749
4750 // Set the size of the views.
4751 for (unsigned int dimIdx = 0; dimIdx < inputTensorInfo.GetNumDimensions(); ++dimIdx)
4752 {
4753 unsigned int dimSize = inputTensorInfo.GetShape()[dimIdx];
4754 if (dimIdx == splitDim)
4755 {
4756 dimSize = splitSize;
4757 }
4758 splitDesc.SetViewSize(j, dimIdx, dimSize);
4759 }
4760
4761 splitDesc.SetViewOriginCoord(j, splitDim, accumSplit);
4762 accumSplit += splitSize;
4763 }
4764 splitDesc.SetAxis(axis);
4765
4766 auto layerName = fmt::format("SplitV:{}:{}", subgraphIndex, operatorIndex);
4767 IConnectableLayer* layer = m_Network->AddSplitterLayer(splitDesc, layerName.c_str());
4768
4769 if (!layer)
4770 {
4771 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4772 operatorIndex, CHECK_LOCATION().AsString()));
4773 }
4774
4775 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4776 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
4777
4778 for (unsigned int k = 0; k < layer->GetNumOutputSlots(); ++k)
4779 {
4780 armnn::TensorInfo tensorInfo = ToTensorInfo(outputs[k], true);
4781 layer->GetOutputSlot(k).SetTensorInfo(tensorInfo);
4782 }
4783
4784 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4785 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
4786}
4787
4788void TfLiteParserImpl::ParseArgMin(size_t subgraphIndex, size_t operatorIndex)
4789{
4790 ParseArgMinMax(subgraphIndex, operatorIndex, armnn::ArgMinMaxFunction::Min);
4791}
4792
4793void TfLiteParserImpl::ParseArgMax(size_t subgraphIndex, size_t operatorIndex)
4794{
4795 ParseArgMinMax(subgraphIndex, operatorIndex, armnn::ArgMinMaxFunction::Max);
4796}
4797
4798void TfLiteParserImpl::ParseArgMinMax(size_t subgraphIndex, size_t operatorIndex, ArgMinMaxFunction argMinMaxFunction)
4799{
4800 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4801 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4802 CHECK_VALID_SIZE(inputs.size(), 2);
4803
4804 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4805 CHECK_VALID_SIZE(outputs.size(), 1);
4806
4807 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4808 armnn::TensorInfo axisTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
4809 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
4810
4811 if (axisTensorInfo.GetNumElements() != 1)
4812 {
4813 throw ParseException(fmt::format("Axis tensor can only have 1 element {}",
4814 CHECK_LOCATION().AsString()));
4815 }
4816
4817 // Check if output tensor type is Signed32 or Signed64
4818 if (outputTensorInfo.GetDataType() != armnn::DataType::Signed32 &&
4819 outputTensorInfo.GetDataType() != armnn::DataType::Signed64)
4820 {
4821 throw ParseException(
4822 fmt::format(
4823 "Output tensor data type is not supported. (Supported types: Signed32 & Signed64) {}",
4824 CHECK_LOCATION().AsString()));
4825 }
4826
4827 // Get const axis value from model and set it to descriptor.
4828 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
4829 if (axisBufferPtr == nullptr)
4830 {
4831 throw ParseException(
4832 fmt::format("Operation has invalid inputs. Failed to read axis. {}",
4833 CHECK_LOCATION().AsString()));
4834 }
4835
4836 std::vector<int32_t> axisData(axisTensorInfo.GetNumElements());
4837 ValidateBuffer(axisBufferPtr, axisTensorInfo, "axis");
4838
4839 ::memcpy(axisData.data(), axisBufferPtr->data.data(), axisTensorInfo.GetNumBytes());
4840 int32_t axis = axisData.front();
4841
4842 auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
4843 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
4844 {
4845 // Square bracket denotes inclusive n while parenthesis denotes exclusive n
4846 // E.g. Rank 4 tensor can have axis in range [-4, 3)
4847 // -1 == 3, -2 == 2, -3 == 1, -4 == 0
4848 throw ParseException(
4849 fmt::format("Operation has invalid axis: {}. Axis must be in range [-n, n) {}",
4850 axis,
4851 CHECK_LOCATION().AsString()));
4852 }
4853
4854 ArgMinMaxDescriptor desc;
4855 desc.m_Axis = axis;
4856 desc.m_Function = argMinMaxFunction;
4857
4858 // Register a ArgMin/ArgMax layer.
4859 auto layerName = argMinMaxFunction == ArgMinMaxFunction::Max ? "ArgMax:{}:{}" : "ArgMin:{}:{}";
4860 auto layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
4861 IConnectableLayer *layer = m_Network->AddArgMinMaxLayer(desc, layerNameFormatted.c_str());
4862
4863 if (!layer)
4864 {
4865 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4866 operatorIndex, CHECK_LOCATION().AsString()));
4867 }
4868
4869 outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
4870 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
4871
4872 // Register input tensor to the layer.
4873 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4874 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
4875
4876 // Register output tensor to the layer.
4877 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4878 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
4879}
4880
4881void TfLiteParserImpl::ParseGather(size_t subgraphIndex, size_t operatorIndex)
4882{
4883 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4884
4885 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4886 CHECK_VALID_SIZE(inputs.size(), 2);
4887 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4888 CHECK_VALID_SIZE(outputs.size(), 1);
4889
4890 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4891 armnn::TensorInfo indicesTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
4892 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
4893
4894 armnn::GatherDescriptor gatherDescriptor;
4895
4896 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4897 const auto* options = operatorPtr->builtin_options.AsGatherOptions();
4898 auto axis = options->axis;
4899
4900 auto layerName = fmt::format("Gather:{}:{}", subgraphIndex, operatorIndex);
4901
4902 auto inputDimensions = static_cast<int32_t>(inputTensorInfo.GetNumDimensions());
4903 auto indicesDimensions = indicesTensorInfo.GetNumDimensions();
4904 auto outputDimensions = outputTensorInfo.GetNumDimensions();
4905 if (((axis < -inputDimensions) && (axis < 0)) || ((axis >= inputDimensions) && (axis > 0)))
4906 {
4907 throw ParseException(
4908 fmt::format("Operation has invalid axis: {} It is out of bounds [ -{}, {} ) {}",
4909 axis,
4910 inputDimensions, inputDimensions,
4911 CHECK_LOCATION().AsString()));
4912 }
4913 if (outputDimensions != static_cast<unsigned int>(inputDimensions) + indicesDimensions - 1)
4914 {
4915 throw ParseException(
4916 fmt::format("Operation has invalid output dimensions: {} Output must be an ({} + {} - 1) -D tensor {}",
4917 outputDimensions,
4918 inputDimensions, indicesDimensions,
4919 CHECK_LOCATION().AsString()));
4920 }
4921
4922 gatherDescriptor.m_Axis = axis;
4923
4924 IConnectableLayer* layer = m_Network->AddGatherLayer(gatherDescriptor, layerName.c_str());
4925
4926 if (!layer)
4927 {
4928 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4929 operatorIndex, CHECK_LOCATION().AsString()));
4930 }
4931
4932 outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
4933 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
4934
4935 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4936 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
4937
4938 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4939 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
4940}
4941
4942void TfLiteParserImpl::ParseGatherNd(size_t subgraphIndex, size_t operatorIndex)
4943{
4944 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4945
4946 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4947 CHECK_VALID_SIZE(inputs.size(), 2);
4948 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4949 CHECK_VALID_SIZE(outputs.size(), 1);
4950
4951 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
4952 armnn::TensorInfo indicesTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
4953
4954 auto layerName = fmt::format("GatherNd:{}:{}", subgraphIndex, operatorIndex);
4955 IConnectableLayer* layer = m_Network->AddGatherNdLayer(layerName.c_str());
4956
4957 if (!layer)
4958 {
4959 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
4960 operatorIndex, CHECK_LOCATION().AsString()));
4961 }
4962
4963 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
4964 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
4965
4966 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
4967 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
4968
4969 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
4970 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
4971}
4972
4973void TfLiteParserImpl::ParseDepthToSpace(size_t subgraphIndex, size_t operatorIndex)
4974{
4975 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
4976
4977 TfLiteParserImpl::TensorRawPtrVector inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
4978 CHECK_VALID_SIZE(inputs.size(), 1);
4979 TfLiteParserImpl::TensorRawPtrVector outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
4980 CHECK_VALID_SIZE(outputs.size(), 1);
4981
4983
4984 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
4985 const auto* options = operatorPtr->builtin_options.AsDepthToSpaceOptions();
4986 auto blockSize = options->block_size;
4987 if (blockSize < 2)
4988 {
4989 throw ParseException(
4990 fmt::format("Operation has invalid block size: {} Block size should be >= 2 {}",
4991 blockSize,
4992 CHECK_LOCATION().AsString()));
4993 }
4994 descriptor.m_BlockSize = armnn::numeric_cast<uint32_t>(blockSize);
4995
4996 auto layerName = fmt::format("DepthToSpace:{}:{}", subgraphIndex, operatorIndex);
4997 IConnectableLayer* layer = m_Network->AddDepthToSpaceLayer(descriptor, layerName.c_str());
4998
4999 if (!layer)
5000 {
5001 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5002 operatorIndex, CHECK_LOCATION().AsString()));
5003 }
5004
5005 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
5006 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5007
5008 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5009 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
5010
5011 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5012 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
5013}
5014
5015void TfLiteParserImpl::ParseSum(size_t subgraphIndex, size_t operatorIndex)
5016{
5017 ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Sum);
5018}
5019
5020void TfLiteParserImpl::ParseReduceProd(size_t subgraphIndex, size_t operatorIndex)
5021{
5022 ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Prod);
5023}
5024
5025void TfLiteParserImpl::ParseReduceMax(size_t subgraphIndex, size_t operatorIndex)
5026{
5027 ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Max);
5028}
5029
5030void TfLiteParserImpl::ParseReduceMin(size_t subgraphIndex, size_t operatorIndex)
5031{
5032 ParseReduce(subgraphIndex, operatorIndex, armnn::ReduceOperation::Min);
5033}
5034
5035void TfLiteParserImpl::ParseReduce(size_t subgraphIndex, size_t operatorIndex, ReduceOperation reduceOperation)
5036{
5037 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5038
5039 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
5040 const auto* options = operatorPtr->builtin_options.AsReducerOptions();
5041
5042 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5043 CHECK_VALID_SIZE(inputs.size(), 2);
5044
5045 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5046 CHECK_VALID_SIZE(outputs.size(), 1);
5047
5048 auto layerName = fmt::format("Reduce:{}:{}", subgraphIndex, operatorIndex);
5049
5050 armnn::TensorInfo inputTensorInfo0 = InputTensorInfo(subgraphIndex, operatorIndex, 0);
5051 armnn::TensorInfo inputTensorInfo1 = InputTensorInfo(subgraphIndex, operatorIndex, 1);
5052
5053 ReduceDescriptor desc;
5054 BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer);
5055 // Get const axis value from model and set it to descriptor.
5056 if (axisBufferPtr != nullptr)
5057 {
5058 std::vector<int32_t> axisData(inputTensorInfo1.GetNumElements());
5059 ValidateBuffer(axisBufferPtr, inputTensorInfo1, "axis");
5060 ::memcpy(axisData.data(), axisBufferPtr->data.data(), inputTensorInfo1.GetNumBytes());
5061
5062 // Convert the axis to unsigned int and remove duplicates.
5063 auto rank = static_cast<int32_t>(inputTensorInfo0.GetNumDimensions());
5064 std::set<unsigned int> uniqueAxis;
5065 std::transform(axisData.begin(),
5066 axisData.end(),
5067 std::inserter(uniqueAxis, uniqueAxis.begin()),
5068 [rank](int i)->unsigned int{
5069 return static_cast<uint32_t>(((i + rank) % rank)); });
5070 desc.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
5071 }
5072 else
5073 {
5074 for (uint32_t i = 0; i < inputTensorInfo0.GetNumDimensions(); ++i)
5075 {
5076 desc.m_vAxis.push_back(i);
5077 }
5078 }
5079
5080 desc.m_KeepDims = options->keep_dims;
5081 desc.m_ReduceOperation = reduceOperation;
5082
5083 // Register a new layer object, Sum.
5084 IConnectableLayer* layer = m_Network->AddReduceLayer(desc, layerName.c_str());
5085
5086 armnn::TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
5087 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5088
5089 // Register input tensor to the layer.
5090 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5091 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
5092
5093 // Register output tensor to the layer.
5094 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5095 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
5096}
5097
5098void TfLiteParserImpl::ParseLocalResponseNormalization(size_t subgraphIndex, size_t operatorIndex)
5099{
5100 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5101
5102 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5103 CHECK_VALID_SIZE(inputs.size(), 1);
5104
5105 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5106 CHECK_VALID_SIZE(outputs.size(), 1);
5107
5108 auto layerName = fmt::format("LRN:{}:{}", subgraphIndex, operatorIndex);
5109 std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
5110
5111 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
5112
5113 const auto& operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
5114 const auto* options = operatorPtr->builtin_options.AsLocalResponseNormalizationOptions();
5115
5116 armnn::NormalizationDescriptor descriptor;
5120 descriptor.m_NormSize = static_cast<uint32_t>(options->radius);
5121 descriptor.m_K = options->bias;
5122 descriptor.m_Alpha = options->alpha;
5123 descriptor.m_Beta = options->beta;
5124
5125 // ArmNN expects normSize to be the full size of the normalization
5126 // window rather than the radius as in TfLite.
5127 descriptor.m_NormSize = 1 + (2 * descriptor.m_NormSize);
5128
5129 IConnectableLayer* layer = m_Network->AddNormalizationLayer(descriptor, layerNameFormatted.c_str());
5130
5131 if (!layer)
5132 {
5133 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5134 operatorIndex, CHECK_LOCATION().AsString()));
5135 }
5136
5137 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
5138 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5139
5140 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5141 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
5142
5143 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5144 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
5145}
5146
5147void TfLiteParserImpl::ParseAbs(size_t subgraphIndex, size_t operatorIndex)
5148{
5149 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Abs);
5150}
5151
5152void TfLiteParserImpl::ParseCeil(size_t subgraphIndex, size_t operatorIndex)
5153{
5154 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Ceil);
5155}
5156
5157void TfLiteParserImpl::ParseExp(size_t subgraphIndex, size_t operatorIndex)
5158{
5159 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Exp);
5160}
5161
5162void TfLiteParserImpl::ParseLog(size_t subgraphIndex, size_t operatorIndex)
5163{
5164 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Log);
5165}
5166
5167void TfLiteParserImpl::ParseLogicalNot(size_t subgraphIndex, size_t operatorIndex)
5168{
5169 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::LogicalNot);
5170}
5171
5172void TfLiteParserImpl::ParseNeg(size_t subgraphIndex, size_t operatorIndex)
5173{
5174 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Neg);
5175}
5176
5177void TfLiteParserImpl::ParsePower(size_t subgraphIndex, size_t operatorIndex)
5178{
5179 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5180
5181 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5182 CHECK_VALID_SIZE(inputs.size(), 2);
5183
5184 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5185 CHECK_VALID_SIZE(outputs.size(), 1);
5186
5187 auto layerName = fmt::format("Power:{}:{}", subgraphIndex, operatorIndex);
5188
5189 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
5190 TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
5191 CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerName, "Input 0", "Input 1");
5192
5193 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Power, layerName.c_str());
5194
5195 if (!layer)
5196 {
5197 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5198 operatorIndex, CHECK_LOCATION().AsString()));
5199 }
5200
5201 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
5202 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
5203 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5204
5205 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5206 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
5207
5208 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5209 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
5210}
5211
5212void TfLiteParserImpl::ParseRsqrt(size_t subgraphIndex, size_t operatorIndex)
5213{
5214 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Rsqrt);
5215}
5216
5217void TfLiteParserImpl::ParseSin(size_t subgraphIndex, size_t operatorIndex)
5218{
5219 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Sin);
5220}
5221
5222void TfLiteParserImpl::ParseSqrt(size_t subgraphIndex, size_t operatorIndex)
5223{
5224 ParseElementwiseUnary(subgraphIndex, operatorIndex, armnn::UnaryOperation::Sqrt);
5225}
5226
5227void TfLiteParserImpl::ParseSquare(size_t subgraphIndex, size_t operatorIndex)
5228{
5229 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5230
5231 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5232 CHECK_VALID_SIZE(inputs.size(), 1);
5233
5234 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5235 CHECK_VALID_SIZE(outputs.size(), 1);
5236
5237 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
5238
5239 auto layerName = fmt::format("Square:{}:{}", subgraphIndex, operatorIndex);
5240 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::Mul, layerName.c_str());
5241 ARMNN_ASSERT(layer != nullptr);
5242
5243 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 0});
5244 CheckMatchingQuantization(inputTensorInfo, outputTensorInfo, layerName, "Input 0", "Output 0");
5245 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5246
5247 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5248 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[0]});
5249
5250 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5251 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
5252}
5253
5254void TfLiteParserImpl::ParseSquaredDifference(size_t subgraphIndex, size_t operatorIndex)
5255{
5256 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5257
5258 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5259 CHECK_VALID_SIZE(inputs.size(), 2);
5260
5261 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5262 CHECK_VALID_SIZE(outputs.size(), 1);
5263
5264 auto layerName = fmt::format("SquaredDifference:{}:{}", subgraphIndex, operatorIndex);
5265
5266 TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
5267 TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
5268
5269 IConnectableLayer* layer = m_Network->AddElementwiseBinaryLayer(BinaryOperation::SqDiff, layerName.c_str());
5270
5271 if (!layer)
5272 {
5273 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5274 operatorIndex, CHECK_LOCATION().AsString()));
5275 }
5276
5277 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
5278 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5279
5280 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5281 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
5282
5283 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5284 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
5285}
5286
5287void TfLiteParserImpl::ParseElementwiseUnary(size_t subgraphIndex, size_t operatorIndex, UnaryOperation unaryOperation)
5288{
5289 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5290
5291 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5292 CHECK_VALID_SIZE(inputs.size(), 1);
5293
5294 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5295 CHECK_VALID_SIZE(outputs.size(), 1);
5296
5297 std::string layerName = std::string(GetUnaryOperationAsCString(unaryOperation)) + ":{}:{}";
5298 std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
5299
5300 ElementwiseUnaryDescriptor desc;
5301 desc.m_Operation = unaryOperation;
5302 IConnectableLayer* layer = m_Network->AddElementwiseUnaryLayer(desc, layerNameFormatted.c_str());
5303
5304 if (!layer)
5305 {
5306 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5307 operatorIndex, CHECK_LOCATION().AsString()));
5308 }
5309
5310 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0});
5311 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5312
5313 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5314 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
5315
5316 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5317 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes);
5318}
5319
5320void TfLiteParserImpl::ParseEqual(size_t subgraphIndex, size_t operatorIndex)
5321{
5322 ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Equal);
5323}
5324
5325void TfLiteParserImpl::ParseNotEqual(size_t subgraphIndex, size_t operatorIndex)
5326{
5327 ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::NotEqual);
5328}
5329
5330void TfLiteParserImpl::ParseGreater(size_t subgraphIndex, size_t operatorIndex)
5331{
5332 ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Greater);
5333}
5334
5335void TfLiteParserImpl::ParseGreaterOrEqual(size_t subgraphIndex, size_t operatorIndex)
5336{
5337 ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::GreaterOrEqual);
5338}
5339
5340void TfLiteParserImpl::ParseLess(size_t subgraphIndex, size_t operatorIndex)
5341{
5342 ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::Less);
5343}
5344
5345void TfLiteParserImpl::ParseLessOrEqual(size_t subgraphIndex, size_t operatorIndex)
5346{
5347 ParseComparison(subgraphIndex, operatorIndex, armnn::ComparisonOperation::LessOrEqual);
5348}
5349
5350void TfLiteParserImpl::ParseComparison(size_t subgraphIndex, size_t operatorIndex,
5351 ComparisonOperation comparisonOperation)
5352{
5353 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5354
5355 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
5356 CHECK_VALID_SIZE(inputs.size(), 2);
5357
5358 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
5359 CHECK_VALID_SIZE(outputs.size(), 1);
5360
5361 auto layerName = std::string(GetComparisonOperationAsCString(comparisonOperation)) + ":{}:{}";
5362 std::string layerNameFormatted = fmt::format(layerName, subgraphIndex, operatorIndex);
5363
5364 armnn::TensorInfo inputTensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 0);
5365 armnn::TensorInfo input1TensorInfo = InputTensorInfo(subgraphIndex, operatorIndex, 1);
5366 CheckMatchingQuantization(inputTensorInfo, input1TensorInfo, layerNameFormatted, "Input 0", "Input 1");
5367
5368 ComparisonDescriptor desc;
5369 desc.m_Operation = comparisonOperation;
5370 IConnectableLayer* layer = m_Network->AddComparisonLayer(desc, layerNameFormatted.c_str());
5371
5372 if (!layer)
5373 {
5374 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5375 operatorIndex, CHECK_LOCATION().AsString()));
5376 }
5377
5378 TensorInfo outputTensorInfo = OutputTensorInfoFromInputs(subgraphIndex, operatorIndex, layer, 0, {0, 1});
5379 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
5380
5381 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
5382 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0], inputTensorIndexes[1]});
5383
5384 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
5385 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
5386}
5387
5388armnn::IConnectableLayer* TfLiteParserImpl::AddReshapeLayer(armnn::IConnectableLayer* layer,
5389 unsigned int outputSlot,
5390 std::string reshapeLayerName,
5391 armnn::TensorInfo outputShape)
5392{
5393 ReshapeDescriptor desc;
5394 desc.m_TargetShape = outputShape.GetShape();
5395
5396 IConnectableLayer* reshapeLayer =
5397 m_Network->AddReshapeLayer(desc, reshapeLayerName.c_str());
5398
5399 auto & prevOutputSlot = layer->GetOutputSlot(outputSlot);
5400 prevOutputSlot.Connect(reshapeLayer->GetInputSlot(0));
5401 reshapeLayer->GetOutputSlot(0).SetTensorInfo(outputShape);
5402 return reshapeLayer;
5403}
5404
5405armnn::IConnectableLayer* TfLiteParserImpl::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
5406 unsigned int outputSlot,
5407 tflite::ActivationFunctionType activationType)
5408{
5409 ActivationDescriptor activationDesc;
5410 std::string layerName = prevLayer->GetName();
5411
5412 switch(activationType)
5413 {
5414 case tflite::ActivationFunctionType_NONE:
5415 {
5416 // this is a no-op: return previous layer
5417 return prevLayer;
5418 }
5419 case tflite::ActivationFunctionType_RELU:
5420 {
5421 activationDesc.m_Function = ActivationFunction::ReLu;
5422 layerName += ":RELU";
5423 break;
5424 }
5425 case tflite::ActivationFunctionType_RELU6:
5426 {
5427 activationDesc.m_Function = ActivationFunction::BoundedReLu;
5428 activationDesc.m_A = 6.0f;
5429 activationDesc.m_B = 0.0f;
5430 layerName += ":RELU6";
5431 break;
5432 }
5433 case tflite::ActivationFunctionType_TANH:
5434 {
5435 activationDesc.m_Function = ActivationFunction::TanH;
5436 activationDesc.m_A = 1.0f;
5437 activationDesc.m_B = 1.0f;
5438 layerName += ":TANH";
5439 break;
5440 }
5441
5442 // I only put these here as a reminder what others we could support
5443 case tflite::ActivationFunctionType_RELU_N1_TO_1:
5444 case tflite::ActivationFunctionType_SIGN_BIT:
5445 default:
5446 {
5447 throw ParseException(
5448 fmt::format("TfLite parser doesn't support fused activation: "
5449 "{}/{} {} ",
5450 activationType,
5451 tflite::EnumNameActivationFunctionType(activationType),
5452 CHECK_LOCATION().AsString()));
5453
5454 }
5455 }
5456
5457 IConnectableLayer* activationLayer =
5458 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
5459
5460 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
5461 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
5462 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
5463 return activationLayer;
5464}
5465
5466armnn::IConnectableLayer* TfLiteParserImpl::AddFusedFloorLayer(armnn::IConnectableLayer* prevLayer,
5467 unsigned int outputSlot)
5468{
5469
5470 auto& prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
5471 DataType dataType = prevOutputSlot.GetTensorInfo().GetDataType();
5472
5473 if (dataType == DataType::Signed32)
5474 {
5475 return prevLayer;
5476 }
5477
5478 std::string layerName = prevLayer->GetName();
5479 IConnectableLayer* floorLayer = m_Network->AddFloorLayer(layerName.c_str());
5480
5481 prevOutputSlot.Connect(floorLayer->GetInputSlot(0));
5482 floorLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
5483
5484 return floorLayer;
5485}
5486
5488{
5489 if (fileName == nullptr)
5490 {
5491 throw InvalidArgumentException(fmt::format("Invalid (null) file name {}",
5492 CHECK_LOCATION().AsString()));
5493 }
5494 std::error_code errorCode;
5495 fs::path pathToFile(fileName);
5496 if (!fs::exists(pathToFile, errorCode))
5497 {
5498 //fmt::format() could not be used here (format error)
5499 std::stringstream msg;
5500 msg << "Cannot find the file (" << fileName << ") errorCode: " << errorCode
5501 << " " << CHECK_LOCATION().AsString();
5502 throw FileNotFoundException(msg.str());
5503 }
5504 if (!fs::is_regular_file(pathToFile))
5505 {
5506 // Exclude non regular files.
5507 throw InvalidArgumentException(fmt::format("File \"{}\" is not a regular file and cannot be loaded.",
5508 pathToFile.c_str()));
5509 }
5510
5511 std::ifstream file(fileName, std::ios::binary);
5512 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
5513 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
5514 fileContent.size());
5515}
5516
5518{
5519 if (binaryContent == nullptr)
5520 {
5521 throw InvalidArgumentException(fmt::format("Invalid (null) binary content {}",
5522 CHECK_LOCATION().AsString()));
5523 }
5524 flatbuffers::Verifier verifier(binaryContent, len);
5525 if (verifier.VerifyBuffer<tflite::Model>() == false)
5526 {
5527 throw ParseException(
5528 fmt::format("Buffer doesn't conform to the expected Tensorflow Lite "
5529 "flatbuffers format. size:{} {}",
5530 len,
5531 CHECK_LOCATION().AsString()));
5532 }
5533 return tflite::UnPackModel(binaryContent);
5534}
5535
5537 size_t subgraphIndex,
5538 size_t operatorIndex)
5539{
5540 CHECK_MODEL(model, subgraphIndex, operatorIndex);
5541
5542 const auto& subgraphPtr = model->subgraphs[subgraphIndex];
5543 const auto& operatorPtr = subgraphPtr->operators[operatorIndex];
5544
5545 size_t inputCount = operatorPtr->inputs.size();
5546 TensorRawPtrVector result;
5547 for (size_t i = 0; i < inputCount; ++i)
5548 {
5549 // If the input location is -1 then assume input is turned off.
5550 if (operatorPtr->inputs[i] == -1)
5551 {
5552 continue;
5553 }
5554 else
5555 {
5556 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
5557 result.push_back(subgraphPtr->tensors[inputId].get());
5558 }
5559 }
5560 return result;
5561}
5562
5564 size_t subgraphIndex,
5565 size_t operatorIndex)
5566{
5567 CHECK_MODEL(model, subgraphIndex, operatorIndex);
5568
5569 const auto& subgraphPtr = model->subgraphs[subgraphIndex];
5570 const auto& operatorPtr = subgraphPtr->operators[operatorIndex];
5571
5572 size_t outputCount = operatorPtr->outputs.size();
5573 TensorRawPtrVector result(outputCount);
5574 for (size_t i = 0; i < outputCount; ++i)
5575 {
5576 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
5577 CHECK_TENSOR(model, subgraphIndex, outputId);
5578 result[i] = subgraphPtr->tensors[outputId].get();
5579 }
5580 return result;
5581}
5582
5584 size_t subgraphIndex)
5585{
5586 CHECK_SUBGRAPH(model, subgraphIndex);
5587 const auto& subgraphPtr = model->subgraphs[subgraphIndex];
5588
5589 size_t inputCount = subgraphPtr->inputs.size();
5590 TensorIdRawPtrVector result(inputCount);
5591 for (size_t i = 0; i < inputCount; ++i)
5592 {
5593 uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]);
5594 CHECK_TENSOR(model, subgraphIndex, inputId);
5595 result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get());
5596 }
5597 return result;
5598}
5599
5601 size_t subgraphIndex)
5602{
5603 CHECK_SUBGRAPH(model, subgraphIndex);
5604 const auto& subgraphPtr = model->subgraphs[subgraphIndex];
5605
5606 size_t outputCount = subgraphPtr->outputs.size();
5607 TensorIdRawPtrVector result(outputCount);
5608 for (size_t i = 0; i < outputCount; ++i)
5609 {
5610 uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]);
5611 result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get());
5612 }
5613 return result;
5614}
5615
5616std::vector<int32_t>& TfLiteParserImpl::GetInputTensorIds(const ModelPtr& model,
5617 size_t subgraphIndex,
5618 size_t operatorIndex)
5619{
5620 CHECK_MODEL(model, subgraphIndex, operatorIndex);
5621 const auto& subgraphPtr = model->subgraphs[subgraphIndex];
5622 const auto& operatorPtr = subgraphPtr->operators[operatorIndex];
5623 return operatorPtr->inputs;
5624}
5625
5626std::vector<int32_t>& TfLiteParserImpl::GetOutputTensorIds(const ModelPtr& model,
5627 size_t subgraphIndex,
5628 size_t operatorIndex)
5629{
5630 CHECK_MODEL(model, subgraphIndex, operatorIndex);
5631 const auto& subgraphPtr = model->subgraphs[subgraphIndex];
5632 const auto& operatorPtr = subgraphPtr->operators[operatorIndex];
5633 return operatorPtr->outputs;
5634}
5635
5636void TfLiteParserImpl::RegisterInputSlots(size_t subgraphIndex,
5637 size_t operatorIndex,
5638 IConnectableLayer* layer,
5639 const std::vector<unsigned int>& tensorIndexes,
5640 unsigned int startingSlotIndex)
5641{
5642 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5643
5644 if (!layer)
5645 {
5646 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5647 operatorIndex, CHECK_LOCATION().AsString()));
5648 }
5649
5650 if (tensorIndexes.size() + startingSlotIndex != layer->GetNumInputSlots())
5651 {
5652 throw ParseException(
5653 fmt::format("The number of tensor inputs ({}) does not match the number expected ({})"
5654 " for subgraph:{} operator index:{} {}",
5655 tensorIndexes.size(),
5656 layer->GetNumInputSlots(),
5657 subgraphIndex,
5658 operatorIndex,
5659 CHECK_LOCATION().AsString()));
5660 }
5661
5662 for (unsigned int index = 0; index < tensorIndexes.size() ; ++index)
5663 {
5664 unsigned int tensorIndex = tensorIndexes[index];
5665 armnn::IInputSlot* slot = &(layer->GetInputSlot(startingSlotIndex + index));
5666 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
5667 }
5668}
5669
5670void TfLiteParserImpl::RegisterOutputSlots(size_t subgraphIndex,
5671 size_t operatorIndex,
5672 IConnectableLayer* layer,
5673 const std::vector<unsigned int>& tensorIndexes)
5674{
5675 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
5676
5677 if (!layer)
5678 {
5679 throw NullPointerException(fmt::format("Layer {} pointer is null {}",
5680 operatorIndex, CHECK_LOCATION().AsString()));
5681 }
5682
5683 if (tensorIndexes.size() != layer->GetNumOutputSlots())
5684 {
5685 throw ParseException(
5686 fmt::format("The number of tensor outputs ({}) does not match the number expected ({})"
5687 " for subgraph:{} operator index:{} {}",
5688 tensorIndexes.size(),
5689 layer->GetNumOutputSlots(),
5690 subgraphIndex,
5691 operatorIndex,
5692 CHECK_LOCATION().AsString()));
5693 }
5694
5695 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
5696 {
5697 unsigned int tensorIndex = tensorIndexes[slotIndex];
5698 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
5699 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
5700 }
5701}
5702
5703void TfLiteParserImpl::SetupInputLayerTensorInfos(size_t subgraphIndex)
5704{
5705 CHECK_SUBGRAPH(m_Model, subgraphIndex);
5706
5707 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
5708 for (auto const& tensorIdAndPtr : inputs)
5709 {
5710 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
5711 m_TensorInfos.insert({tensorIdAndPtr.first, tensorInfo});
5712 }
5713}
5714
5715void TfLiteParserImpl::SetupInputLayers(size_t subgraphIndex)
5716{
5717 CHECK_SUBGRAPH(m_Model, subgraphIndex);
5718
5719 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
5720 for (auto const& tensorIdAndPtr : inputs)
5721 {
5722 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
5723 IConnectableLayer* layer =
5724 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
5725
5726 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
5727 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
5728
5729 RegisterOutputSlots(subgraphIndex,
5730 VIRTUAL_OPERATOR_ID,
5731 layer,
5732 { static_cast<uint32_t>(tensorIdAndPtr.first) });
5733 }
5734}
5735
5736void TfLiteParserImpl::SetupOutputLayers(size_t subgraphIndex)
5737{
5738 CHECK_SUBGRAPH(m_Model, subgraphIndex);
5739
5740 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
5741 for (auto const& tensorIdAndPtr : outputs)
5742 {
5743 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
5744 IConnectableLayer* layer =
5745 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
5746
5747 RegisterInputSlots(subgraphIndex,
5748 VIRTUAL_OPERATOR_ID,
5749 layer,
5750 { static_cast<uint32_t>(tensorIdAndPtr.first) });
5751 }
5752}
5753
5754void TfLiteParserImpl::SetupConstantLayerTensorInfos(size_t subgraph)
5755{
5756 CHECK_SUBGRAPH(m_Model, subgraph);
5757
5758 const auto & subgraphPtr = m_Model->subgraphs[subgraph];
5759 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
5760 {
5761 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
5762 {
5763 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
5764 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
5765 {
5766 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
5767
5768 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
5769
5770 m_TensorInfos.insert({tensorIndex, tensorInfo});
5771 }
5772 }
5773 }
5774}
5775
5776void TfLiteParserImpl::SetupConstantLayers(size_t subgraph)
5777{
5778 CHECK_SUBGRAPH(m_Model, subgraph);
5779
5780 const auto & subgraphPtr = m_Model->subgraphs[subgraph];
5781 for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
5782 {
5783 for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
5784 {
5785 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr &&
5786 m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0)
5787 {
5788 TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get();
5789
5790 if (IsConstTensor(tensorPtr))
5791 {
5792 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
5793 armnn::DataType dataType = tensorInfo.GetDataType();
5794
5795 if (std::find(m_ConstantsToDequantize.begin(), m_ConstantsToDequantize.end(), tensorPtr->buffer)
5796 != m_ConstantsToDequantize.end())
5797 {
5798 dataType = DataType::Float32;
5799 }
5800 auto tensorAndData = CreateConstTensorNonPermuted(tensorPtr, tensorInfo, dataType);
5801
5802 std::string layerName = fmt::format("Constant:{}", tensorPtr->name);
5803 IConnectableLayer *layer = m_Network->AddConstantLayer(tensorAndData.first, layerName.c_str());
5804
5805 layer->GetOutputSlot(0).SetTensorInfo(tensorAndData.first.GetInfo());
5806 RegisterOutputSlots(subgraphIndex,
5807 VIRTUAL_OPERATOR_ID,
5808 layer,
5809 { tensorIndex });
5810 }
5811 else if (ShouldConstantTensorBeCreated(tensorIndex))
5812 {
5813 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
5814 armnn::DataType dataType = tensorInfo.GetDataType();
5815
5816 if (std::find(m_ConstantsToDequantize.begin(), m_ConstantsToDequantize.end(), tensorPtr->buffer)
5817 != m_ConstantsToDequantize.end())
5818 {
5819 dataType = DataType::Float32;
5820 }
5821 // Make sure isConstant flag is set.
5822 tensorInfo.SetConstant();
5823 tensorInfo.SetDataType(dataType);
5824
5825 auto tensorAndData = ConstTensor(tensorInfo, std::vector<uint8_t>(tensorInfo.GetNumBytes()));
5826
5827 std::string layerName = fmt::format("Constant:{}", tensorPtr->name);
5828 IConnectableLayer* layer = m_Network->AddConstantLayer(tensorAndData, layerName.c_str());
5829
5830 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
5831 RegisterOutputSlots(subgraphIndex,
5832 VIRTUAL_OPERATOR_ID,
5833 layer,
5834 {tensorIndex});
5835 }
5836 else
5837 {
5838 throw ParseException(
5839 fmt::format("Invalid Tensor: Tensor should be constant. {}",
5840 CHECK_LOCATION().AsString()));
5841 }
5842 }
5843 }
5844 }
5845}
5846
5847// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
5849{
5850 CHECK_BUFFER(model, bufferIndex);
5851 return model->buffers[bufferIndex].get();
5852}
5853
5854template<typename T>
5855std::pair<armnn::ConstTensor, TfLiteParserImpl::SupportedDataStorage>
5856TfLiteParserImpl::CreateConstTensorAndStoreData(TfLiteParserImpl::BufferRawPtr bufferPtr,
5858 armnn::TensorInfo& tensorInfo,
5860{
5861 // Make sure isConstant flag is set.
5862 tensorInfo.SetConstant();
5863
5864 auto constData = CreateConstTensorImpl<T>(bufferPtr,
5865 tensorPtr,
5866 tensorInfo,
5867 permutationVector);
5868 TfLiteParserImpl::SupportedDataStorage storage(std::move(constData.second));
5869 return std::make_pair(constData.first, std::move(storage));
5870}
5871
5872bool TfLiteParserImpl::ShouldConstantTensorBeCreated(unsigned int tensorIndex)
5873{
5874 // If the TensorIndex appears in the list of ConstantsToBeCreated then return true
5875 return (std::find(m_ConstantsToBeCreated.begin(), m_ConstantsToBeCreated.end(), tensorIndex)
5876 != m_ConstantsToBeCreated.end());
5877}
5878
5879bool TfLiteParserImpl::IsConstTensor(TensorRawPtr tensorPtr)
5880{
5881 CHECK_TENSOR_PTR(tensorPtr);
5882 bool isConst = true;
5883
5884 auto buffer = GetBuffer(m_Model, tensorPtr->buffer);
5885 if (buffer->data.size() == 0)
5886 {
5887 isConst = false;
5888 }
5889
5890 return isConst;
5891}
5892
5893std::pair<armnn::ConstTensor, TfLiteParserImpl::SupportedDataStorage>
5894TfLiteParserImpl::CreateConstTensorPermuted(TensorRawPtr tensorPtr,
5895 armnn::TensorInfo& tensorInfo,
5896 armnn::Optional<armnn::PermutationVector&> permutationVector)
5897{
5898 CHECK_TENSOR_PTR(tensorPtr);
5899 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
5900 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
5901
5902 // Make sure isConstant flag is set.
5903 tensorInfo.SetConstant();
5904
5905 switch (tensorInfo.GetDataType())
5906 {
5908 return CreateConstTensorAndStoreData<float>(bufferPtr,
5909 tensorPtr,
5910 tensorInfo,
5911 permutationVector);
5913 return CreateConstTensorAndStoreData<uint8_t>(bufferPtr,
5914 tensorPtr,
5915 tensorInfo,
5916 permutationVector);
5918 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
5919 tensorPtr,
5920 tensorInfo,
5921 permutationVector);
5923 return CreateConstTensorAndStoreData<int8_t>(bufferPtr,
5924 tensorPtr,
5925 tensorInfo,
5926 permutationVector);
5928 return CreateConstTensorAndStoreData<int32_t>(bufferPtr,
5929 tensorPtr,
5930 tensorInfo,
5931 permutationVector);
5932 default:
5933 {
5934 std::stringstream errString;
5935 errString << "Unexpected datatype when creating const tensor: "
5936 << armnn::GetDataTypeName(tensorInfo.GetDataType())
5937 << " shape:" << tensorInfo.GetShape()
5938 << CHECK_LOCATION().AsString();
5939 throw ParseException(errString.str());
5940 }
5941 }
5942}
5943
5944armnn::ConstTensor TfLiteParserImpl::CreateConstTensorNonPermuted(TensorRawPtr tensorPtr,
5945 armnn::TensorInfo& tensorInfo)
5946{
5947 CHECK_TENSOR_PTR(tensorPtr);
5948 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
5949 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
5950
5951 // Make sure isConstant flag is set.
5952 tensorInfo.SetConstant();
5953
5954 return ConstTensor(tensorInfo, bufferPtr->data.data());
5955}
5956
5957std::pair<armnn::ConstTensor, std::unique_ptr<float[]>>
5958TfLiteParserImpl::CreateConstTensorNonPermuted(TensorRawPtr tensorPtr,
5959 armnn::TensorInfo& tensorInfo,
5960 armnn::DataType inputDataType)
5961{
5962 CHECK_TENSOR_PTR(tensorPtr);
5963 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
5964 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
5965
5966 // Make sure isConstant flag is set.
5967 tensorInfo.SetConstant();
5968
5969 if (inputDataType == DataType::Float32 && tensorInfo.GetDataType() != DataType::Float32)
5970 {
5971 try
5972 {
5973 TensorInfo constTensorInfo(tensorInfo.GetShape(), DataType::Float32, 0.0f, 0, true);
5974 std::unique_ptr<float[]> data = armnnUtils::ToFloatArray(bufferPtr->data, tensorInfo);
5975 return std::make_pair(ConstTensor(constTensorInfo, data.get()), std::move(data));
5976 }
5977 catch (InvalidArgumentException&)
5978 {
5979 throw ParseException(
5980 fmt::format("Unsupported input/weights combination: Input {} not supported with Weights {}",
5981 GetDataTypeName(DataType::Float32),
5982 GetDataTypeName(tensorInfo.GetDataType()),
5983 CHECK_LOCATION().AsString()));
5984 }
5985 }
5986 else
5987 {
5988 return std::make_pair(ConstTensor(tensorInfo, bufferPtr->data.data()), std::unique_ptr<float[]>());
5989 }
5990}
5991
5992std::pair<armnn::ConstTensor*, std::unique_ptr<float[]>>
5993TfLiteParserImpl::CreateConstTensorPtr(TensorRawPtr tensorPtr, armnn::TensorInfo& inputTensorInfo)
5994{
5995 CHECK_TENSOR_PTR(tensorPtr);
5996 armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr);
5997 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
5998 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
5999
6000 // Make sure isConstant flag is set.
6001 tensorInfo.SetConstant();
6002
6003 if (inputTensorInfo.GetDataType() == DataType::Float32 && tensorInfo.GetDataType() != DataType::Float32)
6004 {
6005 try
6006 {
6007 TensorInfo constTensorInfo(tensorInfo.GetShape(), DataType::Float32, 0.0f, 0, true);
6008 std::unique_ptr<float[]> data = armnnUtils::ToFloatArray(bufferPtr->data, tensorInfo);
6009 return std::make_pair(new ConstTensor(constTensorInfo, data.get()), std::move(data));
6010 }
6011 catch (InvalidArgumentException&)
6012 {
6013 throw ParseException(
6014 fmt::format("Unsupported input/weights combination: Input {} not supported with Weights {}",
6015 GetDataTypeName(DataType::Float32),
6016 GetDataTypeName(tensorInfo.GetDataType()),
6017 CHECK_LOCATION().AsString()));
6018 }
6019 }
6020 else
6021 {
6022 return std::make_pair(new ConstTensor(tensorInfo, bufferPtr->data.data()), std::unique_ptr<float[]>());
6023 }
6024}
6025
6027 const std::string& name) const
6028{
6029 CHECK_SUBGRAPH(m_Model, subgraphId);
6030 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
6031 for (auto const& input : inputs)
6032 {
6033 if (input.second->name == name)
6034 {
6035 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
6036 auto inputTensorInfo = ToTensorInfo(input.second);
6037 // Input tensors are always treated as constant tensors during network execution.
6038 inputTensorInfo.SetConstant(true);
6039 return std::make_pair(bindingId, inputTensorInfo);
6040 }
6041 }
6042
6043 std::stringstream bindings;
6044 for (auto const& input : inputs)
6045 {
6046 bindings << "'" << input.second->name << "' ";
6047 }
6048
6049 throw ParseException(
6050 fmt::format("No input binding found for subgraph:{} and name:{}. "
6051 "Possible inputs are: [{}] {}",
6052 subgraphId,
6053 name,
6054 bindings.str(),
6055 CHECK_LOCATION().AsString()));
6056}
6057
6059 const std::string& name) const
6060{
6061 CHECK_SUBGRAPH(m_Model, subgraphId);
6062 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
6063 for (unsigned int i = 0; i < outputs.size(); ++i)
6064 {
6065 auto const output = outputs[i];
6066 if (output.second->name == name)
6067 {
6068 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
6069 std::vector<unsigned int> shape = m_OverriddenOutputShapes.size() > 0 ?
6070 m_OverriddenOutputShapes[i] : AsUnsignedVector(output.second->shape);
6071 return std::make_pair(bindingId, ToTensorInfo(output.second, shape));
6072 }
6073 }
6074
6075 std::stringstream bindings;
6076 for (auto const& output : outputs)
6077 {
6078 bindings << "'" << output.second->name << "' ";
6079 }
6080
6081 throw ParseException(
6082 fmt::format("No output binding found for subgraph:{} and name:{}. "
6083 "Possible outputs are: [{}] {}",
6084 subgraphId,
6085 name,
6086 bindings.str(),
6087 CHECK_LOCATION().AsString()));
6088}
6089
6091{
6092 return m_Model->subgraphs.size();
6093}
6094
6095std::vector<std::string> TfLiteParserImpl::GetSubgraphInputTensorNames(size_t subgraphId) const
6096{
6097 CHECK_SUBGRAPH(m_Model, subgraphId);
6098 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
6099 std::vector<std::string> result;
6100 result.reserve(inputs.size());
6101 for (auto const& input : inputs)
6102 {
6103 result.push_back(input.second->name);
6104 }
6105 return result;
6106}
6107
6108std::vector<std::string> TfLiteParserImpl::GetSubgraphOutputTensorNames(size_t subgraphId) const
6109{
6110 CHECK_SUBGRAPH(m_Model, subgraphId);
6111 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
6112 std::vector<std::string> result;
6113 result.reserve(outputs.size());
6114 for (auto const& output : outputs)
6115 {
6116 result.push_back(output.second->name);
6117 }
6118 return result;
6119}
6120
6122{
6123 return TFLITE_PARSER_VERSION;
6124}
6125
6126TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]>&& data)
6127: m_FloatData(std::move(data))
6128, m_Uint8Data(nullptr)
6129, m_Int8Data(nullptr)
6130, m_Int32Data(nullptr)
6131{
6132}
6133
6134TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]>&& data)
6135: m_FloatData(nullptr)
6136, m_Uint8Data(std::move(data))
6137, m_Int8Data(nullptr)
6138, m_Int32Data(nullptr)
6139{
6140}
6141
6142TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int8_t[]>&& data)
6143: m_FloatData(nullptr)
6144, m_Uint8Data(nullptr)
6145, m_Int8Data(std::move(data))
6146, m_Int32Data(nullptr)
6147{
6148}
6149
6150TfLiteParserImpl::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]>&& data)
6151: m_FloatData(nullptr)
6152, m_Uint8Data(nullptr)
6153, m_Int8Data(nullptr)
6154, m_Int32Data(std::move(data))
6155{
6156}
6157
6158void TfLiteParserImpl::ValidateBuffer(BufferRawPtr bufferPtr,
6159 const armnn::TensorInfo& tensorInfo,
6160 const std::string& bufferName)
6161{
6162 if (bufferPtr == nullptr)
6163 {
6164 throw ParseException(bufferName + " buffer pointer is null.");
6165 }
6166
6167 if (bufferPtr->data.empty())
6168 {
6169 throw ParseException(bufferName + " buffer data is empty.");
6170 }
6171
6172 size_t requiredBytes = tensorInfo.GetNumBytes();
6173 if (bufferPtr->data.size() < requiredBytes)
6174 {
6175 throw ParseException(fmt::format(bufferName + " buffer too small. Expected at least {} bytes, got {}.",
6176 requiredBytes, bufferPtr->data.size()));
6177 }
6178}
6179
6180
6181
6182} // armnnTfLiteParser
#define ARMNN_ASSERT(COND)
Definition Assert.hpp:14
#define CHECK_TENSOR_PTR(TENSOR_PTR)
#define CHECK_LOCATION()
#define ARMNN_LOG(severity)
Definition Logging.hpp:212
#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX)
#define ARMNN_THROW_PARSE_EXCEPTION(msg)
#define CHECK_BUFFER(MODEL, BUFFER_INDEX)
#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX)
#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX)
#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID)
#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX)
#define CHECK_VALID_SIZE(ACTUAL,...)
#define CHECKED_NON_NEGATIVE(VALUE)
const TensorInfo & GetInfo() const
Definition Tensor.hpp:297
virtual const char * what() const noexcept override
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition INetwork.hpp:81
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
virtual std::vector< TensorShape > InferOutputShapes(const std::vector< TensorShape > &inputShapes) const =0
Infer the shape of the output(s) based on the provided input shape(s)
virtual unsigned int GetNumInputSlots() const =0
Returns the number of connectable input slots.
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
virtual LayerType GetType() const =0
Returns the armnn::LayerType of this layer.
virtual const char * GetName() const =0
Returns the name of the layer.
static INetworkPtr Create(const NetworkOptions &networkOptions={})
Definition Network.cpp:682
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
virtual int Connect(IInputSlot &destination)=0
virtual const IConnectableLayer & GetOwningIConnectableLayer() const =0
virtual const TensorInfo & GetTensorInfo() const =0
bool has_value() const noexcept
Definition Optional.hpp:53
float GetQuantizationScale() const
Definition Tensor.cpp:461
const TensorShape & GetShape() const
Definition Tensor.hpp:193
unsigned int GetNumDimensions() const
Definition Tensor.hpp:197
bool IsTypeSpaceMatch(const TensorInfo &other) const
Check that the types are the same and, if quantize, that the quantization parameters are the same.
Definition Tensor.cpp:432
void SetDataType(DataType type)
Definition Tensor.hpp:201
int32_t GetQuantizationOffset() const
Definition Tensor.cpp:482
bool IsQuantized() const
Definition Tensor.cpp:508
unsigned int GetNumElements() const
Definition Tensor.hpp:198
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
Definition Tensor.cpp:518
unsigned int GetNumBytes() const
Definition Tensor.cpp:427
void SetShape(const TensorShape &newShape)
Definition Tensor.hpp:195
DataType GetDataType() const
Definition Tensor.hpp:200
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition Tensor.cpp:174
unsigned int GetNumElements() const
Function that calculates the tensor elements by multiplying all dimension size which are Specified.
Definition Tensor.cpp:181
size_t GetSubgraphCount() const
Return the number of subgraphs in the parsed model.
static void Destroy(ITfLiteParser *parser)
BindingPointInfo GetNetworkOutputBindingInfo(size_t subgraphId, const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a flatbuffers binary file on disk.
BindingPointInfo GetNetworkInputBindingInfo(size_t subgraphId, const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const
Return the output tensor names for a given subgraph.
static ITfLiteParserPtr Create(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent)
Create the network from a flatbuffers binary.
static ITfLiteParser * CreateRaw(const armnn::Optional< TfLiteParserOptions > &options=armnn::EmptyOptional())
std::vector< std::string > GetSubgraphInputTensorNames(size_t subgraphId) const
Return the input tensor names for a given subgraph.
armnn::INetworkPtr LoadModel(std::unique_ptr< tflite::ModelT > model)
size_t GetSubgraphCount() const
Return the number of subgraphs in the parsed model.
static TensorIdRawPtrVector GetSubgraphOutputs(const ModelPtr &model, size_t subgraphIndex)
static TensorIdRawPtrVector GetSubgraphInputs(const ModelPtr &model, size_t subgraphIndex)
static TensorRawPtrVector GetOutputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
armnn::INetworkPtr CreateNetworkFromBinary(const std::vector< uint8_t > &binaryContent)
Create the network from a flatbuffers binary.
static TensorRawPtrVector GetInputs(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
BindingPointInfo GetNetworkOutputBindingInfo(size_t subgraphId, const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network output identified by the given layer...
static BufferRawPtr GetBuffer(const ModelPtr &model, size_t bufferIndex)
static armnn::TensorInfo OutputShapeOfSqueeze(std::vector< uint32_t > squeezeDims, const armnn::TensorInfo &inputTensorInfo)
static ModelPtr LoadModelFromBinary(const uint8_t *binaryContent, size_t len)
std::vector< TensorIdRawPtr > TensorIdRawPtrVector
static std::vector< int32_t > & GetInputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
BindingPointInfo GetNetworkInputBindingInfo(size_t subgraphId, const std::string &name) const
Retrieve binding info (layer id and tensor info) for the network input identified by the given layer ...
std::vector< std::string > GetSubgraphOutputTensorNames(size_t subgraphId) const
Return the output tensor names for a given subgraph.
std::unique_ptr< tflite::SubGraphT > SubgraphPtr
static const std::string GetVersion()
Retrieve version in X.Y.Z form.
const tflite::BufferT * BufferRawPtr
std::unique_ptr< tflite::OperatorT > OperatorPtr
static armnn::TensorInfo OutputShapeOfReshape(const armnn::TensorInfo &inputTensorInfo, const std::vector< int32_t > &targetDimsIn)
std::vector< TensorRawPtr > TensorRawPtrVector
std::unique_ptr< tflite::ModelT > ModelPtr
const tflite::TensorT * TensorRawPtr
TfLiteParserImpl(const armnn::Optional< ITfLiteParser::TfLiteParserOptions > &options=armnn::EmptyOptional())
armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile)
Create the network from a flatbuffers binary file on disk.
static ModelPtr LoadModelFromFile(const char *fileName)
std::vector< std::string > GetSubgraphInputTensorNames(size_t subgraphId) const
Return the input tensor names for a given subgraph.
static std::vector< int32_t > & GetOutputTensorIds(const ModelPtr &model, size_t subgraphIndex, size_t operatorIndex)
#define TFLITE_PARSER_VERSION
TFLITE_PARSER_VERSION: "X.Y.Z" where: X = Major version number Y = Minor version number Z = Patch ver...
Definition Version.hpp:25
const armnnSerializer::Pooling2dDescriptor * Pooling2dDescriptor
bool CheckShape(const armnn::TensorShape &actual, const std::vector< uint32_t > &expected)
armnn::TensorInfo ToTensorInfo(TensorRawPtr tensorPtr)
Copyright (c) 2021 ARM Limited and Contributors.
constexpr char const * GetComparisonOperationAsCString(ComparisonOperation operation)
constexpr char const * GetUnaryOperationAsCString(UnaryOperation operation)
UnaryOperation
Definition Types.hpp:126
ComparisonOperation
Definition Types.hpp:110
constexpr const char * GetDataTypeName(DataType dataType)
std::vector< BackendOptions > NetworkOptions
ActivationFunction
Definition Types.hpp:87
ViewsDescriptor SplitterDescriptor
SpaceToDepthDescriptor DepthToSpaceDescriptor
A DepthToSpaceDescriptor for the DepthToSpaceLayer.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
PoolingAlgorithm
Definition Types.hpp:152
ResizeMethod
Definition Types.hpp:168
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
Definition Types.hpp:311
ReduceOperation
Definition Types.hpp:159
constexpr unsigned int MaxNumOfTensorDimensions
Definition Types.hpp:31
SoftmaxDescriptor LogSoftmaxDescriptor
A LogSoftmaxDescriptor for the LogSoftmaxLayer.
LstmDescriptor UnidirectionalSequenceLstmDescriptor
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition INetwork.hpp:339
@ LocalBrightness
Krichevsky 2012: Local Brightness Normalization.
Definition Types.hpp:217
DataType
Definition Types.hpp:49
ArgMinMaxFunction
Definition Types.hpp:104
void IgnoreUnused(Ts &&...)
std::pair< armnn::ConstTensor, std::unique_ptr< T[]> > CreateConstTensorImpl(const T *bufferPtr, armnn::TensorInfo &tensorInfo, const armnn::Optional< armnn::PermutationVector & > permutationVector)
armnn::BindingPointInfo BindingPointInfo
unsigned int ComputeWrappedIndex(int idx, unsigned int numDimsIn)
std::unique_ptr< ITfLiteParser, void(*)(ITfLiteParser *parser)> ITfLiteParserPtr
void ProcessConcatInputTensorInfo(armnn::TensorInfo &inputTensorInfo, armnn::OriginsDescriptor &concatDescriptor, const unsigned int &concatAxis, unsigned int inputIndex, unsigned int &mergeDimOrigin)
std::unique_ptr< float[]> ToFloatArray(const std::vector< PrimitiveType > &data, const armnn::TensorInfo &tensorInfo)
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
Definition Permute.cpp:125
unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH,...
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu,...
int m_Axis
Axis to reduce across the input tensor.
ArgMinMaxFunction m_Function
Specify if the function is to find Min or Max.
Struct for the users to pass backend specific options.
std::vector< unsigned int > m_BlockShape
Block shape values.
std::vector< std::pair< unsigned int, unsigned int > > m_Crops
The values to crop from the input dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
TensorShape m_BroadcastToShape
Target shape value.
const char * m_Function
std::string AsString() const
std::string FileLine() const
ComparisonOperation m_Operation
Specifies the comparison operation to execute.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationY
Dilation along y axis.
uint32_t m_PadTop
Padding top value in the height dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_DilationX
Dilation along x axis.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
bool m_BiasEnabled
Enable/disable bias.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_PadBack
Padding back value in the depth dimension.
uint32_t m_DilationZ
Dilation along z axis.
uint32_t m_DilationY
Dilation along y axis.
uint32_t m_StrideZ
Stride value when proceeding through input for the depth dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
DataLayout m_DataLayout
The data layout to be used (NDHWC, NCDHW).
uint32_t m_PadFront
Padding front value in the depth dimension.
uint32_t m_DilationX
Dilation along x axis.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
bool m_BiasEnabled
Enable/disable bias.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_DilationY
Dilation factor value for height dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_DilationX
Dilation factor value for width dimension.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
bool m_BiasEnabled
Enable/disable bias.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
uint32_t m_NumClasses
Number of classes.
float m_NmsScoreThreshold
NMS score threshold.
float m_NmsIouThreshold
Intersection over union threshold.
float m_ScaleY
Center size encoding scale y.
uint32_t m_DetectionsPerClass
Detections per classes, used in Regular NMS.
bool m_UseRegularNms
Use Regular NMS.
uint32_t m_MaxClassesPerDetection
Maximum numbers of classes per detection, used in Fast NMS.
float m_ScaleH
Center size encoding scale height.
float m_ScaleW
Center size encoding scale weight.
float m_ScaleX
Center size encoding scale x.
uint32_t m_MaxDetections
Maximum numbers of detections.
UnaryOperation m_Operation
Specifies the elementwiseUnary operation to execute.
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
bool m_ConstantWeights
Enable/disable constant weights and biases.
bool m_BiasEnabled
Enable/disable bias.
int32_t m_Axis
The axis in params to gather indices from.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
float m_CellIntermediateScale
Cell intermediate quantization scale.
float m_InputIntermediateScale
Input intermediate quantization scale.
bool m_PeepholeEnabled
Enable/disable peephole.
bool m_TimeMajor
Enable/disable time major.
int32_t m_HiddenStateZeroPoint
Hidden State zero point.
bool m_LayerNormEnabled
Enable/disable layer normalization.
float m_ClippingThresCell
Clipping threshold value for the cell state.
bool m_ProjectionEnabled
Enable/disable the projection layer.
float m_ClippingThresProj
Clipping threshold value for the projection.
float m_OutputIntermediateScale
Output intermediate quantization scale.
bool m_CifgEnabled
Enable/disable cifg (coupled input & forget gate).
uint32_t m_ActivationFunc
The activation function to use.
float m_HiddenStateScale
Hidden State quantization scale.
float m_ForgetIntermediateScale
Forget intermediate quantization scale.
const ConstTensor * m_InputLayerNormWeights
const ConstTensor * m_RecurrentToCellWeights
const ConstTensor * m_InputToForgetWeights
const ConstTensor * m_CellToForgetWeights
const ConstTensor * m_RecurrentToInputWeights
const ConstTensor * m_ProjectionBias
const ConstTensor * m_CellToInputWeights
const ConstTensor * m_InputToCellWeights
const ConstTensor * m_CellBias
const ConstTensor * m_RecurrentToOutputWeights
const ConstTensor * m_InputToOutputWeights
const ConstTensor * m_OutputGateBias
const ConstTensor * m_OutputLayerNormWeights
const ConstTensor * m_InputGateBias
const ConstTensor * m_ProjectionWeights
const ConstTensor * m_ForgetGateBias
const ConstTensor * m_CellLayerNormWeights
const ConstTensor * m_RecurrentToForgetWeights
const ConstTensor * m_ForgetLayerNormWeights
const ConstTensor * m_CellToOutputWeights
const ConstTensor * m_InputToInputWeights
std::vector< unsigned int > m_Axis
Values for the dimensions to reduce.
bool m_KeepDims
Enable/disable keep dimensions. If true, then the reduced dimensions that are of length 1 are kept.
NormalizationAlgorithmMethod m_NormMethodType
Normalization method algorithm to use (LocalBrightness, LocalContrast).
float m_Alpha
Alpha value for the normalization equation.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
float m_Beta
Beta value for the normalization equation.
float m_K
Kappa value used for the across channel normalization equation.
uint32_t m_NormSize
Depth radius value.
NormalizationAlgorithmChannel m_NormChannelType
Normalization channel algorithm to use (Across, Within).
float m_PadValue
Optional value to use for padding, defaults to 0.
PaddingMode m_PaddingMode
Specifies the Padding mode (Constant, Reflect or Symmetric)
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding for input dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_PoolWidth
Pooling width value.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
bool m_KeepDims
if true then output shape has no change.
std::vector< uint32_t > m_vAxis
The indices of the dimensions to reduce.
ReduceOperation m_ReduceOperation
Specifies the reduction operation to execute.
TensorShape m_TargetShape
Target shape value.
bool m_HalfPixelCenters
Half Pixel Centers.
uint32_t m_TargetHeight
Target height value.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
uint32_t m_TargetWidth
Target width value.
bool m_AlignCorners
Aligned corners.
float m_Beta
Exponentiation value.
std::vector< unsigned int > m_BlockShape
Block shape value.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< std::pair< unsigned int, unsigned int > > m_PadList
Specifies the padding values for the input dimension: heightPad{top, bottom} widthPad{left,...
unsigned int m_BlockSize
Scalar specifying the input block size. It must be >= 1.
TensorShape m_InputShape
Required shape of all input tensors.
uint32_t m_Axis
0-based axis along which to stack the input tensors.
uint32_t m_NumInputs
Number of input tensors.
std::vector< int > m_Stride
Stride values for the input that will be sliced.
std::vector< int > m_Begin
Begin values for the input that will be sliced.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
int32_t m_BeginMask
Begin mask value.
int32_t m_ShrinkAxisMask
Shrink axis mask value. If set, the nth specification shrinks the dimensionality by 1.
int32_t m_NewAxisMask
New axis mask value.
std::vector< int > m_End
End values for the input that will be sliced.
int32_t m_EndMask
End mask value.
int32_t m_EllipsisMask
Ellipsis mask value.
std::vector< uint32_t > m_Multiples
The vector to multiply the input shape by.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< unsigned int > m_OutputShape
bool m_OutputShapeEnabled
Output shape if it has been specified.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.