blob: fca6a6c9ed94b3f730a9bcab39f7eb57e3bbbf7e [file] [log] [blame]
Sadik Armagan62483be2020-10-23 17:14:43 +01001//
2// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
8#include <armnn/ArmNN.hpp>
9#include <armnn/BackendHelper.hpp>
10#include <armnn/utility/Assert.hpp>
Sadik Armagan67e95f22020-10-29 16:14:54 +000011#include <armnn/utility/NumericCast.hpp>
Sadik Armagan62483be2020-10-23 17:14:43 +010012
13#include <tensorflow/lite/builtin_ops.h>
14#include <tensorflow/lite/c/builtin_op_data.h>
15#include <tensorflow/lite/c/common.h>
16#include <tensorflow/lite/minimal_logging.h>
17
18namespace
19{
20
21// Macro to call an Is<layer_name>Supported function and log caller name together with reason for lack of support
22#define FORWARD_LAYER_SUPPORT_FUNC(funcName, tfLiteContext, func, backends, supported, ...) \
23try \
24{ \
25 for (auto&& backendId : backends) \
26 { \
27 auto layerSupportObject = armnn::GetILayerSupportByBackendId(backendId); \
28 if (layerSupportObject) \
29 { \
30 std::string reasonIfUnsupported; \
31 supported = \
32 layerSupportObject->func(__VA_ARGS__, armnn::Optional<std::string&>(reasonIfUnsupported)); \
33 if (supported) \
34 { \
35 break; \
36 } \
37 else \
38 { \
39 if (reasonIfUnsupported.size() > 0) \
40 { \
41 TF_LITE_KERNEL_LOG( \
42 tfLiteContext, "%s: not supported by armnn: %s", funcName, reasonIfUnsupported.c_str()); \
43 } \
44 else \
45 { \
46 TF_LITE_KERNEL_LOG(tfLiteContext, "%s: not supported by armnn", funcName); \
47 } \
48 } \
49 } \
50 else \
51 { \
52 TF_LITE_KERNEL_LOG(tfLiteContext, "%s: backend not registered: %s", funcName, backendId.Get().c_str()); \
53 } \
54 } \
55 if (!supported) \
56 { \
57 TF_LITE_KERNEL_LOG(tfLiteContext, "%s: not supported by any specified backend", funcName); \
58 } \
59} \
60catch (const armnn::InvalidArgumentException &e) \
61{ \
62 throw armnn::InvalidArgumentException(e, "Failed to check layer support", CHECK_LOCATION()); \
63}
64
65TfLiteStatus ValidateNumInputs(TfLiteContext* tfLiteContext,
66 TfLiteNode* tfLiteNode,
67 const unsigned int expectedSize,
68 int nodeIndex)
69{
70 auto numInputs = tfLiteNode->inputs->size;
71 if (numInputs != expectedSize)
72 {
73 TF_LITE_MAYBE_KERNEL_LOG(
74 tfLiteContext, "TfLiteArmnnDelegate: Unexpected number of inputs (%d != %d) in node #%d",
75 numInputs, expectedSize, nodeIndex);
76 return kTfLiteError;
77 }
78 return kTfLiteOk;
79}
80
81TfLiteStatus ValidateNumOutputs(TfLiteContext* tfLiteContext,
82 TfLiteNode* tfLiteNode,
83 const unsigned int expectedSize,
84 int nodeIndex)
85{
86 auto numOutputs = tfLiteNode->outputs->size;
87 if (numOutputs != expectedSize)
88 {
89 TF_LITE_MAYBE_KERNEL_LOG(
90 tfLiteContext, "TfLiteArmnnDelegate: Unexpected number of outputs (%d != %d) in node #%d",
91 numOutputs, expectedSize, nodeIndex);
92 return kTfLiteError;
93 }
94 return kTfLiteOk;
95}
96
97bool IsDynamicTensor(const TfLiteTensor& tfLiteTensor)
98{
99 auto tensorAllocationType = tfLiteTensor.allocation_type;
100 if (tensorAllocationType == kTfLiteDynamic)
101 {
102 return true;
103 }
104 return false;
105}
106
Sadik Armagan67e95f22020-10-29 16:14:54 +0000107TfLiteStatus Connect(armnn::IConnectableLayer* layer,
108 TfLiteNode* tfLiteNode,
109 armnnDelegate::DelegateData& data)
110{
111 ARMNN_ASSERT(tfLiteNode->inputs->size == layer->GetNumInputSlots());
112 ARMNN_ASSERT(tfLiteNode->outputs->size == layer->GetNumOutputSlots());
113
114 // Connect the input slots
115 for (unsigned int inputIndex = 0; inputIndex < layer->GetNumInputSlots(); ++inputIndex)
116 {
117 data.m_OutputSlotForNode[tfLiteNode->inputs->data[inputIndex]]->Connect(layer->GetInputSlot(inputIndex));
118 }
119
120 // Prepare output slots
121 for (unsigned int outputIndex = 0; outputIndex < layer->GetNumOutputSlots(); ++outputIndex)
122 {
123 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(outputIndex);
124 data.m_OutputSlotForNode[tfLiteNode->outputs->data[outputIndex]] = &outputSlot;
125 }
126 return kTfLiteOk;
127}
128
129armnn::IConnectableLayer* BroadcastTensor(const armnn::TensorInfo& inputInfo0,
130 const armnn::TensorInfo& inputInfo1,
131 armnn::IConnectableLayer* startLayer,
132 TfLiteContext* tfLiteContext,
133 TfLiteNode* tfLiteNode,
134 armnnDelegate::DelegateData& delegateData)
135{
136 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
137 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
138
139 if (inputDimensions0 == inputDimensions1)
140 {
141 auto status = Connect(startLayer, tfLiteNode, delegateData);
142 if(status == kTfLiteOk)
143 {
144 return startLayer;
145 }
146 else
147 {
148 return nullptr;
149 }
150 }
151
152 unsigned int biggerInputDimensions = std::max(inputDimensions0, inputDimensions1);
153 unsigned int dimDifference =
154 std::abs(armnn::numeric_cast<int>(inputDimensions0) - armnn::numeric_cast<int>(inputDimensions1));
155
156 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
157 const armnn::TensorInfo& smallInfo = input0IsSmaller ? inputInfo0 : inputInfo1;
158 const armnn::TensorShape& smallShape = smallInfo.GetShape();
159
160 std::vector<unsigned int> reshapedDimensions(biggerInputDimensions, 1);
161 for (unsigned int i = dimDifference; i < biggerInputDimensions; ++i)
162 {
163 reshapedDimensions[i] = smallShape[i - dimDifference];
164 }
165
166 armnn::TensorInfo reshapedInfo = smallInfo;
167 reshapedInfo.SetShape(armnn::TensorShape{ armnn::numeric_cast<unsigned int>(reshapedDimensions.size()),
168 reshapedDimensions.data() });
169
170 armnn::ReshapeDescriptor reshapeDescriptor;
171 bool isSupported = false;
172 FORWARD_LAYER_SUPPORT_FUNC(__func__,
173 tfLiteContext,
174 IsReshapeSupported,
175 delegateData.m_Backends,
176 isSupported,
177 smallInfo,
178 reshapedInfo,
179 reshapeDescriptor);
180 if (!isSupported)
181 {
182 return nullptr;
183 }
184
185 ARMNN_ASSERT(delegateData.m_Network != nullptr);
186 // Add Reshape layer
187 reshapeDescriptor.m_TargetShape = reshapedInfo.GetShape();
188
189 armnn::IConnectableLayer* reshapeLayer = delegateData.m_Network->AddReshapeLayer(reshapeDescriptor);
190 ARMNN_ASSERT(reshapeLayer != nullptr);
191 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
192
193 if (input0IsSmaller)
194 {
195 delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[0]]->Connect(reshapeLayer->GetInputSlot(0));
196 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(0));
197 delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[1]]->Connect(startLayer->GetInputSlot(1));
198 }
199 else
200 {
201 delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[1]]->Connect(reshapeLayer->GetInputSlot(0));
202 reshapeLayer->GetOutputSlot(0).Connect(startLayer->GetInputSlot(1));
203 delegateData.m_OutputSlotForNode[tfLiteNode->inputs->data[0]]->Connect(startLayer->GetInputSlot(0));
204 }
205
206 // Prepare output slots
207 for (unsigned int outputIndex = 0; outputIndex < startLayer->GetNumOutputSlots(); ++outputIndex)
208 {
209 armnn::IOutputSlot& outputSlot = startLayer->GetOutputSlot(outputIndex);
210 delegateData.m_OutputSlotForNode[tfLiteNode->outputs->data[outputIndex]] = &outputSlot;
211 }
212
213 return reshapeLayer;
214}
215
216TfLiteStatus FusedActivation(TfLiteContext* tfLiteContext,
217 TfLiteNode* tfLiteNode,
218 TfLiteFusedActivation activationType,
219 armnn::IConnectableLayer* prevLayer,
220 unsigned int outputSlotIndex,
221 armnnDelegate::DelegateData& data)
222{
223
224 armnn::IOutputSlot& outputSlot = prevLayer->GetOutputSlot(outputSlotIndex);
225 const armnn::TensorInfo& activationOutputInfo = outputSlot.GetTensorInfo();
226
227 armnn::ActivationDescriptor activationDesc;
228
229 switch (activationType)
230 {
231 case kTfLiteActNone:
232 {
233 // No Activation
234 return kTfLiteOk;
235 }
236 case kTfLiteActRelu:
237 {
238 activationDesc.m_Function = armnn::ActivationFunction::ReLu;
239 break;
240 }
241 case kTfLiteActRelu1:
242 {
243 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
244 activationDesc.m_A = 1.0f;
245 activationDesc.m_B = -1.0f;
246 break;
247 }
248 case kTfLiteActRelu6:
249 {
250 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
251 activationDesc.m_A = 6.0f;
252 activationDesc.m_B = 0.0f;
253 break;
254 }
255 case kTfLiteActSigmoid:
256 {
257 activationDesc.m_Function = armnn::ActivationFunction::Sigmoid;
258 break;
259 }
260 case kTfLiteActTanh:
261 {
262 activationDesc.m_Function = armnn::ActivationFunction::TanH;
263 activationDesc.m_A = 1.0f;
264 activationDesc.m_B = 1.0f;
265 break;
266 }
267 default:
268 return kTfLiteError;
269 }
270
271 bool isSupported = false;
272 FORWARD_LAYER_SUPPORT_FUNC(__func__,
273 tfLiteContext,
274 IsActivationSupported,
275 data.m_Backends,
276 isSupported,
277 prevLayer->GetOutputSlot(0).GetTensorInfo(),
278 activationOutputInfo,
279 activationDesc);
280 if (!isSupported)
281 {
282 return kTfLiteError;
283 }
284 armnn::IConnectableLayer* activationLayer = data.m_Network->AddActivationLayer(activationDesc);
285
286 ARMNN_ASSERT(activationLayer != nullptr);
287 activationLayer->GetOutputSlot(0).SetTensorInfo(activationOutputInfo);
288
289 // Connect and prepare output slots
290 for (unsigned int outputIndex = 0; outputIndex < activationLayer->GetNumOutputSlots(); ++outputIndex)
291 {
292 data.m_OutputSlotForNode[tfLiteNode->outputs->data[outputIndex]]->Connect(activationLayer->GetInputSlot(0));
293 armnn::IOutputSlot& outputSlot = activationLayer->GetOutputSlot(outputIndex);
294 data.m_OutputSlotForNode[tfLiteNode->outputs->data[outputIndex]] = &outputSlot;
295 }
296 return kTfLiteOk;
297}
298
Sadik Armagan62483be2020-10-23 17:14:43 +0100299armnn::TensorInfo GetTensorInfoForTfLiteTensor(const TfLiteTensor& tfLiteTensor)
300{
301 armnn::DataType type;
302 switch (tfLiteTensor.type)
303 {
304 case kTfLiteBool:
305 type = armnn::DataType::Boolean;
306 break;
307 case kTfLiteFloat32:
308 type = armnn::DataType::Float32;
309 break;
310 case kTfLiteFloat16:
311 type = armnn::DataType::Float16;
312 break;
313 case kTfLiteUInt8:
314 type = armnn::DataType::QAsymmU8;
315 break;
316 case kTfLiteInt8:
317 type = armnn::DataType::QSymmS8;
318 break;
319 case kTfLiteInt16:
320 type = armnn::DataType::QSymmS16;
321 break;
322 case kTfLiteInt32:
323 type = armnn::DataType::Signed32;
324 break;
325 default:
326 throw armnn::Exception("TfLiteArmnnDelegate: Unsupported data type: " + tfLiteTensor.type);
327 }
328
329 armnn::TensorInfo ret;
330 auto tensorDimensionSize = tfLiteTensor.dims->size;
331 if (tensorDimensionSize == 0)
332 {
333 armnn::TensorShape tensorShape(armnn::Dimensionality::NotSpecified);
334 ret = armnn::TensorInfo(tensorShape, type);
335 }
336 else
337 {
338 std::vector<unsigned int> tensorDims(tensorDimensionSize);
339 bool dimensionsSpecificity[5] = { true, true, true, true, true };
340 for (unsigned int i = 0; i < tensorDimensionSize; ++i) {
341 auto dim = tfLiteTensor.dims->data[i];
342 if (dim == 0)
343 {
344 dimensionsSpecificity[i] = false;
345 }
346 tensorDims[i] = dim;
347 }
348 armnn::TensorShape tensorShape(tensorDimensionSize, tensorDims.data(), dimensionsSpecificity);
349 ret = armnn::TensorInfo(tensorShape, type);
350 }
351
352 auto quantizationInfo = tfLiteTensor.quantization;
353 if (quantizationInfo.type == kTfLiteAffineQuantization)
354 {
355 // get per-channel quantization parameters
356 const auto* affineQuantization =
357 reinterpret_cast<TfLiteAffineQuantization*>(tfLiteTensor.quantization.params);
Sadik Armagan67e95f22020-10-29 16:14:54 +0000358 if (affineQuantization->scale->size > 1)
Sadik Armagan62483be2020-10-23 17:14:43 +0100359 {
Sadik Armagan67e95f22020-10-29 16:14:54 +0000360 std::vector<float> quantizationScales;
361 for (unsigned int i = 1; i < affineQuantization->scale->size; ++i)
362 {
363 quantizationScales.push_back(affineQuantization->scale->data[i]);
364 }
365 ret.SetQuantizationScales(quantizationScales);
366 ret.SetQuantizationDim(armnn::MakeOptional<unsigned int>(affineQuantization->quantized_dimension));
Sadik Armagan62483be2020-10-23 17:14:43 +0100367 }
Sadik Armagan67e95f22020-10-29 16:14:54 +0000368 else
369 {
370 ret.SetQuantizationScale(affineQuantization->scale->data[0]);
371 ret.SetQuantizationOffset(affineQuantization->zero_point->data[0]);
372 }
Sadik Armagan62483be2020-10-23 17:14:43 +0100373 }
374 else
375 {
376 auto quantizationParameters = tfLiteTensor.params;
377 ret.SetQuantizationScale(quantizationParameters.scale);
378 ret.SetQuantizationOffset(quantizationParameters.zero_point);
379 }
380
381 return ret;
382}
383
Sadik Armagan62483be2020-10-23 17:14:43 +0100384} // namespace anonymous