From bd18eab07a8f30492de1e462b1815189014cb8d5 Mon Sep 17 00:00:00 2001 From: Cathal Corbett Date: Tue, 15 Nov 2022 12:56:16 +0000 Subject: IVGCVSW-7345 Add Pooling2d support to TOSA Reference Backend Signed-off-by: Cathal Corbett Change-Id: I73a47e513fe2d064ef233b121a68ef2edf0396dc --- .../test/AdditionEndToEndTestImpl.hpp | 2 +- .../test/Pooling2dEndToEndTestImpl.hpp | 172 +++++++++++++++++ src/backends/tosaCommon/TosaLayerSupportRules.hpp | 26 +++ src/backends/tosaCommon/TosaMappings.cpp | 35 +++- src/backends/tosaCommon/TosaMappings.hpp | 2 +- .../AvgPool2DIgnoreValueOperator.cpp | 107 +++++++++++ .../AvgPool2DIgnoreValueOperator.hpp | 19 ++ .../tosaCommon/operatorMappings/CMakeLists.txt | 4 + .../operatorMappings/Pooling2DOperator.cpp | 60 ++++++ .../operatorMappings/Pooling2DOperator.hpp | 19 ++ .../operatorMappings/TosaCommonOperators.hpp | 10 + .../operatorMappings/TosaOperatorUtils.hpp | 147 ++++++++++++++ .../test/AvgPool2DIgnoreValueChecker.hpp | 125 ++++++++++++ src/backends/tosaCommon/test/CMakeLists.txt | 3 +- .../tosaCommon/test/OneToManyMappingTests.cpp | 84 ++++++++ .../tosaCommon/test/OneToOneMappingTests.cpp | 213 +++++++++++++++++++++ .../tosaCommon/test/TosaOperatorMappingTests.cpp | 124 ------------ src/backends/tosaCommon/test/TosaTestUtils.hpp | 162 ++++++++++++++++ src/backends/tosaReference/TosaRefLayerSupport.cpp | 170 +++++++++++++--- .../tosaReference/test/TosaRefEndToEndTests.cpp | 47 ++++- .../test/TosaRefLayerSupportTests.cpp | 128 ++++++++++++- 21 files changed, 1496 insertions(+), 163 deletions(-) create mode 100644 src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp create mode 100644 src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp create mode 100644 src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp create mode 100644 src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp create mode 100644 src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp create mode 100644 src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp create mode 100644 src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp create mode 100644 src/backends/tosaCommon/test/OneToManyMappingTests.cpp create mode 100644 src/backends/tosaCommon/test/OneToOneMappingTests.cpp delete mode 100644 src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp create mode 100644 src/backends/tosaCommon/test/TosaTestUtils.hpp diff --git a/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp index a1a8bac0e7..f33521888f 100644 --- a/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp +++ b/src/backends/backendsCommon/test/AdditionEndToEndTestImpl.hpp @@ -105,4 +105,4 @@ void AdditionEndToEndFloat16(const std::vector& backends) EndToEndLayerTestImpl(std::move(network), inputTensorData, expectedOutputData, backends); } -} // anonymous namespaceS \ No newline at end of file +} // anonymous namespace diff --git a/src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp b/src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp new file mode 100644 index 0000000000..addd27caca --- /dev/null +++ b/src/backends/backendsCommon/test/Pooling2dEndToEndTestImpl.hpp @@ -0,0 +1,172 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + +#include +#include + +#include +#include + +#include + +namespace +{ + +using namespace armnn; + +template +armnn::INetworkPtr CreatePooling2dNetwork(const armnn::TensorShape& inputShape, + const armnn::TensorShape& outputShape, + PaddingMethod padMethod = PaddingMethod::Exclude, + PoolingAlgorithm poolAlg = PoolingAlgorithm::Max, + const float qScale = 1.0f, + const int32_t qOffset = 0) +{ + INetworkPtr network(INetwork::Create()); + + TensorInfo inputTensorInfo(inputShape, DataType, qScale, qOffset, true); + TensorInfo outputTensorInfo(outputShape, DataType, qScale, qOffset, true); + + Pooling2dDescriptor descriptor; + descriptor.m_PoolType = poolAlg; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 3; + descriptor.m_StrideX = descriptor.m_StrideY = 1; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = padMethod; + descriptor.m_DataLayout = DataLayout::NHWC; + + IConnectableLayer* pool = network->AddPooling2dLayer(descriptor, "pool"); + IConnectableLayer* input = network->AddInputLayer(0, "input"); + IConnectableLayer* output = network->AddOutputLayer(0, "output"); + + Connect(input, pool, inputTensorInfo, 0, 0); + Connect(pool, output, outputTensorInfo, 0, 0); + + return network; +} + +template> +void MaxPool2dEndToEnd(const std::vector& backends, + PaddingMethod padMethod = PaddingMethod::Exclude) +{ + const TensorShape& inputShape = { 1, 3, 3, 1 }; + const TensorShape& outputShape = { 1, 3, 3, 1 }; + + INetworkPtr network = CreatePooling2dNetwork(inputShape, outputShape, padMethod); + + CHECK(network); + + std::vector inputData{ 1, 2, 3, + 4, 5, 6, + 7, 8, 9 }; + std::vector expectedOutput{ 5, 6, 6, + 8, 9, 9, + 8, 9, 9 }; + + std::map> inputTensorData = { { 0, inputData } }; + std::map> expectedOutputData = { { 0, expectedOutput } }; + + EndToEndLayerTestImpl(std::move(network), inputTensorData, expectedOutputData, backends); +} + +template +void MaxPool2dEndToEndFloat16(const std::vector& backends) +{ + using namespace half_float::literal; + using Half = half_float::half; + + const TensorShape& inputShape = { 1, 3, 3, 1 }; + const TensorShape& outputShape = { 1, 3, 3, 1 }; + + INetworkPtr network = CreatePooling2dNetwork(inputShape, outputShape); + CHECK(network); + + std::vector inputData{ 1._h, 2._h, 3._h, + 4._h, 5._h, 6._h, + 7._h, 8._h, 9._h }; + std::vector expectedOutput{ 5._h, 6._h, 6._h, + 8._h, 9._h, 9._h, + 8._h, 9._h, 9._h }; + + std::map> inputTensorData = { { 0, inputData } }; + std::map> expectedOutputData = { { 0, expectedOutput } }; + + EndToEndLayerTestImpl(std::move(network), inputTensorData, expectedOutputData, backends); +} + +template> +void AvgPool2dEndToEnd(const std::vector& backends, + PaddingMethod padMethod = PaddingMethod::Exclude) +{ + const TensorShape& inputShape = { 1, 3, 3, 1 }; + const TensorShape& outputShape = { 1, 3, 3, 1 }; + + INetworkPtr network = CreatePooling2dNetwork( + inputShape, outputShape, padMethod, PoolingAlgorithm::Average); + CHECK(network); + + std::vector inputData{ 1, 2, 3, + 4, 5, 6, + 7, 8, 9 }; + std::vector expectedOutput; + if (padMethod == PaddingMethod::Exclude) + { + expectedOutput = { 3 , 3.5 , 4 , + 4.5, 5 , 5.5, + 6 , 6.5, 7 }; + } + else + { + expectedOutput = { 1.33333, 2.33333, 1.77778, + 3 , 5 , 3.66667, + 2.66667, 4.33333, 3.11111 }; + } + + std::map> inputTensorData = { { 0, inputData } }; + std::map> expectedOutputData = { { 0, expectedOutput } }; + + EndToEndLayerTestImpl(std::move(network), + inputTensorData, + expectedOutputData, + backends, + 0.00001f); +} + +template +void AvgPool2dEndToEndFloat16(const std::vector& backends, + PaddingMethod padMethod = PaddingMethod::IgnoreValue) +{ + using namespace half_float::literal; + using Half = half_float::half; + + const TensorShape& inputShape = { 1, 3, 3, 1 }; + const TensorShape& outputShape = { 1, 3, 3, 1 }; + + INetworkPtr network = CreatePooling2dNetwork( + inputShape, outputShape, padMethod, PoolingAlgorithm::Average); + CHECK(network); + + std::vector inputData{ 1._h, 2._h, 3._h, + 4._h, 5._h, 6._h, + 7._h, 8._h, 9._h }; + std::vector expectedOutput{ 1.33333_h, 2.33333_h, 1.77778_h, + 3._h , 5._h , 3.66667_h, + 2.66667_h, 4.33333_h, 3.11111_h }; + + std::map> inputTensorData = { { 0, inputData } }; + std::map> expectedOutputData = { { 0, expectedOutput } }; + + EndToEndLayerTestImpl(std::move(network), + inputTensorData, + expectedOutputData, + backends, + 0.00001f); +} + +} // anonymous namespace diff --git a/src/backends/tosaCommon/TosaLayerSupportRules.hpp b/src/backends/tosaCommon/TosaLayerSupportRules.hpp index 2a2b08da99..792908c619 100644 --- a/src/backends/tosaCommon/TosaLayerSupportRules.hpp +++ b/src/backends/tosaCommon/TosaLayerSupportRules.hpp @@ -38,3 +38,29 @@ struct TosaTensorNumDimensionsWithinBounds : public Rule m_Res = (tensor->GetShape().size() <= MaxNumOfTensorDimensions) || (!tensor->GetShape().empty()); } }; + +struct TosaAssertSize : public Rule +{ + template + explicit TosaAssertSize(const Container& c1, const Container& c2) + { + m_Res = (c1.size() == c2.size()); + } +}; + +struct TosaContainerContains : public Rule +{ + explicit TosaContainerContains(std::tuple& check, const std::vector>& c) + { + for (auto item: c) + { + if (std::get<0>(check) == std::get<0>(item) + && std::get<1>(check) == std::get<1>(item)) + { + m_Res = true; + return; + } + } + m_Res = false; + } +}; diff --git a/src/backends/tosaCommon/TosaMappings.cpp b/src/backends/tosaCommon/TosaMappings.cpp index 71d2012cbc..a37eaf29b3 100644 --- a/src/backends/tosaCommon/TosaMappings.cpp +++ b/src/backends/tosaCommon/TosaMappings.cpp @@ -23,10 +23,18 @@ void SetBasicBlockConstantTensorData(Layer* layer, TosaSerializationBasicBlock* } } +TosaSerializationBasicBlock* CreateEmptyTosaSerializationBasicBlock() +{ + // empty basic block when no tosa mapping implemented/exists + TosaSerializationOperator* op = + new TosaSerializationOperator(Op_UNKNOWN, Attribute_NONE, nullptr, {}, {}); + return new TosaSerializationBasicBlock("", {op}, {}, {}, {}); +} + TosaSerializationBasicBlock* GetTosaMapping(const LayerType type, const std::vector& inputs, const std::vector& outputs, - const BaseDescriptor& /*descriptor*/, + const BaseDescriptor& descriptor, bool isMain = false) { switch (type) @@ -35,11 +43,30 @@ TosaSerializationBasicBlock* GetTosaMapping(const LayerType type, { return ConvertAdditionToTosaOperator(inputs, outputs, isMain); } + case LayerType::Pooling2d: + { + auto poolDesc = PolymorphicDowncast(&descriptor); + + bool avgPoolIgnoreValue = + (poolDesc->m_PoolType == PoolingAlgorithm::Average) && + (poolDesc->m_PaddingMethod == PaddingMethod::IgnoreValue); + + if (poolDesc->m_PoolType == PoolingAlgorithm::L2) + { + return CreateEmptyTosaSerializationBasicBlock(); + } + else if (avgPoolIgnoreValue) + { + return ConvertAvgPool2DIgnoreValueToTosaOperator(inputs, outputs, isMain, poolDesc); + } + else + { + return ConvertPooling2DToTosaOperator(inputs, outputs, isMain, poolDesc); + } + } default: { - // empty basic block when no tosa mapping implemented/exists - TosaSerializationOperator* op = new TosaSerializationOperator(Op_UNKNOWN, Attribute_NONE, nullptr, {}, {}); - return new TosaSerializationBasicBlock("", {op}, {}, {}, {}); + return CreateEmptyTosaSerializationBasicBlock(); } } } diff --git a/src/backends/tosaCommon/TosaMappings.hpp b/src/backends/tosaCommon/TosaMappings.hpp index 7f137ca322..8df2493d6e 100644 --- a/src/backends/tosaCommon/TosaMappings.hpp +++ b/src/backends/tosaCommon/TosaMappings.hpp @@ -8,7 +8,7 @@ #include #include -#include "operatorMappings/AdditionOperator.hpp" +#include "operatorMappings/TosaCommonOperators.hpp" using namespace armnn; using namespace tosa; diff --git a/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp new file mode 100644 index 0000000000..b3d2687c30 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.cpp @@ -0,0 +1,107 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "Pooling2DOperator.hpp" + +TosaSerializationBasicBlock* ConvertAvgPool2DIgnoreValueToTosaOperator(const std::vector& inputs, + const std::vector& outputs, + bool isMain, + const Pooling2dDescriptor* poolDescriptor) +{ + + // A helper function with static global variables ensures uniqueness + // for dynamically generating input, output and block names + std::string padInputName = std::string("Op_PAD_input0_") + GetUniqueTosaMappingID(); + std::string padOutputName = std::string("Op_PAD_intermediate0_") + GetUniqueTosaMappingID(); + std::string poolOutputName = std::string("Op_AVG_POOL2D_output0_") + GetUniqueTosaMappingID(); + std::string blockName = std::string("Op_AVG_POOL2D_block_") + GetUniqueTosaMappingID(); + + // If it's the first block, overwrite block name with main. + if (isMain) + { + blockName = std::string("main"); + } + + std::vector paddings; + if (poolDescriptor->m_DataLayout == DataLayout::NHWC) + { + paddings = {0, + 0, + static_cast(poolDescriptor->m_PadTop), + static_cast(poolDescriptor->m_PadBottom), + static_cast(poolDescriptor->m_PadLeft), + static_cast(poolDescriptor->m_PadRight), + 0, + 0 + }; + } + else + { + paddings = {0, + 0, + 0, + 0, + static_cast(poolDescriptor->m_PadTop), + static_cast(poolDescriptor->m_PadBottom), + static_cast(poolDescriptor->m_PadLeft), + static_cast(poolDescriptor->m_PadRight) + }; + } + + TosaPadAttribute padAttribute(paddings, 0, 0.0f); + TosaSerializationOperator* opPad = new TosaSerializationOperator(Op_PAD, + Attribute_PadAttribute, + &padAttribute, + {padInputName}, + {padOutputName}); + + std::vector pad = {0, 0, 0, 0}; + std::vector kernel = {static_cast(poolDescriptor->m_PoolHeight), + static_cast(poolDescriptor->m_PoolWidth)}; + std::vector stride = {static_cast(poolDescriptor->m_StrideY), + static_cast(poolDescriptor->m_StrideX)}; + TosaPoolAttribute poolAttribute(pad, kernel, stride, 0, 0, ArmNNToDType(inputs[0]->GetDataType())); + + TosaSerializationOperator* opPool = new TosaSerializationOperator(Op_AVG_POOL2D, + Attribute_PoolAttribute, + &poolAttribute, + {padOutputName}, + {poolOutputName}); + + std::vector inputShape = GetTosaTensorShape(inputs[0]->GetShape()); + DType inputDType = ArmNNToDType(inputs[0]->GetDataType()); + + std::vector outputShape = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType = ArmNNToDType(outputs[0]->GetDataType()); + + std::vector intermediateShape; + if (poolDescriptor->m_DataLayout == DataLayout::NHWC) + { + intermediateShape = {inputShape[0], + inputShape[1] + paddings[2] + paddings[3], + inputShape[2] + paddings[4] + paddings[5], + inputShape[3]}; + } + else + { + intermediateShape = {inputShape[0], + inputShape[1], + inputShape[2] + paddings[4] + paddings[5], + inputShape[3] + paddings[6] + paddings[7]}; + } + + TosaSerializationTensor* inputTensor = new TosaSerializationTensor(padInputName, inputShape, inputDType, {}); + TosaSerializationTensor* intermediateTensor = new TosaSerializationTensor( + padOutputName, intermediateShape, inputDType, {}); + TosaSerializationTensor* outputTensor = new TosaSerializationTensor(poolOutputName, outputShape, outputDType, {}); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + {opPad, opPool}, // operators + {inputTensor, intermediateTensor, outputTensor}, // tensors + {padInputName}, // inputs + {poolOutputName}); // outputs +} \ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp new file mode 100644 index 0000000000..63ae190cc9 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/AvgPool2DIgnoreValueOperator.hpp @@ -0,0 +1,19 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include +#include "TosaOperatorUtils.hpp" + +using namespace armnn; +using namespace tosa; + +TosaSerializationBasicBlock* ConvertAvgPool2DIgnoreValueToTosaOperator(const std::vector& inputs, + const std::vector& outputs, + bool isMain, + const Pooling2dDescriptor* poolDescriptor); diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt index 9fc33e9205..7fdc9226af 100644 --- a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt +++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt @@ -6,6 +6,10 @@ list(APPEND armnnTosaBackendOperators_sources AdditionOperator.hpp AdditionOperator.cpp + AvgPool2DIgnoreValueOperator.hpp + AvgPool2DIgnoreValueOperator.cpp + Pooling2DOperator.hpp + Pooling2DOperator.cpp TosaOperatorUtils.hpp ) diff --git a/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp new file mode 100644 index 0000000000..cd707edb3a --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.cpp @@ -0,0 +1,60 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "Pooling2DOperator.hpp" + +TosaSerializationBasicBlock* ConvertPooling2DToTosaOperator(const std::vector& inputs, + const std::vector& outputs, + bool isMain, + const Pooling2dDescriptor* poolDescriptor) +{ + std::string poolType = (poolDescriptor->m_PoolType == PoolingAlgorithm::Max) ? "Op_MAX" : "Op_AVG"; + Op opcode = (poolDescriptor->m_PoolType == PoolingAlgorithm::Max) ? Op_MAX_POOL2D : Op_AVG_POOL2D; + + // A helper function with static global variables ensures uniqueness + // for dynamically generating input, output and block names + std::string input0Name = poolType + std::string("_POOL2D_input0_") + GetUniqueTosaMappingID(); + std::string outputName = poolType + std::string("_POOL2D_output0_") + GetUniqueTosaMappingID(); + std::string blockName = poolType + std::string("_POOL2D_block_") + GetUniqueTosaMappingID(); + + // If it's the first block, overwrite block name with main. + if (isMain) + { + blockName = std::string("main"); + } + + std::vector pad = {static_cast(poolDescriptor->m_PadTop), + static_cast(poolDescriptor->m_PadBottom), + static_cast(poolDescriptor->m_PadLeft), + static_cast(poolDescriptor->m_PadRight)}; + std::vector kernel = {static_cast(poolDescriptor->m_PoolHeight), + static_cast(poolDescriptor->m_PoolWidth)}; + std::vector stride = {static_cast(poolDescriptor->m_StrideY), + static_cast(poolDescriptor->m_StrideX)}; + TosaPoolAttribute attribute(pad, kernel, stride, 0, 0, ArmNNToDType(inputs[0]->GetDataType())); + + TosaSerializationOperator* op = new TosaSerializationOperator(opcode, + Attribute_PoolAttribute, + &attribute, + {input0Name}, + {outputName}); + + std::vector inputShape0 = GetTosaTensorShape(inputs[0]->GetShape()); + DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType()); + + std::vector outputShape0 = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType()); + + TosaSerializationTensor* inputTensor0 = new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {}); + TosaSerializationTensor* outputTensor0 = new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + {op}, // operators + {inputTensor0, outputTensor0}, // tensors + {input0Name}, // inputs + {outputName}); // outputs +} \ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp new file mode 100644 index 0000000000..22d2a3ae29 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/Pooling2DOperator.hpp @@ -0,0 +1,19 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include +#include "TosaOperatorUtils.hpp" + +using namespace armnn; +using namespace tosa; + +TosaSerializationBasicBlock* ConvertPooling2DToTosaOperator(const std::vector& inputs, + const std::vector& outputs, + bool isMain, + const Pooling2dDescriptor* poolDescriptor); diff --git a/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp new file mode 100644 index 0000000000..00b5f0fa68 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/TosaCommonOperators.hpp @@ -0,0 +1,10 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "AdditionOperator.hpp" +#include "AvgPool2DIgnoreValueOperator.hpp" +#include "Pooling2DOperator.hpp" \ No newline at end of file diff --git a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp index f580a53ebc..f51b2109b4 100644 --- a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp +++ b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp @@ -59,3 +59,150 @@ inline std::string GetUniqueTosaMappingID() { return std::to_string(++uniqueTosaMappingID); } + +// Function to return Tosa Op as string. +inline std::string TosaOpToString(Op tosaOp) +{ + switch (tosaOp) + { + case Op_ADD: + return "Op_ADD"; + case Op_AVG_POOL2D: + return "Op_AVG_POOL2D"; + case Op_MAX_POOL2D: + return "Op_MAX_POOL2D"; + case Op_PAD: + return "Op_PAD"; + case Op_UNKNOWN: + return "Op_UNKNOWN"; + case Op_ARGMAX: + return "Op_ARGMAX"; + case Op_CONV2D: + return "Op_CONV2D"; + case Op_CONV3D: + return "Op_CONV3D"; + case Op_DEPTHWISE_CONV2D: + return "Op_DEPTHWISE_CONV2D"; + case Op_FULLY_CONNECTED: + return "Op_FULLY_CONNECTED"; + case Op_MATMUL: + return "Op_MATMUL"; + case Op_TRANSPOSE_CONV2D: + return "Op_TRANSPOSE_CONV2D"; + case Op_CLAMP: + return "Op_CLAMP"; + case Op_RESERVED: + return "Op_RESERVED"; + case Op_SIGMOID: + return "Op_SIGMOID"; + case Op_TANH: + return "Op_TANH"; + case Op_ARITHMETIC_RIGHT_SHIFT: + return "Op_ARITHMETIC_RIGHT_SHIFT"; + case Op_BITWISE_AND: + return "Op_BITWISE_AND"; + case Op_BITWISE_OR: + return "Op_BITWISE_OR"; + case Op_BITWISE_XOR: + return "Op_BITWISE_XOR"; + case Op_INTDIV: + return "Op_INTDIV"; + case Op_LOGICAL_AND: + return "Op_LOGICAL_AND"; + case Op_LOGICAL_LEFT_SHIFT: + return "Op_LOGICAL_LEFT_SHIFT"; + case Op_LOGICAL_RIGHT_SHIFT: + return "Op_LOGICAL_RIGHT_SHIFT"; + case Op_LOGICAL_OR: + return "Op_LOGICAL_OR"; + case Op_LOGICAL_XOR: + return "Op_LOGICAL_XOR"; + case Op_MAXIMUM: + return "Op_MAXIMUM"; + case Op_MINIMUM: + return "Op_MINIMUM"; + case Op_MUL: + return "Op_MUL"; + case Op_POW: + return "Op_POW"; + case Op_SUB: + return "Op_SUB"; + case Op_TABLE: + return "Op_TABLE"; + case Op_ABS: + return "Op_ABS"; + case Op_BITWISE_NOT: + return "Op_BITWISE_NOT"; + case Op_CEIL: + return "Op_CEIL"; + case Op_CLZ: + return "Op_CLZ"; + case Op_EXP: + return "Op_EXP"; + case Op_FLOOR: + return "Op_FLOOR"; + case Op_LOG: + return "Op_LOG"; + case Op_LOGICAL_NOT: + return "Op_LOGICAL_NOT"; + case Op_NEGATE: + return "Op_NEGATE"; + case Op_RECIPROCAL: + return "Op_RECIPROCAL"; + case Op_RSQRT: + return "Op_RSQRT"; + case Op_SELECT: + return "Op_SELECT"; + case Op_EQUAL: + return "Op_EQUAL"; + case Op_GREATER: + return "Op_GREATER"; + case Op_GREATER_EQUAL: + return "Op_GREATER_EQUAL"; + case Op_REDUCE_ANY: + return "Op_REDUCE_ANY"; + case Op_REDUCE_ALL: + return "Op_REDUCE_ALL"; + case Op_REDUCE_MAX: + return "Op_REDUCE_MAX"; + case Op_REDUCE_MIN: + return "Op_REDUCE_MIN"; + case Op_REDUCE_PRODUCT: + return "Op_REDUCE_PRODUCT"; + case Op_REDUCE_SUM: + return "Op_REDUCE_SUM"; + case Op_CONCAT: + return "Op_CONCAT"; + case Op_RESHAPE: + return "Op_RESHAPE"; + case Op_REVERSE: + return "Op_REVERSE"; + case Op_SLICE: + return "Op_SLICE"; + case Op_TILE: + return "Op_TILE"; + case Op_TRANSPOSE: + return "Op_TRANSPOSE"; + case Op_GATHER: + return "Op_GATHER"; + case Op_SCATTER: + return "Op_SCATTER"; + case Op_RESIZE: + return "Op_RESIZE"; + case Op_CAST: + return "Op_CAST"; + case Op_RESCALE: + return "Op_RESCALE"; + case Op_CONST: + return "Op_CONST"; + case Op_IDENTITY: + return "Op_IDENTITY"; + case Op_CUSTOM: + return "Op_CUSTOM"; + case Op_COND_IF: + return "Op_COND_IF"; + case Op_WHILE_LOOP: + return "Op_WHILE_LOOP"; + } + return ""; +} diff --git a/src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp b/src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp new file mode 100644 index 0000000000..8869b3a8ff --- /dev/null +++ b/src/backends/tosaCommon/test/AvgPool2DIgnoreValueChecker.hpp @@ -0,0 +1,125 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "TosaTestUtils.hpp" + +using namespace armnn; +using namespace tosa; + +void VerifyAvgPool2DIgnoreValue(TosaSerializationBasicBlock* basicBlock, + std::vector> inputShape, + std::vector> outputShape, + std::vector> intermediateShape, + const BaseDescriptor& descriptor, + DType dataType = DType_FP32) +{ + uint32_t numInputs = static_cast(inputShape.size()); + uint32_t numOutputs = static_cast(outputShape.size()); + std::string operatorString0 = TosaOpToString(Op_PAD); + std::string operatorString1 = TosaOpToString(Op_AVG_POOL2D); + + std::string blockStr = operatorString1 + "_block_"; + CHECK(basicBlock->GetName().find(blockStr) != std::string::npos); + CHECK(basicBlock->GetInputs().size() == numInputs); + CHECK(basicBlock->GetOutputs().size() == numOutputs); + CHECK(basicBlock->GetOperators().size() == 2); + CHECK(basicBlock->GetTensors().size() == 3); + + // + // Verify padding operator first. + // + + TosaSerializationOperator* padOp = basicBlock->GetOperators().at(0); + uint32_t padOpOutputs = 1; + CHECK(padOp->GetInputTensorNames().size() == numInputs); + CHECK(padOp->GetOutputTensorNames().size() == padOpOutputs); + + for (uint32_t i = 0; i < numInputs; i++) + { + std::basic_string blockInputName = basicBlock->GetInputs()[i]; + std::basic_string operatorInputName = padOp->GetInputTensorNames()[i]; + + std::string opStr = operatorString0 + "_input" + std::to_string(i) + "_"; + + CHECK(blockInputName == operatorInputName); + CHECK(basicBlock->GetTensorByName(blockInputName)); + CHECK(blockInputName.find(opStr) != std::string::npos); + + TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorInputName); + CHECK(tensor->GetDtype() == dataType); + CHECK(tensor->GetData().size() == 0); + CHECK(tensor->GetShape() == inputShape[static_cast(i)]); + } + + for (uint32_t i = 0; i < padOpOutputs; i++) + { + std::basic_string operatorOutputName = padOp->GetOutputTensorNames()[i]; + std::string opStr = operatorString0 + "_intermediate" + std::to_string(i) + "_"; + + CHECK(basicBlock->GetTensorByName(operatorOutputName)); + CHECK(operatorOutputName.find(opStr) != std::string::npos); + + TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorOutputName); + CHECK(tensor->GetDtype() == dataType); + CHECK(tensor->GetData().size() == 0); + CHECK(tensor->GetShape() == intermediateShape[static_cast(i)]); + } + + CHECK(padOp->GetAttributeType() == Attribute_PadAttribute); + CHECK(padOp->GetOp() == Op_PAD); + + VerifyTosaAttributeFromDescriptor(descriptor, + padOp->GetAttribute(), + LayerType::Pooling2d); + + // + // Verify average pool operator second. + // + + TosaSerializationOperator* poolOp = basicBlock->GetOperators().at(1); + uint32_t poolOpInputs = 1; + CHECK(poolOp->GetInputTensorNames().size() == poolOpInputs); + CHECK(poolOp->GetOutputTensorNames().size() == numOutputs); + + for (uint32_t i = 0; i < poolOpInputs; i++) + { + std::basic_string operatorInputName = poolOp->GetInputTensorNames()[i]; + std::string opStr = operatorString0 + "_intermediate" + std::to_string(i) + "_"; + + CHECK(basicBlock->GetTensorByName(operatorInputName)); + CHECK(operatorInputName.find(opStr) != std::string::npos); + + TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorInputName); + CHECK(tensor->GetDtype() == dataType); + CHECK(tensor->GetData().size() == 0); + CHECK(tensor->GetShape() == intermediateShape[static_cast(i)]); + } + + for (uint32_t i = 0; i < numOutputs; i++) + { + std::basic_string blockOutputName = basicBlock->GetOutputs()[i]; + std::basic_string operatorOutputName = poolOp->GetOutputTensorNames()[i]; + + std::string opStr = operatorString1 + "_output" + std::to_string(i) + "_"; + + CHECK(blockOutputName == operatorOutputName); + CHECK(basicBlock->GetTensorByName(blockOutputName)); + CHECK(blockOutputName.find(opStr) != std::string::npos); + + TosaSerializationTensor* tensor = basicBlock->GetTensorByName(operatorOutputName); + CHECK(tensor->GetDtype() == dataType); + CHECK(tensor->GetData().size() == 0); + CHECK(tensor->GetShape() == outputShape[static_cast(i)]); + } + + CHECK(poolOp->GetAttributeType() == Attribute_PoolAttribute); + CHECK(poolOp->GetOp() == Op_AVG_POOL2D); + + VerifyTosaAttributeFromDescriptor(descriptor, + poolOp->GetAttribute(), + LayerType::Pooling2d, + 1); + +} \ No newline at end of file diff --git a/src/backends/tosaCommon/test/CMakeLists.txt b/src/backends/tosaCommon/test/CMakeLists.txt index 56cc66e51c..34c4b0c009 100644 --- a/src/backends/tosaCommon/test/CMakeLists.txt +++ b/src/backends/tosaCommon/test/CMakeLists.txt @@ -4,7 +4,8 @@ # list(APPEND armnnTosaBackendUnitTests_sources - TosaOperatorMappingTests.cpp + OneToManyMappingTests.cpp + OneToOneMappingTests.cpp ) add_library(armnnTosaBackendUnitTests OBJECT ${armnnTosaBackendUnitTests_sources}) diff --git a/src/backends/tosaCommon/test/OneToManyMappingTests.cpp b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp new file mode 100644 index 0000000000..98fd563da1 --- /dev/null +++ b/src/backends/tosaCommon/test/OneToManyMappingTests.cpp @@ -0,0 +1,84 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "AvgPool2DIgnoreValueChecker.hpp" + +using namespace armnn; +using namespace tosa; + +TEST_SUITE("TosaOperatorMappingOneToManyTests") +{ +TEST_CASE("GetTosaMapping_AvgPool2DIgnoreValueLayer") +{ + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Average; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue; + + armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32); + armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 1, 4, 4 }}; + std::vector> intermediateShape = {{ 1, 1, 6, 6 }}; + std::vector> outputShape = {{ 1, 1, 3, 3 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::Pooling2d, {&inputTensorInfo}, {&outputTensorInfo}, descriptor, false); + VerifyAvgPool2DIgnoreValue(basicBlock, + inputShape, + outputShape, + intermediateShape, + descriptor); +} + +TEST_CASE("GetTosaMappingFromLayer_AvgPool2DIgnoreValueLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Average; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue; + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* pool = net->AddPooling2dLayer(descriptor, "pool"); + IConnectableLayer* output = net->AddOutputLayer(0, "output"); + + input0->GetOutputSlot(0).Connect(pool->GetInputSlot(0)); + pool->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32); + armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 1, 4, 4 }}; + std::vector> intermediateShape = {{ 1, 1, 6, 6 }}; + std::vector> outputShape = {{ 1, 1, 3, 3 }}; + + input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo); + pool->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + + TosaSerializationBasicBlock* basicBlock = + GetTosaMappingFromLayer(PolymorphicDowncast(pool), false); + VerifyAvgPool2DIgnoreValue(basicBlock, + inputShape, + outputShape, + intermediateShape, + descriptor); +} +} \ No newline at end of file diff --git a/src/backends/tosaCommon/test/OneToOneMappingTests.cpp b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp new file mode 100644 index 0000000000..04d1eb46aa --- /dev/null +++ b/src/backends/tosaCommon/test/OneToOneMappingTests.cpp @@ -0,0 +1,213 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "TosaTestUtils.hpp" + +using namespace armnn; +using namespace tosa; + +TEST_SUITE("TosaOperatorMappingOneToOneTests") +{ +TEST_CASE("GetTosaMapping_AdditionLayer") +{ + TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true); + + std::vector> inputShape = {{ 1, 2, 4, 2 }, { 1, 2, 4, 2 }}; + std::vector> outputShape = {{ 1, 2, 4, 2 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor(), false); + AssertTosaOneToOneMappingBasicBlock( + basicBlock, inputShape, outputShape, Op_ADD, Attribute_NONE, BaseDescriptor(), LayerType::Addition); +} + +TEST_CASE("GetTosaMappingFromLayer_AdditionLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* input1 = net->AddInputLayer(1, "input1"); + IConnectableLayer* add = net->AddAdditionLayer("add"); + IConnectableLayer* output = net->AddOutputLayer(0, "output"); + + input0->GetOutputSlot(0).Connect(add->GetInputSlot(0)); + input1->GetOutputSlot(0).Connect(add->GetInputSlot(1)); + add->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true); + + input0->GetOutputSlot(0).SetTensorInfo(info); + input1->GetOutputSlot(0).SetTensorInfo(info); + add->GetOutputSlot(0).SetTensorInfo(info); + + std::vector> inputShape = {{ 1, 2, 4, 2 }, { 1, 2, 4, 2 }}; + std::vector> outputShape = {{ 1, 2, 4, 2 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMappingFromLayer(PolymorphicDowncast(add), false); + AssertTosaOneToOneMappingBasicBlock( + basicBlock, inputShape, outputShape, Op_ADD, Attribute_NONE, BaseDescriptor(), LayerType::Addition); +} + +TEST_CASE("GetTosaMapping_MaxPool2DLayer") +{ + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Max; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude; + + armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32); + armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 1, 4, 4 }}; + std::vector> outputShape = {{ 1, 1, 3, 3 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::Pooling2d, {&inputTensorInfo}, {&outputTensorInfo}, descriptor, false); + AssertTosaOneToOneMappingBasicBlock( + basicBlock, inputShape, outputShape, Op_MAX_POOL2D, Attribute_PoolAttribute, descriptor, LayerType::Pooling2d); +} + +TEST_CASE("GetTosaMappingFromLayer_MaxPool2DLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Max; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude; + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* pool = net->AddPooling2dLayer(descriptor, "pool"); + IConnectableLayer* output = net->AddOutputLayer(0, "output"); + + input0->GetOutputSlot(0).Connect(pool->GetInputSlot(0)); + pool->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32); + armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 1, 4, 4 }}; + std::vector> outputShape = {{ 1, 1, 3, 3 }}; + + input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo); + pool->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + + TosaSerializationBasicBlock* basicBlock = + GetTosaMappingFromLayer(PolymorphicDowncast(pool), false); + AssertTosaOneToOneMappingBasicBlock( + basicBlock, inputShape, outputShape, Op_MAX_POOL2D, Attribute_PoolAttribute, descriptor, LayerType::Pooling2d); +} + +TEST_CASE("GetTosaMapping_AvgPool2DLayer") +{ + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Average; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude; + + armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32); + armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 1, 4, 4 }}; + std::vector> outputShape = {{ 1, 1, 3, 3 }}; + + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::Pooling2d, {&inputTensorInfo}, {&outputTensorInfo}, descriptor, false); + AssertTosaOneToOneMappingBasicBlock(basicBlock, + inputShape, + outputShape, + Op_AVG_POOL2D, + Attribute_PoolAttribute, + descriptor, + LayerType::Pooling2d); +} + +TEST_CASE("GetTosaMappingFromLayer_AvgPool2DLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Average; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude; + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* pool = net->AddPooling2dLayer(descriptor, "pool"); + IConnectableLayer* output = net->AddOutputLayer(0, "output"); + + input0->GetOutputSlot(0).Connect(pool->GetInputSlot(0)); + pool->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + armnn::TensorInfo inputTensorInfo({ 1, 1, 4, 4 }, DataType::Float32); + armnn::TensorInfo outputTensorInfo({ 1, 1, 3, 3 }, DataType::Float32); + + std::vector> inputShape = {{ 1, 1, 4, 4 }}; + std::vector> outputShape = {{ 1, 1, 3, 3 }}; + + input0->GetOutputSlot(0).SetTensorInfo(inputTensorInfo); + pool->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + + TosaSerializationBasicBlock* basicBlock = + GetTosaMappingFromLayer(PolymorphicDowncast(pool), false); + AssertTosaOneToOneMappingBasicBlock(basicBlock, + inputShape, + outputShape, + Op_AVG_POOL2D, + Attribute_PoolAttribute, + descriptor, + LayerType::Pooling2d); +} + +TEST_CASE("GetTosaMapping_Unimplemented") +{ + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor(), false); + + CHECK(basicBlock->GetName() == ""); + CHECK(basicBlock->GetTensors().size() == 0); + CHECK(basicBlock->GetOperators().size() == 1); + CHECK(basicBlock->GetInputs().size() == 0); + CHECK(basicBlock->GetOutputs().size() == 0); + + TosaSerializationOperator* op = basicBlock->GetOperators()[0]; + CHECK(op->GetAttributeType() == Attribute_NONE); + CHECK(op->GetOp() == tosa::Op_UNKNOWN); + CHECK(op->GetInputTensorNames().size() == 0); + CHECK(op->GetOutputTensorNames().size() == 0); +} +} diff --git a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp deleted file mode 100644 index d7cc5d8f91..0000000000 --- a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include - -#include - -#include - -using namespace armnn; -using namespace tosa; - -void AssertTosaOneToOneMappingBasicBlock(TosaSerializationBasicBlock* basicBlock, - std::vector shape, - uint32_t numInputs, - uint32_t numOutputs, - Op tosaOp, - std::string operatorString, - DType dataType = DType_FP32) -{ - std::string blockStr = operatorString + "_block_"; - CHECK(basicBlock->GetName().find(blockStr) != std::string::npos); - CHECK(basicBlock->GetInputs().size() == numInputs); - CHECK(basicBlock->GetOutputs().size() == numOutputs); - CHECK(basicBlock->GetOperators().size() == 1); - CHECK(basicBlock->GetTensors().size() == (numInputs + numOutputs)); - - TosaSerializationOperator* op = basicBlock->GetOperators().at(0); - CHECK(op->GetInputTensorNames().size() == numInputs); - CHECK(op->GetOutputTensorNames().size() == numOutputs); - - for (uint32_t i = 0; i < numInputs; i++) - { - std::basic_string blockInputName = basicBlock->GetInputs()[i]; - std::basic_string operatorInputName = op->GetInputTensorNames()[i]; - std::basic_string tensorName = basicBlock->GetTensors()[i]->GetName(); - - std::string opStr = operatorString + "_input" + std::to_string(i) + "_"; - - CHECK(blockInputName == operatorInputName); - CHECK(tensorName == operatorInputName); - CHECK(blockInputName.find(opStr) != std::string::npos); - } - - for (uint32_t i = 0; i < numOutputs; i++) - { - std::basic_string blockOutputName = basicBlock->GetOutputs()[i]; - std::basic_string operatorOutputName = op->GetOutputTensorNames()[i]; - std::basic_string tensorName = basicBlock->GetTensors()[numInputs + i]->GetName(); - - std::string opStr = operatorString + "_output" + std::to_string(i) + "_"; - - CHECK(blockOutputName == operatorOutputName); - CHECK(tensorName == operatorOutputName); - CHECK(blockOutputName.find(opStr) != std::string::npos); - } - - CHECK(op->GetAttributeType() == Attribute_NONE); - CHECK(op->GetOp() == tosaOp); - - TosaSerializationTensor* tensor0 = basicBlock->GetTensors()[0]; - CHECK(tensor0->GetDtype() == dataType); - CHECK(tensor0->GetData().size() == 0); - CHECK(tensor0->GetShape() == shape); -} - -TEST_SUITE("TosaOperatorMappingOneToOneTests") -{ -TEST_CASE("GetTosaMapping_AdditionLayer") -{ - TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true); - TosaSerializationBasicBlock* basicBlock = - GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor(), false); - AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD"); -} - -TEST_CASE("GetTosaMappingFromLayer_AdditionLayer") -{ - IRuntime::CreationOptions options; - IRuntimePtr runtime(IRuntime::Create(options)); - - // Builds up the structure of the network. - INetworkPtr net(INetwork::Create()); - - IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); - IConnectableLayer* input1 = net->AddInputLayer(1, "input1"); - IConnectableLayer* add = net->AddAdditionLayer("add"); - IConnectableLayer* output = net->AddOutputLayer(0, "output"); - - input0->GetOutputSlot(0).Connect(add->GetInputSlot(0)); - input1->GetOutputSlot(0).Connect(add->GetInputSlot(1)); - add->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - - TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true); - - input0->GetOutputSlot(0).SetTensorInfo(info); - input1->GetOutputSlot(0).SetTensorInfo(info); - add->GetOutputSlot(0).SetTensorInfo(info); - - TosaSerializationBasicBlock* basicBlock = - GetTosaMappingFromLayer(PolymorphicDowncast(add), false); - AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD"); -} - -TEST_CASE("GetTosaMapping_Unimplemented") -{ - TosaSerializationBasicBlock* basicBlock = - GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor(), false); - - CHECK(basicBlock->GetName() == ""); - CHECK(basicBlock->GetTensors().size() == 0); - CHECK(basicBlock->GetOperators().size() == 1); - CHECK(basicBlock->GetInputs().size() == 0); - CHECK(basicBlock->GetOutputs().size() == 0); - - TosaSerializationOperator* op = basicBlock->GetOperators()[0]; - CHECK(op->GetAttributeType() == Attribute_NONE); - CHECK(op->GetOp() == tosa::Op_UNKNOWN); - CHECK(op->GetInputTensorNames().size() == 0); - CHECK(op->GetOutputTensorNames().size() == 0); -} -} diff --git a/src/backends/tosaCommon/test/TosaTestUtils.hpp b/src/backends/tosaCommon/test/TosaTestUtils.hpp new file mode 100644 index 0000000000..a362bde10d --- /dev/null +++ b/src/backends/tosaCommon/test/TosaTestUtils.hpp @@ -0,0 +1,162 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include + +#include + +using namespace armnn; +using namespace tosa; + +inline void VerifyTosaAttributeFromDescriptor(const BaseDescriptor& descriptor, + const TosaAttributeBase* attribute, + LayerType type, + uint32_t mappingOpNumber = 0) +{ + switch (type) + { + case LayerType::Pooling2d: + { + auto poolDesc = PolymorphicDowncast(&descriptor); + std::vector pad = {static_cast(poolDesc->m_PadTop), + static_cast(poolDesc->m_PadBottom), + static_cast(poolDesc->m_PadLeft), + static_cast(poolDesc->m_PadRight)}; + + bool avgPoolIgnoreValue = + (poolDesc->m_PoolType == PoolingAlgorithm::Average) && + (poolDesc->m_PaddingMethod == PaddingMethod::IgnoreValue); + if (avgPoolIgnoreValue) + { + if (mappingOpNumber == 0) + { + if (poolDesc->m_DataLayout == DataLayout::NHWC) + { + pad = {0, + 0, + static_cast(poolDesc->m_PadTop), + static_cast(poolDesc->m_PadBottom), + static_cast(poolDesc->m_PadLeft), + static_cast(poolDesc->m_PadRight), + 0, + 0 + }; + } + else + { + pad = {0, + 0, + 0, + 0, + static_cast(poolDesc->m_PadTop), + static_cast(poolDesc->m_PadBottom), + static_cast(poolDesc->m_PadLeft), + static_cast(poolDesc->m_PadRight) + }; + } + + TosaPadAttribute padAttribute(attribute); + + CHECK(pad == padAttribute.padding()); + CHECK(0.0f == padAttribute.pad_const_fp()); + CHECK(0 == padAttribute.pad_const_int()); + + break; + } + pad = {0, 0, 0, 0}; + } + + std::vector kernel = {static_cast(poolDesc->m_PoolHeight), + static_cast(poolDesc->m_PoolWidth)}; + std::vector stride = {static_cast(poolDesc->m_StrideY), + static_cast(poolDesc->m_StrideX)}; + TosaPoolAttribute poolAttribute(attribute); + CHECK(pad == poolAttribute.pad()); + CHECK(kernel == poolAttribute.kernel()); + CHECK(stride == poolAttribute.stride()); + } + default: + break; + } + return; +} + +inline void AssertTosaOneToOneMappingBasicBlock(TosaSerializationBasicBlock* basicBlock, + std::vector> inputShape, + std::vector> outputShape, + Op tosaOp, + Attribute tosaAttribute, + const BaseDescriptor& descriptor, + LayerType type, + DType dataType = DType_FP32) +{ + uint32_t numInputs = static_cast(inputShape.size()); + uint32_t numOutputs = static_cast(outputShape.size()); + std::string operatorString = TosaOpToString(tosaOp); + + std::string blockStr = operatorString + "_block_"; + CHECK(basicBlock->GetName().find(blockStr) != std::string::npos); + CHECK(basicBlock->GetInputs().size() == numInputs); + CHECK(basicBlock->GetOutputs().size() == numOutputs); + CHECK(basicBlock->GetOperators().size() == 1); + CHECK(basicBlock->GetTensors().size() == (numInputs + numOutputs)); + + TosaSerializationOperator* op = basicBlock->GetOperators().at(0); + CHECK(op->GetInputTensorNames().size() == numInputs); + CHECK(op->GetOutputTensorNames().size() == numOutputs); + + for (uint32_t i = 0; i < numInputs; i++) + { + std::basic_string blockInputName = basicBlock->GetInputs()[i]; + std::basic_string operatorInputName = op->GetInputTensorNames()[i]; + std::basic_string tensorName = basicBlock->GetTensors()[i]->GetName(); + + std::string opStr = operatorString + "_input" + std::to_string(i) + "_"; + + CHECK(blockInputName == operatorInputName); + CHECK(tensorName == operatorInputName); + CHECK(blockInputName.find(opStr) != std::string::npos); + } + + for (uint32_t i = 0; i < numOutputs; i++) + { + std::basic_string blockOutputName = basicBlock->GetOutputs()[i]; + std::basic_string operatorOutputName = op->GetOutputTensorNames()[i]; + std::basic_string tensorName = basicBlock->GetTensors()[numInputs + i]->GetName(); + + std::string opStr = operatorString + "_output" + std::to_string(i) + "_"; + + CHECK(blockOutputName == operatorOutputName); + CHECK(tensorName == operatorOutputName); + CHECK(blockOutputName.find(opStr) != std::string::npos); + } + + CHECK(op->GetAttributeType() == tosaAttribute); + CHECK(op->GetOp() == tosaOp); + + for (uint32_t i = 0; i < numInputs; i++) + { + TosaSerializationTensor* tensor = basicBlock->GetTensors()[i]; + CHECK(tensor->GetDtype() == dataType); + CHECK(tensor->GetData().size() == 0); + CHECK(tensor->GetShape() == inputShape[static_cast(i)]); + } + + for (uint32_t i = 0; i < numOutputs; i++) + { + TosaSerializationTensor* tensor = basicBlock->GetTensors()[i + inputShape.size()]; + CHECK(tensor->GetDtype() == dataType); + CHECK(tensor->GetData().size() == 0); + CHECK(tensor->GetShape() == outputShape[static_cast(i)]); + } + + VerifyTosaAttributeFromDescriptor(descriptor, + op->GetAttribute(), + type); +} \ No newline at end of file diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp index a39bfb6c4d..ce4abbf921 100644 --- a/src/backends/tosaReference/TosaRefLayerSupport.cpp +++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp @@ -13,24 +13,25 @@ #include #include +#include namespace armnn { -static bool RunTosaLayerChecks(TosaSerializationOperator* op, - const std::vector& inputs, - const std::vector& outputs, - const std::vector& supportedAttributes, - const std::vector& supportedTypes, - Optional reasonIfUnsupported) +static bool RunTosaLayerChecksSingleDataType(TosaSerializationOperator* op, + const std::vector& inputs, + const std::vector& outputs, + const std::vector& supportedAttributes, + const std::vector& supportedTypes, + Optional reasonIfUnsupported) { bool supported = true; - std::string opCode = std::to_string(op->GetOp()); + std::string opString = TosaOpToString(op->GetOp()); // Check Attribute from operator (GetAttribute) supported &= CheckSupportRule(TosaOperatorAttributeOfAny(op, supportedAttributes), reasonIfUnsupported, - std::string("TOSA Reference Operator: " + opCode + + std::string("TOSA Reference Operator: " + opString + " has an unsupported attribute.").c_str()); for (auto input : inputs) @@ -40,14 +41,14 @@ static bool RunTosaLayerChecks(TosaSerializationOperator* op, // Check Dtype from tensor (GetDtype) supported &= CheckSupportRule(TosaTypeAnyOf(input, supportedTypes), reasonIfUnsupported, - std::string("TOSA Reference Operator: " + opCode + " for input: " + + std::string("TOSA Reference Operator: " + opString + " for input: " + input->GetName() + " has an unsupported data type: " + dataTypeCode).c_str()); // Check Shape from tensor (GetShape) supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(input), reasonIfUnsupported, - std::string("Tosa Reference Operator: " + opCode + " for input: " + + std::string("Tosa Reference Operator: " + opString + " for input: " + input->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str()); } @@ -58,20 +59,72 @@ static bool RunTosaLayerChecks(TosaSerializationOperator* op, // Check Dtype from tensor (GetDtype) supported &= CheckSupportRule(TosaTypeAnyOf(output, supportedTypes), reasonIfUnsupported, - std::string("TOSA Reference Operator: " + opCode + " for output: " + + std::string("TOSA Reference Operator: " + opString + " for output: " + output->GetName() + " has an unsupported data type: " + dataTypeCode).c_str()); // Check Shape from tensor (GetShape) supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(output), reasonIfUnsupported, - std::string("Tosa Reference Operator: " + opCode + " for output: " + + std::string("Tosa Reference Operator: " + opString + " for output: " + output->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str()); } return supported; } +static bool RunTosaLayerChecksInputOutputDataType(TosaSerializationOperator* op, + const std::vector& inputs, + const std::vector& outputs, + const std::vector& supportedAttributes, + const std::vector>& supportedMappingTypes, + Optional reasonIfUnsupported) +{ + bool supported = true; + + std::string opString = TosaOpToString(op->GetOp()); + + // Check Attribute from operator (GetAttribute) + supported &= CheckSupportRule(TosaOperatorAttributeOfAny(op, supportedAttributes), reasonIfUnsupported, + std::string("TOSA Reference Operator: " + opString + + " has an unsupported attribute.").c_str()); + + supported &= CheckSupportRule(TosaAssertSize(inputs, outputs), reasonIfUnsupported, + std::string("TOSA Reference Operator: " + opString + + " must have 1-to-1 mapping of inputs-to-outputs.").c_str()); + + for (uint32_t i = 0; i < inputs.size(); i++) + { + auto input = inputs[i]; + auto output = outputs[i]; + std::string inputDataTypeCode = std::to_string(input->GetDtype()); + std::string outputDataTypeCode = std::to_string(output->GetDtype()); + std::tuple mappingType(input->GetDtype(), output->GetDtype()); + + // Check Dtype from tensor (GetDtype) + supported &= CheckSupportRule(TosaContainerContains(mappingType, supportedMappingTypes), + reasonIfUnsupported, + std::string("TOSA Reference Operator: " + opString + " for input: " + + input->GetName() + " and output: " + output->GetName() + + " has an unsupported input data type: " + inputDataTypeCode + + " to output data type: " + outputDataTypeCode).c_str()); + + // Check Shape from tensor (GetShape) + supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(input), + reasonIfUnsupported, + std::string("Tosa Reference Operator: " + opString + " for input: " + + input->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str()); + + // Check Shape from tensor (GetShape) + supported &= CheckSupportRule(TosaTensorNumDimensionsWithinBounds(output), + reasonIfUnsupported, + std::string("Tosa Reference Operator: " + opString + " for output: " + + output->GetName() + " exceeds MaxNumOfTensorDimensions.").c_str()); + } + + return supported; +} + static bool IsTosaLayerSupported(TosaSerializationOperator* op, const std::vector& inputs, const std::vector& outputs, @@ -81,8 +134,6 @@ static bool IsTosaLayerSupported(TosaSerializationOperator* op, { case tosa::Op_ADD: { - bool supported = true; - std::vector supportedAttributes = { Attribute_NONE @@ -97,14 +148,84 @@ static bool IsTosaLayerSupported(TosaSerializationOperator* op, }; // Check the attribute, data types and bounds for inputs and outputs. - supported = RunTosaLayerChecks(op, - inputs, - outputs, - supportedAttributes, - supportedTypes, - reasonIfUnsupported); - - return supported; + return RunTosaLayerChecksSingleDataType(op, + inputs, + outputs, + supportedAttributes, + supportedTypes, + reasonIfUnsupported); + } + case tosa::Op_AVG_POOL2D: + { + std::vector supportedAttributes = + { + Attribute_PoolAttribute + }; + + std::vector> supportedTypesMapping = + { + std::tuple(DType_FP16, DType_FP16), + std::tuple(DType_FP16, DType_FP32), + std::tuple(DType_FP32, DType_FP32), + std::tuple(DType_INT8, DType_INT32), + std::tuple(DType_INT16, DType_INT32) + }; + + // Check the attribute, data types and bounds for inputs and outputs. + return RunTosaLayerChecksInputOutputDataType(op, + inputs, + outputs, + supportedAttributes, + supportedTypesMapping, + reasonIfUnsupported); + } + case tosa::Op_MAX_POOL2D: + { + std::vector supportedAttributes = + { + Attribute_PoolAttribute + }; + + std::vector supportedTypes = + { + DType_FP16, + DType_FP32, + DType_INT8, + DType_INT16 + }; + + // Check the attribute, data types and bounds for inputs and outputs. + return RunTosaLayerChecksSingleDataType(op, + inputs, + outputs, + supportedAttributes, + supportedTypes, + reasonIfUnsupported); + } + case tosa::Op_PAD: + { + std::vector supportedAttributes = + { + Attribute_PadAttribute + }; + + std::vector supportedTypes = + { + DType_FP16, + DType_FP32, + DType_INT8, + DType_INT16, + DType_INT32, + DType_BOOL + }; + + // Check the attribute, data types and bounds for inputs and outputs. + return RunTosaLayerChecksSingleDataType(op, + inputs, + outputs, + supportedAttributes, + supportedTypes, + reasonIfUnsupported); } default: SetValueChecked(reasonIfUnsupported, "Operation is currently unsupported by the TOSA Reference Backend."); @@ -136,6 +257,11 @@ bool TosaRefLayerSupport::IsLayerSupported(const LayerType& type, case LayerType::Input: case LayerType::Output: return true; + case LayerType::Pooling2d: + // Setup inputs and outputs + inputInfos.push_back(&infos[0]); + outputInfos.push_back(&infos[1]); + break; default: break; } diff --git a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp index 54d6db6cef..fbe1265fe3 100644 --- a/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp +++ b/src/backends/tosaReference/test/TosaRefEndToEndTests.cpp @@ -6,27 +6,60 @@ #include "backendsCommon/test/EndToEndTestImpl.hpp" #include "backendsCommon/test/AdditionEndToEndTestImpl.hpp" +#include "backendsCommon/test/Pooling2dEndToEndTestImpl.hpp" #include TEST_SUITE("TosaRefEndToEnd") { -std::vector tosaDefaultBackends = { "TosaRef" }; +std::vector tosaDefaultBackends = { "TosaRef" }; // Addition -TEST_CASE("TosaRefEndtoEndTestFloat32") +TEST_CASE("TosaRefAdditionEndtoEndTestFloat32") { - AdditionEndToEnd(tosaDefaultBackends); + AdditionEndToEnd(tosaDefaultBackends); } -TEST_CASE("TosaRefEndtoEndTestInt32") +TEST_CASE("TosaRefAdditionEndtoEndTestInt32") { - AdditionEndToEnd(tosaDefaultBackends); + AdditionEndToEnd(tosaDefaultBackends); } -TEST_CASE("TosaRefEndtoEndTestFloat16") +TEST_CASE("TosaRefAdditionEndtoEndTestFloat16") { - AdditionEndToEndFloat16(tosaDefaultBackends); + AdditionEndToEndFloat16(tosaDefaultBackends); +} + +// Max Pool 2D +TEST_CASE("TosaRefMaxPool2DEndtoEndTestFloat32") +{ + MaxPool2dEndToEnd(tosaDefaultBackends); +} + +TEST_CASE("TosaRefMaxPool2DEndtoEndTestFloat16") +{ + MaxPool2dEndToEndFloat16(tosaDefaultBackends); +} + +TEST_CASE("TosaRefMaxPool2DIgnoreValueEndtoEndTestFloat32") +{ + MaxPool2dEndToEnd(tosaDefaultBackends, PaddingMethod::IgnoreValue); +} + +// Average Pool 2D +TEST_CASE("TosaRefAvgPool2DEndtoEndTestFloat32") +{ + AvgPool2dEndToEnd(tosaDefaultBackends); +} + +TEST_CASE("TosaRefAvgPool2DEndtoEndTestFloat16") +{ + AvgPool2dEndToEndFloat16(tosaDefaultBackends); +} + +TEST_CASE("TosaRefAvgPool2DIgnoreValueEndtoEndTestFloat32") +{ + AvgPool2dEndToEnd(tosaDefaultBackends, PaddingMethod::IgnoreValue); } } \ No newline at end of file diff --git a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp index 47f31380a5..48eca344bc 100644 --- a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp +++ b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp @@ -57,9 +57,131 @@ TEST_CASE("IsLayerSupportedTosaReferenceAdditionUnsupported") reasonIfNotSupported); CHECK(!supported); - REQUIRE(reasonIfNotSupported.find("TOSA Reference Operator: 14 for input: Op_ADD_input0_") != std::string::npos); - REQUIRE(reasonIfNotSupported.find("TOSA Reference Operator: 14 for input: Op_ADD_input1_") != std::string::npos); - REQUIRE(reasonIfNotSupported.find("TOSA Reference Operator: 14 for output: Op_ADD_output0_") != std::string::npos); + REQUIRE(reasonIfNotSupported.find( + "TOSA Reference Operator: Op_ADD for input: Op_ADD_input0_") != std::string::npos); + REQUIRE(reasonIfNotSupported.find( + "TOSA Reference Operator: Op_ADD for input: Op_ADD_input1_") != std::string::npos); + REQUIRE(reasonIfNotSupported.find( + "TOSA Reference Operator: Op_ADD for output: Op_ADD_output0_") != std::string::npos); +} + +TEST_CASE("IsLayerSupportedTosaReferenceMaxPooling2d") +{ + armnn::TensorShape inShape = {1,1,3,4}; + armnn::TensorShape outShape = {1,1,3,4}; + armnn::TensorInfo in(inShape, armnn::DataType::Float32); + armnn::TensorInfo out(outShape, armnn::DataType::Float32); + + armnn::Pooling2dDescriptor desc; + armnn::TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d, + {in, out}, + desc, + armnn::EmptyOptional(), + armnn::EmptyOptional(), + reasonIfNotSupported); + + CHECK(supported); +} + +TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2d_IgnoreValue") +{ + armnn::TensorShape inShape = {1,1,3,4}; + armnn::TensorShape outShape = {1,1,3,4}; + armnn::TensorInfo in(inShape, armnn::DataType::Float32); + armnn::TensorInfo out(outShape, armnn::DataType::Float32); + + armnn::Pooling2dDescriptor desc; + desc.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue; + desc.m_PoolType = armnn::PoolingAlgorithm::Average; + + armnn::TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d, + {in, out}, + desc, + armnn::EmptyOptional(), + armnn::EmptyOptional(), + reasonIfNotSupported); + + CHECK(supported); +} + +TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2d_InputOutputDatatypeDifferent") +{ + armnn::TensorShape inShape = {1,1,3,4}; + armnn::TensorShape outShape = {1,1,3,4}; + armnn::TensorInfo in(inShape, armnn::DataType::QAsymmS8); + armnn::TensorInfo out(outShape, armnn::DataType::Signed32); + + armnn::Pooling2dDescriptor desc; + desc.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue; + desc.m_PoolType = armnn::PoolingAlgorithm::Average; + + armnn::TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d, + {in, out}, + desc, + armnn::EmptyOptional(), + armnn::EmptyOptional(), + reasonIfNotSupported); + + CHECK(supported); +} + +TEST_CASE("IsLayerSupportedTosaReferenceMaxPooling2dUnsupported") +{ + armnn::TensorShape inShape = {1,1,3,4}; + armnn::TensorShape outShape = {1,1,3,4}; + armnn::TensorInfo in(inShape, armnn::DataType::Signed64); + armnn::TensorInfo out(outShape, armnn::DataType::Signed64); + + armnn::Pooling2dDescriptor desc; + armnn::TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d, + {in, out}, + desc, + armnn::EmptyOptional(), + armnn::EmptyOptional(), + reasonIfNotSupported); + + CHECK(!supported); + REQUIRE(reasonIfNotSupported.find( + "TOSA Reference Operator: Op_MAX_POOL2D for input: Op_MAX_POOL2D_input0_") != std::string::npos); + REQUIRE(reasonIfNotSupported.find( + "TOSA Reference Operator: Op_MAX_POOL2D for output: Op_MAX_POOL2D_output0_") != std::string::npos); +} + +TEST_CASE("IsLayerSupportedTosaReferenceAvgPooling2dUnsupported_InputOutputDatatypeDifferent") +{ + armnn::TensorShape inShape = {1,1,3,4}; + armnn::TensorShape outShape = {1,1,3,4}; + armnn::TensorInfo in(inShape, armnn::DataType::Float32); + armnn::TensorInfo out(outShape, armnn::DataType::Float16); + + armnn::Pooling2dDescriptor desc; + desc.m_PaddingMethod = armnn::PaddingMethod::IgnoreValue; + desc.m_PoolType = armnn::PoolingAlgorithm::Average; + + armnn::TosaRefLayerSupport supportChecker; + std::string reasonIfNotSupported; + auto supported = supportChecker.IsLayerSupported(armnn::LayerType::Pooling2d, + {in, out}, + desc, + armnn::EmptyOptional(), + armnn::EmptyOptional(), + reasonIfNotSupported); + + CHECK(!supported); + REQUIRE(reasonIfNotSupported.find( + "TOSA Reference Operator: Op_AVG_POOL2D for input: Op_PAD_intermediate0_") != std::string::npos); + REQUIRE(reasonIfNotSupported.find( + " and output: Op_AVG_POOL2D_output0_") != std::string::npos); + REQUIRE(reasonIfNotSupported.find( + " has an unsupported input data type: 8 to output data type: 10") != std::string::npos); } } -- cgit v1.2.1