blob: 517b11ced8aa303de9a4af395f5d2e9845b479e7 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Teresa Charlinec5f7d12021-10-22 17:15:00 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +00005#include <aclCommon/ArmComputeTensorUtils.hpp>
6#include <aclCommon/ArmComputeUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +00007
Francis Murtagh351d13d2018-09-24 15:01:18 +01008#include "armnn/Exceptions.hpp"
telsoa014fcda012018-03-09 14:13:49 +00009#include <armnn/Descriptors.hpp>
10
Cathal Corbett4b19d222022-05-11 20:12:17 +010011#include <fmt/format.h>
12
telsoa014fcda012018-03-09 14:13:49 +000013namespace armnn
14{
15namespace armcomputetensorutils
16{
17
Derek Lambertid466a542020-01-22 15:37:29 +000018arm_compute::DataType GetArmComputeDataType(armnn::DataType dataType, bool multiScales)
telsoa014fcda012018-03-09 14:13:49 +000019{
20 switch(dataType)
21 {
Narumol Prangnawarat250d3922020-03-30 16:11:04 +010022 case armnn::DataType::BFloat16:
23 return arm_compute::DataType::BFLOAT16;
Mike Kelly130ec602019-11-08 12:08:35 +000024 case armnn::DataType::Boolean:
25 return arm_compute::DataType::U8;
telsoa01c577f2c2018-08-31 09:22:23 +010026 case armnn::DataType::Float16:
27 return arm_compute::DataType::F16;
telsoa014fcda012018-03-09 14:13:49 +000028 case armnn::DataType::Float32:
telsoa014fcda012018-03-09 14:13:49 +000029 return arm_compute::DataType::F32;
Ryan OShea9add1202020-02-07 10:06:33 +000030 case armnn::DataType::QAsymmS8:
31 return arm_compute::DataType::QASYMM8_SIGNED;
Derek Lambertif90c56d2020-01-10 17:14:08 +000032 case armnn::DataType::QAsymmU8:
telsoa014fcda012018-03-09 14:13:49 +000033 return arm_compute::DataType::QASYMM8;
Derek Lambertif90c56d2020-01-10 17:14:08 +000034 case armnn::DataType::QSymmS16:
Aron Virginas-Tar7a3e2fe2019-06-27 18:54:47 +010035 return arm_compute::DataType::QSYMM16;
Inki Daed4619e22020-09-10 15:33:54 +090036 case armnn::DataType::Signed64:
37 return arm_compute::DataType::S64;
Finn Williamsfd271062019-12-04 14:27:27 +000038 case armnn::DataType::QSymmS8:
Derek Lambertid466a542020-01-22 15:37:29 +000039 {
40 return multiScales ? arm_compute::DataType::QSYMM8_PER_CHANNEL : arm_compute::DataType::QSYMM8;
41 }
telsoa014fcda012018-03-09 14:13:49 +000042 case armnn::DataType::Signed32:
telsoa014fcda012018-03-09 14:13:49 +000043 return arm_compute::DataType::S32;
telsoa014fcda012018-03-09 14:13:49 +000044 default:
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010045 ARMNN_ASSERT_MSG(false, "Unknown data type");
telsoa014fcda012018-03-09 14:13:49 +000046 return arm_compute::DataType::UNKNOWN;
telsoa014fcda012018-03-09 14:13:49 +000047 }
48}
49
Cathal Corbettfd5bec42022-03-03 15:13:23 +000050armnn::DataType GetArmNNDataType(arm_compute::DataType dataType)
51{
52 switch(dataType)
53 {
54 case arm_compute::DataType::BFLOAT16:
55 return armnn::DataType::BFloat16;
56 case arm_compute::DataType::U8:
57 return armnn::DataType::Boolean;
58 case arm_compute::DataType::F16:
59 return armnn::DataType::Float16;
60 case arm_compute::DataType::F32:
61 return armnn::DataType::Float32;
62 case arm_compute::DataType::QASYMM8_SIGNED:
63 return armnn::DataType::QAsymmS8;
64 case arm_compute::DataType::QASYMM8:
65 return armnn::DataType::QAsymmU8;
66 case arm_compute::DataType::QSYMM16:
67 return armnn::DataType::QSymmS16;
68 case arm_compute::DataType::S64:
69 return armnn::DataType::Signed64;
70 case arm_compute::DataType::QSYMM8_PER_CHANNEL:
71 return armnn::DataType::QSymmS8;
72 case arm_compute::DataType::QSYMM8:
73 return armnn::DataType::QSymmS8;
74 case arm_compute::DataType::S32:
75 return armnn::DataType::Signed32;
76 default:
77 ARMNN_ASSERT_MSG(false, "Unknown data type");
78 return armnn::DataType::Float32;
79 }
80}
81
Matthew Benthamfd899962018-12-31 15:49:42 +000082arm_compute::Coordinates BuildArmComputeReductionCoordinates(size_t inputDimensions,
83 unsigned int originalInputRank,
84 const std::vector<unsigned int>& armnnAxes)
85{
86 arm_compute::Coordinates outAclCoords;
87
88 if (armnnAxes.empty())
89 {
90 // If no reduction axes were provided, then the input must be reduced along all dimensions.
91 // Since Compute Library does not accept an empty vector as the reduction dimensions, we then
92 // manually create a vector including all the input dimensions (in reversed order) as:
93 //
94 // { inputDimensions - 1, inputDimensions - 2, ..., 1, 0 }
95 //
96 outAclCoords.set_num_dimensions(inputDimensions);
97 std::generate(outAclCoords.begin(), outAclCoords.end(), [d = inputDimensions - 1] () mutable { return d--; });
98 }
99 else
100 {
101 // Create a vector of reduction dimensions (in reversed order) with the given reduction axes.
102 //
103 // Adjust the given reduction axes according to the original rank of the input tensor (before ACL applied any
104 // dimension correction).
105 // For example, if the input tensor originally had 4 dimensions, and one of the reduction axes was 2, then the
106 // new value for that reduction axis should be 1.
107 //
108 // Example:
109 // ArmNN input shape = { 1, 1, 3, 2 } -> ACL input shape = { 2, 3 }
110 // ArmNN reduction axis = { 2 } -> ACL reduction axis = { 1 }
111 // ArmNN reduction axis = { 3 } -> ACL reduction axis = { 0 }
112 //
113 // The transformation: ACL reduction axis index = original rank - ArmNN reduction axis index - 1
114 //
115 outAclCoords.set_num_dimensions(armnnAxes.size());
116 std::transform(armnnAxes.begin(), armnnAxes.end(),
117 outAclCoords.begin(),
118 [originalInputRank](unsigned int i){ return originalInputRank - i - 1; });
119 }
120
121 return outAclCoords;
122}
123
telsoa014fcda012018-03-09 14:13:49 +0000124arm_compute::TensorShape BuildArmComputeTensorShape(const armnn::TensorShape& tensorShape)
125{
126 arm_compute::TensorShape shape;
127
telsoa01c577f2c2018-08-31 09:22:23 +0100128 // armnn tensors are (batch, channels, height, width).
129 // arm_compute tensors are (width, height, channels, batch).
telsoa014fcda012018-03-09 14:13:49 +0000130 for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); i++)
131 {
telsoa01c577f2c2018-08-31 09:22:23 +0100132 // Note that our dimensions are stored in the opposite order to ACL's.
Matthew Bentham89105282018-11-20 14:33:33 +0000133 shape.set(tensorShape.GetNumDimensions() - i - 1, tensorShape[i], false);
telsoa014fcda012018-03-09 14:13:49 +0000134
135 // TensorShape::set() flattens leading ones, so that batch size 1 cannot happen.
telsoa01c577f2c2018-08-31 09:22:23 +0100136 // arm_compute tensors expect this.
telsoa014fcda012018-03-09 14:13:49 +0000137 }
138
139 // prevent arm_compute issue where tensor is flattened to nothing
140 if (shape.num_dimensions() == 0)
141 {
142 shape.set_num_dimensions(1);
143 }
144
145 return shape;
146}
147
148// Utility function used to build a TensorInfo object, that can be used to initialise
149// ARM Compute Tensor and CLTensor allocators.
Cathal Corbett4452baf2022-05-13 09:55:59 +0100150// Note: this utility ignores the value of armnn::TensorInfo.IsConstant(). ACL tensors
151// default to constant but Arm NN ones default to non constant. In the cases where
152// we expect ACL to treat a tensor as constant that value must be set after this
153// utility has been called.
telsoa014fcda012018-03-09 14:13:49 +0000154arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo)
155{
Derek Lambertid466a542020-01-22 15:37:29 +0000156 bool multiScales = tensorInfo.HasMultipleQuantizationScales();
telsoa014fcda012018-03-09 14:13:49 +0000157 const arm_compute::TensorShape aclTensorShape = BuildArmComputeTensorShape(tensorInfo.GetShape());
Derek Lambertid466a542020-01-22 15:37:29 +0000158 const arm_compute::DataType aclDataType = GetArmComputeDataType(tensorInfo.GetDataType(), multiScales);
Aron Virginas-Tar13b653f2019-11-01 11:40:39 +0000159
Derek Lambertid466a542020-01-22 15:37:29 +0000160 const arm_compute::QuantizationInfo aclQuantizationInfo = multiScales ?
Aron Virginas-Tar13b653f2019-11-01 11:40:39 +0000161 arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScales()) :
162 arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScale(), tensorInfo.GetQuantizationOffset());
telsoa014fcda012018-03-09 14:13:49 +0000163
164 return arm_compute::TensorInfo(aclTensorShape, 1, aclDataType, aclQuantizationInfo);
165}
166
Francis Murtagh351d13d2018-09-24 15:01:18 +0100167arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo,
168 armnn::DataLayout dataLayout)
169{
Aron Virginas-Tar13b653f2019-11-01 11:40:39 +0000170 arm_compute::TensorInfo aclTensorInfo = BuildArmComputeTensorInfo(tensorInfo);
171 aclTensorInfo.set_data_layout(ConvertDataLayout(dataLayout));
Francis Murtagh351d13d2018-09-24 15:01:18 +0100172
Aron Virginas-Tar13b653f2019-11-01 11:40:39 +0000173 return aclTensorInfo;
Francis Murtagh351d13d2018-09-24 15:01:18 +0100174}
175
Matteo Martincigh747ef822018-12-18 09:26:39 +0000176arm_compute::DataLayout ConvertDataLayout(armnn::DataLayout dataLayout)
177{
178 switch(dataLayout)
179 {
180 case armnn::DataLayout::NHWC : return arm_compute::DataLayout::NHWC;
181
182 case armnn::DataLayout::NCHW : return arm_compute::DataLayout::NCHW;
183
Teresa Charlinec5f7d12021-10-22 17:15:00 +0100184 case armnn::DataLayout::NDHWC : return arm_compute::DataLayout::NDHWC;
185
186 case armnn::DataLayout::NCDHW : return arm_compute::DataLayout::NCDHW;
187
Matteo Martincigh747ef822018-12-18 09:26:39 +0000188 default: throw InvalidArgumentException("Unknown armnn::DataLayout: [" +
189 std::to_string(static_cast<int>(dataLayout)) + "]");
190 }
191}
192
Sadik Armagana3600ba2019-10-10 10:43:20 +0100193arm_compute::PoolingLayerInfo BuildArmComputePoolingLayerInfo(const Pooling2dDescriptor& descriptor,
194 bool fpMixedPrecision)
telsoa014fcda012018-03-09 14:13:49 +0000195{
telsoa01c577f2c2018-08-31 09:22:23 +0100196 // Resolve ARM Compute layer parameters.
Ryan OSheabab8fa92022-03-09 10:29:02 +0000197 const arm_compute::PoolingType poolingType = ConvertPoolingAlgorithmToAclPoolingType(descriptor.m_PoolType);
telsoa01c577f2c2018-08-31 09:22:23 +0100198
Ryan OSheabab8fa92022-03-09 10:29:02 +0000199 const arm_compute::DataLayout dataLayout = ConvertDataLayout(descriptor.m_DataLayout);
Teresa Charlinc809a292020-01-31 10:21:44 +0000200
telsoa01c577f2c2018-08-31 09:22:23 +0100201 bool isGlobalPooling = (descriptor.m_StrideX==0 && descriptor.m_StrideY==0);
202 //use specific constructor if global pooling
203 if(isGlobalPooling)
204 {
Teresa Charlinc809a292020-01-31 10:21:44 +0000205 return arm_compute::PoolingLayerInfo(poolingType, dataLayout);
telsoa01c577f2c2018-08-31 09:22:23 +0100206 }
207
Ryan OSheabab8fa92022-03-09 10:29:02 +0000208 const arm_compute::DimensionRoundingType rounding = ConvertOutputShapeRoundingToAclDimensionRoundingType(
telsoa014fcda012018-03-09 14:13:49 +0000209 descriptor.m_OutputShapeRounding);
Ryan OSheabab8fa92022-03-09 10:29:02 +0000210 const arm_compute::PadStrideInfo padStrideInfo(descriptor.m_StrideX,
telsoa014fcda012018-03-09 14:13:49 +0000211 descriptor.m_StrideY,
212 descriptor.m_PadLeft,
213 descriptor.m_PadRight,
214 descriptor.m_PadTop,
215 descriptor.m_PadBottom,
216 rounding);
217
218 const bool excludePadding = (descriptor.m_PaddingMethod == PaddingMethod::Exclude);
219
Ryan OSheabab8fa92022-03-09 10:29:02 +0000220 const arm_compute::Size2D poolSize(descriptor.m_PoolWidth, descriptor.m_PoolHeight);
surmeh01bceff2f2018-03-29 16:29:27 +0100221
Teresa Charlinc809a292020-01-31 10:21:44 +0000222 return arm_compute::PoolingLayerInfo(poolingType, poolSize, dataLayout, padStrideInfo, excludePadding,
223 fpMixedPrecision);
telsoa014fcda012018-03-09 14:13:49 +0000224}
225
Ryan OSheabab8fa92022-03-09 10:29:02 +0000226arm_compute::Pooling3dLayerInfo BuildArmComputePooling3dLayerInfo(const Pooling3dDescriptor& descriptor,
227 bool fpMixedPrecision)
228{
229 const arm_compute::PoolingType poolingType = ConvertPoolingAlgorithmToAclPoolingType(descriptor.m_PoolType);
230
231 bool isGlobalPooling = (descriptor.m_StrideX==0 && descriptor.m_StrideY==0 && descriptor.m_StrideZ==0);
232 //use specific constructor if global pooling
233 if(isGlobalPooling)
234 {
235 return arm_compute::Pooling3dLayerInfo(poolingType);
236 }
237
238 const arm_compute::Size3D poolSize(descriptor.m_PoolWidth, descriptor.m_PoolHeight, descriptor.m_PoolDepth);
239
240 const arm_compute::Size3D stride(descriptor.m_StrideX,
241 descriptor.m_StrideY,
242 descriptor.m_StrideZ);
243
244 const arm_compute::Padding3D padding(descriptor.m_PadLeft,
245 descriptor.m_PadRight,
246 descriptor.m_PadTop,
247 descriptor.m_PadBottom,
248 descriptor.m_PadFront,
249 descriptor.m_PadBack);
250
251 const bool excludePadding = (descriptor.m_PaddingMethod == PaddingMethod::Exclude);
252
253 const arm_compute::DimensionRoundingType rounding = ConvertOutputShapeRoundingToAclDimensionRoundingType(
254 descriptor.m_OutputShapeRounding);
255
256 return arm_compute::Pooling3dLayerInfo(poolingType,
257 poolSize,
258 stride,
259 padding,
260 excludePadding,
261 fpMixedPrecision,
262 rounding);
263}
264
telsoa014fcda012018-03-09 14:13:49 +0000265arm_compute::NormalizationLayerInfo BuildArmComputeNormalizationLayerInfo(const NormalizationDescriptor& descriptor)
266{
267 const arm_compute::NormType normType =
268 ConvertNormalizationAlgorithmChannelToAclNormType(descriptor.m_NormChannelType);
269 return arm_compute::NormalizationLayerInfo(normType,
270 descriptor.m_NormSize,
271 descriptor.m_Alpha,
272 descriptor.m_Beta,
273 descriptor.m_K,
274 false);
275}
276
277arm_compute::PermutationVector BuildArmComputePermutationVector(const armnn::PermutationVector& perm)
278{
279 arm_compute::PermutationVector aclPerm;
280
281 unsigned int start = 0;
surmeh01bceff2f2018-03-29 16:29:27 +0100282 while ((start < perm.GetSize()) && (start == perm[start]))
telsoa014fcda012018-03-09 14:13:49 +0000283 {
284 ++start;
285 }
286
287 for (unsigned int i = start; i < perm.GetSize(); ++i)
288 {
289 aclPerm.set(i - start, perm[i] - start);
290 }
Mike Kellyc9ea45a2020-02-28 18:11:58 +0000291 return aclPerm;
292}
telsoa014fcda012018-03-09 14:13:49 +0000293
Mike Kellyc9ea45a2020-02-28 18:11:58 +0000294arm_compute::PermutationVector BuildArmComputeTransposeVector(const armnn::PermutationVector& perm)
295{
296 arm_compute::PermutationVector aclPerm;
297 std::map<unsigned int, unsigned int> permuteMappings;
298 for (unsigned int i = 0; i < perm.GetSize(); ++i)
299 {
300 permuteMappings[perm[i]] = i;
301 }
302
303 std::vector<unsigned int> permuteVector;
304 for (unsigned int i = 0; i < perm.GetSize(); ++i)
305 {
306 permuteVector.push_back(permuteMappings.at(i));
307 }
308
309 unsigned int start = 0;
310 while ((start < perm.GetSize()) && (start == permuteVector[start]))
311 {
312 ++start;
313 }
314
315 for (unsigned int i = start; i < perm.GetSize(); ++i)
316 {
317 aclPerm.set(i - start, permuteVector[i] - start);
318 }
telsoa014fcda012018-03-09 14:13:49 +0000319 return aclPerm;
320}
321
Sadik Armaganf4464322018-12-20 16:19:12 +0000322arm_compute::Size2D BuildArmComputeSize2D(const unsigned int width, const unsigned int height)
323{
324 return arm_compute::Size2D(width, height);
325}
326
Kevin May263d7092022-11-29 14:34:48 +0000327arm_compute::PixelValue GetPixelValue(const arm_compute::ITensorInfo* tensorInfo, float value)
Mike Kelly0a08ec62019-07-25 08:39:31 +0100328{
Matthew Sloyan2e5d0b22021-10-21 14:05:31 +0100329 switch (tensorInfo->data_type())
Mike Kelly0a08ec62019-07-25 08:39:31 +0100330 {
Mike Kelly0a08ec62019-07-25 08:39:31 +0100331 case arm_compute::DataType::F16:
Kevin May263d7092022-11-29 14:34:48 +0000332 {
333 arm_compute::PixelValue pixelValue = arm_compute::PixelValue(static_cast<Half>(value));
334 if (isinf(pixelValue.get<Half>())) {
335 throw InvalidArgumentException("Under/Overflow converting float value [" + std::to_string(value) +
336 "] to fp16: [" + std::to_string(pixelValue.get<Half>()) + "]");
337 }
338 return pixelValue;
339 }
Mike Kelly0a08ec62019-07-25 08:39:31 +0100340 case arm_compute::DataType::F32:
Kevin May263d7092022-11-29 14:34:48 +0000341 return arm_compute::PixelValue(value);
Mike Kelly130ec602019-11-08 12:08:35 +0000342 case arm_compute::DataType::QASYMM8:
Kevin May263d7092022-11-29 14:34:48 +0000343 return arm_compute::PixelValue(static_cast<uint8_t>(value));
Mike Kelly130ec602019-11-08 12:08:35 +0000344 case arm_compute::DataType::QSYMM16:
Kevin May263d7092022-11-29 14:34:48 +0000345 return arm_compute::PixelValue(static_cast<int16_t>(value));
Tamas Nyirid3065d72021-11-12 11:22:50 +0000346 case arm_compute::DataType::QSYMM8:
Sadik Armagane5d0b932020-04-09 15:48:44 +0100347 case arm_compute::DataType::QASYMM8_SIGNED:
Mike Kelly130ec602019-11-08 12:08:35 +0000348 case arm_compute::DataType::QSYMM8_PER_CHANNEL:
Kevin May263d7092022-11-29 14:34:48 +0000349 return arm_compute::PixelValue(static_cast<int8_t>(value));
Sadik Armagana792a052020-06-23 16:22:23 +0100350 case arm_compute::DataType::S32:
Kevin May263d7092022-11-29 14:34:48 +0000351 return arm_compute::PixelValue(static_cast<int32_t>(value));
Mike Kelly0a08ec62019-07-25 08:39:31 +0100352 default:
353 throw InvalidArgumentException("Unsupported DataType: [" +
Matthew Sloyan2e5d0b22021-10-21 14:05:31 +0100354 std::to_string(static_cast<int>(tensorInfo->data_type())) + "]");
Mike Kelly0a08ec62019-07-25 08:39:31 +0100355 }
356}
357
Cathal Corbett4b19d222022-05-11 20:12:17 +0100358unsigned int ComputeDepthwiseConv2dDepthMultiplier(armnn::DataLayout layout,
359 const arm_compute::TensorShape& weightsShape,
360 const arm_compute::TensorShape& inputShape)
361{
362 unsigned int depthMultiplier;
363 if (layout == armnn::DataLayout::NHWC)
364 {
365 depthMultiplier = static_cast<uint32_t>(weightsShape[0]) / static_cast<uint32_t>(inputShape[0]);
366 }
367 else if (layout == armnn::DataLayout::NCHW)
368 {
369 depthMultiplier = static_cast<uint32_t>(weightsShape[2]) / static_cast<uint32_t>(inputShape[2]);
370 }
371 else
372 {
373 throw InvalidArgumentException(fmt::format("Unknown data layout for tensor conversion: {}",
374 GetDataLayoutName(layout)));
375 }
376 return depthMultiplier;
377}
378
telsoa014fcda012018-03-09 14:13:49 +0000379} // namespace armcomputetensorutils
380} // namespace armnn