blob: a2cdc83a6498ceb441688a8ca3994da97f2ede6d [file] [log] [blame]
Sadik Armagan62483be2020-10-23 17:14:43 +01001//
Mike Kelly04d82292023-01-19 18:29:40 +00002// Copyright © 2020-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 Sloyan81ec9942021-10-12 10:26:30 +01008#include <armnn_delegate.hpp>
9
Sadik Armagan62483be2020-10-23 17:14:43 +010010#include <armnn/ArmNN.hpp>
11#include <armnn/BackendHelper.hpp>
12#include <armnn/utility/Assert.hpp>
Sadik Armagan67e95f22020-10-29 16:14:54 +000013#include <armnn/utility/NumericCast.hpp>
Sadik Armagan62483be2020-10-23 17:14:43 +010014
Sadik Armagan6e36a642020-11-10 21:18:41 +000015#include <armnnUtils/Permute.hpp>
Ryan OSheaa544f0f2023-01-25 18:10:20 +000016#include <armnnUtils/TensorUtils.hpp>
Sadik Armagan6e36a642020-11-10 21:18:41 +000017
Sadik Armagan62483be2020-10-23 17:14:43 +010018#include <tensorflow/lite/builtin_ops.h>
19#include <tensorflow/lite/c/builtin_op_data.h>
20#include <tensorflow/lite/c/common.h>
21#include <tensorflow/lite/minimal_logging.h>
Matthew Sloyan11572322023-03-16 10:17:51 +000022#include <tensorflow/lite/kernels/kernel_util.h>
Sadik Armagan05e9fd22020-11-17 12:01:47 +000023
Matthew Sloyanc49aacc2023-04-28 17:27:26 +010024#include <numeric>
25
Sadik Armagan62483be2020-10-23 17:14:43 +010026namespace
27{
28
Sadik Armagan32ca1442020-11-13 17:51:56 +000029uint32_t NonNegative(int32_t value, int nodeIndex)
30{
31 if (value < 0)
32 {
Keith Davis892fafe2020-11-26 17:40:35 +000033 throw armnn::Exception(
Matthew Sloyan11572322023-03-16 10:17:51 +000034 "TfLiteArmnnDelegate: Non-negative value in node " + std::to_string(static_cast<int>(nodeIndex)));
Sadik Armagan32ca1442020-11-13 17:51:56 +000035 }
36 else
37 {
38 return static_cast<uint32_t>(value);
39 }
40}
41
Ryan OSheaa544f0f2023-01-25 18:10:20 +000042void ExpandTensorRankToEqual(armnn::TensorInfo& inputInfo0,
43 armnn::TensorInfo& inputInfo1)
Sadik Armagan67e95f22020-10-29 16:14:54 +000044{
45 unsigned int inputDimensions0 = inputInfo0.GetNumDimensions();
46 unsigned int inputDimensions1 = inputInfo1.GetNumDimensions();
47
48 if (inputDimensions0 == inputDimensions1)
49 {
Ryan OSheaa544f0f2023-01-25 18:10:20 +000050 return;
Sadik Armagan67e95f22020-10-29 16:14:54 +000051 }
52
53 unsigned int biggerInputDimensions = std::max(inputDimensions0, inputDimensions1);
Sadik Armagan67e95f22020-10-29 16:14:54 +000054
55 bool input0IsSmaller = inputDimensions0 < inputDimensions1;
Ryan OSheaa544f0f2023-01-25 18:10:20 +000056 armnn::TensorInfo& smallInfo = input0IsSmaller ? inputInfo0 : inputInfo1;
57 const armnn::TensorShape& newShape = armnnUtils::ExpandDimsToRank(smallInfo.GetShape(), biggerInputDimensions);
Sadik Armagan67e95f22020-10-29 16:14:54 +000058
Ryan OSheaa544f0f2023-01-25 18:10:20 +000059 smallInfo.SetShape(newShape);
Narumol Prangnawarat7684b182021-08-12 14:48:15 +010060}
61
Sadik Armagan32ca1442020-11-13 17:51:56 +000062void CalcPadding(uint32_t inputSize,
63 uint32_t filterSize,
64 uint32_t stride,
65 uint32_t dilation,
66 uint32_t& paddingFront,
67 uint32_t& paddingBack,
68 TfLitePadding padding)
69{
70 paddingFront = 0;
71 paddingBack = 0;
72 if (padding == kTfLitePaddingSame)
73 {
74 uint32_t outputSize = (inputSize + stride - 1) / stride;
75 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
76 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
77 if (temp > inputSize)
78 {
79 paddingFront = (temp - inputSize) / 2;
80 paddingBack = (temp - inputSize) - paddingFront;
81 }
82 }
83}
84
Matthew Sloyanc52190a2023-05-08 11:33:55 +010085// Function that calculates explicit padding when the output shape is known.
86// At the moment the output is only given as an input parameter in Transpose Convolution,
87// not in Convolution and Depthwise Convolution
88void CalcPadding(uint32_t inputSize,
89 uint32_t filterSize,
90 uint32_t stride,
91 uint32_t dilation,
92 uint32_t& paddingFront,
93 uint32_t& paddingBack,
94 TfLitePadding padding,
95 uint32_t outputSize)
96{
97 armnn::IgnoreUnused(dilation);
98 paddingFront = 0;
99 paddingBack = 0;
100 if (padding == kTfLitePaddingSame)
101 {
102 uint32_t totalPadding = (inputSize - 1) * stride + filterSize - outputSize;
103 paddingFront = totalPadding / 2;
104 paddingBack = totalPadding - paddingFront;
105 }
106}
107
Matthew Sloyand30bfb52021-04-18 16:40:00 +0100108unsigned int ComputeWrappedIndex(int index, unsigned int numDimensions)
109{
110 int numDims = armnn::numeric_cast<int>(numDimensions);
111 int wrappedIndex = index < 0 ? numDims + index : index;
Ryan OSheac229b3f2023-06-27 22:34:54 +0100112
113 if (wrappedIndex < 0 || wrappedIndex >= numDims)
114 {
115 throw armnn::ParseException("Unable to compute wrapped index");
116 }
Matthew Sloyand30bfb52021-04-18 16:40:00 +0100117
118 return static_cast<unsigned int>(wrappedIndex);
119};
120
Jim Flynn4b2f3472021-10-13 21:20:07 +0100121bool AreAllSigned32(const armnn::TensorInfo& inputInfo1,
122 const armnn::TensorInfo& inputInfo2,
123 const armnn::TensorInfo& outputInfo)
124{
125 return (armnn::DataType::Signed32 == inputInfo1.GetDataType()) &&
126 (armnn::DataType::Signed32 == inputInfo2.GetDataType()) &&
127 (armnn::DataType::Signed32 == outputInfo.GetDataType());
128}
129
Sadik Armagan90a119b2022-08-05 16:12:49 +0100130void UpdateConstantTensorOutputs(const armnn::TensorInfo& inputInfo, armnn::TensorInfo& outputInfo)
131{
132 // If input tensor info is constant and output tensor info shape is not specified
133 // set the output shape from input shape
134 if (inputInfo.IsConstant() && outputInfo.GetShape().GetDimensionality() == armnn::Dimensionality::NotSpecified)
135 {
136 outputInfo.SetShape(inputInfo.GetShape());
137 }
Sadik Armagan90a119b2022-08-05 16:12:49 +0100138}
139
Matthew Sloyan2b04ec32023-04-26 11:42:46 +0100140void SetupConcatViewOrigin(const armnn::TensorInfo& inputTensorInfo,
141 armnn::OriginsDescriptor& concatDescriptor,
142 const unsigned int concatAxis,
143 unsigned int inputIndex,
144 unsigned int& mergeDimOrigin)
145{
146 const uint32_t inputRank = concatDescriptor.GetNumDimensions();
147
148 // double check dimensions of the tensors
149 if (inputTensorInfo.GetNumDimensions() != inputRank)
150 {
151 throw armnn::ParseException("The number of dimensions for input tensors "
152 "of the concatenation operator should be: " + std::to_string(inputRank));
153 }
154
155 for (unsigned int j = 0; j < concatAxis; ++j)
156 {
157 concatDescriptor.SetViewOriginCoord(inputIndex, j, 0);
158 }
159
160 concatDescriptor.SetViewOriginCoord(inputIndex, concatAxis, mergeDimOrigin);
161 mergeDimOrigin += inputTensorInfo.GetShape()[concatAxis];
162
163 for (unsigned int j = concatAxis + 1; j < inputRank; ++j)
164 {
165 concatDescriptor.SetViewOriginCoord(inputIndex, j, 0);
166 }
167}
168
Matthew Sloyanc49aacc2023-04-28 17:27:26 +0100169TfLiteStatus CreateOutputTensorShape(const armnn::TensorInfo& inputTensorInfo,
170 const std::vector<int32_t>& targetShape,
171 armnn::ReshapeDescriptor& reshapeDesc)
172{
173 std::vector<unsigned int> outputDims(targetShape.begin(), targetShape.end());
174 const auto stretchDim = std::find(targetShape.begin(), targetShape.end(), -1);
175
176 if (stretchDim != targetShape.end())
177 {
178 if (std::find(std::next(stretchDim), targetShape.end(), -1) != targetShape.end())
179 {
180 // Return kTfLiteError and log the error after returning
181 return kTfLiteError;
182 }
183
184 auto targetNumElements =
185 armnn::numeric_cast<unsigned int>(
186 std::accumulate(targetShape.begin(), targetShape.end(), -1, std::multiplies<int32_t>()));
187
188 auto stretchIndex = static_cast<size_t>(std::distance(targetShape.begin(), stretchDim));
Tianle Cheng20773482023-10-03 12:01:11 +0100189
190 if (targetNumElements == 0)
191 {
192 // To handle the edge case that input and output both have zero elements
193 outputDims[stretchIndex] = 0;
194 }
195 else
196 {
197 outputDims[stretchIndex] = inputTensorInfo.GetNumElements() / targetNumElements;
198 }
Matthew Sloyanc49aacc2023-04-28 17:27:26 +0100199 }
200
201 armnn::TensorShape outputShape = armnn::TensorShape(static_cast<unsigned int>(outputDims.size()),
202 outputDims.data());
203 reshapeDesc.m_TargetShape = outputShape;
204 return kTfLiteOk;
205}
206
Matthew Sloyan3504e422023-05-03 13:53:02 +0100207armnn::TensorInfo OutputShapeOfSqueeze(std::vector<uint32_t> squeezeDims,
208 const armnn::TensorInfo& inputTensorInfo)
209{
210 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
211
212 if (inputTensorInfo.GetNumDimensions() > 4)
213 {
214 std::stringstream ss;
215 ss << "Input tensor has unexpected number of dimensions:"
216 << inputTensorInfo.GetNumDimensions()
217 << " shape:" << inputTensorInfo.GetShape()
218 << " "
219 << CHECK_LOCATION().AsString();
220 throw armnn::ParseException(ss.str());
221 }
222
223 if (squeezeDims.empty())
224 {
225 squeezeDims.assign(dimensionSequence, dimensionSequence + inputTensorInfo.GetNumDimensions());
226 }
227
228 std::vector<uint32_t> outputDims;
229 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
230 {
231 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
232 auto currentDimension = inputTensorInfo.GetShape()[i];
233 if (skipSqueeze || currentDimension != 1)
234 {
235 outputDims.push_back(currentDimension);
236 }
237 }
238
239 if (outputDims.size() > 4)
240 {
241 std::stringstream ss;
242 ss << "Output tensor has unexpected number of dimensions:"
243 << inputTensorInfo.GetNumDimensions()
244 << " shape:" << inputTensorInfo.GetShape()
245 << " "
246 << CHECK_LOCATION().AsString();
247 throw armnn::ParseException(ss.str());
248 }
249
250 armnn::TensorShape outShape = armnn::TensorShape(static_cast<unsigned int>(outputDims.size()), outputDims.data());
251
252 // We need to preserve the tensor type and the quantization data as well
253 armnn::TensorInfo outTensorInfo = inputTensorInfo;
254 outTensorInfo.SetShape(outShape);
255
256 return outTensorInfo;
257}
258
Sadik Armagan62483be2020-10-23 17:14:43 +0100259} // namespace anonymous