| // |
| // Copyright © 2017 Arm Ltd. All rights reserved. |
| // SPDX-License-Identifier: MIT |
| // |
| |
| #include "armnn/Tensor.hpp" |
| #include "armnn/Utils.hpp" |
| #include "armnn/Exceptions.hpp" |
| #include "armnn/TypesUtils.hpp" |
| |
| #include <armnn/utility/Assert.hpp> |
| #include <armnn/utility/NumericCast.hpp> |
| |
| #include <iostream> |
| |
| #include <sstream> |
| |
| namespace armnn |
| { |
| |
| // --- |
| // --- TensorShape |
| // --- |
| |
| TensorShape::TensorShape() |
| : m_NumDimensions(0), m_Dimensionality(Dimensionality::Specified) |
| { |
| } |
| |
| TensorShape::TensorShape(unsigned int numDimensions, bool initDimensionsSpecificity) |
| : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified) |
| { |
| CheckValidNumDimensions(numDimensions); |
| |
| std::fill(m_Dimensions.begin(), m_Dimensions.begin() + m_NumDimensions, 0); |
| std::fill(m_DimensionsSpecificity.begin(), m_DimensionsSpecificity.begin() + m_NumDimensions, |
| initDimensionsSpecificity); |
| } |
| |
| TensorShape::TensorShape(const unsigned int numDimensions, const unsigned int* const dimensionSizes) |
| : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified) |
| { |
| CheckValidNumDimensions(numDimensions); |
| |
| if (dimensionSizes == nullptr) |
| { |
| throw InvalidArgumentException("Tensor dimensionSizes must not be NULL"); |
| } |
| |
| std::copy(dimensionSizes, dimensionSizes + numDimensions, m_Dimensions.begin()); |
| std::fill(m_DimensionsSpecificity.begin(), m_DimensionsSpecificity.begin() + m_NumDimensions, true); |
| } |
| |
| TensorShape::TensorShape(std::initializer_list<unsigned int> dimensionSizeList) |
| : TensorShape(armnn::numeric_cast<unsigned int>(dimensionSizeList.size()), dimensionSizeList.begin()) |
| { |
| } |
| |
| TensorShape::TensorShape(unsigned int numDimensions, |
| const unsigned int* const dimensionSizes, |
| const bool* const dimensionsSpecificity) |
| : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified) |
| { |
| CheckValidNumDimensions(numDimensions); |
| |
| if (dimensionSizes == nullptr) |
| { |
| throw InvalidArgumentException("Tensor dimensionSizes must not be NULL"); |
| } |
| |
| if (dimensionsSpecificity == nullptr) |
| { |
| throw InvalidArgumentException("Tensor dimensionsSpecificity must not be NULL"); |
| } |
| |
| std::copy(dimensionSizes, dimensionSizes + numDimensions, m_Dimensions.begin()); |
| std::copy(dimensionsSpecificity, dimensionsSpecificity + numDimensions, m_DimensionsSpecificity.begin()); |
| } |
| |
| TensorShape::TensorShape(std::initializer_list<unsigned int> dimensionSizeList, |
| std::initializer_list<bool> dimensionsSpecificityList) |
| { |
| auto numDimensions = static_cast<unsigned int>(dimensionSizeList.size()); |
| if (dimensionsSpecificityList.size() != numDimensions) |
| { |
| throw InvalidArgumentException("Tensors dimensionSizeList and dimensionsSpecificityList must be same size"); |
| } |
| |
| *this = TensorShape(numDimensions, dimensionSizeList.begin(), dimensionsSpecificityList.begin()); |
| } |
| |
| TensorShape::TensorShape(Dimensionality dimensionality) |
| : m_Dimensionality(dimensionality) |
| { |
| switch (dimensionality) |
| { |
| case Dimensionality::Specified: |
| throw InvalidArgumentException("Use other constructor to specify the rest of the values, this one is only " |
| "for tensors that have an unknown number of dimensions or that are scalar"); |
| break; |
| case Dimensionality::NotSpecified: |
| m_NumDimensions = 0; |
| m_Dimensions = {0}; |
| m_DimensionsSpecificity = {false}; |
| break; |
| case Dimensionality::Scalar: |
| m_NumDimensions = 1; |
| m_Dimensions = {1}; |
| m_DimensionsSpecificity = {true}; |
| break; |
| default: |
| throw InvalidArgumentException("Invalid Dimensionality value"); |
| } |
| } |
| |
| TensorShape::TensorShape(const TensorShape& other) |
| : m_NumDimensions(other.m_NumDimensions), m_Dimensionality(other.m_Dimensionality) |
| { |
| std::copy(other.m_Dimensions.cbegin(), other.m_Dimensions.cbegin() + other.m_NumDimensions, m_Dimensions.begin()); |
| std::copy(other.m_DimensionsSpecificity.cbegin(), other.m_DimensionsSpecificity.cbegin() + other.m_NumDimensions, |
| m_DimensionsSpecificity.begin()); |
| } |
| |
| TensorShape& TensorShape::operator =(const TensorShape& other) |
| { |
| m_NumDimensions = other.m_NumDimensions; |
| m_Dimensionality = other.m_Dimensionality; |
| std::copy(other.m_Dimensions.cbegin(), other.m_Dimensions.cbegin() + other.m_NumDimensions, m_Dimensions.begin()); |
| std::copy(other.m_DimensionsSpecificity.cbegin(), other.m_DimensionsSpecificity.cbegin() + other.m_NumDimensions, |
| m_DimensionsSpecificity.begin()); |
| return *this; |
| } |
| |
| // read |
| unsigned int TensorShape::operator[](unsigned int i) const |
| { |
| CheckUnspecifiedNumDimensions(); |
| CheckDimensionIndex(i); |
| CheckDimensionSpecified(i); |
| |
| return m_Dimensions.at(i); |
| } |
| |
| // read and write |
| unsigned int& TensorShape::operator[](unsigned int i) |
| { |
| if (Dimensionality::Scalar == m_Dimensionality) |
| { |
| std::stringstream errorMessage; |
| errorMessage << "TensorShape with Dimensionality::Scalar must be const to use operator[]"; |
| throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION()); |
| } |
| CheckUnspecifiedNumDimensions(); |
| CheckDimensionIndex(i); |
| CheckDimensionSpecified(i); |
| |
| return m_Dimensions.at(i); |
| } |
| |
| bool TensorShape::operator==(const TensorShape& other) const |
| { |
| return ((m_NumDimensions == other.m_NumDimensions) && |
| (m_Dimensionality == other.m_Dimensionality) && |
| std::equal(m_Dimensions.cbegin(), m_Dimensions.cbegin() + m_NumDimensions, other.m_Dimensions.cbegin()) && |
| std::equal(m_DimensionsSpecificity.cbegin(), m_DimensionsSpecificity.cbegin() + m_NumDimensions, |
| other.m_DimensionsSpecificity.cbegin())); |
| } |
| |
| bool TensorShape::operator!=(const TensorShape& other) const |
| { |
| return !(*this == other); |
| } |
| |
| unsigned int TensorShape::GetNumDimensions() const |
| { |
| CheckUnspecifiedNumDimensions(); |
| |
| return m_NumDimensions; |
| } |
| |
| unsigned int TensorShape::GetNumElements() const |
| { |
| CheckUnspecifiedNumDimensions(); |
| |
| if (m_NumDimensions == 0) |
| { |
| return 0; |
| } |
| |
| unsigned int count = 1; |
| bool atLeastOneDimensionSpecified = false; |
| for (unsigned int i = 0; i < m_NumDimensions; ++i) |
| { |
| if (m_DimensionsSpecificity[i]) |
| { |
| atLeastOneDimensionSpecified = true; |
| count *= m_Dimensions[i]; |
| } |
| } |
| |
| if (atLeastOneDimensionSpecified) |
| { |
| return count; |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| bool TensorShape:: GetDimensionSpecificity(unsigned int i) const |
| { |
| CheckUnspecifiedNumDimensions(); |
| CheckDimensionIndex(i); |
| |
| return m_DimensionsSpecificity[i]; |
| } |
| |
| void TensorShape::SetNumDimensions(unsigned int numDimensions, bool initDimensionsSpecificity) |
| { |
| CheckScalar(); |
| CheckSpecifiedNumDimensions(); |
| CheckValidNumDimensions(numDimensions); |
| |
| m_NumDimensions = numDimensions; |
| m_Dimensionality = Dimensionality::Specified; |
| std::fill(m_Dimensions.begin(), m_Dimensions.begin() + m_NumDimensions, 0); |
| std::fill(m_DimensionsSpecificity.begin(), m_DimensionsSpecificity.begin() + m_NumDimensions, |
| initDimensionsSpecificity); |
| } |
| |
| void TensorShape::SetDimensionSize(unsigned int i, unsigned int dimensionSize) |
| { |
| CheckScalar(); |
| CheckDimensionIndex(i); |
| |
| m_Dimensions[i] = dimensionSize; |
| m_DimensionsSpecificity[i] = true; |
| } |
| |
| bool TensorShape::AreAllDimensionsSpecified() const |
| { |
| CheckUnspecifiedNumDimensions(); |
| |
| bool areAllDimensionsSpecified = true; |
| for (unsigned int i = 0; i < m_NumDimensions; ++i) |
| { |
| if (!m_DimensionsSpecificity[i]) |
| { |
| areAllDimensionsSpecified = false; |
| break; |
| } |
| } |
| return areAllDimensionsSpecified; |
| } |
| |
| bool TensorShape::IsAtLeastOneDimensionSpecified() const |
| { |
| CheckUnspecifiedNumDimensions(); |
| |
| bool isAtLeastOneDimensionSpecified = false; |
| for (unsigned int i = 0; i < m_NumDimensions; ++i) |
| { |
| if (m_DimensionsSpecificity[i]) |
| { |
| isAtLeastOneDimensionSpecified = true; |
| break; |
| } |
| } |
| return isAtLeastOneDimensionSpecified; |
| } |
| |
| void TensorShape::CheckDimensionIndex(unsigned int i) const |
| { |
| if (i >= m_NumDimensions) |
| { |
| std::stringstream errorMessage; |
| errorMessage << "Invalid dimension index: " << i << " (number of dimensions is " << m_NumDimensions << ")"; |
| throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION()); |
| } |
| } |
| |
| void TensorShape::CheckValidNumDimensions(unsigned int numDimensions) |
| { |
| if (numDimensions < 1) |
| { |
| throw InvalidArgumentException("Tensor numDimensions must be greater than 0", CHECK_LOCATION()); |
| } |
| |
| if (numDimensions > MaxNumOfTensorDimensions) |
| { |
| throw InvalidArgumentException("Tensor numDimensions must be less than or equal to MaxNumOfTensorDimensions" |
| , CHECK_LOCATION()); |
| } |
| } |
| |
| void TensorShape::CheckDimensionSpecified(unsigned int i) const |
| { |
| if (!m_DimensionsSpecificity[i]) |
| { |
| std::stringstream errorMessage; |
| errorMessage << "Dimension index: " << i << " not specified. Tensor shape not inferred yet."; |
| throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION()); |
| } |
| } |
| |
| void TensorShape::CheckScalar() const |
| { |
| if (Dimensionality::Scalar == m_Dimensionality) |
| { |
| std::stringstream errorMessage; |
| errorMessage << "Invalid action on a tensor shape that holds a scalar value."; |
| throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION()); |
| } |
| } |
| |
| void TensorShape::CheckUnspecifiedNumDimensions() const |
| { |
| if (Dimensionality::NotSpecified == m_Dimensionality) |
| { |
| std::stringstream errorMessage; |
| errorMessage << "Invalid action on a tensor shape that has unknown number of dimensions."; |
| throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION()); |
| } |
| } |
| |
| void TensorShape::CheckSpecifiedNumDimensions() const |
| { |
| if (Dimensionality::Specified == m_Dimensionality) |
| { |
| std::stringstream errorMessage; |
| errorMessage << "Invalid action on a tensor shape that has known number of dimensions."; |
| throw InvalidArgumentException(errorMessage.str(), CHECK_LOCATION()); |
| } |
| } |
| |
| // --- |
| // --- TensorInfo |
| // --- |
| |
| TensorInfo::TensorInfo() |
| : m_DataType(DataType::Float32) |
| { |
| } |
| |
| TensorInfo::TensorInfo(const TensorShape& shape, |
| DataType dataType, |
| float quantizationScale, |
| int32_t quantizationOffset) |
| : m_Shape(shape) |
| , m_DataType(dataType) |
| { |
| SetQuantizationScale(quantizationScale); |
| SetQuantizationOffset(quantizationOffset); |
| } |
| |
| TensorInfo::TensorInfo(unsigned int numDimensions, |
| const unsigned int* dimensionSizes, |
| DataType dataType, |
| float quantizationScale, |
| int32_t quantizationOffset) |
| : m_Shape(numDimensions, dimensionSizes) |
| , m_DataType(dataType) |
| { |
| SetQuantizationScale(quantizationScale); |
| SetQuantizationOffset(quantizationOffset); |
| } |
| |
| TensorInfo::TensorInfo(const TensorShape& shape, |
| DataType dataType, |
| const std::vector<float>& quantizationScales, |
| unsigned int quantizationDim) |
| : m_Shape(shape) |
| , m_DataType(dataType) |
| { |
| SetQuantizationScales(quantizationScales); |
| SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim)); |
| } |
| |
| TensorInfo::TensorInfo(unsigned int numDimensions, |
| const unsigned int* dimensionSizes, |
| DataType dataType, |
| const std::vector<float>& quantizationScales, |
| unsigned int quantizationDim) |
| : m_Shape(numDimensions, dimensionSizes) |
| , m_DataType(dataType) |
| { |
| SetQuantizationScales(quantizationScales); |
| SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim)); |
| } |
| |
| TensorInfo::TensorInfo(const TensorInfo& other) |
| : m_Shape(other.m_Shape) |
| , m_DataType(other.m_DataType) |
| , m_Quantization(other.m_Quantization) |
| {} |
| |
| TensorInfo& TensorInfo::operator=(const TensorInfo& other) |
| { |
| m_Shape = other.m_Shape; |
| m_DataType = other.m_DataType; |
| m_Quantization = other.m_Quantization; |
| return *this; |
| } |
| |
| bool TensorInfo::operator==(const TensorInfo& other) const |
| { |
| return ((m_Shape == other.m_Shape) && |
| (m_DataType == other.m_DataType) && |
| (m_Quantization == other.m_Quantization)); |
| } |
| |
| bool TensorInfo::operator!=(const TensorInfo& other) const |
| { |
| return !(*this == other); |
| } |
| |
| unsigned int TensorInfo::GetNumBytes() const |
| { |
| return GetDataTypeSize(m_DataType) * GetNumElements(); |
| } |
| |
| bool TensorInfo::IsTypeSpaceMatch(const TensorInfo& other) const |
| { |
| bool match = true; |
| |
| match &= m_DataType == other.m_DataType; |
| |
| if (IsQuantized() && !HasMultipleQuantizationScales()) |
| { |
| match &= GetQuantizationScale() == other.GetQuantizationScale() && |
| GetQuantizationOffset() == other.GetQuantizationOffset(); |
| } |
| return match; |
| } |
| |
| bool TensorInfo::HasPerAxisQuantization() const |
| { |
| return HasMultipleQuantizationScales() || m_Quantization.m_QuantizationDim.has_value(); |
| } |
| |
| std::vector<float> TensorInfo::GetQuantizationScales() const |
| { |
| return m_Quantization.m_Scales; |
| } |
| |
| void TensorInfo::SetQuantizationScales(const std::vector<float>& scales) |
| { |
| m_Quantization.m_Scales = scales; |
| } |
| |
| float TensorInfo::GetQuantizationScale() const |
| { |
| if (m_Quantization.m_Scales.empty()) |
| { |
| // NOTE: old default for backward compatibility |
| return 1.0f; |
| } |
| |
| ARMNN_ASSERT(!HasMultipleQuantizationScales()); |
| return m_Quantization.m_Scales[0]; |
| } |
| |
| void TensorInfo::SetQuantizationScale(float scale) |
| { |
| m_Quantization.m_Scales = { scale }; |
| } |
| |
| int32_t TensorInfo::GetQuantizationOffset() const |
| { |
| if (!m_Quantization.m_Offset.has_value()) |
| { |
| // NOTE: old default for backward compatibility |
| return 0; |
| } |
| |
| return m_Quantization.m_Offset.value(); |
| } |
| |
| void TensorInfo::SetQuantizationOffset(int32_t offset) |
| { |
| m_Quantization.m_Offset = MakeOptional<int32_t>(offset); |
| } |
| |
| Optional<unsigned int> TensorInfo::GetQuantizationDim() const |
| { |
| return m_Quantization.m_QuantizationDim; |
| } |
| |
| void TensorInfo::SetQuantizationDim(const Optional<unsigned int>& quantizationDim) |
| { |
| m_Quantization.m_QuantizationDim = quantizationDim; |
| } |
| |
| bool TensorInfo::IsQuantized() const |
| { |
| return IsQuantizedType(m_DataType); |
| } |
| |
| // --- |
| // --- BaseTensor |
| // --- |
| |
| template<typename MemoryType> |
| BaseTensor<MemoryType>::BaseTensor() |
| : m_MemoryArea(nullptr) |
| { |
| } |
| |
| template<typename MemoryType> |
| BaseTensor<MemoryType>::BaseTensor(const TensorInfo& info, MemoryType memoryArea) |
| : m_MemoryArea(memoryArea) |
| , m_Info(info) |
| { |
| } |
| |
| template<typename MemoryType> |
| BaseTensor<MemoryType>::BaseTensor(const BaseTensor<MemoryType>& other) |
| : m_MemoryArea(other.m_MemoryArea) |
| , m_Info(other.GetInfo()) |
| { |
| } |
| |
| template<typename MemoryType> |
| BaseTensor<MemoryType>& BaseTensor<MemoryType>::operator =(const BaseTensor<MemoryType>& other) |
| { |
| m_Info = other.m_Info; |
| m_MemoryArea = other.m_MemoryArea; |
| return *this; |
| } |
| |
| // Explicit instantiations. |
| template class BaseTensor<const void*>; |
| template class BaseTensor<void*>; |
| |
| } // namespace armnn |