blob: 2c2908371928417b49c981b85abd276f08a37350 [file] [log] [blame]
Sadik Armagan62483be2020-10-23 17:14:43 +01001//
Ryan OShea4c231de2023-01-17 15:19:20 +00002// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved.
Sadik Armagan62483be2020-10-23 17:14:43 +01003// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
Matthew Sloyan11572322023-03-16 10:17:51 +00008#include <ClassicDelegateUtils.hpp>
David Monahan1670b0c2020-11-18 14:40:27 +00009
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>
13#include <tensorflow/lite/minimal_logging.h>
14
15namespace armnnDelegate
16{
17
Sadik Armagan937565b2021-04-21 14:03:28 +010018TfLiteStatus VisitCastOperator(DelegateData& delegateData,
19 TfLiteContext* tfLiteContext,
20 TfLiteNode* tfLiteNode,
21 int nodeIndex,
22 int32_t operatorCode)
23{
24 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
25 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
26
27 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
28 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
29 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
30 {
31 return kTfLiteError;
32 }
33
34 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
35 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
36 {
37 return kTfLiteError;
38 }
39
40 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
Sadik Armagan90a119b2022-08-05 16:12:49 +010041 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
Sadik Armagan937565b2021-04-21 14:03:28 +010042
43 bool isSupported = false;
Cathal Corbett53837672022-09-01 11:34:37 +010044 armnn::BackendId setBackend;
Sadik Armagan937565b2021-04-21 14:03:28 +010045 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
46 {
Sadik Armaganbfa767c2022-02-09 14:58:03 +000047 FORWARD_LAYER_SUPPORT_FUNC("CAST",
Sadik Armagan937565b2021-04-21 14:03:28 +010048 tfLiteContext,
49 IsCastSupported,
50 delegateData.m_Backends,
51 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +010052 setBackend,
Sadik Armagan937565b2021-04-21 14:03:28 +010053 inputTensorInfo,
54 outInfo);
55 };
56
57 // If the m_Network is a nullptr, this signals that a prerequisite TfLite callback is required to clarify the
58 // support for the operator
59 // If supported, VisitCastOperator will be called again to add the layer to the network as seen further below
60 if (!delegateData.m_Network)
61 {
62 validateFunc(outputTensorInfo, isSupported);
63 return isSupported ? kTfLiteOk : kTfLiteError;
64 }
65
66 // Add a Cast layer
67 armnn::IConnectableLayer* layer = delegateData.m_Network->AddCastLayer();
Cathal Corbett53837672022-09-01 11:34:37 +010068 layer->SetBackendId(setBackend);
Sadik Armagan937565b2021-04-21 14:03:28 +010069 ARMNN_ASSERT(layer != nullptr);
70
71 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
72 outputSlot.SetTensorInfo(outputTensorInfo);
73
Ryan OShea4c231de2023-01-17 15:19:20 +000074 // try to connect the Constant Inputs if there are any
75 if(ProcessInputs(layer,delegateData, tfLiteContext, tfLiteNode) != kTfLiteOk )
76 {
77 return kTfLiteError;
78 }
79
Sadik Armagan937565b2021-04-21 14:03:28 +010080 // Connect
81 return Connect(layer, tfLiteNode, delegateData);
82}
83
Sadik Armagan62483be2020-10-23 17:14:43 +010084TfLiteStatus VisitReshapeOperator(DelegateData& delegateData,
85 TfLiteContext* tfLiteContext,
86 TfLiteNode* tfLiteNode,
87 int nodeIndex,
88 int32_t operatorCode)
89{
David Monahan1670b0c2020-11-18 14:40:27 +000090 auto numInputs = tfLiteNode->inputs->size;
Finn Williams6f9f9902020-11-13 13:23:15 +000091
David Monahan1670b0c2020-11-18 14:40:27 +000092 if (numInputs == 2)
93 {
94 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 2, nodeIndex));
95 }
96 else
97 {
98 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
99 }
100 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
101
102 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
103 const TfLiteTensor& tfLiteInputTensor0 = tfLiteTensors[tfLiteNode->inputs->data[0]];
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000104 if (!IsValid(tfLiteContext, tfLiteInputTensor0, operatorCode, nodeIndex))
David Monahan1670b0c2020-11-18 14:40:27 +0000105 {
David Monahan1670b0c2020-11-18 14:40:27 +0000106 return kTfLiteError;
107 }
108
109 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000110 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
David Monahan1670b0c2020-11-18 14:40:27 +0000111 {
David Monahan1670b0c2020-11-18 14:40:27 +0000112 return kTfLiteError;
113 }
114
115 const armnn::TensorInfo& inputTensorInfo0 = GetTensorInfoForTfLiteTensor(tfLiteInputTensor0);
Sadik Armagan90a119b2022-08-05 16:12:49 +0100116 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor, true);
David Monahan1670b0c2020-11-18 14:40:27 +0000117
118 armnn::ReshapeDescriptor reshapeDesc;
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000119 std::vector<int32_t> targetShape;
Finn Williamsf806c4d2021-02-22 15:13:12 +0000120
121 TfLiteReshapeParams* reshapeOptions = reinterpret_cast<TfLiteReshapeParams*>(tfLiteNode->builtin_data);
David Monahan1670b0c2020-11-18 14:40:27 +0000122
123 // The new shape can be defined by either a second input tensor or by a builtin option, we need to check for both.
Finn Williamsf806c4d2021-02-22 15:13:12 +0000124 // Options might be set without valid data. we need to check the dimensions are in a valid range.
125 if (reshapeOptions && reshapeOptions->num_dimensions > 0 && reshapeOptions->num_dimensions <= 8)
126 {
127 for (int i=0; i < reshapeOptions->num_dimensions; ++i)
128 {
129 targetShape.push_back(reshapeOptions->shape[i]);
130 }
131 }
132 else if (numInputs == 2)
David Monahan1670b0c2020-11-18 14:40:27 +0000133 {
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000134 // Get shape from the second input tensor
135 const TfLiteTensor& tfLiteShapeInputTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000136 if (!IsValid(tfLiteContext, tfLiteShapeInputTensor, operatorCode, nodeIndex))
David Monahane03d9c22020-11-20 09:58:54 +0000137 {
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000138 return kTfLiteError;
139 }
140
141 if (tfLiteShapeInputTensor.dims->size != 1)
142 {
143 TF_LITE_MAYBE_KERNEL_LOG(tfLiteContext,
144 "TfLiteArmnnDelegate: Target 'shape' input is not a 1D tensor in "
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000145 "operator #%d node #%d: Falling back to TfLiteOptions.",
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000146 operatorCode, nodeIndex);
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000147 }
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000148 else
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000149 {
Matthew Sloyanf00f6c22020-12-07 13:33:24 +0000150 // Get the shape data out of the input tensor
151 auto* shapeTensorDataPtr = tflite::GetTensorData<int32_t>(&tfLiteShapeInputTensor);
152 auto shapeTensorNumValues = tfLiteShapeInputTensor.dims->data[0];
153 for (auto i=0; i < shapeTensorNumValues; ++i)
154 {
155 targetShape.push_back(*(shapeTensorDataPtr+i));
156 }
David Monahane03d9c22020-11-20 09:58:54 +0000157 }
158 }
Finn Williamsf806c4d2021-02-22 15:13:12 +0000159 else
David Monahane03d9c22020-11-20 09:58:54 +0000160 {
Finn Williamsf806c4d2021-02-22 15:13:12 +0000161 TF_LITE_MAYBE_KERNEL_LOG(tfLiteContext,
162 "Target shape not defined in reshape parameters or input tensor. "
163 "At least one method required in operator #%d node #%d: ",
164 operatorCode, nodeIndex);
165 return kTfLiteError;
David Monahan1670b0c2020-11-18 14:40:27 +0000166 }
David Monahane03d9c22020-11-20 09:58:54 +0000167
168 // Use the data to create the required tensor shape.
169 if (CreateOutputTensorShape(inputTensorInfo0, targetShape, reshapeDesc) != kTfLiteOk)
David Monahan1670b0c2020-11-18 14:40:27 +0000170 {
171 TF_LITE_MAYBE_KERNEL_LOG(tfLiteContext,
David Monahane03d9c22020-11-20 09:58:54 +0000172 "TfLiteArmnnDelegate: At most one component of shape can be -1 in: "
173 "operator #%d node #%d: ",
David Monahan1670b0c2020-11-18 14:40:27 +0000174 operatorCode, nodeIndex);
David Monahane03d9c22020-11-20 09:58:54 +0000175 return kTfLiteError;
176 }
177
178 if (reshapeDesc.m_TargetShape.GetNumElements() != inputTensorInfo0.GetNumElements())
179 {
Narumol Prangnawarat7f6c6672020-11-24 18:40:42 +0000180 TF_LITE_MAYBE_KERNEL_LOG(
181 tfLiteContext,
182 "TfLiteArmnnDelegate: Reshape, number of elements in output shape does not match input "
183 "operator #%d node #%d: ",
184 operatorCode, nodeIndex);
David Monahane03d9c22020-11-20 09:58:54 +0000185 return kTfLiteError;
David Monahan1670b0c2020-11-18 14:40:27 +0000186 }
187
188 bool isSupported = false;
Cathal Corbett53837672022-09-01 11:34:37 +0100189 armnn::BackendId setBackend;
David Monahan1670b0c2020-11-18 14:40:27 +0000190 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
191 {
Sadik Armaganbfa767c2022-02-09 14:58:03 +0000192 FORWARD_LAYER_SUPPORT_FUNC("RESHAPE",
David Monahan1670b0c2020-11-18 14:40:27 +0000193 tfLiteContext,
194 IsReshapeSupported,
195 delegateData.m_Backends,
196 isSupported,
Cathal Corbett53837672022-09-01 11:34:37 +0100197 setBackend,
David Monahan1670b0c2020-11-18 14:40:27 +0000198 inputTensorInfo0,
199 outInfo,
200 reshapeDesc);
201 };
202
203 if (!delegateData.m_Network)
204 {
205 validateFunc(outputTensorInfo, isSupported);
206 return isSupported ? kTfLiteOk : kTfLiteError;
207 }
208
209 armnn::IConnectableLayer* layer = delegateData.m_Network->AddReshapeLayer(reshapeDesc);
Cathal Corbett53837672022-09-01 11:34:37 +0100210 layer->SetBackendId(setBackend);
David Monahan1670b0c2020-11-18 14:40:27 +0000211 ARMNN_ASSERT(layer != nullptr);
212
213 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
214 outputSlot.SetTensorInfo(outputTensorInfo);
215
Ryan OShea4c231de2023-01-17 15:19:20 +0000216 // try to connect the Constant Inputs if there are any
217 if(ProcessInputs(layer,delegateData, tfLiteContext, tfLiteNode) != kTfLiteOk )
218 {
219 return kTfLiteError;
220 }
221
David Monahan1670b0c2020-11-18 14:40:27 +0000222 // Connect
223 return Connect(layer, tfLiteNode, delegateData);
Sadik Armagan62483be2020-10-23 17:14:43 +0100224}
225
226TfLiteStatus VisitSqueezeOperator(DelegateData& delegateData,
227 TfLiteContext* tfLiteContext,
228 TfLiteNode* tfLiteNode,
229 int nodeIndex,
230 int32_t operatorCode)
231{
Matthew Sloyan3504e422023-05-03 13:53:02 +0100232 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
233 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
Finn Williams6f9f9902020-11-13 13:23:15 +0000234
Matthew Sloyan3504e422023-05-03 13:53:02 +0100235 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
236 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
237 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
238 {
239 return kTfLiteError;
240 }
241
242 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
243 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
244 {
245 return kTfLiteError;
246 }
247
248 auto* options = reinterpret_cast<TfLiteSqueezeParams*>(tfLiteNode->builtin_data);
249
250 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
251
252 std::vector<uint32_t> squeezeDim;
253 // A single negative dim index is interpreted as a negative index in python
254 // Meaning the index will be the shape size plus the negative index value
255 if (options->num_squeeze_dims == 1 && options->squeeze_dims[0] < 0)
256 {
257 int32_t dim = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions()) + options->squeeze_dims[0];
258 squeezeDim.push_back(static_cast<uint32_t>(dim));
259 }
260 else
261 {
262 for (int32_t i = 0; i < options->num_squeeze_dims; ++i)
263 {
264 squeezeDim.push_back(static_cast<uint32_t>(options->squeeze_dims[i]));
265 }
266 }
267
268 armnn::TensorInfo outputTensorInfo = OutputShapeOfSqueeze(squeezeDim, inputTensorInfo);
269
270 armnn::ReshapeDescriptor reshapeDesc;
271 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
272
273 bool isSupported = false;
274 armnn::BackendId setBackend;
275 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
276 {
277 FORWARD_LAYER_SUPPORT_FUNC("SQUEEZE",
278 tfLiteContext,
279 IsReshapeSupported,
280 delegateData.m_Backends,
281 isSupported,
282 setBackend,
283 inputTensorInfo,
284 outInfo,
285 reshapeDesc);
286 };
287
288 if (!delegateData.m_Network)
289 {
290 validateFunc(outputTensorInfo, isSupported);
291 return isSupported ? kTfLiteOk : kTfLiteError;
292 }
293
294 armnn::IConnectableLayer* layer = delegateData.m_Network->AddReshapeLayer(reshapeDesc);
295 layer->SetBackendId(setBackend);
296 ARMNN_ASSERT(layer != nullptr);
297
298 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
299 outputSlot.SetTensorInfo(outputTensorInfo);
300
301 // try to connect the Constant Inputs if there are any
302 if(ProcessInputs(layer, delegateData, tfLiteContext, tfLiteNode) != kTfLiteOk)
303 {
304 return kTfLiteError;
305 }
306
307 // Connect
308 return Connect(layer, tfLiteNode, delegateData);
Sadik Armagan62483be2020-10-23 17:14:43 +0100309}
310
311TfLiteStatus VisitExpandDimsOperator(DelegateData& delegateData,
312 TfLiteContext* tfLiteContext,
313 TfLiteNode* tfLiteNode,
314 int nodeIndex,
315 int32_t operatorCode)
316{
Matthew Sloyan3504e422023-05-03 13:53:02 +0100317 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 2, nodeIndex));
318 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
Finn Williams6f9f9902020-11-13 13:23:15 +0000319
Matthew Sloyan3504e422023-05-03 13:53:02 +0100320 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
321 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
322 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
323 {
324 return kTfLiteError;
325 }
326
327 const TfLiteTensor& tfLiteAxisTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
328 if (!IsValid(tfLiteContext, tfLiteAxisTensor, operatorCode, nodeIndex))
329 {
330 return kTfLiteError;
331 }
332
333 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
334 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
335 {
336 return kTfLiteError;
337 }
338
339 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
340 armnn::TensorInfo outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor);
341
342 auto* axisTensorData = tflite::GetTensorData<int32_t>(&tfLiteAxisTensor);
343 int32_t axis = axisTensorData[0];
344
345 int32_t inputDimSize = static_cast<int32_t>(inputTensorInfo.GetShape().GetNumDimensions());
346 if (axis > inputDimSize || axis < 0 - (inputDimSize + 1))
347 {
348 TF_LITE_MAYBE_KERNEL_LOG(
349 tfLiteContext,
350 "TfLiteArmnnOpaqueDelegate: Axis must be in range "
351 "[0 - (inputDimSize + 1), inputDimSize] inclusive.");
352 return kTfLiteError;
353 }
354
355 if(axis < 0)
356 {
357 axis = inputDimSize + axis + 1;
358 }
359
360 std::vector<unsigned int> shape(static_cast<unsigned int>(inputDimSize) + 1);
361 unsigned int inputShapeIndex = 0;
362 for (unsigned int i = 0; i < static_cast<unsigned int>(inputDimSize + 1); ++i)
363 {
364 if (i == static_cast<unsigned int>(axis))
365 {
366 shape[i] = 1;
367 }
368 else
369 {
370 shape[i] = inputTensorInfo.GetShape()[inputShapeIndex];
371 ++inputShapeIndex;
372 }
373 }
374
375 armnn::ReshapeDescriptor reshapeDesc;
376 reshapeDesc.m_TargetShape = armnn::TensorShape(static_cast<unsigned int>(inputDimSize + 1), shape.data());
377
378 bool isSupported = false;
379 armnn::BackendId setBackend;
380 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
381 {
382 FORWARD_LAYER_SUPPORT_FUNC("EXPAND_DIMS",
383 tfLiteContext,
384 IsReshapeSupported,
385 delegateData.m_Backends,
386 isSupported,
387 setBackend,
388 inputTensorInfo,
389 outInfo,
390 reshapeDesc);
391 };
392
393 if (!delegateData.m_Network)
394 {
395 validateFunc(outputTensorInfo, isSupported);
396 return isSupported ? kTfLiteOk : kTfLiteError;
397 }
398
399 armnn::IConnectableLayer* layer = delegateData.m_Network->AddReshapeLayer(reshapeDesc);
400 layer->SetBackendId(setBackend);
401 ARMNN_ASSERT(layer != nullptr);
402
403 armnn::IOutputSlot& outputSlot = layer->GetOutputSlot(0);
404 outputTensorInfo.SetShape(reshapeDesc.m_TargetShape);
405 outputSlot.SetTensorInfo(outputTensorInfo);
406
407 // try to connect the Constant Inputs if there are any
408 if(ProcessInputs(layer, delegateData, tfLiteContext, tfLiteNode) != kTfLiteOk)
409 {
410 return kTfLiteError;
411 }
412
413 // Connect
414 return Connect(layer, tfLiteNode, delegateData);
Sadik Armagan62483be2020-10-23 17:14:43 +0100415}
416
417} // namespace armnnDelegate