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)
343 {
344 }
345
TensorInfo(const TensorShape & shape,DataType dataType,float quantizationScale,int32_t quantizationOffset)346 TensorInfo::TensorInfo(const TensorShape& shape,
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
TensorInfo(unsigned int numDimensions,const unsigned int * dimensionSizes,DataType dataType,float quantizationScale,int32_t quantizationOffset)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
TensorInfo(const TensorShape & shape,DataType dataType,const std::vector<float> & quantizationScales,unsigned int quantizationDim)369 TensorInfo::TensorInfo(const TensorShape& shape,
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
TensorInfo(unsigned int numDimensions,const unsigned int * dimensionSizes,DataType dataType,const std::vector<float> & quantizationScales,unsigned int quantizationDim)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
TensorInfo(const TensorInfo & other)392 TensorInfo::TensorInfo(const TensorInfo& other)
393 : m_Shape(other.m_Shape)
394 , m_DataType(other.m_DataType)
395 , m_Quantization(other.m_Quantization)
396 {}
397
operator =(const TensorInfo & other)398 TensorInfo& TensorInfo::operator=(const TensorInfo& other)
399 {
400 m_Shape = other.m_Shape;
401 m_DataType = other.m_DataType;
402 m_Quantization = other.m_Quantization;
403 return *this;
404 }
405
operator ==(const TensorInfo & other) const406 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
operator !=(const TensorInfo & other) const413 bool TensorInfo::operator!=(const TensorInfo& other) const
414 {
415 return !(*this == other);
416 }
417
GetNumBytes() const418 unsigned int TensorInfo::GetNumBytes() const
419 {
420 return GetDataTypeSize(m_DataType) * GetNumElements();
421 }
422
IsTypeSpaceMatch(const TensorInfo & other) const423 bool TensorInfo::IsTypeSpaceMatch(const TensorInfo& other) const
424 {
425 bool match = true;
426
427 match &= m_DataType == other.m_DataType;
428
429 if (IsQuantized() && !HasMultipleQuantizationScales())
430 {
431 match &= GetQuantizationScale() == other.GetQuantizationScale() &&
432 GetQuantizationOffset() == other.GetQuantizationOffset();
433 }
434 return match;
435 }
436
HasPerAxisQuantization() const437 bool TensorInfo::HasPerAxisQuantization() const
438 {
439 return HasMultipleQuantizationScales() || m_Quantization.m_QuantizationDim.has_value();
440 }
441
GetQuantizationScales() const442 std::vector<float> TensorInfo::GetQuantizationScales() const
443 {
444 return m_Quantization.m_Scales;
445 }
446
SetQuantizationScales(const std::vector<float> & scales)447 void TensorInfo::SetQuantizationScales(const std::vector<float>& scales)
448 {
449 m_Quantization.m_Scales = scales;
450 }
451
GetQuantizationScale() const452 float TensorInfo::GetQuantizationScale() const
453 {
454 if (m_Quantization.m_Scales.empty())
455 {
456 // NOTE: old default for backward compatibility
457 return 1.0f;
458 }
459
460 ARMNN_ASSERT(!HasMultipleQuantizationScales());
461 return m_Quantization.m_Scales[0];
462 }
463
SetQuantizationScale(float scale)464 void TensorInfo::SetQuantizationScale(float scale)
465 {
466 m_Quantization.m_Scales = { scale };
467 }
468
GetQuantizationOffset() const469 int32_t TensorInfo::GetQuantizationOffset() const
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
SetQuantizationOffset(int32_t offset)480 void TensorInfo::SetQuantizationOffset(int32_t offset)
481 {
482 m_Quantization.m_Offset = MakeOptional<int32_t>(offset);
483 }
484
GetQuantizationDim() const485 Optional<unsigned int> TensorInfo::GetQuantizationDim() const
486 {
487 return m_Quantization.m_QuantizationDim;
488 }
489
SetQuantizationDim(const Optional<unsigned int> & quantizationDim)490 void TensorInfo::SetQuantizationDim(const Optional<unsigned int>& quantizationDim)
491 {
492 m_Quantization.m_QuantizationDim = quantizationDim;
493 }
494
IsQuantized() const495 bool TensorInfo::IsQuantized() const
496 {
497 return IsQuantizedType(m_DataType);
498 }
499
500 // ---
501 // --- BaseTensor
502 // ---
503
504 template<typename MemoryType>
BaseTensor()505 BaseTensor<MemoryType>::BaseTensor()
506 : m_MemoryArea(nullptr)
507 {
508 }
509
510 template<typename MemoryType>
BaseTensor(const TensorInfo & info,MemoryType memoryArea)511 BaseTensor<MemoryType>::BaseTensor(const TensorInfo& info, MemoryType memoryArea)
512 : m_MemoryArea(memoryArea)
513 , m_Info(info)
514 {
515 }
516
517 template<typename MemoryType>
BaseTensor(const BaseTensor<MemoryType> & other)518 BaseTensor<MemoryType>::BaseTensor(const BaseTensor<MemoryType>& other)
519 : m_MemoryArea(other.m_MemoryArea)
520 , m_Info(other.GetInfo())
521 {
522 }
523
524 template<typename MemoryType>
operator =(const BaseTensor<MemoryType> & other)525 BaseTensor<MemoryType>& BaseTensor<MemoryType>::operator =(const BaseTensor<MemoryType>& other)
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
537