blob: a9645149b42dfd6c07ed13eda82668fcf95bc889 [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
Finn Williams6f9f9902020-11-13 13:23:15 +00008#include <armnn/utility/IgnoreUnused.hpp>
9
Sadik Armagan62483be2020-10-23 17:14:43 +010010#include <tensorflow/lite/builtin_ops.h>
11#include <tensorflow/lite/c/builtin_op_data.h>
12#include <tensorflow/lite/c/common.h>
Matthew Sloyan91c41712020-11-13 09:47:35 +000013#include <tensorflow/lite/kernels/internal/tensor_ctypes.h>
Sadik Armagan62483be2020-10-23 17:14:43 +010014#include <tensorflow/lite/minimal_logging.h>
15
Matthew Sloyan91c41712020-11-13 09:47:35 +000016#include <algorithm>
17#include <iterator>
18#include <string>
19#include <vector>
20
Sadik Armagan62483be2020-10-23 17:14:43 +010021namespace armnnDelegate
22{
23
Matthew Sloyan91c41712020-11-13 09:47:35 +000024void SetupConcatViewOrigin(const armnn::TensorInfo& inputTensorInfo,
25 armnn::OriginsDescriptor& concatDescriptor,
26 const unsigned int concatAxis,
27 unsigned int inputIndex,
28 unsigned int& mergeDimOrigin)
29{
30 const uint32_t inputRank = concatDescriptor.GetNumDimensions();
31
32 // double check dimensions of the tensors
33 if (inputTensorInfo.GetNumDimensions() != inputRank)
34 {
35 throw armnn::ParseException("The number of dimensions for input tensors "
36 "of the concatenation operator should be: " + std::to_string(inputRank));
37 }
38
39 for (unsigned int j = 0; j < concatAxis; ++j)
40 {
41 concatDescriptor.SetViewOriginCoord(inputIndex, j, 0);
42 }
43
44 concatDescriptor.SetViewOriginCoord(inputIndex, concatAxis, mergeDimOrigin);
45 mergeDimOrigin += inputTensorInfo.GetShape()[concatAxis];
46
47 for (unsigned int j = concatAxis + 1; j < inputRank; ++j)
48 {
49 concatDescriptor.SetViewOriginCoord(inputIndex, j, 0);
50 }
51}
52
53TfLiteStatus VisitConcatenationOperator(DelegateData& delegateData,
54 TfLiteContext* tfLiteContext,
55 TfLiteNode* tfLiteNode,
56 int nodeIndex,
57 int32_t tfLiteConcatOperatorCode)
58{
59 unsigned int numInputs = tfLiteNode->inputs->size;
60 if (numInputs < 2)
61 {
62 TF_LITE_MAYBE_KERNEL_LOG(
63 tfLiteContext, "TfLiteArmnnDelegate: Minimum number of inputs (%d != %d) in node #%d",
64 2, numInputs, nodeIndex);
65 return kTfLiteError;
66 }
67 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
68
69 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
70
71 std::vector<armnn::TensorInfo> inputTensorInfos;
72 for (unsigned int i = 0; i < numInputs; ++i)
73 {
74 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[i]];
75 if(!IsValid(&tfLiteInputTensor))
76 {
77 TF_LITE_MAYBE_KERNEL_LOG(
78 tfLiteContext,
79 "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
80 tfLiteConcatOperatorCode, nodeIndex);
81 return kTfLiteError;
82 }
83 if (IsDynamicTensor(tfLiteInputTensor))
84 {
85 TF_LITE_MAYBE_KERNEL_LOG(
86 tfLiteContext,
87 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
88 tfLiteConcatOperatorCode, nodeIndex);
89 return kTfLiteError;
90 }
91
92 armnn::TensorInfo inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
93 inputTensorInfos.emplace_back(inputTensorInfo);
94 }
95
96 // Convert input tensors to const armnn::TensorInfo* type for FORWARD_LAYER_SUPPORT_FUNC.
97 std::vector<const armnn::TensorInfo*> inputConstTensorInfos;
98 std::transform(inputTensorInfos.begin(),
99 inputTensorInfos.end(),
100 std::back_inserter(inputConstTensorInfos),
101 [](armnn::TensorInfo& t)->const armnn::TensorInfo*{ return &t; });
102
103 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
104 if(!IsValid(&tfLiteOutputTensor))
105 {
106 TF_LITE_MAYBE_KERNEL_LOG(
107 tfLiteContext,
108 "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ",
109 tfLiteConcatOperatorCode, nodeIndex);
110 return kTfLiteError;
111 }
112 if (IsDynamicTensor(tfLiteOutputTensor))
113 {
114 TF_LITE_MAYBE_KERNEL_LOG(
115 tfLiteContext,
116 "TfLiteArmnnDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
117 tfLiteConcatOperatorCode, nodeIndex);
118 return kTfLiteError;
119 }
120
121 // Setup OriginsDescriptor, axis and view origin
122 unsigned int numConcatView = static_cast<unsigned int>(numInputs);
123 uint32_t inputRank = tfLiteTensors[tfLiteNode->inputs->data[0]].dims->size;
124
125 auto* concatenationParameters = reinterpret_cast<TfLiteConcatenationParams*>(tfLiteNode->builtin_data);
126 const unsigned int concatDimInput = static_cast<unsigned int>(
127 (static_cast<int>(inputRank) + concatenationParameters->axis) % static_cast<int>(inputRank));
128
129 armnn::OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatView), inputRank);
130 concatDescriptor.SetConcatAxis(concatDimInput);
131
132 unsigned int mergeDimOrigin = 0;
133 for (unsigned int viewIndex = 0; viewIndex < numConcatView; ++viewIndex)
134 {
135 armnn::TensorInfo inputTensorInfo = GetTensorInfoForTfLiteTensor(
136 tfLiteTensors[tfLiteNode->inputs->data[viewIndex]]);
137
138 // Sets up concatDescriptor view origin
139 SetupConcatViewOrigin(inputTensorInfo, concatDescriptor, concatDimInput, viewIndex, mergeDimOrigin);
140 }
141
142 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor);
143
144 // Check if supported
145 bool isSupported = false;
146 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
147 {
148 FORWARD_LAYER_SUPPORT_FUNC(__func__,
149 tfLiteContext,
150 IsConcatSupported,
151 delegateData.m_Backends,
152 isSupported,
153 inputConstTensorInfos,
154 outputTensorInfo,
155 concatDescriptor);
156 };
157
158 if (!delegateData.m_Network)
159 {
160 validateFunc(outputTensorInfo, isSupported);
161 return isSupported ? kTfLiteOk : kTfLiteError;
162 }
163
164 // Setup layer and connect.
165 armnn::IConnectableLayer* concatenationLayer = delegateData.m_Network->AddConcatLayer(concatDescriptor);
166 ARMNN_ASSERT(concatenationLayer != nullptr);
167
168 armnn::IOutputSlot& outputSlot = concatenationLayer->GetOutputSlot(0);
169 outputSlot.SetTensorInfo(outputTensorInfo);
170 Connect(concatenationLayer, tfLiteNode, delegateData);
171
172 if (!concatenationParameters)
173 {
174 // No Activation
175 return kTfLiteOk;
176 }
177
178 // Check activation
179 TfLiteFusedActivation activationType = concatenationParameters->activation;
180 return FusedActivation(tfLiteContext, tfLiteNode, activationType, concatenationLayer, 0, delegateData);
181}
182
183TfLiteStatus VisitMeanOperator(DelegateData& delegateData,
184 TfLiteContext* tfLiteContext,
185 TfLiteNode* tfLiteNode,
186 int nodeIndex,
187 int32_t tfLiteMeanOperatorCode)
188{
189 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 2, nodeIndex));
190 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
191
192 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
193 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
194 if(!IsValid(&tfLiteInputTensor))
195 {
196 TF_LITE_MAYBE_KERNEL_LOG(
197 tfLiteContext,
198 "TfLiteArmnnDelegate: Invalid input tensor in operator #%d node #%d: ",
199 tfLiteMeanOperatorCode, nodeIndex);
200 return kTfLiteError;
201 }
202 if (IsDynamicTensor(tfLiteInputTensor))
203 {
204 TF_LITE_MAYBE_KERNEL_LOG(
205 tfLiteContext,
206 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
207 tfLiteMeanOperatorCode, nodeIndex);
208 return kTfLiteError;
209 }
210
211 const TfLiteTensor& tfLiteAxisTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
212 if(!IsValid(&tfLiteAxisTensor))
213 {
214 TF_LITE_MAYBE_KERNEL_LOG(
215 tfLiteContext,
216 "TfLiteArmnnDelegate: Invalid axis tensor in operator #%d node #%d: ",
217 tfLiteMeanOperatorCode, nodeIndex);
218 return kTfLiteError;
219 }
220 if (IsDynamicTensor(tfLiteAxisTensor))
221 {
222 TF_LITE_MAYBE_KERNEL_LOG(
223 tfLiteContext,
224 "TfLiteArmnnDelegate: Dynamic axis tensors are not supported in operator #%d node #%d: ",
225 tfLiteMeanOperatorCode, nodeIndex);
226 return kTfLiteError;
227 }
228
229 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
230 if(!IsValid(&tfLiteOutputTensor))
231 {
232 TF_LITE_MAYBE_KERNEL_LOG(
233 tfLiteContext,
234 "TfLiteArmnnDelegate: Invalid output tensor in operator #%d node #%d: ",
235 tfLiteAxisTensor, nodeIndex);
236 return kTfLiteError;
237 }
238 if (IsDynamicTensor(tfLiteOutputTensor))
239 {
240 TF_LITE_MAYBE_KERNEL_LOG(
241 tfLiteContext,
242 "TfLiteArmnnDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
243 tfLiteMeanOperatorCode, nodeIndex);
244 return kTfLiteError;
245 }
246
247 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
248 const armnn::TensorInfo& axisTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteAxisTensor);
249 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor);
250
251 auto* axisTensorData = tflite::GetTensorData<int32_t>(&tfLiteAxisTensor);
252
253 std::vector<int32_t> axis;
254 // Add axis data to vector to be converter to unsigned int and assigned to descriptor axis.
255 for (unsigned int i = 0; i < axisTensorInfo.GetNumElements(); ++i)
256 {
257 axis.emplace_back(axisTensorData[i]);
258 }
259
260 // Convert the axis to unsigned int and remove duplicates.
261 unsigned int rank = inputTensorInfo.GetNumDimensions();
262 std::set<unsigned int> uniqueAxis;
263 std::transform(axis.begin(),
264 axis.end(),
265 std::inserter(uniqueAxis, uniqueAxis.begin()),
266 [rank](int i)->unsigned int{ return (i + rank) % rank; });
267
268 // Setup MeanDescriptor and assign axis and keepDims
269 armnn::MeanDescriptor desc;
270 desc.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
271 desc.m_KeepDims = inputTensorInfo.GetNumDimensions() == outputTensorInfo.GetNumDimensions() ? true : false;
272
273 // Check if supported
274 bool isSupported = false;
275 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
276 {
277 FORWARD_LAYER_SUPPORT_FUNC(__func__,
278 tfLiteContext,
279 IsMeanSupported,
280 delegateData.m_Backends,
281 isSupported,
282 inputTensorInfo,
283 outputTensorInfo,
284 desc);
285 };
286
287 if (!delegateData.m_Network)
288 {
289 validateFunc(outputTensorInfo, isSupported);
290 return isSupported ? kTfLiteOk : kTfLiteError;
291 }
292
293 // Setup layer and connect.
294 armnn::IConnectableLayer* meanLayer = delegateData.m_Network->AddMeanLayer(desc);
295 ARMNN_ASSERT(meanLayer != nullptr);
296
297 armnn::IOutputSlot& outputSlot = meanLayer->GetOutputSlot(0);
298 outputSlot.SetTensorInfo(outputTensorInfo);
299 return Connect(meanLayer, tfLiteNode, delegateData);
300}
301
Sadik Armagan62483be2020-10-23 17:14:43 +0100302TfLiteStatus VisitControlOperator(DelegateData& delegateData,
303 TfLiteContext* tfLiteContext,
304 TfLiteNode* tfLiteNode,
305 int nodeIndex,
Matthew Sloyan91c41712020-11-13 09:47:35 +0000306 int32_t operatorCode)
Sadik Armagan62483be2020-10-23 17:14:43 +0100307{
Finn Williams6f9f9902020-11-13 13:23:15 +0000308 armnn::IgnoreUnused(delegateData,
309 tfLiteContext,
310 tfLiteNode,
311 nodeIndex,
Matthew Sloyan91c41712020-11-13 09:47:35 +0000312 operatorCode);
313
314 switch(operatorCode)
315 {
316 case kTfLiteBuiltinConcatenation:
317 return VisitConcatenationOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
318 case kTfLiteBuiltinMean:
319 return VisitMeanOperator(delegateData, tfLiteContext, tfLiteNode, nodeIndex, operatorCode);
320 default:
321 return kTfLiteError;
322 }
Sadik Armagan62483be2020-10-23 17:14:43 +0100323}
324
325} // namespace armnnDelegate