ArmNN
 22.11
ArmComputeTensorUtils.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
7 
8 #include "armnn/Exceptions.hpp"
9 #include <armnn/Descriptors.hpp>
10 
11 #include <fmt/format.h>
12 
13 namespace armnn
14 {
15 namespace armcomputetensorutils
16 {
17 
18 arm_compute::DataType GetArmComputeDataType(armnn::DataType dataType, bool multiScales)
19 {
20  switch(dataType)
21  {
23  return arm_compute::DataType::BFLOAT16;
25  return arm_compute::DataType::U8;
27  return arm_compute::DataType::F16;
29  return arm_compute::DataType::F32;
31  return arm_compute::DataType::QASYMM8_SIGNED;
33  return arm_compute::DataType::QASYMM8;
35  return arm_compute::DataType::QSYMM16;
37  return arm_compute::DataType::S64;
39  {
40  return multiScales ? arm_compute::DataType::QSYMM8_PER_CHANNEL : arm_compute::DataType::QSYMM8;
41  }
43  return arm_compute::DataType::S32;
44  default:
45  ARMNN_ASSERT_MSG(false, "Unknown data type");
46  return arm_compute::DataType::UNKNOWN;
47  }
48 }
49 
50 armnn::DataType GetArmNNDataType(arm_compute::DataType dataType)
51 {
52  switch(dataType)
53  {
54  case arm_compute::DataType::BFLOAT16:
56  case arm_compute::DataType::U8:
58  case arm_compute::DataType::F16:
60  case arm_compute::DataType::F32:
62  case arm_compute::DataType::QASYMM8_SIGNED:
64  case arm_compute::DataType::QASYMM8:
66  case arm_compute::DataType::QSYMM16:
68  case arm_compute::DataType::S64:
70  case arm_compute::DataType::QSYMM8_PER_CHANNEL:
72  case arm_compute::DataType::QSYMM8:
74  case arm_compute::DataType::S32:
76  default:
77  ARMNN_ASSERT_MSG(false, "Unknown data type");
79  }
80 }
81 
82 arm_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 
124 arm_compute::TensorShape BuildArmComputeTensorShape(const armnn::TensorShape& tensorShape)
125 {
126  arm_compute::TensorShape shape;
127 
128  // armnn tensors are (batch, channels, height, width).
129  // arm_compute tensors are (width, height, channels, batch).
130  for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); i++)
131  {
132  // Note that our dimensions are stored in the opposite order to ACL's.
133  shape.set(tensorShape.GetNumDimensions() - i - 1, tensorShape[i], false);
134 
135  // TensorShape::set() flattens leading ones, so that batch size 1 cannot happen.
136  // arm_compute tensors expect this.
137  }
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.
150 // 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.
154 arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo)
155 {
156  bool multiScales = tensorInfo.HasMultipleQuantizationScales();
157  const arm_compute::TensorShape aclTensorShape = BuildArmComputeTensorShape(tensorInfo.GetShape());
158  const arm_compute::DataType aclDataType = GetArmComputeDataType(tensorInfo.GetDataType(), multiScales);
159 
160  const arm_compute::QuantizationInfo aclQuantizationInfo = multiScales ?
161  arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScales()) :
162  arm_compute::QuantizationInfo(tensorInfo.GetQuantizationScale(), tensorInfo.GetQuantizationOffset());
163 
164  return arm_compute::TensorInfo(aclTensorShape, 1, aclDataType, aclQuantizationInfo);
165 }
166 
167 arm_compute::TensorInfo BuildArmComputeTensorInfo(const armnn::TensorInfo& tensorInfo,
168  armnn::DataLayout dataLayout)
169 {
170  arm_compute::TensorInfo aclTensorInfo = BuildArmComputeTensorInfo(tensorInfo);
171  aclTensorInfo.set_data_layout(ConvertDataLayout(dataLayout));
172 
173  return aclTensorInfo;
174 }
175 
176 arm_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 
184  case armnn::DataLayout::NDHWC : return arm_compute::DataLayout::NDHWC;
185 
186  case armnn::DataLayout::NCDHW : return arm_compute::DataLayout::NCDHW;
187 
188  default: throw InvalidArgumentException("Unknown armnn::DataLayout: [" +
189  std::to_string(static_cast<int>(dataLayout)) + "]");
190  }
191 }
192 
193 arm_compute::PoolingLayerInfo BuildArmComputePoolingLayerInfo(const Pooling2dDescriptor& descriptor,
194  bool fpMixedPrecision)
195 {
196  // Resolve ARM Compute layer parameters.
197  const arm_compute::PoolingType poolingType = ConvertPoolingAlgorithmToAclPoolingType(descriptor.m_PoolType);
198 
199  const arm_compute::DataLayout dataLayout = ConvertDataLayout(descriptor.m_DataLayout);
200 
201  bool isGlobalPooling = (descriptor.m_StrideX==0 && descriptor.m_StrideY==0);
202  //use specific constructor if global pooling
203  if(isGlobalPooling)
204  {
205  return arm_compute::PoolingLayerInfo(poolingType, dataLayout);
206  }
207 
208  const arm_compute::DimensionRoundingType rounding = ConvertOutputShapeRoundingToAclDimensionRoundingType(
209  descriptor.m_OutputShapeRounding);
210  const arm_compute::PadStrideInfo padStrideInfo(descriptor.m_StrideX,
211  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 
220  const arm_compute::Size2D poolSize(descriptor.m_PoolWidth, descriptor.m_PoolHeight);
221 
222  return arm_compute::PoolingLayerInfo(poolingType, poolSize, dataLayout, padStrideInfo, excludePadding,
223  fpMixedPrecision);
224 }
225 
226 arm_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 
265 arm_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 
277 arm_compute::PermutationVector BuildArmComputePermutationVector(const armnn::PermutationVector& perm)
278 {
279  arm_compute::PermutationVector aclPerm;
280 
281  unsigned int start = 0;
282  while ((start < perm.GetSize()) && (start == perm[start]))
283  {
284  ++start;
285  }
286 
287  for (unsigned int i = start; i < perm.GetSize(); ++i)
288  {
289  aclPerm.set(i - start, perm[i] - start);
290  }
291  return aclPerm;
292 }
293 
294 arm_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  }
319  return aclPerm;
320 }
321 
322 arm_compute::Size2D BuildArmComputeSize2D(const unsigned int width, const unsigned int height)
323 {
324  return arm_compute::Size2D(width, height);
325 }
326 
327 arm_compute::PixelValue GetPixelValue(const arm_compute::ITensorInfo* tensorInfo, float pixelValue)
328 {
329  switch (tensorInfo->data_type())
330  {
331  case arm_compute::DataType::F16:
332  return arm_compute::PixelValue(static_cast<Half>(pixelValue));
333  case arm_compute::DataType::F32:
334  return arm_compute::PixelValue(pixelValue);
335  case arm_compute::DataType::QASYMM8:
336  return arm_compute::PixelValue(static_cast<uint8_t>(pixelValue));
337  case arm_compute::DataType::QSYMM16:
338  return arm_compute::PixelValue(static_cast<int16_t>(pixelValue));
339  case arm_compute::DataType::QSYMM8:
340  case arm_compute::DataType::QASYMM8_SIGNED:
341  case arm_compute::DataType::QSYMM8_PER_CHANNEL:
342  return arm_compute::PixelValue(static_cast<int8_t>(pixelValue));
343  case arm_compute::DataType::S32:
344  return arm_compute::PixelValue(static_cast<int32_t>(pixelValue));
345  default:
346  throw InvalidArgumentException("Unsupported DataType: [" +
347  std::to_string(static_cast<int>(tensorInfo->data_type())) + "]");
348  }
349 }
350 
351 unsigned int ComputeDepthwiseConv2dDepthMultiplier(armnn::DataLayout layout,
352  const arm_compute::TensorShape& weightsShape,
353  const arm_compute::TensorShape& inputShape)
354 {
355  unsigned int depthMultiplier;
356  if (layout == armnn::DataLayout::NHWC)
357  {
358  depthMultiplier = static_cast<uint32_t>(weightsShape[0]) / static_cast<uint32_t>(inputShape[0]);
359  }
360  else if (layout == armnn::DataLayout::NCHW)
361  {
362  depthMultiplier = static_cast<uint32_t>(weightsShape[2]) / static_cast<uint32_t>(inputShape[2]);
363  }
364  else
365  {
366  throw InvalidArgumentException(fmt::format("Unknown data layout for tensor conversion: {}",
367  GetDataLayoutName(layout)));
368  }
369  return depthMultiplier;
370 }
371 
372 } // namespace armcomputetensorutils
373 } // namespace armnn
constexpr const char * GetDataLayoutName(DataLayout dataLayout)
Definition: TypesUtils.hpp:222
DataLayout
Definition: Types.hpp:62
const TensorShape & GetShape() const
Definition: Tensor.hpp:191
arm_compute::PoolingType ConvertPoolingAlgorithmToAclPoolingType(PoolingAlgorithm poolingAlgorithm)
std::array< unsigned int, MaxNumOfTensorDimensions > Coordinates
The padding fields don&#39;t count and are ignored.
arm_compute::NormType ConvertNormalizationAlgorithmChannelToAclNormType(NormalizationAlgorithmChannel channelType)
Copyright (c) 2021 ARM Limited and Contributors.
SizeType GetSize() const
Definition: Types.hpp:338
std::vector< float > GetQuantizationScales() const
Definition: Tensor.cpp:451
const armnnSerializer::Pooling2dDescriptor * Pooling2dDescriptor
bool HasMultipleQuantizationScales() const
Definition: Tensor.hpp:201
DataType
Definition: Types.hpp:48
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
arm_compute::DimensionRoundingType ConvertOutputShapeRoundingToAclDimensionRoundingType(OutputShapeRounding rounding)
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:478
float GetQuantizationScale() const
Definition: Tensor.cpp:461
DataType GetDataType() const
Definition: Tensor.hpp:198
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
const armnnSerializer::Pooling3dDescriptor * Pooling3dDescriptor