ArmNN
 21.05
Tensor.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "armnn/Tensor.hpp"
7 #include "armnn/Utils.hpp"
8 #include "armnn/Exceptions.hpp"
9 #include "armnn/TypesUtils.hpp"
10 
11 #include <armnn/utility/Assert.hpp>
13 
14 #include <iostream>
15 
16 #include <sstream>
17 
18 namespace armnn
19 {
20 
21 // ---
22 // --- TensorShape
23 // ---
24 
26  : m_NumDimensions(0), m_Dimensionality(Dimensionality::Specified)
27 {
28 }
29 
30 TensorShape::TensorShape(unsigned int numDimensions, bool initDimensionsSpecificity)
31  : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified)
32 {
33  CheckValidNumDimensions(numDimensions);
34 
35  std::fill(m_Dimensions.begin(), m_Dimensions.begin() + m_NumDimensions, 0);
36  std::fill(m_DimensionsSpecificity.begin(), m_DimensionsSpecificity.begin() + m_NumDimensions,
37  initDimensionsSpecificity);
38 }
39 
40 TensorShape::TensorShape(const unsigned int numDimensions, const unsigned int* const dimensionSizes)
41  : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified)
42 {
43  CheckValidNumDimensions(numDimensions);
44 
45  if (dimensionSizes == nullptr)
46  {
47  throw InvalidArgumentException("Tensor dimensionSizes must not be NULL");
48  }
49 
50  std::copy(dimensionSizes, dimensionSizes + numDimensions, m_Dimensions.begin());
51  std::fill(m_DimensionsSpecificity.begin(), m_DimensionsSpecificity.begin() + m_NumDimensions, true);
52 }
53 
54 TensorShape::TensorShape(std::initializer_list<unsigned int> dimensionSizeList)
55  : TensorShape(armnn::numeric_cast<unsigned int>(dimensionSizeList.size()), dimensionSizeList.begin())
56 {
57 }
58 
59 TensorShape::TensorShape(unsigned int numDimensions,
60  const unsigned int* const dimensionSizes,
61  const bool* const dimensionsSpecificity)
62  : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified)
63 {
64  CheckValidNumDimensions(numDimensions);
65 
66  if (dimensionSizes == nullptr)
67  {
68  throw InvalidArgumentException("Tensor dimensionSizes must not be NULL");
69  }
70 
71  if (dimensionsSpecificity == nullptr)
72  {
73  throw InvalidArgumentException("Tensor dimensionsSpecificity must not be NULL");
74  }
75 
76  std::copy(dimensionSizes, dimensionSizes + numDimensions, m_Dimensions.begin());
77  std::copy(dimensionsSpecificity, dimensionsSpecificity + numDimensions, m_DimensionsSpecificity.begin());
78 }
79 
80 TensorShape::TensorShape(std::initializer_list<unsigned int> dimensionSizeList,
81  std::initializer_list<bool> dimensionsSpecificityList)
82 {
83  auto numDimensions = static_cast<unsigned int>(dimensionSizeList.size());
84  if (dimensionsSpecificityList.size() != numDimensions)
85  {
86  throw InvalidArgumentException("Tensors dimensionSizeList and dimensionsSpecificityList must be same size");
87  }
88 
89  *this = TensorShape(numDimensions, dimensionSizeList.begin(), dimensionsSpecificityList.begin());
90 }
91 
93 : m_Dimensionality(dimensionality)
94 {
95  switch (dimensionality)
96  {
98  throw InvalidArgumentException("Use other constructor to specify the rest of the values, this one is only "
99  "for tensors that have an unknown number of dimensions or that are scalar");
100  break;
102  m_NumDimensions = 0;
103  m_Dimensions = {0};
104  m_DimensionsSpecificity = {false};
105  break;
107  m_NumDimensions = 1;
108  m_Dimensions = {1};
109  m_DimensionsSpecificity = {true};
110  break;
111  default:
112  throw InvalidArgumentException("Invalid Dimensionality value");
113  }
114 }
115 
117  : m_NumDimensions(other.m_NumDimensions), m_Dimensionality(other.m_Dimensionality)
118 {
119  std::copy(other.m_Dimensions.cbegin(), other.m_Dimensions.cbegin() + other.m_NumDimensions, m_Dimensions.begin());
120  std::copy(other.m_DimensionsSpecificity.cbegin(), other.m_DimensionsSpecificity.cbegin() + other.m_NumDimensions,
121  m_DimensionsSpecificity.begin());
122 }
123 
125 {
126  m_NumDimensions = other.m_NumDimensions;
127  m_Dimensionality = other.m_Dimensionality;
128  std::copy(other.m_Dimensions.cbegin(), other.m_Dimensions.cbegin() + other.m_NumDimensions, m_Dimensions.begin());
129  std::copy(other.m_DimensionsSpecificity.cbegin(), other.m_DimensionsSpecificity.cbegin() + other.m_NumDimensions,
130  m_DimensionsSpecificity.begin());
131  return *this;
132 }
133 
134 // read
135 unsigned int TensorShape::operator[](unsigned int i) const
136 {
137  CheckUnspecifiedNumDimensions();
138  CheckDimensionIndex(i);
139  CheckDimensionSpecified(i);
140 
141  return m_Dimensions.at(i);
142 }
143 
144 // read and write
145 unsigned int& TensorShape::operator[](unsigned int i)
146 {
147  if (Dimensionality::Scalar == m_Dimensionality)
148  {
149  std::stringstream errorMessage;
150  errorMessage << "TensorShape with Dimensionality::Scalar must be const to use operator[]";
151  throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION());
152  }
153  CheckUnspecifiedNumDimensions();
154  CheckDimensionIndex(i);
155  CheckDimensionSpecified(i);
156 
157  return m_Dimensions.at(i);
158 }
159 
160 bool TensorShape::operator==(const TensorShape& other) const
161 {
162  return ((m_NumDimensions == other.m_NumDimensions) &&
163  (m_Dimensionality == other.m_Dimensionality) &&
164  std::equal(m_Dimensions.cbegin(), m_Dimensions.cbegin() + m_NumDimensions, other.m_Dimensions.cbegin()) &&
165  std::equal(m_DimensionsSpecificity.cbegin(), m_DimensionsSpecificity.cbegin() + m_NumDimensions,
166  other.m_DimensionsSpecificity.cbegin()));
167 }
168 
169 bool TensorShape::operator!=(const TensorShape& other) const
170 {
171  return !(*this == other);
172 }
173 
174 unsigned int TensorShape::GetNumDimensions() const
175 {
176  CheckUnspecifiedNumDimensions();
177 
178  return m_NumDimensions;
179 }
180 
181 unsigned int TensorShape::GetNumElements() const
182 {
183  CheckUnspecifiedNumDimensions();
184 
185  if (m_NumDimensions == 0)
186  {
187  return 0;
188  }
189 
190  unsigned int count = 1;
191  bool atLeastOneDimensionSpecified = false;
192  for (unsigned int i = 0; i < m_NumDimensions; ++i)
193  {
194  if (m_DimensionsSpecificity[i])
195  {
196  atLeastOneDimensionSpecified = true;
197  count *= m_Dimensions[i];
198  }
199  }
200 
201  if (atLeastOneDimensionSpecified)
202  {
203  return count;
204  }
205  else
206  {
207  return 0;
208  }
209 }
210 
211 bool TensorShape:: GetDimensionSpecificity(unsigned int i) const
212 {
213  CheckUnspecifiedNumDimensions();
214  CheckDimensionIndex(i);
215 
216  return m_DimensionsSpecificity[i];
217 }
218 
219 void TensorShape::SetNumDimensions(unsigned int numDimensions, bool initDimensionsSpecificity)
220 {
221  CheckScalar();
222  CheckSpecifiedNumDimensions();
223  CheckValidNumDimensions(numDimensions);
224 
225  m_NumDimensions = numDimensions;
226  m_Dimensionality = Dimensionality::Specified;
227  std::fill(m_Dimensions.begin(), m_Dimensions.begin() + m_NumDimensions, 0);
228  std::fill(m_DimensionsSpecificity.begin(), m_DimensionsSpecificity.begin() + m_NumDimensions,
229  initDimensionsSpecificity);
230 }
231 
232 void TensorShape::SetDimensionSize(unsigned int i, unsigned int dimensionSize)
233 {
234  CheckScalar();
235  CheckDimensionIndex(i);
236 
237  m_Dimensions[i] = dimensionSize;
238  m_DimensionsSpecificity[i] = true;
239 }
240 
242 {
243  CheckUnspecifiedNumDimensions();
244 
245  bool areAllDimensionsSpecified = true;
246  for (unsigned int i = 0; i < m_NumDimensions; ++i)
247  {
248  if (!m_DimensionsSpecificity[i])
249  {
250  areAllDimensionsSpecified = false;
251  break;
252  }
253  }
254  return areAllDimensionsSpecified;
255 }
256 
258 {
259  CheckUnspecifiedNumDimensions();
260 
261  bool isAtLeastOneDimensionSpecified = false;
262  for (unsigned int i = 0; i < m_NumDimensions; ++i)
263  {
264  if (m_DimensionsSpecificity[i])
265  {
266  isAtLeastOneDimensionSpecified = true;
267  break;
268  }
269  }
270  return isAtLeastOneDimensionSpecified;
271 }
272 
273 void TensorShape::CheckDimensionIndex(unsigned int i) const
274 {
275  if (i >= m_NumDimensions)
276  {
277  std::stringstream errorMessage;
278  errorMessage << "Invalid dimension index: " << i << " (number of dimensions is " << m_NumDimensions << ")";
279  throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION());
280  }
281 }
282 
283 void TensorShape::CheckValidNumDimensions(unsigned int numDimensions)
284 {
285  if (numDimensions < 1)
286  {
287  throw InvalidArgumentException("Tensor numDimensions must be greater than 0", CHECK_LOCATION());
288  }
289 
290  if (numDimensions > MaxNumOfTensorDimensions)
291  {
292  throw InvalidArgumentException("Tensor numDimensions must be less than or equal to MaxNumOfTensorDimensions"
293  , CHECK_LOCATION());
294  }
295 }
296 
297 void TensorShape::CheckDimensionSpecified(unsigned int i) const
298 {
299  if (!m_DimensionsSpecificity[i])
300  {
301  std::stringstream errorMessage;
302  errorMessage << "Dimension index: " << i << " not specified. Tensor shape not inferred yet.";
303  throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION());
304  }
305 }
306 
307 void TensorShape::CheckScalar() const
308 {
309  if (Dimensionality::Scalar == m_Dimensionality)
310  {
311  std::stringstream errorMessage;
312  errorMessage << "Invalid action on a tensor shape that holds a scalar value.";
313  throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION());
314  }
315 }
316 
317 void TensorShape::CheckUnspecifiedNumDimensions() const
318 {
319  if (Dimensionality::NotSpecified == m_Dimensionality)
320  {
321  std::stringstream errorMessage;
322  errorMessage << "Invalid action on a tensor shape that has unknown number of dimensions.";
323  throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION());
324  }
325 }
326 
327 void TensorShape::CheckSpecifiedNumDimensions() const
328 {
329  if (Dimensionality::Specified == m_Dimensionality)
330  {
331  std::stringstream errorMessage;
332  errorMessage << "Invalid action on a tensor shape that has known number of dimensions.";
333  throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION());
334  }
335 }
336 
337 // ---
338 // --- TensorInfo
339 // ---
340 
342 : m_DataType(DataType::Float32)
343 {
344 }
345 
347  DataType dataType,
348  float quantizationScale,
349  int32_t quantizationOffset)
350  : m_Shape(shape)
351  , m_DataType(dataType)
352 {
353  SetQuantizationScale(quantizationScale);
354  SetQuantizationOffset(quantizationOffset);
355 }
356 
357 TensorInfo::TensorInfo(unsigned int numDimensions,
358  const unsigned int* dimensionSizes,
359  DataType dataType,
360  float quantizationScale,
361  int32_t quantizationOffset)
362  : m_Shape(numDimensions, dimensionSizes)
363  , m_DataType(dataType)
364 {
365  SetQuantizationScale(quantizationScale);
366  SetQuantizationOffset(quantizationOffset);
367 }
368 
370  DataType dataType,
371  const std::vector<float>& quantizationScales,
372  unsigned int quantizationDim)
373  : m_Shape(shape)
374  , m_DataType(dataType)
375 {
376  SetQuantizationScales(quantizationScales);
377  SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim));
378 }
379 
380 TensorInfo::TensorInfo(unsigned int numDimensions,
381  const unsigned int* dimensionSizes,
382  DataType dataType,
383  const std::vector<float>& quantizationScales,
384  unsigned int quantizationDim)
385  : m_Shape(numDimensions, dimensionSizes)
386  , m_DataType(dataType)
387 {
388  SetQuantizationScales(quantizationScales);
389  SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim));
390 }
391 
393 : m_Shape(other.m_Shape)
394 , m_DataType(other.m_DataType)
395 , m_Quantization(other.m_Quantization)
396 {}
397 
399 {
400  m_Shape = other.m_Shape;
401  m_DataType = other.m_DataType;
402  m_Quantization = other.m_Quantization;
403  return *this;
404 }
405 
406 bool TensorInfo::operator==(const TensorInfo& other) const
407 {
408  return ((m_Shape == other.m_Shape) &&
409  (m_DataType == other.m_DataType) &&
410  (m_Quantization == other.m_Quantization));
411 }
412 
413 bool TensorInfo::operator!=(const TensorInfo& other) const
414 {
415  return !(*this == other);
416 }
417 
418 unsigned int TensorInfo::GetNumBytes() const
419 {
420  return GetDataTypeSize(m_DataType) * GetNumElements();
421 }
422 
423 bool TensorInfo::IsTypeSpaceMatch(const TensorInfo& other) const
424 {
425  bool match = true;
426 
427  match &= m_DataType == other.m_DataType;
428 
430  {
431  match &= GetQuantizationScale() == other.GetQuantizationScale() &&
433  }
434  return match;
435 }
436 
438 {
439  return HasMultipleQuantizationScales() || m_Quantization.m_QuantizationDim.has_value();
440 }
441 
442 std::vector<float> TensorInfo::GetQuantizationScales() const
443 {
444  return m_Quantization.m_Scales;
445 }
446 
447 void TensorInfo::SetQuantizationScales(const std::vector<float>& scales)
448 {
449  m_Quantization.m_Scales = scales;
450 }
451 
453 {
454  if (m_Quantization.m_Scales.empty())
455  {
456  // NOTE: old default for backward compatibility
457  return 1.0f;
458  }
459 
461  return m_Quantization.m_Scales[0];
462 }
463 
465 {
466  m_Quantization.m_Scales = { scale };
467 }
468 
470 {
471  if (!m_Quantization.m_Offset.has_value())
472  {
473  // NOTE: old default for backward compatibility
474  return 0;
475  }
476 
477  return m_Quantization.m_Offset.value();
478 }
479 
481 {
482  m_Quantization.m_Offset = MakeOptional<int32_t>(offset);
483 }
484 
486 {
487  return m_Quantization.m_QuantizationDim;
488 }
489 
491 {
492  m_Quantization.m_QuantizationDim = quantizationDim;
493 }
494 
496 {
497  return IsQuantizedType(m_DataType);
498 }
499 
500 // ---
501 // --- BaseTensor
502 // ---
503 
504 template<typename MemoryType>
506  : m_MemoryArea(nullptr)
507 {
508 }
509 
510 template<typename MemoryType>
511 BaseTensor<MemoryType>::BaseTensor(const TensorInfo& info, MemoryType memoryArea)
512  : m_MemoryArea(memoryArea)
513  , m_Info(info)
514 {
515 }
516 
517 template<typename MemoryType>
519  : m_MemoryArea(other.m_MemoryArea)
520  , m_Info(other.GetInfo())
521 {
522 }
523 
524 template<typename MemoryType>
526 {
527  m_Info = other.m_Info;
528  m_MemoryArea = other.m_MemoryArea;
529  return *this;
530 }
531 
532 // Explicit instantiations.
533 template class BaseTensor<const void*>;
534 template class BaseTensor<void*>;
535 
536 } // namespace armnn
unsigned int GetNumElements() const
Function that calculates the tensor elements by multiplying all dimension size which are Specified...
Definition: Tensor.cpp:181
bool operator!=(const TensorShape &other) const
Inequality comparison operator.
Definition: Tensor.cpp:169
unsigned int operator[](unsigned int i) const
Read only operator.
Definition: Tensor.cpp:135
bool IsTypeSpaceMatch(const TensorInfo &other) const
Check that the types are the same and, if quantize, that the quantization parameters are the same...
Definition: Tensor.cpp:423
Dimensionality
Definition: Types.hpp:136
TensorShape & operator=(const TensorShape &other)
Assignation function.
Definition: Tensor.cpp:124
bool AreAllDimensionsSpecified() const
Checks if there is at least one dimension not specified.
Definition: Tensor.cpp:241
constexpr bool IsQuantizedType()
Definition: TypesUtils.hpp:260
bool HasPerAxisQuantization() const
Definition: Tensor.cpp:437
Optional< unsigned int > GetQuantizationDim() const
Definition: Tensor.cpp:485
unsigned int GetNumBytes() const
Definition: Tensor.cpp:418
Copyright (c) 2021 ARM Limited and Contributors.
std::vector< float > GetQuantizationScales() const
Definition: Tensor.cpp:442
bool HasMultipleQuantizationScales() const
Definition: Tensor.hpp:197
bool GetDimensionSpecificity(unsigned int i) const
Gets information about if the dimension size has been specified or not.
Definition: Tensor.cpp:211
bool operator==(const TensorShape &other) const
Equality comparison operator.
Definition: Tensor.cpp:160
TensorShape()
Empty (invalid) constructor.
Definition: Tensor.cpp:25
TensorInfo()
Empty (invalid) constructor.
Definition: Tensor.cpp:341
DataType
Definition: Types.hpp:36
int32_t GetQuantizationOffset() const
Definition: Tensor.cpp:469
float GetQuantizationScale() const
Definition: Tensor.cpp:452
void SetNumDimensions(unsigned int numDimensions, bool initDimensionsSpecificity=false)
Sets the tensor rank and therefore the Dimensionality is set to Specified if it was not...
Definition: Tensor.cpp:219
void SetQuantizationScale(float scale)
Definition: Tensor.cpp:464
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
MemoryType m_MemoryArea
Definition: Tensor.hpp:299
const TensorInfo & GetInfo() const
Definition: Tensor.hpp:282
#define CHECK_LOCATION()
Definition: Exceptions.hpp:197
TensorInfo & operator=(const TensorInfo &other)
Definition: Tensor.cpp:398
void SetQuantizationDim(const Optional< unsigned int > &quantizationDim)
Definition: Tensor.cpp:490
bool operator==(const TensorInfo &other) const
Definition: Tensor.cpp:406
bool operator!=(const TensorInfo &other) const
Definition: Tensor.cpp:413
BaseTensor()
Empty (invalid) constructor.
Definition: Tensor.cpp:505
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
void SetDimensionSize(unsigned int i, unsigned int dimensionSize)
Sets the size of the indicated dimension and Specificity for that dimension is set to true...
Definition: Tensor.cpp:232
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
void SetQuantizationOffset(int32_t offset)
Definition: Tensor.cpp:480
void SetQuantizationScales(const std::vector< float > &scales)
Definition: Tensor.cpp:447
bool IsAtLeastOneDimensionSpecified() const
Checks if there is at least one dimension specified.
Definition: Tensor.cpp:257
bool IsQuantized() const
Definition: Tensor.cpp:495
constexpr unsigned int MaxNumOfTensorDimensions
Definition: Types.hpp:19
unsigned int GetNumElements() const
Definition: Tensor.hpp:192
constexpr unsigned int GetDataTypeSize(DataType dataType)
Definition: TypesUtils.hpp:137