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