blob: 1ecad503d4bd701d10cb6283c576862762d2e34e [file]
//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//
#include <armnn/Tensor.hpp>
#include <armnn/utility/IgnoreUnused.hpp>
#include <doctest/doctest.h>
using namespace armnn;
TEST_SUITE("Tensor")
{
struct TensorInfoFixture
{
TensorInfoFixture()
{
unsigned int sizes[] = {6,7,8,9};
m_TensorInfo = TensorInfo(4, sizes, DataType::Float32);
}
~TensorInfoFixture() {};
TensorInfo m_TensorInfo;
};
TEST_CASE_FIXTURE(TensorInfoFixture, "ConstructShapeUsingListInitialization")
{
TensorShape listInitializedShape{ 6, 7, 8, 9 };
CHECK(listInitializedShape == m_TensorInfo.GetShape());
}
TEST_CASE_FIXTURE(TensorInfoFixture, "ConstructTensorInfo")
{
CHECK(m_TensorInfo.GetNumDimensions() == 4);
CHECK(m_TensorInfo.GetShape()[0] == 6); // <= Outer most
CHECK(m_TensorInfo.GetShape()[1] == 7);
CHECK(m_TensorInfo.GetShape()[2] == 8);
CHECK(m_TensorInfo.GetShape()[3] == 9); // <= Inner most
}
TEST_CASE_FIXTURE(TensorInfoFixture, "CopyConstructTensorInfo")
{
TensorInfo copyConstructed(m_TensorInfo);
CHECK(copyConstructed.GetNumDimensions() == 4);
CHECK(copyConstructed.GetShape()[0] == 6);
CHECK(copyConstructed.GetShape()[1] == 7);
CHECK(copyConstructed.GetShape()[2] == 8);
CHECK(copyConstructed.GetShape()[3] == 9);
}
TEST_CASE_FIXTURE(TensorInfoFixture, "TensorInfoEquality")
{
TensorInfo copyConstructed(m_TensorInfo);
CHECK(copyConstructed == m_TensorInfo);
}
TEST_CASE_FIXTURE(TensorInfoFixture, "TensorInfoInequality")
{
TensorInfo other;
unsigned int sizes[] = {2,3,4,5};
other = TensorInfo(4, sizes, DataType::Float32);
CHECK(other != m_TensorInfo);
}
TEST_CASE_FIXTURE(TensorInfoFixture, "TensorInfoAssignmentOperator")
{
TensorInfo copy;
copy = m_TensorInfo;
CHECK(copy == m_TensorInfo);
}
TEST_CASE("CopyNoQuantizationTensorInfo")
{
TensorInfo infoA;
infoA.SetShape({ 5, 6, 7, 8 });
infoA.SetDataType(DataType::QAsymmU8);
TensorInfo infoB;
infoB.SetShape({ 5, 6, 7, 8 });
infoB.SetDataType(DataType::QAsymmU8);
infoB.SetQuantizationScale(10.0f);
infoB.SetQuantizationOffset(5);
infoB.SetQuantizationDim(Optional<unsigned int>(1));
CHECK((infoA.GetShape() == TensorShape({ 5, 6, 7, 8 })));
CHECK((infoA.GetDataType() == DataType::QAsymmU8));
CHECK(infoA.GetQuantizationScale() == 1);
CHECK(infoA.GetQuantizationOffset() == 0);
CHECK(!infoA.GetQuantizationDim().has_value());
CHECK(infoA != infoB);
infoA = infoB;
CHECK(infoA == infoB);
CHECK((infoA.GetShape() == TensorShape({ 5, 6, 7, 8 })));
CHECK((infoA.GetDataType() == DataType::QAsymmU8));
CHECK(infoA.GetQuantizationScale() == 10.0f);
CHECK(infoA.GetQuantizationOffset() == 5);
CHECK(infoA.GetQuantizationDim().value() == 1);
}
TEST_CASE("CopyDifferentQuantizationTensorInfo")
{
TensorInfo infoA;
infoA.SetShape({ 5, 6, 7, 8 });
infoA.SetDataType(DataType::QAsymmU8);
infoA.SetQuantizationScale(10.0f);
infoA.SetQuantizationOffset(5);
infoA.SetQuantizationDim(Optional<unsigned int>(1));
TensorInfo infoB;
infoB.SetShape({ 5, 6, 7, 8 });
infoB.SetDataType(DataType::QAsymmU8);
infoB.SetQuantizationScale(11.0f);
infoB.SetQuantizationOffset(6);
infoB.SetQuantizationDim(Optional<unsigned int>(2));
CHECK((infoA.GetShape() == TensorShape({ 5, 6, 7, 8 })));
CHECK((infoA.GetDataType() == DataType::QAsymmU8));
CHECK(infoA.GetQuantizationScale() == 10.0f);
CHECK(infoA.GetQuantizationOffset() == 5);
CHECK(infoA.GetQuantizationDim().value() == 1);
CHECK(infoA != infoB);
infoA = infoB;
CHECK(infoA == infoB);
CHECK((infoA.GetShape() == TensorShape({ 5, 6, 7, 8 })));
CHECK((infoA.GetDataType() == DataType::QAsymmU8));
CHECK(infoA.GetQuantizationScale() == 11.0f);
CHECK(infoA.GetQuantizationOffset() == 6);
CHECK(infoA.GetQuantizationDim().value() == 2);
}
void CheckTensor(const ConstTensor& t)
{
t.GetInfo();
}
TEST_CASE("TensorVsConstTensor")
{
int mutableDatum = 2;
const int immutableDatum = 3;
armnn::Tensor uninitializedTensor;
armnn::ConstTensor uninitializedTensor2;
uninitializedTensor2 = uninitializedTensor;
armnn::Tensor t(TensorInfo(), &mutableDatum);
armnn::ConstTensor ct(TensorInfo(), &immutableDatum);
// Checks that both Tensor and ConstTensor can be passed as a ConstTensor.
CheckTensor(t);
CheckTensor(ct);
}
TEST_CASE("ModifyTensorInfo")
{
TensorInfo info;
info.SetShape({ 5, 6, 7, 8 });
CHECK((info.GetShape() == TensorShape({ 5, 6, 7, 8 })));
info.SetDataType(DataType::QAsymmU8);
CHECK((info.GetDataType() == DataType::QAsymmU8));
info.SetQuantizationScale(10.0f);
CHECK(info.GetQuantizationScale() == 10.0f);
info.SetQuantizationOffset(5);
CHECK(info.GetQuantizationOffset() == 5);
}
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.
CHECK(shape[2] == 2);
shape[2] = 20;
CHECK(shape[2] == 20);
// Checks the version of operator[] which returns a reference.
CHECK(constShape[2] == 2);
}
TEST_CASE("TensorInfoPerAxisQuantization")
{
// Old constructor
TensorInfo tensorInfo0({ 1, 1 }, DataType::Float32, 2.0f, 1);
CHECK(!tensorInfo0.HasMultipleQuantizationScales());
CHECK(tensorInfo0.GetQuantizationScale() == 2.0f);
CHECK(tensorInfo0.GetQuantizationOffset() == 1);
CHECK(tensorInfo0.GetQuantizationScales()[0] == 2.0f);
CHECK(!tensorInfo0.GetQuantizationDim().has_value());
// Set per-axis quantization scales
std::vector<float> perAxisScales{ 3.0f, 4.0f };
tensorInfo0.SetQuantizationScales(perAxisScales);
CHECK(tensorInfo0.HasMultipleQuantizationScales());
CHECK(tensorInfo0.GetQuantizationScales() == perAxisScales);
// Set per-tensor quantization scale
tensorInfo0.SetQuantizationScale(5.0f);
CHECK(!tensorInfo0.HasMultipleQuantizationScales());
CHECK(tensorInfo0.GetQuantizationScales()[0] == 5.0f);
// Set quantization offset
tensorInfo0.SetQuantizationDim(Optional<unsigned int>(1));
CHECK(tensorInfo0.GetQuantizationDim().value() == 1);
// New constructor
perAxisScales = { 6.0f, 7.0f };
TensorInfo tensorInfo1({ 1, 1 }, DataType::Float32, perAxisScales, 1);
CHECK(tensorInfo1.HasMultipleQuantizationScales());
CHECK(tensorInfo1.GetQuantizationOffset() == 0);
CHECK(tensorInfo1.GetQuantizationScales() == perAxisScales);
CHECK(tensorInfo1.GetQuantizationDim().value() == 1);
}
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 );
CHECK(armnn::Dimensionality::Scalar == shape.GetDimensionality());
float scalarValue = *reinterpret_cast<float*>(tensor.GetMemoryArea());
CHECK_MESSAGE(mutableDatum == scalarValue, "Scalar value is " << scalarValue);
armnn::TensorShape shape_equal;
armnn::TensorShape shape_different;
shape_equal = shape;
CHECK(shape_equal == shape);
CHECK(shape_different != shape);
CHECK_MESSAGE(1 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements());
CHECK_MESSAGE(1 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK(true == shape.GetDimensionSpecificity(0));
CHECK(shape.AreAllDimensionsSpecified());
CHECK(shape.IsAtLeastOneDimensionSpecified());
CHECK(1 == shape[0]);
CHECK(1 == tensor.GetShape()[0]);
CHECK(1 == tensor.GetInfo().GetShape()[0]);
CHECK_THROWS_AS( shape[1], InvalidArgumentException );
float newMutableDatum = 42.f;
std::memcpy(tensor.GetMemoryArea(), &newMutableDatum, sizeof(float));
scalarValue = *reinterpret_cast<float*>(tensor.GetMemoryArea());
CHECK_MESSAGE(newMutableDatum == scalarValue, "Scalar value is " << scalarValue);
}
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 );
CHECK(armnn::Dimensionality::NotSpecified == shape.GetDimensionality());
CHECK_THROWS_AS( shape[0], InvalidArgumentException );
CHECK_THROWS_AS( shape.GetNumElements(), InvalidArgumentException );
CHECK_THROWS_AS( shape.GetNumDimensions(), InvalidArgumentException );
armnn::TensorShape shape_equal;
armnn::TensorShape shape_different;
shape_equal = shape;
CHECK(shape_equal == shape);
CHECK(shape_different != shape);
}
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 );
CHECK(armnn::Dimensionality::Specified == shape.GetDimensionality());
CHECK_MESSAGE(0 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements());
CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK(false == shape.GetDimensionSpecificity(0));
CHECK(false == shape.GetDimensionSpecificity(1));
CHECK(false == shape.GetDimensionSpecificity(2));
CHECK(!shape.AreAllDimensionsSpecified());
CHECK(!shape.IsAtLeastOneDimensionSpecified());
armnn::TensorShape shape_equal;
armnn::TensorShape shape_different;
shape_equal = shape;
CHECK(shape_equal == shape);
CHECK(shape_different != shape);
}
TEST_CASE("TensorShape_DynamicTensorType1_unknownSomeDimensionsSizes")
{
std::vector<float> 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 );
CHECK(armnn::Dimensionality::Specified == shape.GetDimensionality());
CHECK_MESSAGE(6 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements());
CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK(true == shape.GetDimensionSpecificity(0));
CHECK(false == shape.GetDimensionSpecificity(1));
CHECK(true == shape.GetDimensionSpecificity(2));
CHECK(!shape.AreAllDimensionsSpecified());
CHECK(shape.IsAtLeastOneDimensionSpecified());
CHECK_THROWS_AS(shape[1], InvalidArgumentException);
CHECK_THROWS_AS(tensor.GetShape()[1], InvalidArgumentException);
CHECK_THROWS_AS(tensor.GetInfo().GetShape()[1], InvalidArgumentException);
CHECK(2 == shape[0]);
CHECK(2 == tensor.GetShape()[0]);
CHECK(2 == tensor.GetInfo().GetShape()[0]);
CHECK_THROWS_AS( shape[1], InvalidArgumentException );
CHECK(3 == shape[2]);
CHECK(3 == tensor.GetShape()[2]);
CHECK(3 == tensor.GetInfo().GetShape()[2]);
armnn::TensorShape shape_equal;
armnn::TensorShape shape_different;
shape_equal = shape;
CHECK(shape_equal == shape);
CHECK(shape_different != shape);
}
TEST_CASE("TensorShape_DynamicTensorType1_transitionFromUnknownToKnownDimensionsSizes")
{
std::vector<float> 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);
CHECK(armnn::Dimensionality::Specified == shape.GetDimensionality());
CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK(false == shape.GetDimensionSpecificity(0));
CHECK(false == shape.GetDimensionSpecificity(1));
CHECK(false == shape.GetDimensionSpecificity(2));
CHECK(!shape.AreAllDimensionsSpecified());
CHECK(!shape.IsAtLeastOneDimensionSpecified());
// Specify dimension 0 and 2.
shape.SetDimensionSize(0, 2);
shape.SetDimensionSize(2, 3);
CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK_MESSAGE(6 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements());
CHECK(true == shape.GetDimensionSpecificity(0));
CHECK(false == shape.GetDimensionSpecificity(1));
CHECK(true == shape.GetDimensionSpecificity(2));
CHECK(!shape.AreAllDimensionsSpecified());
CHECK(shape.IsAtLeastOneDimensionSpecified());
info.SetShape(shape);
armnn::Tensor tensor2( info, &mutableDatum );
CHECK(2 == shape[0]);
CHECK(2 == tensor2.GetShape()[0]);
CHECK(2 == tensor2.GetInfo().GetShape()[0]);
CHECK_THROWS_AS(shape[1], InvalidArgumentException);
CHECK_THROWS_AS(tensor.GetShape()[1], InvalidArgumentException);
CHECK_THROWS_AS(tensor.GetInfo().GetShape()[1], InvalidArgumentException);
CHECK(3 == shape[2]);
CHECK(3 == tensor2.GetShape()[2]);
CHECK(3 == tensor2.GetInfo().GetShape()[2]);
armnn::TensorShape shape_equal;
armnn::TensorShape shape_different;
shape_equal = shape;
CHECK(shape_equal == shape);
CHECK(shape_different != shape);
// Specify dimension 1.
shape.SetDimensionSize(1, 5);
CHECK_MESSAGE(3 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK_MESSAGE(30 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements());
CHECK(true == shape.GetDimensionSpecificity(0));
CHECK(true == shape.GetDimensionSpecificity(1));
CHECK(true == shape.GetDimensionSpecificity(2));
CHECK(shape.AreAllDimensionsSpecified());
CHECK(shape.IsAtLeastOneDimensionSpecified());
}
TEST_CASE("Tensor_emptyConstructors")
{
auto shape = armnn::TensorShape();
CHECK_MESSAGE( 0 == shape.GetNumDimensions(), "Number of dimensions is " << shape.GetNumDimensions());
CHECK_MESSAGE( 0 == shape.GetNumElements(), "Number of elements is " << shape.GetNumElements());
CHECK( armnn::Dimensionality::Specified == shape.GetDimensionality());
CHECK( shape.AreAllDimensionsSpecified());
CHECK_THROWS_AS( shape[0], InvalidArgumentException );
auto tensor = armnn::Tensor();
CHECK_MESSAGE( 0 == tensor.GetNumDimensions(), "Number of dimensions is " << tensor.GetNumDimensions());
CHECK_MESSAGE( 0 == tensor.GetNumElements(), "Number of elements is " << tensor.GetNumElements());
CHECK_MESSAGE( 0 == tensor.GetShape().GetNumDimensions(), "Number of dimensions is " <<
tensor.GetShape().GetNumDimensions());
CHECK_MESSAGE( 0 == tensor.GetShape().GetNumElements(), "Number of dimensions is " <<
tensor.GetShape().GetNumElements());
CHECK( armnn::Dimensionality::Specified == tensor.GetShape().GetDimensionality());
CHECK( tensor.GetShape().AreAllDimensionsSpecified());
CHECK_THROWS_AS( tensor.GetShape()[0], InvalidArgumentException );
}
}