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