ArmNN
 24.02
TensorUtils.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017-2023 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
7 
8 #include <armnn/Exceptions.hpp>
9 
11 #include <armnn/utility/Assert.hpp>
13 
14 #include <fmt/format.h>
15 
16 using namespace armnn;
17 
18 namespace armnnUtils
19 {
20 
21 TensorShape GetTensorShape(unsigned int numberOfBatches,
22  unsigned int numberOfChannels,
23  unsigned int height,
24  unsigned int width,
25  const DataLayout dataLayout)
26 {
27  switch (dataLayout)
28  {
29  case DataLayout::NCHW:
30  return TensorShape({numberOfBatches, numberOfChannels, height, width});
31  case DataLayout::NHWC:
32  return TensorShape({numberOfBatches, height, width, numberOfChannels});
33  default:
34  throw InvalidArgumentException("Unknown data layout ["
35  + std::to_string(static_cast<int>(dataLayout)) +
36  "]", CHECK_LOCATION());
37  }
38 }
39 
40 TensorInfo GetTensorInfo(unsigned int numberOfBatches,
41  unsigned int numberOfChannels,
42  unsigned int height,
43  unsigned int width,
44  const DataLayout dataLayout,
45  const DataType dataType)
46 {
47  switch (dataLayout)
48  {
49  case DataLayout::NCHW:
50  return TensorInfo({numberOfBatches, numberOfChannels, height, width}, dataType);
51  case DataLayout::NHWC:
52  return TensorInfo({numberOfBatches, height, width, numberOfChannels}, dataType);
53  default:
54  throw InvalidArgumentException("Unknown data layout ["
55  + std::to_string(static_cast<int>(dataLayout)) +
56  "]", CHECK_LOCATION());
57  }
58 }
59 
60 TensorInfo GetTensorInfo(unsigned int numberOfBatches,
61  unsigned int numberOfChannels,
62  unsigned int depth,
63  unsigned int height,
64  unsigned int width,
65  const DataLayout dataLayout,
66  const DataType dataType)
67 {
68  switch (dataLayout)
69  {
70  case DataLayout::NDHWC:
71  return TensorInfo({numberOfBatches, depth, height, width, numberOfChannels}, dataType);
72  case DataLayout::NCDHW:
73  return TensorInfo({numberOfBatches, numberOfChannels, depth, height, width}, dataType);
74  default:
75  throw InvalidArgumentException("Unknown data layout ["
76  + std::to_string(static_cast<int>(dataLayout)) +
77  "]", CHECK_LOCATION());
78  }
79 }
80 
81 std::pair<float, float> FindMinMax(ITensorHandle* tensorHandle)
82 {
83  auto tensor_data = static_cast<const float *>(tensorHandle->Map(true));
84  auto tensor_size = tensorHandle->GetShape().GetNumElements();
85 
86  // Set min/max initially to first value in tensor
87  float min = tensor_data[0];
88  float max = tensor_data[0];
89 
90  // Loop over rest of tensor and update min/max if necessary
91  for (unsigned int val = 1; val < tensor_size; val++)
92  {
93  if (tensor_data[val] < min)
94  {
95  min = tensor_data[val];
96  }
97  else if (tensor_data[val] > max)
98  {
99  max = tensor_data[val];
100  }
101  }
102 
103  tensorHandle->Unmap();
104 
105  return std::make_pair(min, max);
106 }
107 
108 TensorShape ReduceDims(const TensorShape& tensorShape, unsigned int dimensions)
109 {
110  if (tensorShape.GetNumDimensions() <= dimensions)
111  {
112  return tensorShape;
113  }
114  std::vector<unsigned int> newShape;
115 
116  unsigned int dimsToSkip = tensorShape.GetNumDimensions() - dimensions;
117  unsigned int dimsSkipped = 0;
118  bool insertRemainder = false;
119 
120  for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
121  {
122  if (tensorShape[i] == 1 && dimsSkipped < dimsToSkip && !insertRemainder)
123  {
124  ++dimsSkipped;
125  continue;
126  }
127  newShape.push_back(tensorShape[i]);
128  // Once we insert the first dimension we can't skip any more
129  insertRemainder = true;
130  }
131  return TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data());
132 }
133 
134 TensorInfo ReduceDims(const TensorInfo& tensorInfo, unsigned int dimensions)
135 {
136  TensorInfo strippedTensor(tensorInfo);
137  TensorShape strippedShape = ReduceDims(tensorInfo.GetShape(), dimensions);
138  strippedTensor.SetShape(strippedShape);
139  return strippedTensor;
140 }
141 
142 TensorShape ExpandDims(const TensorShape& tensorShape, int axis)
143 {
144  unsigned int outputDim = tensorShape.GetNumDimensions() + 1;
145 
146  if (axis < -armnn::numeric_cast<int>(outputDim) || axis > armnn::numeric_cast<int>(tensorShape.GetNumDimensions()))
147  {
148  throw InvalidArgumentException(fmt::format("Invalid expansion axis {} for {}D input tensor. {}",
149  axis,
150  tensorShape.GetNumDimensions(),
151  CHECK_LOCATION().AsString()));
152  }
153 
154  if (axis < 0)
155  {
156  axis = armnn::numeric_cast<int>(outputDim) + axis;
157  }
158 
159  std::vector<unsigned int> outputShape;
160  outputShape.reserve(tensorShape.GetNumDimensions());
161  for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
162  {
163  outputShape.push_back(tensorShape[i]);
164  }
165  outputShape.insert(outputShape.begin() + axis, 1);
166 
167  return { outputDim, outputShape.data() };
168 }
169 
170 TensorShape ExpandDimsToRank(const TensorShape& tensorShape, unsigned int rank)
171 {
172  // Can't expand if rank is smaller than current shape
173  if (tensorShape.GetNumDimensions() >= rank)
174  {
175  return tensorShape;
176  }
177 
178  std::vector<unsigned int> newShape;
179 
180  // First add 1s to the beginning of the tensorInfo to fill in the space
181  for (unsigned int i = 0; i < rank - tensorShape.GetNumDimensions(); ++i)
182  {
183  newShape.push_back(1);
184  }
185 
186  // Then iterate through the original shape and append it to the new shape with the added 1s
187  for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
188  {
189  newShape.push_back(tensorShape[i]);
190  }
191 
192  return TensorShape(static_cast<unsigned int>(newShape.size()), newShape.data());
193 }
194 
195 std::vector<unsigned int> SqueezeDims(const TensorShape& tensorShape)
196 {
197  std::vector<unsigned int> squeezedDims;
198 
199  for (unsigned int i = 0; i < tensorShape.GetNumDimensions(); ++i)
200  {
201  if (tensorShape[i] != 1)
202  {
203  squeezedDims.push_back(tensorShape[i]);
204  }
205  }
206  return squeezedDims;
207 }
208 
209 unsigned int GetNumElementsBetween(const TensorShape& shape,
210  const unsigned int firstAxisInclusive,
211  const unsigned int lastAxisExclusive)
212 {
213  if (firstAxisInclusive > lastAxisExclusive)
214  {
215  throw armnn::InvalidArgumentException(fmt::format(
216  "GetNumElementsBetween: firstAxisInclusive [{}D] is greater than lastAxisExclusive [{}D]",
217  firstAxisInclusive,
218  lastAxisExclusive));
219  }
220  if (lastAxisExclusive > shape.GetNumDimensions())
221  {
222  throw armnn::InvalidArgumentException(fmt::format(
223  "{}: lastAxisExclusive [{}D] is greater than the number of dimensions of the tensor shape [{}D]"
224  "GetNumElementsBetween",
225  lastAxisExclusive,
226  shape.GetNumDimensions()));
227  }
228  unsigned int count = 1;
229  for (unsigned int i = firstAxisInclusive; i < lastAxisExclusive; i++)
230  {
231  count *= shape[i];
232  }
233  return count;
234 }
235 
236 unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
237 {
238  if (axis >= armnn::numeric_cast<int>(inputDimension))
239  {
240  throw armnn::InvalidArgumentException(fmt::format(
241  "{}: axis index [{}] is not less than the number of dimensions [{}D]",
242  "GetUnsignedAxis",
243  axis,
244  inputDimension));
245  }
246  if (axis < -armnn::numeric_cast<int>(inputDimension))
247  {
248  throw armnn::InvalidArgumentException(fmt::format(
249  "{}: axis index [{}] lower than the negative of the number of dimensions [{}]",
250  "GetUnsignedAxis",
251  axis,
252  -armnn::numeric_cast<int>(inputDimension)));
253  }
254 
255  unsigned int uAxis = axis < 0 ?
256  inputDimension - armnn::numeric_cast<unsigned int>(abs(axis))
257  : armnn::numeric_cast<unsigned int>(axis);
258  return uAxis;
259 }
260 
261 unsigned int GetNumElementsAfter(const armnn::TensorShape& shape, unsigned int axis)
262 {
263  unsigned int numDim = shape.GetNumDimensions();
264  if (axis >= numDim)
265  {
266  throw armnn::InvalidArgumentException(fmt::format(
267  "{}: axis index [{}D] indexes beyond the number of dimesions of the tensor shape [{}D]",
268  "GetNumElementsAfter",
269  axis,
270  numDim));
271  }
272  unsigned int count = 1;
273  for (unsigned int i = axis+1; i < numDim; i++)
274  {
275  count *= shape[i];
276  }
277  return count;
278 }
279 
280 std::pair<unsigned int, std::vector<float>> GetPerAxisParams(const armnn::TensorInfo& info)
281 {
282  const std::vector<float>& scales = info.GetQuantizationScales();
283  armnn::Optional<unsigned int> quantizationDim = info.GetQuantizationDim();
284  if (!info.HasPerAxisQuantization())
285  {
287  std::string("Per-axis quantization params not set for tensor of type ") +
288  armnn::GetDataTypeName(info.GetDataType()), CHECK_LOCATION());
289  }
290  unsigned int axisFactor = GetNumElementsAfter(info.GetShape(), quantizationDim.value()) ;
291 
292  return { axisFactor, scales };
293 }
294 
295 template<typename PrimitiveType>
296 void CheckSizes(const std::vector<PrimitiveType>& data, const armnn::TensorInfo& tensorInfo, unsigned int size = 1)
297 {
298  if (data.size() / size != tensorInfo.GetNumElements())
299  {
301  fmt::format("The data does not contain the expected number of elements {} != {}. {}",
302  data.size(), tensorInfo.GetNumElements(), CHECK_LOCATION().AsString()));
303  }
304 }
305 
306 template<typename PrimitiveType>
307 std::unique_ptr<float[]> ToFloatArray(const std::vector<PrimitiveType>& data, const armnn::TensorInfo& tensorInfo)
308 {
309  CheckSizes(data, tensorInfo);
310 
311  std::unique_ptr<float[]> returnBuffer(new float[tensorInfo.GetNumElements()]);
312 
313  if (tensorInfo.HasPerAxisQuantization())
314  {
315  unsigned int axis = tensorInfo.GetQuantizationDim().value();
316  auto axisDimensionality = tensorInfo.GetShape()[axis];
317  auto axisFactor = armnnUtils::GetNumElementsAfter(tensorInfo.GetShape(), axis);
318 
319  for (unsigned int i = 0; i < tensorInfo.GetNumElements(); ++i)
320  {
321  unsigned int axisIndex;
322 
323  if (i < axisFactor)
324  {
325  axisIndex = 0;
326  }
327  else
328  {
329  axisIndex = (i / axisFactor) % axisDimensionality;
330  }
331  returnBuffer[i] = Dequantize<PrimitiveType>(data[i],
332  tensorInfo.GetQuantizationScales()[axisIndex],
333  tensorInfo.GetQuantizationOffset());
334  }
335  }
336  else
337  {
338  for (unsigned int i = 0; i < tensorInfo.GetNumElements(); ++i)
339  {
340  returnBuffer[i] = Dequantize<PrimitiveType>(data[i],
341  tensorInfo.GetQuantizationScale(),
342  tensorInfo.GetQuantizationOffset());
343  }
344  }
345  return returnBuffer;
346 }
347 
348 std::unique_ptr<float[]> ToFloatArray(const std::vector<uint8_t>& data, const armnn::TensorInfo& tensorInfo)
349 {
350  if (tensorInfo.GetDataType() == DataType::QAsymmS8 || tensorInfo.GetDataType() == DataType::QSymmS8)
351  {
352  CheckSizes(data, tensorInfo);
353  std::vector<int8_t> buffer(tensorInfo.GetNumElements());
354  ::memcpy(buffer.data(), data.data(), data.size());
355  return ToFloatArray<int8_t>(buffer, tensorInfo);
356  }
357  else if (tensorInfo.GetDataType() == DataType::QAsymmU8)
358  {
359  CheckSizes(data, tensorInfo);
360  return ToFloatArray<uint8_t>(data, tensorInfo);
361  }
362  else if (tensorInfo.GetDataType() == DataType::Signed32)
363  {
364  CheckSizes(data, tensorInfo, 4);
365  std::vector<int32_t> buffer(tensorInfo.GetNumElements());
366  ::memcpy(buffer.data(), data.data(), data.size());
367  return ToFloatArray<int32_t>(buffer, tensorInfo);
368  }
369  else if (tensorInfo.GetDataType() == DataType::Signed64)
370  {
371  CheckSizes(data, tensorInfo, 8);
372  std::vector<int64_t> buffer(tensorInfo.GetNumElements());
373  ::memcpy(buffer.data(), data.data(), data.size());
374  return ToFloatArray<int64_t>(buffer, tensorInfo);
375  }
377  fmt::format("Unsupported datatype {}. {}",
378  GetDataTypeName(tensorInfo.GetDataType()),
379  CHECK_LOCATION().AsString()));
380 }
381 
382 } // namespace armnnUtils
armnn::TensorInfo::GetNumElements
unsigned int GetNumElements() const
Definition: Tensor.hpp:198
armnn::Optional< unsigned int >
armnn::DataLayout::NCDHW
@ NCDHW
armnn::DataLayout
DataLayout
Definition: Types.hpp:62
armnnUtils::SqueezeDims
std::vector< unsigned int > SqueezeDims(const armnn::TensorShape &tensorShape)
Definition: TensorUtils.cpp:195
armnn::TensorInfo::GetQuantizationScales
std::vector< float > GetQuantizationScales() const
Definition: Tensor.cpp:451
armnn::DataLayout::NHWC
@ NHWC
armnnUtils::GetUnsignedAxis
unsigned int GetUnsignedAxis(const unsigned int inputDimension, const int axis)
Definition: TensorUtils.cpp:236
armnn::TensorInfo::GetQuantizationScale
float GetQuantizationScale() const
Definition: Tensor.cpp:461
armnnUtils::ExpandDimsToRank
armnn::TensorShape ExpandDimsToRank(const armnn::TensorShape &tensorShape, unsigned int rank)
Definition: TensorUtils.cpp:170
armnn::TensorInfo
Definition: Tensor.hpp:152
armnn::GetDataTypeName
constexpr const char * GetDataTypeName(DataType dataType)
Definition: TypesUtils.hpp:233
CHECK_LOCATION
#define CHECK_LOCATION()
Definition: Exceptions.hpp:203
armnn::ITensorHandle
Definition: ITensorHandle.hpp:16
armnn::ITensorHandle::GetShape
virtual TensorShape GetShape() const =0
Get the number of elements for each dimension ordered from slowest iterating dimension to fastest ite...
armnn::DataType::QAsymmU8
@ QAsymmU8
armnn::DataType::QSymmS8
@ QSymmS8
armnn::TensorInfo::HasPerAxisQuantization
bool HasPerAxisQuantization() const
Definition: Tensor.cpp:446
NumericCast.hpp
TensorUtils.hpp
armnnUtils::GetPerAxisParams
std::pair< unsigned int, std::vector< float > > GetPerAxisParams(const armnn::TensorInfo &info)
Definition: TensorUtils.cpp:280
armnn::DataLayout::NDHWC
@ NDHWC
Assert.hpp
armnn::TensorShape
Definition: Tensor.hpp:20
ITensorHandle.hpp
armnn::TensorShape::GetNumDimensions
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
armnn::TensorInfo::GetQuantizationDim
Optional< unsigned int > GetQuantizationDim() const
Definition: Tensor.cpp:494
armnn::DataType
DataType
Definition: Types.hpp:48
armnnUtils
Definition: CompatibleTypes.hpp:10
armnn::InvalidArgumentException
Definition: Exceptions.hpp:80
armnn::GetTensorInfo
const TensorInfo & GetTensorInfo(const ITensorHandle *tensorHandle)
float32 helpers
Definition: RefWorkloadUtils.hpp:33
armnnUtils::ExpandDims
armnn::TensorShape ExpandDims(const armnn::TensorShape &tensorShape, int axis)
Definition: TensorUtils.cpp:142
armnn::ITensorHandle::Unmap
virtual void Unmap() const =0
Unmap the tensor data.
armnn::BoostLogSeverityMapping::info
@ info
armnn::TensorInfo::GetDataType
DataType GetDataType() const
Definition: Tensor.hpp:200
armnn::DataType::Signed32
@ Signed32
armnn::DataType::QAsymmS8
@ QAsymmS8
armnnUtils::GetNumElementsBetween
unsigned int GetNumElementsBetween(const armnn::TensorShape &shape, unsigned int firstAxisInclusive, unsigned int lastAxisExclusive)
Definition: TensorUtils.cpp:209
armnn::abs
Definition: Abs.hpp:13
armnn::TensorInfo::GetShape
const TensorShape & GetShape() const
Definition: Tensor.hpp:193
armnnUtils::CheckSizes
void CheckSizes(const std::vector< PrimitiveType > &data, const armnn::TensorInfo &tensorInfo, unsigned int size=1)
Definition: TensorUtils.cpp:296
armnnUtils::GetNumElementsAfter
unsigned int GetNumElementsAfter(const armnn::TensorShape &shape, unsigned int axis)
Definition: TensorUtils.cpp:261
armnn::TensorInfo::SetShape
void SetShape(const TensorShape &newShape)
Definition: Tensor.hpp:195
Exceptions.hpp
armnn
Copyright (c) 2021 ARM Limited and Contributors.
Definition: 01_00_quick_start.dox:6
armnnUtils::FindMinMax
std::pair< float, float > FindMinMax(armnn::ITensorHandle *tensorHandle)
Definition: TensorUtils.cpp:81
armnnUtils::ReduceDims
armnn::TensorShape ReduceDims(const armnn::TensorShape &tensorInfo, unsigned int dimensions)
Definition: TensorUtils.cpp:108
armnn::DataType::Signed64
@ Signed64
armnn::TensorInfo::GetQuantizationOffset
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:478
armnnUtils::ToFloatArray
std::unique_ptr< float[]> ToFloatArray(const std::vector< PrimitiveType > &data, const armnn::TensorInfo &tensorInfo)
Definition: TensorUtils.cpp:307
armnnUtils::GetTensorShape
armnn::TensorShape GetTensorShape(unsigned int numberOfBatches, unsigned int numberOfChannels, unsigned int height, unsigned int width, const armnn::DataLayout dataLayout)
Definition: TensorUtils.cpp:21
armnn::TensorShape::GetNumElements
unsigned int GetNumElements() const
Function that calculates the tensor elements by multiplying all dimension size which are Specified.
Definition: Tensor.cpp:181
armnn::OptionalReferenceSwitch::value
const T & value() const
Definition: Optional.hpp:146
armnn::DataLayout::NCHW
@ NCHW
armnn::ITensorHandle::Map
virtual const void * Map(bool blocking=true) const =0
Map the tensor data for access.