From 11f6ace7cd8bf8dd2cf40bdab57a66bfb0f25f0e Mon Sep 17 00:00:00 2001 From: Teresa Charlin Date: Tue, 23 Jun 2020 18:30:57 +0100 Subject: IVGCVSW-5020 Refactor TensorShape to host dynamic tensors and scalar values Signed-off-by: Teresa Charlin Change-Id: I63f97fad080dbfeec6433c2548f0311173051a6a --- include/armnn/Tensor.hpp | 109 +++++++++++++++++- include/armnn/Types.hpp | 7 ++ src/armnn/Tensor.cpp | 258 +++++++++++++++++++++++++++++++++++++----- src/armnn/test/TensorTest.cpp | 205 ++++++++++++++++++++++++++++++++- 4 files changed, 547 insertions(+), 32 deletions(-) diff --git a/include/armnn/Tensor.hpp b/include/armnn/Tensor.hpp index 545b71a03f..9e49f75d25 100644 --- a/include/armnn/Tensor.hpp +++ b/include/armnn/Tensor.hpp @@ -23,31 +23,130 @@ public: /// Empty (invalid) constructor. TensorShape(); - TensorShape(unsigned int numDimensions); - + /// Constructor for TensorShape + /// @param numDimensions - Tensor rank. + /// @param initDimensionsSpecificity (optional) - value to initialize the specificity of each dimension size. + explicit TensorShape(unsigned int numDimensions, bool initDimensionsSpecificity = true); + + /// Constructor for TensorShape + /// @param numDimensions - Tensor rank. + /// @param dimensionSizes - Size of each of dimension. TensorShape(unsigned int numDimensions, const unsigned int* dimensionSizes); + /// Constructor for TensorShape + /// @param dimensionSizeList - Size of each of dimension. TensorShape(std::initializer_list dimensionSizeList); + /// Copy Constructor for TensorShape + /// @param other - TensorShape to copy from. TensorShape(const TensorShape& other); + /// Constructor for TensorShape + /// @param numDimensions - Tensor rank. + /// @param dimensionSizes - Size of each of dimension. + /// @param dimensionsSpecificity - Flags to indicate which dimension has its size specified. + TensorShape(unsigned int numDimensions, const unsigned int* dimensionSizes, const bool* dimensionsSpecificity); + + /// Constructor for TensorShape + /// @param dimensionSizeList - Size of each of dimension. + /// @param dimensionsSpecificityList - Flags to indicate which dimension size is specified. + TensorShape(std::initializer_list dimensionSizeList, + std::initializer_list dimensionsSpecificityList); + + /// Constructor for TensorShape + /// @param dimensionality - Parameter to indicate if the Tensor is a Scalar, a Tensor of known dimensionality + /// or a Tensor of unknown dimensionality. + explicit TensorShape(Dimensionality dimensionality); + + /// Assignation function + /// @param other - TensorShape to copy from. TensorShape& operator=(const TensorShape& other); + /// Read only operator + /// @param i - Dimension index. unsigned int operator[](unsigned int i) const; + /// Read and write operator + /// @param i - Dimension index. unsigned int& operator[](unsigned int i); + /// Equality comparison operator + /// @param other - TensorShape to compare with. bool operator==(const TensorShape& other) const; + + /// Inequality comparison operator + /// @param other - TensorShape to compare with. bool operator!=(const TensorShape& other) const; - unsigned int GetNumDimensions() const { return m_NumDimensions; } + /// Function that returns the tensor rank. + /// @return - Tensor rank. + unsigned int GetNumDimensions() const; + + /// Function that calculates the tensor elements by multiplying all dimension size which are Specified. + /// @return - Total number of elements in the tensor. unsigned int GetNumElements() const; + /// Function that returns the tensor type. + /// @return - Parameter to indicate if the Tensor is a scalar, a Tensor of known dimensionality or + /// a Tensor of unknown dimensionality + Dimensionality GetDimensionality() const { return m_Dimensionality; } + + /// Gets information about if the dimension size has been specified or not + /// @param i - Dimension index. + /// @return - Flag to indicate if the dimension "i" has a specified size. + bool GetDimensionSpecificity(unsigned int i) const; + + /// Sets the tensor rank and therefore the Dimensionality is set to Specified if it was not. + /// @param numDimensions - Tensor rank. + /// @param initDimensionsSpecificity (optional) - value to initialize the specificity of each dimension size. + void SetNumDimensions(unsigned int numDimensions, bool initDimensionsSpecificity = false); + + /// Sets the size of the indicated dimension and Specificity for that dimension is set to true. + /// @param i - Dimension index. + /// @param dimensionSize - size of one dimension. + void SetDimensionSize(unsigned int i, unsigned int dimensionSize); + + /// Checks if there is at least one dimension not specified. AND of all array elements. + /// @return - True when all dimension sizes are specified. False when at least one dimension size is not specified. + bool AreAllDimensionsSpecified() const; + + /// Checks if there is at least one dimension specified. OR of all array elements. + /// @return - True at least one dimension sizes is specified. False when all dimension sizes are not specified. + bool IsAtLeastOneDimensionSpecified() const; + private: - std::array m_Dimensions; - unsigned int m_NumDimensions; + /// Array of the dimension sizes. + std::array m_Dimensions{}; + /// Array of flags to indicate if the size of each of the dimensions is specified or not + std::array m_DimensionsSpecificity = {true}; + + /// Tensor rank + unsigned int m_NumDimensions{}; + + /// Tensor type: Specified, NotSpecified or Scalar. + Dimensionality m_Dimensionality = Dimensionality::Specified; + + /// Checks if the dimension index given is within range. + /// @param i - Dimension index. void CheckDimensionIndex(unsigned int i) const; + + /// Checks if the tensor rank given is within range. + /// @param numDimensions - Tensor rank. + static void CheckValidNumDimensions(unsigned int numDimensions) ; + + /// Checks if the size of the dimension index given is specified. + /// @param i - Dimension index. + void CheckDimensionSpecified(unsigned int i) const; + + /// Checks if this is a scalar. + void CheckScalar() const; + + /// Checks if the number of dimensions is unknown, i.e. rank is unspecified. + void CheckUnspecifiedNumDimensions() const; + + /// Checks if the number of dimensions is known, i.e. rank is specified. + void CheckSpecifiedNumDimensions() const; }; class TensorInfo diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp index fb6f134766..11d807cd89 100644 --- a/include/armnn/Types.hpp +++ b/include/armnn/Types.hpp @@ -106,6 +106,13 @@ enum class ResizeMethod NearestNeighbor = 1 }; +enum class Dimensionality +{ + NotSpecified = 0, + Specified = 1, + Scalar = 2 +}; + /// /// The padding method modifies the output of pooling layers. /// In both supported methods, the values are ignored (they are diff --git a/src/armnn/Tensor.cpp b/src/armnn/Tensor.cpp index 7cb2f0ea13..6ad46c59c9 100644 --- a/src/armnn/Tensor.cpp +++ b/src/armnn/Tensor.cpp @@ -12,6 +12,8 @@ #include +#include + #include namespace armnn @@ -22,81 +24,147 @@ namespace armnn // --- TensorShape::TensorShape() - : m_NumDimensions(0) + : m_NumDimensions(0), m_Dimensionality(Dimensionality::Specified) { } -TensorShape::TensorShape(unsigned int numDimensions) - : m_NumDimensions(numDimensions) +TensorShape::TensorShape(unsigned int numDimensions, bool initDimensionsSpecificity) + : m_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified) { - if (numDimensions < 1) - { - throw InvalidArgumentException("Tensor numDimensions must be greater than 0"); - } - - if (numDimensions > MaxNumOfTensorDimensions) - { - throw InvalidArgumentException("Tensor numDimensions must be less than or equal to MaxNumOfTensorDimensions"); - } + 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_NumDimensions(numDimensions), m_Dimensionality(Dimensionality::Specified) { - if (numDimensions < 1) - { - throw InvalidArgumentException("Tensor numDimensions must be greater than 0"); - } + CheckValidNumDimensions(numDimensions); - if (numDimensions > MaxNumOfTensorDimensions) + if (dimensionSizes == nullptr) { - throw InvalidArgumentException("Tensor numDimensions must be less than or equal to MaxNumOfTensorDimensions"); + 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) - : TensorShape(boost::numeric_cast(dimensionSizeList.size()), dimensionSizeList.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_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) && - std::equal(m_Dimensions.cbegin(), m_Dimensions.cbegin() + m_NumDimensions, other.m_Dimensions.cbegin())); + (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 @@ -104,20 +172,104 @@ 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; - for (unsigned int i = 0; i < m_NumDimensions; i++) + bool atLeastOneDimensionSpecified = false; + for (unsigned int i = 0; i < m_NumDimensions; ++i) + { + if (m_DimensionsSpecificity[i] && m_Dimensions[i] != 0) + { + atLeastOneDimensionSpecified = true; + count *= m_Dimensions[i]; + } + } + + if (atLeastOneDimensionSpecified) + { + return count; + } + else { - count *= m_Dimensions[i]; + return 0; } +} - return count; +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 @@ -130,6 +282,60 @@ void TensorShape::CheckDimensionIndex(unsigned int i) const } } +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 // --- diff --git a/src/armnn/test/TensorTest.cpp b/src/armnn/test/TensorTest.cpp index 2e28a9ae7d..ed3925539b 100644 --- a/src/armnn/test/TensorTest.cpp +++ b/src/armnn/test/TensorTest.cpp @@ -4,6 +4,8 @@ // #include #include +#include + namespace armnn { @@ -135,12 +137,16 @@ BOOST_AUTO_TEST_CASE(ModifyTensorInfo) BOOST_AUTO_TEST_CASE(TensorShapeOperatorBrackets) { + const TensorShape constShape({0,1,2,3}); TensorShape shape({0,1,2,3}); + // Checks version of operator[] which returns an unsigned int. BOOST_TEST(shape[2] == 2); - // Checks the version of operator[] which returns a reference. shape[2] = 20; BOOST_TEST(shape[2] == 20); + + // Checks the version of operator[] which returns a reference. + BOOST_TEST(constShape[2] == 2); } BOOST_AUTO_TEST_CASE(TensorInfoPerAxisQuantization) @@ -177,4 +183,201 @@ BOOST_AUTO_TEST_CASE(TensorInfoPerAxisQuantization) BOOST_CHECK(tensorInfo1.GetQuantizationDim().value() == 1); } +BOOST_AUTO_TEST_CASE(TensorShape_scalar) +{ + float mutableDatum = 3.1416f; + + const armnn::TensorShape shape (armnn::Dimensionality::Scalar ); + armnn::TensorInfo info ( shape, DataType::Float32 ); + const armnn::Tensor tensor ( info, &mutableDatum ); + + BOOST_CHECK(armnn::Dimensionality::Scalar == shape.GetDimensionality()); + float scalarValue = *reinterpret_cast(tensor.GetMemoryArea()); + BOOST_CHECK_MESSAGE(mutableDatum == scalarValue, "Scalar value is " << scalarValue); + + armnn::TensorShape shape_equal; + armnn::TensorShape shape_different; + shape_equal = shape; + BOOST_TEST(shape_equal == shape); + BOOST_TEST(shape_different != shape); + BOOST_CHECK_MESSAGE(1 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements()); + BOOST_CHECK_MESSAGE(1 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK(true == shape.GetDimensionSpecificity(0)); + BOOST_CHECK(shape.AreAllDimensionsSpecified()); + BOOST_CHECK(shape.IsAtLeastOneDimensionSpecified()); + + BOOST_TEST(1 == shape[0]); + BOOST_TEST(1 == tensor.GetShape()[0]); + BOOST_TEST(1 == tensor.GetInfo().GetShape()[0]); + BOOST_CHECK_THROW( shape[1], InvalidArgumentException ); + + float newMutableDatum = 42.f; + std::memcpy(tensor.GetMemoryArea(), &newMutableDatum, sizeof(float)); + scalarValue = *reinterpret_cast(tensor.GetMemoryArea()); + BOOST_CHECK_MESSAGE(newMutableDatum == scalarValue, "Scalar value is " << scalarValue); +} + +BOOST_AUTO_TEST_CASE(TensorShape_DynamicTensorType1_unknownNumberDimensions) +{ + float mutableDatum = 3.1416f; + + armnn::TensorShape shape (armnn::Dimensionality::NotSpecified ); + armnn::TensorInfo info ( shape, DataType::Float32 ); + armnn::Tensor tensor ( info, &mutableDatum ); + + BOOST_CHECK(armnn::Dimensionality::NotSpecified == shape.GetDimensionality()); + BOOST_CHECK_THROW( shape[0], InvalidArgumentException ); + BOOST_CHECK_THROW( shape.GetNumElements(), InvalidArgumentException ); + BOOST_CHECK_THROW( shape.GetNumDimensions(), InvalidArgumentException ); + + armnn::TensorShape shape_equal; + armnn::TensorShape shape_different; + shape_equal = shape; + BOOST_TEST(shape_equal == shape); + BOOST_TEST(shape_different != shape); +} + +BOOST_AUTO_TEST_CASE(TensorShape_DynamicTensorType1_unknownAllDimensionsSizes) +{ + float mutableDatum = 3.1416f; + + armnn::TensorShape shape ( 3, false ); + armnn::TensorInfo info ( shape, DataType::Float32 ); + armnn::Tensor tensor ( info, &mutableDatum ); + + BOOST_CHECK(armnn::Dimensionality::Specified == shape.GetDimensionality()); + BOOST_CHECK_MESSAGE(0 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements()); + BOOST_CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK(false == shape.GetDimensionSpecificity(0)); + BOOST_CHECK(false == shape.GetDimensionSpecificity(1)); + BOOST_CHECK(false == shape.GetDimensionSpecificity(2)); + BOOST_CHECK(!shape.AreAllDimensionsSpecified()); + BOOST_CHECK(!shape.IsAtLeastOneDimensionSpecified()); + + armnn::TensorShape shape_equal; + armnn::TensorShape shape_different; + shape_equal = shape; + BOOST_TEST(shape_equal == shape); + BOOST_TEST(shape_different != shape); +} + +BOOST_AUTO_TEST_CASE(TensorShape_DynamicTensorType1_unknownSomeDimensionsSizes) +{ + std::vector mutableDatum { 42.f, 42.f, 42.f, + 0.0f, 0.1f, 0.2f }; + + armnn::TensorShape shape ( {2, 0, 3}, {true, false, true} ); + armnn::TensorInfo info ( shape, DataType::Float32 ); + armnn::Tensor tensor ( info, &mutableDatum ); + + BOOST_CHECK(armnn::Dimensionality::Specified == shape.GetDimensionality()); + BOOST_CHECK_MESSAGE(6 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements()); + BOOST_CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK(true == shape.GetDimensionSpecificity(0)); + BOOST_CHECK(false == shape.GetDimensionSpecificity(1)); + BOOST_CHECK(true == shape.GetDimensionSpecificity(2)); + BOOST_CHECK(!shape.AreAllDimensionsSpecified()); + BOOST_CHECK(shape.IsAtLeastOneDimensionSpecified()); + + BOOST_CHECK_THROW(shape[1], InvalidArgumentException); + BOOST_CHECK_THROW(tensor.GetShape()[1], InvalidArgumentException); + BOOST_CHECK_THROW(tensor.GetInfo().GetShape()[1], InvalidArgumentException); + + BOOST_TEST(2 == shape[0]); + BOOST_TEST(2 == tensor.GetShape()[0]); + BOOST_TEST(2 == tensor.GetInfo().GetShape()[0]); + BOOST_CHECK_THROW( shape[1], InvalidArgumentException ); + + BOOST_TEST(3 == shape[2]); + BOOST_TEST(3 == tensor.GetShape()[2]); + BOOST_TEST(3 == tensor.GetInfo().GetShape()[2]); + + armnn::TensorShape shape_equal; + armnn::TensorShape shape_different; + shape_equal = shape; + BOOST_TEST(shape_equal == shape); + BOOST_TEST(shape_different != shape); +} + +BOOST_AUTO_TEST_CASE(TensorShape_DynamicTensorType1_transitionFromUnknownToKnownDimensionsSizes) +{ + std::vector mutableDatum { 42.f, 42.f, 42.f, + 0.0f, 0.1f, 0.2f }; + + armnn::TensorShape shape (armnn::Dimensionality::NotSpecified ); + armnn::TensorInfo info ( shape, DataType::Float32 ); + armnn::Tensor tensor ( info, &mutableDatum ); + + // Specify the number of dimensions + shape.SetNumDimensions(3); + BOOST_CHECK(armnn::Dimensionality::Specified == shape.GetDimensionality()); + BOOST_CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK(false == shape.GetDimensionSpecificity(0)); + BOOST_CHECK(false == shape.GetDimensionSpecificity(1)); + BOOST_CHECK(false == shape.GetDimensionSpecificity(2)); + BOOST_CHECK(!shape.AreAllDimensionsSpecified()); + BOOST_CHECK(!shape.IsAtLeastOneDimensionSpecified()); + + // Specify dimension 0 and 2. + shape.SetDimensionSize(0, 2); + shape.SetDimensionSize(2, 3); + BOOST_CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK_MESSAGE(6 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements()); + BOOST_CHECK(true == shape.GetDimensionSpecificity(0)); + BOOST_CHECK(false == shape.GetDimensionSpecificity(1)); + BOOST_CHECK(true == shape.GetDimensionSpecificity(2)); + BOOST_CHECK(!shape.AreAllDimensionsSpecified()); + BOOST_CHECK(shape.IsAtLeastOneDimensionSpecified()); + + info.SetShape(shape); + armnn::Tensor tensor2( info, &mutableDatum ); + BOOST_TEST(2 == shape[0]); + BOOST_TEST(2 == tensor2.GetShape()[0]); + BOOST_TEST(2 == tensor2.GetInfo().GetShape()[0]); + + BOOST_CHECK_THROW(shape[1], InvalidArgumentException); + BOOST_CHECK_THROW(tensor.GetShape()[1], InvalidArgumentException); + BOOST_CHECK_THROW(tensor.GetInfo().GetShape()[1], InvalidArgumentException); + + BOOST_TEST(3 == shape[2]); + BOOST_TEST(3 == tensor2.GetShape()[2]); + BOOST_TEST(3 == tensor2.GetInfo().GetShape()[2]); + + armnn::TensorShape shape_equal; + armnn::TensorShape shape_different; + shape_equal = shape; + BOOST_TEST(shape_equal == shape); + BOOST_TEST(shape_different != shape); + + // Specify dimension 1. + shape.SetDimensionSize(1, 5); + BOOST_CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK_MESSAGE(30 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements()); + BOOST_CHECK(true == shape.GetDimensionSpecificity(0)); + BOOST_CHECK(true == shape.GetDimensionSpecificity(1)); + BOOST_CHECK(true == shape.GetDimensionSpecificity(2)); + BOOST_CHECK(shape.AreAllDimensionsSpecified()); + BOOST_CHECK(shape.IsAtLeastOneDimensionSpecified()); +} + +BOOST_AUTO_TEST_CASE(Tensor_emptyConstructors) +{ + auto shape = armnn::TensorShape(); + BOOST_CHECK_MESSAGE( 0 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions()); + BOOST_CHECK_MESSAGE( 0 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements()); + BOOST_CHECK( armnn::Dimensionality::Specified == shape.GetDimensionality()); + BOOST_CHECK( shape.AreAllDimensionsSpecified()); + BOOST_CHECK_THROW( shape[0], InvalidArgumentException ); + + auto tensor = armnn::Tensor(); + BOOST_CHECK_MESSAGE( 0 == tensor.GetNumDimensions(), "Number of dimensions is " << tensor.GetNumDimensions()); + BOOST_CHECK_MESSAGE( 0 == tensor.GetNumElements(), "Number of elements is " << tensor.GetNumElements()); + BOOST_CHECK_MESSAGE( 0 == tensor.GetShape().GetNumDimensions(), "Number of dimensions is " << + tensor.GetShape().GetNumDimensions()); + BOOST_CHECK_MESSAGE( 0 == tensor.GetShape().GetNumElements(), "Number of dimensions is " << + tensor.GetShape().GetNumElements()); + BOOST_CHECK( armnn::Dimensionality::Specified == tensor.GetShape().GetDimensionality()); + BOOST_CHECK( tensor.GetShape().AreAllDimensionsSpecified()); + BOOST_CHECK_THROW( tensor.GetShape()[0], InvalidArgumentException ); +} BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.1