// // 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 #include #include #include 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 dimensionSizeList) : TensorShape(boost::numeric_cast(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 dimensionSizeList, std::initializer_list dimensionsSpecificityList) { auto numDimensions = static_cast(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(); CheckUnspecifiedNumDimensions(); 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& quantizationScales, unsigned int quantizationDim) : m_Shape(shape) , m_DataType(dataType) { SetQuantizationScales(quantizationScales); SetQuantizationDim(MakeOptional(quantizationDim)); } TensorInfo::TensorInfo(unsigned int numDimensions, const unsigned int* dimensionSizes, DataType dataType, const std::vector& quantizationScales, unsigned int quantizationDim) : m_Shape(numDimensions, dimensionSizes) , m_DataType(dataType) { SetQuantizationScales(quantizationScales); SetQuantizationDim(MakeOptional(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 TensorInfo::GetQuantizationScales() const { return m_Quantization.m_Scales; } void TensorInfo::SetQuantizationScales(const std::vector& 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(offset); } Optional TensorInfo::GetQuantizationDim() const { return m_Quantization.m_QuantizationDim; } void TensorInfo::SetQuantizationDim(const Optional& quantizationDim) { m_Quantization.m_QuantizationDim = quantizationDim; } bool TensorInfo::IsQuantized() const { return IsQuantizedType(m_DataType); } // --- // --- BaseTensor // --- template BaseTensor::BaseTensor() : m_MemoryArea(nullptr) { } template BaseTensor::BaseTensor(const TensorInfo& info, MemoryType memoryArea) : m_MemoryArea(memoryArea) , m_Info(info) { } template BaseTensor::BaseTensor(const BaseTensor& other) : m_MemoryArea(other.m_MemoryArea) , m_Info(other.GetInfo()) { } template BaseTensor& BaseTensor::operator =(const BaseTensor& other) { m_Info = other.m_Info; m_MemoryArea = other.m_MemoryArea; return *this; } // Explicit instantiations. template class BaseTensor; template class BaseTensor; } // namespace armnn