blob: 3b116d90e0886463015e2d0092ae6b2c7eacb1a8 [file] [log] [blame]
//
// Copyright © 2017,2024 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), m_IsConstant(false)
{
}
TensorInfo::TensorInfo(const TensorShape& shape,
DataType dataType,
float quantizationScale,
int32_t quantizationOffset,
bool isConstant)
: m_Shape(shape)
, m_DataType(dataType)
, m_IsConstant(isConstant)
{
SetQuantizationScale(quantizationScale);
SetQuantizationOffset(quantizationOffset);
}
TensorInfo::TensorInfo(unsigned int numDimensions,
const unsigned int* dimensionSizes,
DataType dataType,
float quantizationScale,
int32_t quantizationOffset,
bool isConstant)
: m_Shape(numDimensions, dimensionSizes), m_DataType(dataType), m_IsConstant(isConstant)
{
SetQuantizationScale(quantizationScale);
SetQuantizationOffset(quantizationOffset);
}
TensorInfo::TensorInfo(const TensorShape& shape,
DataType dataType,
const std::vector<float>& quantizationScales,
unsigned int quantizationDim,
bool isConstant)
: m_Shape(shape)
, m_DataType(dataType)
, m_IsConstant(isConstant)
{
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,
bool isConstant)
: m_Shape(numDimensions, dimensionSizes)
, m_DataType(dataType)
, m_IsConstant(isConstant)
{
SetQuantizationScales(quantizationScales);
SetQuantizationDim(MakeOptional<unsigned int>(quantizationDim));
}
TensorInfo::TensorInfo(const TensorInfo& other)
: m_Shape(other.m_Shape)
, m_DataType(other.m_DataType)
, m_IsConstant(other.m_IsConstant)
, 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;
m_IsConstant = other.m_IsConstant;
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) &&
(m_IsConstant == other.m_IsConstant));
}
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);
}
bool TensorInfo::IsConstant() const
{
return m_IsConstant;
}
void TensorInfo::SetConstant(const bool IsConstant)
{
m_IsConstant = IsConstant;
}
// ---
// --- 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