• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
8 #include <armnn/utility/IgnoreUnused.hpp>
9 
10 #include <tensorflow/lite/builtin_ops.h>
11 #include <tensorflow/lite/c/builtin_op_data.h>
12 #include <tensorflow/lite/c/common.h>
13 #include <tensorflow/lite/kernels/internal/tensor_ctypes.h>
14 #include <tensorflow/lite/minimal_logging.h>
15 
16 #include <algorithm>
17 #include <iterator>
18 #include <string>
19 #include <vector>
20 
21 namespace armnnDelegate
22 {
23 
SetupConcatViewOrigin(const armnn::TensorInfo & inputTensorInfo,armnn::OriginsDescriptor & concatDescriptor,const unsigned int concatAxis,unsigned int inputIndex,unsigned int & mergeDimOrigin)24 void 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 
VisitConcatenationOperator(DelegateData & delegateData,TfLiteContext * tfLiteContext,TfLiteNode * tfLiteNode,int nodeIndex,int32_t tfLiteConcatOperatorCode)53 TfLiteStatus 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 
VisitMeanOperator(DelegateData & delegateData,TfLiteContext * tfLiteContext,TfLiteNode * tfLiteNode,int nodeIndex,int32_t tfLiteMeanOperatorCode)183 TfLiteStatus 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 
VisitControlOperator(DelegateData & delegateData,TfLiteContext * tfLiteContext,TfLiteNode * tfLiteNode,int nodeIndex,int32_t operatorCode)302 TfLiteStatus VisitControlOperator(DelegateData& delegateData,
303                                   TfLiteContext* tfLiteContext,
304                                   TfLiteNode* tfLiteNode,
305                                   int nodeIndex,
306                                   int32_t operatorCode)
307 {
308     armnn::IgnoreUnused(delegateData,
309                         tfLiteContext,
310                         tfLiteNode,
311                         nodeIndex,
312                         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     }
323 }
324 
325 } // namespace armnnDelegate
326