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 #include <armnn/utility/NumericCast.hpp>
13
14 #include <iostream>
15
16 #include <sstream>
17
18 namespace armnn
19 {
20
21 // ---
22 // --- TensorShape
23 // ---
24
TensorShape()25 TensorShape::TensorShape()
26 : m_NumDimensions(0), m_Dimensionality(Dimensionality::Specified)
27 {
28 }
29
TensorShape(unsigned int numDimensions,bool initDimensionsSpecificity)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
TensorShape(const unsigned int numDimensions,const unsigned int * const dimensionSizes)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
TensorShape(std::initializer_list<unsigned int> dimensionSizeList)54 TensorShape::TensorShape(std::initializer_list<unsigned int> dimensionSizeList)
55 : TensorShape(armnn::numeric_cast<unsigned int>(dimensionSizeList.size()), dimensionSizeList.begin())
56 {
57 }
58
TensorShape(unsigned int numDimensions,const unsigned int * const dimensionSizes,const bool * const dimensionsSpecificity)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
TensorShape(std::initializer_list<unsigned int> dimensionSizeList,std::initializer_list<bool> dimensionsSpecificityList)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
TensorShape(Dimensionality dimensionality)92 TensorShape::TensorShape(Dimensionality dimensionality)
93 : m_Dimensionality(dimensionality)
94 {
95 switch (dimensionality)
96 {
97 case Dimensionality::Specified:
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;
101 case Dimensionality::NotSpecified:
102 m_NumDimensions = 0;
103 m_Dimensions = {0};
104 m_DimensionsSpecificity = {false};
105 break;
106 case Dimensionality::Scalar:
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
TensorShape(const TensorShape & other)116 TensorShape::TensorShape(const TensorShape& other)
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
operator =(const TensorShape & other)124 TensorShape& TensorShape::operator =(const TensorShape& other)
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
operator [](unsigned int i) const135 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
operator [](unsigned int i)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
operator ==(const TensorShape & other) const160 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
operator !=(const TensorShape & other) const169 bool TensorShape::operator!=(const TensorShape& other) const
170 {
171 return !(*this == other);
172 }
173
GetNumDimensions() const174 unsigned int TensorShape::GetNumDimensions() const
175 {
176 CheckUnspecifiedNumDimensions();
177
178 return m_NumDimensions;
179 }
180
GetNumElements() const181 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
GetDimensionSpecificity(unsigned int i) const211 bool TensorShape:: GetDimensionSpecificity(unsigned int i) const
212 {
213 CheckUnspecifiedNumDimensions();
214 CheckDimensionIndex(i);
215
216 return m_DimensionsSpecificity[i];
217 }
218
SetNumDimensions(unsigned int numDimensions,bool initDimensionsSpecificity)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
SetDimensionSize(unsigned int i,unsigned int dimensionSize)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
AreAllDimensionsSpecified() const241 bool TensorShape::AreAllDimensionsSpecified() const
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
IsAtLeastOneDimensionSpecified() const257 bool TensorShape::IsAtLeastOneDimensionSpecified() const
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
CheckDimensionIndex(unsigned int i) const273 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
CheckValidNumDimensions(unsigned int numDimensions)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
CheckDimensionSpecified(unsigned int i) const297 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
CheckScalar() const307 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
CheckUnspecifiedNumDimensions() const317 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
CheckSpecifiedNumDimensions() const327 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
TensorInfo()341 TensorInfo::TensorInfo()
342 : m_DataType(DataType::Float32), m_IsConstant(false)
343 {
344 }
345
TensorInfo(const TensorShape & shape,DataType dataType,float quantizationScale,int32_t quantizationOffset,bool isConstant)346 TensorInfo::TensorInfo(const TensorShape& shape,
347 DataType dataType,
348 float quantizationScale,
349 int32_t quantizationOffset,
350 bool isConstant)
351 : m_Shape(shape)
352 , m_DataType(dataType)
353 , m_IsConstant(isConstant)
354 {
355 SetQuantizationScale(quantizationScale);
356 SetQuantizationOffset(quantizationOffset);
357 }
358
TensorInfo(unsigned int numDimensions,const unsigned int * dimensionSizes,DataType dataType,float quantizationScale,int32_t quantizationOffset,bool isConstant)359 TensorInfo::TensorInfo(unsigned int numDimensions,
360 const unsigned int* dimensionSizes,
361 DataType dataType,
362 float quantizationScale,
363 int32_t quantizationOffset,
364 bool isConstant)
365 : m_Shape(numDimensions, dimensionSizes), m_DataType(dataType), m_IsConstant(isConstant)
366 {
367 SetQuantizationScale(quantizationScale);
368 SetQuantizationOffset(quantizationOffset);
369 }
370
TensorInfo(const TensorShape & shape,DataType dataType,const std::vector<float> & quantizationScales,unsigned int quantizationDim,bool isConstant)371 TensorInfo::TensorInfo(const TensorShape& shape,
372 DataType dataType,
373 const std::vector<float>& quantizationScales,
374 unsigned int quantizationDim,
375 bool isConstant)
376 : m_Shape(shape)
377 , m_DataType(dataType)
378 , m_IsConstant(isConstant)
379 {
380 SetQuantizationScales(quantizationScales);
381 SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim));
382 }
383
TensorInfo(unsigned int numDimensions,const unsigned int * dimensionSizes,DataType dataType,const std::vector<float> & quantizationScales,unsigned int quantizationDim,bool isConstant)384 TensorInfo::TensorInfo(unsigned int numDimensions,
385 const unsigned int* dimensionSizes,
386 DataType dataType,
387 const std::vector<float>& quantizationScales,
388 unsigned int quantizationDim,
389 bool isConstant)
390 : m_Shape(numDimensions, dimensionSizes)
391 , m_DataType(dataType)
392 , m_IsConstant(isConstant)
393 {
394 SetQuantizationScales(quantizationScales);
395 SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim));
396 }
397
TensorInfo(const TensorInfo & other)398 TensorInfo::TensorInfo(const TensorInfo& other)
399 : m_Shape(other.m_Shape)
400 , m_DataType(other.m_DataType)
401 , m_IsConstant(other.m_IsConstant)
402 , m_Quantization(other.m_Quantization)
403 {}
404
operator =(const TensorInfo & other)405 TensorInfo& TensorInfo::operator=(const TensorInfo& other)
406 {
407 m_Shape = other.m_Shape;
408 m_DataType = other.m_DataType;
409 m_Quantization = other.m_Quantization;
410 m_IsConstant = other.m_IsConstant;
411 return *this;
412 }
413
operator ==(const TensorInfo & other) const414 bool TensorInfo::operator==(const TensorInfo& other) const
415 {
416 return ((m_Shape == other.m_Shape) &&
417 (m_DataType == other.m_DataType) &&
418 (m_Quantization == other.m_Quantization) &&
419 (m_IsConstant == other.m_IsConstant));
420 }
421
operator !=(const TensorInfo & other) const422 bool TensorInfo::operator!=(const TensorInfo& other) const
423 {
424 return !(*this == other);
425 }
426
GetNumBytes() const427 unsigned int TensorInfo::GetNumBytes() const
428 {
429 return GetDataTypeSize(m_DataType) * GetNumElements();
430 }
431
IsTypeSpaceMatch(const TensorInfo & other) const432 bool TensorInfo::IsTypeSpaceMatch(const TensorInfo& other) const
433 {
434 bool match = true;
435
436 match &= m_DataType == other.m_DataType;
437
438 if (IsQuantized() && !HasMultipleQuantizationScales())
439 {
440 match &= GetQuantizationScale() == other.GetQuantizationScale() &&
441 GetQuantizationOffset() == other.GetQuantizationOffset();
442 }
443 return match;
444 }
445
HasPerAxisQuantization() const446 bool TensorInfo::HasPerAxisQuantization() const
447 {
448 return HasMultipleQuantizationScales() || m_Quantization.m_QuantizationDim.has_value();
449 }
450
GetQuantizationScales() const451 std::vector<float> TensorInfo::GetQuantizationScales() const
452 {
453 return m_Quantization.m_Scales;
454 }
455
SetQuantizationScales(const std::vector<float> & scales)456 void TensorInfo::SetQuantizationScales(const std::vector<float>& scales)
457 {
458 m_Quantization.m_Scales = scales;
459 }
460
GetQuantizationScale() const461 float TensorInfo::GetQuantizationScale() const
462 {
463 if (m_Quantization.m_Scales.empty())
464 {
465 // NOTE: old default for backward compatibility
466 return 1.0f;
467 }
468
469 ARMNN_ASSERT(!HasMultipleQuantizationScales());
470 return m_Quantization.m_Scales[0];
471 }
472
SetQuantizationScale(float scale)473 void TensorInfo::SetQuantizationScale(float scale)
474 {
475 m_Quantization.m_Scales = { scale };
476 }
477
GetQuantizationOffset() const478 int32_t TensorInfo::GetQuantizationOffset() const
479 {
480 if (!m_Quantization.m_Offset.has_value())
481 {
482 // NOTE: old default for backward compatibility
483 return 0;
484 }
485
486 return m_Quantization.m_Offset.value();
487 }
488
SetQuantizationOffset(int32_t offset)489 void TensorInfo::SetQuantizationOffset(int32_t offset)
490 {
491 m_Quantization.m_Offset = MakeOptional<int32_t>(offset);
492 }
493
GetQuantizationDim() const494 Optional<unsigned int> TensorInfo::GetQuantizationDim() const
495 {
496 return m_Quantization.m_QuantizationDim;
497 }
498
SetQuantizationDim(const Optional<unsigned int> & quantizationDim)499 void TensorInfo::SetQuantizationDim(const Optional<unsigned int>& quantizationDim)
500 {
501 m_Quantization.m_QuantizationDim = quantizationDim;
502 }
503
IsQuantized() const504 bool TensorInfo::IsQuantized() const
505 {
506 return IsQuantizedType(m_DataType);
507 }
508
IsConstant() const509 bool TensorInfo::IsConstant() const
510 {
511 return m_IsConstant;
512 }
513
SetConstant(const bool IsConstant)514 void TensorInfo::SetConstant(const bool IsConstant)
515 {
516 m_IsConstant = IsConstant;
517 }
518
519 // ---
520 // --- BaseTensor
521 // ---
522
523 template<typename MemoryType>
BaseTensor()524 BaseTensor<MemoryType>::BaseTensor()
525 : m_MemoryArea(nullptr)
526 {
527 }
528
529 template<typename MemoryType>
BaseTensor(const TensorInfo & info,MemoryType memoryArea)530 BaseTensor<MemoryType>::BaseTensor(const TensorInfo& info, MemoryType memoryArea)
531 : m_MemoryArea(memoryArea)
532 , m_Info(info)
533 {
534 }
535
536 template<typename MemoryType>
BaseTensor(const BaseTensor<MemoryType> & other)537 BaseTensor<MemoryType>::BaseTensor(const BaseTensor<MemoryType>& other)
538 : m_MemoryArea(other.m_MemoryArea)
539 , m_Info(other.GetInfo())
540 {
541 }
542
543 template<typename MemoryType>
operator =(const BaseTensor<MemoryType> & other)544 BaseTensor<MemoryType>& BaseTensor<MemoryType>::operator =(const BaseTensor<MemoryType>& other)
545 {
546 m_Info = other.m_Info;
547 m_MemoryArea = other.m_MemoryArea;
548 return *this;
549 }
550
551 // Explicit instantiations.
552 template class BaseTensor<const void*>;
553 template class BaseTensor<void*>;
554
555 } // namespace armnn
556