blob: ea4ebbf5bb9a32628de2cc53831596db4fabf57a [file] [log] [blame]
Tianle Cheng92ce35c2023-07-25 16:41:00 +01001//
2// Copyright © 2023 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/minimal_logging.h>
14#include <tensorflow/lite/kernels/internal/tensor_ctypes.h>
15#include <tensorflow/lite/schema/schema_generated.h>
16
17namespace armnnDelegate
18{
19TfLiteStatus ValidateTileOperator(DelegateData& delegateData,
20 TfLiteContext* tfLiteContext,
21 const armnn::TensorInfo& inputInfo,
22 const armnn::TensorInfo& outputInfo,
23 const armnn::TileDescriptor& descriptor)
24{
25 bool isSupported = false;
26 FORWARD_LAYER_SUPPORT_FUNC("TILE",
27 tfLiteContext,
28 IsTileSupported,
29 delegateData.m_Backends,
30 isSupported,
31 armnn::BackendId(),
32 inputInfo,
33 outputInfo,
34 descriptor);
35 return isSupported ? kTfLiteOk : kTfLiteError;
36}
37
38TfLiteStatus VisitTileOperator(DelegateData& delegateData,
39 TfLiteContext* tfLiteContext,
40 TfLiteNode* tfLiteNode,
41 int nodeIndex,
42 int32_t tileOperatorCode)
43{
44 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 2, nodeIndex));
45 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
46
47 const TfLiteTensor* tfLiteTensors = tfLiteContext->tensors;
48
49 // The input contains the data that should be tiled
50 const TfLiteTensor& tfLiteInputTensor = tfLiteTensors[tfLiteNode->inputs->data[0]];
51 if (IsDynamicTensor(tfLiteInputTensor))
52 {
53 TF_LITE_MAYBE_KERNEL_LOG(
54 tfLiteContext,
55 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
56 tileOperatorCode, nodeIndex);
57 return kTfLiteError;
58 }
59
60 // The multiples tensor contains the number of copies for each axis
61 const TfLiteTensor& tfLiteMultiplesTensor = tfLiteTensors[tfLiteNode->inputs->data[1]];
62 if (IsDynamicTensor(tfLiteMultiplesTensor))
63 {
64 TF_LITE_MAYBE_KERNEL_LOG(
65 tfLiteContext,
66 "TfLiteArmnnDelegate: Dynamic input tensors are not supported in operator #%d node #%d: ",
67 tileOperatorCode, nodeIndex);
68 return kTfLiteError;
69 }
70
71 // The output tensor
72 const TfLiteTensor& tfLiteOutputTensor = tfLiteTensors[tfLiteNode->outputs->data[0]];
73 if (IsDynamicTensor(tfLiteOutputTensor))
74 {
75 TF_LITE_MAYBE_KERNEL_LOG(
76 tfLiteContext,
77 "TfLiteArmnnDelegate: Dynamic output tensors are not supported in operator #%d node #%d: ",
78 tileOperatorCode, nodeIndex);
79 return kTfLiteError;
80 }
81
82 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteInputTensor);
83 const armnn::TensorInfo& multiplesTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteMultiplesTensor);
84 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteTensor(tfLiteOutputTensor);
85
86 // Multiples length must be the same as the number of dimension in input tensor
87 if (multiplesTensorInfo.GetNumElements() != inputTensorInfo.GetNumDimensions())
88 {
89 TF_LITE_MAYBE_KERNEL_LOG(
90 tfLiteContext,
91 "TfLiteArmnnDelegate: The Multiples length must be the same as the number of dimension in input tensor",
92 "Operator: #%d node #%d: ",
93 tileOperatorCode, nodeIndex);
94 return kTfLiteError;
95 }
96
97 // Get the Multiples data: In armnn, the values of the multiples input tensor is saved in the operator descriptor
98 // We have to read it from the input tensor and write it the descriptor
99 auto* multiplesTensorDataPtr = tflite::GetTensorData<int32_t>(&tfLiteMultiplesTensor);
100 auto multiplesTensorNum = tfLiteMultiplesTensor.dims->data[0];
101 std::vector<int32_t> multiplesIntData(multiplesTensorDataPtr, multiplesTensorDataPtr + multiplesTensorNum);
102
103 // The multiples must be positive
104 for (auto multiple : multiplesIntData)
105 {
106 if (multiple < 0)
107 {
108 TF_LITE_MAYBE_KERNEL_LOG(
109 tfLiteContext,
110 "TfLiteArmnnDelegate: The Multiples must be positive values",
111 "Operator: #%d node #%d: ",
112 tileOperatorCode, nodeIndex);
113 return kTfLiteError;
114 }
115 }
116
117 // The original input from TFLite is int32, and we have to make it as uint32 for our descriptor
118 std::vector<uint32_t> multiplesUintData;
119 std::transform(multiplesIntData.begin(),
120 multiplesIntData.end(),
121 std::back_inserter(multiplesUintData),
122 [] (const int value)
123 {
124 return static_cast<uint32_t>(value);
125 });
126
127 armnn::TileDescriptor tileDescriptor;
128 tileDescriptor.m_Multiples = multiplesUintData;
129
130 // Check output dimensions
131 if (inputTensorInfo.GetNumDimensions() != outputTensorInfo.GetNumDimensions())
132 {
133 TF_LITE_MAYBE_KERNEL_LOG(
134 tfLiteContext,
135 "TfLiteArmnnDelegate: Input tensor dimension and output tensor dimension differ",
136 "Operator: #%d node #%d: ",
137 tileOperatorCode, nodeIndex);
138 return kTfLiteError;
139 }
140
141 // No network pointer indicates that only support for this operator should be checked
142 if (!delegateData.m_Network)
143 {
144 return ValidateTileOperator(delegateData,
145 tfLiteContext,
146 inputTensorInfo,
147 outputTensorInfo,
148 tileDescriptor);
149 }
150
Mike Kelly07169c82023-08-02 13:23:09 +0100151 auto layerName = GetLayerName(armnn::LayerType::Tile, nodeIndex);
Tianle Cheng92ce35c2023-07-25 16:41:00 +0100152 armnn::IConnectableLayer* layer = delegateData.m_Network->AddTileLayer(tileDescriptor, layerName.c_str());
153
154 if (layer == nullptr)
155 {
156 return kTfLiteError;
157 }
158
159 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
160
Mike Kelly07169c82023-08-02 13:23:09 +0100161 if (ProcessInputs(layer, delegateData, tfLiteContext, tfLiteNode, nodeIndex) != kTfLiteOk)
Tianle Cheng92ce35c2023-07-25 16:41:00 +0100162 {
163 return kTfLiteError;
164 }
165
166 return Connect(layer, tfLiteNode, delegateData);
167}
168
169} // namespace armnnDelegate