IVGCVSW-7345 Add Pooling2d support to TOSA Reference Backend

Signed-off-by: Cathal Corbett <cathal.corbett@arm.com>
Change-Id: I73a47e513fe2d064ef233b121a68ef2edf0396dc
diff --git a/src/backends/tosaReference/TosaRefLayerSupport.cpp b/src/backends/tosaReference/TosaRefLayerSupport.cpp
index a39bfb6..ce4abbf 100644
--- a/src/backends/tosaReference/TosaRefLayerSupport.cpp
+++ b/src/backends/tosaReference/TosaRefLayerSupport.cpp
@@ -13,24 +13,25 @@
 
 #include <vector>
 #include <array>
+#include <tuple>
 
 namespace armnn
 {
 
-static bool RunTosaLayerChecks(TosaSerializationOperator* op,
-                               const std::vector<TosaSerializationTensor*>& inputs,
-                               const std::vector<TosaSerializationTensor*>& outputs,
-                               const std::vector<Attribute>& supportedAttributes,
-                               const std::vector<DType>& supportedTypes,
-                               Optional<string&> reasonIfUnsupported)
+static bool RunTosaLayerChecksSingleDataType(TosaSerializationOperator* op,
+                                             const std::vector<TosaSerializationTensor*>& inputs,
+                                             const std::vector<TosaSerializationTensor*>& outputs,
+                                             const std::vector<Attribute>& supportedAttributes,
+                                             const std::vector<DType>& supportedTypes,
+                                             Optional<string&> 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 @@
         // 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 @@
         // 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<TosaSerializationTensor*>& inputs,
+                                                  const std::vector<TosaSerializationTensor*>& outputs,
+                                                  const std::vector<Attribute>& supportedAttributes,
+                                                  const std::vector<std::tuple<DType,DType>>& supportedMappingTypes,
+                                                  Optional<string&> 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<DType, DType> 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<TosaSerializationTensor*>& inputs,
                                  const std::vector<TosaSerializationTensor*>& outputs,
@@ -81,8 +134,6 @@
     {
         case tosa::Op_ADD:
         {
-            bool supported = true;
-
             std::vector<Attribute> supportedAttributes =
             {
                 Attribute_NONE
@@ -97,14 +148,84 @@
             };
 
             // Check the attribute, data types and bounds for inputs and outputs.
-            supported = RunTosaLayerChecks(op,
-                                           inputs,
-                                           outputs,
-                                           supportedAttributes,
-                                           supportedTypes,
-                                           reasonIfUnsupported);
+            return RunTosaLayerChecksSingleDataType(op,
+                                                    inputs,
+                                                    outputs,
+                                                    supportedAttributes,
+                                                    supportedTypes,
+                                                    reasonIfUnsupported);
+        }
+        case tosa::Op_AVG_POOL2D:
+        {
+            std::vector<Attribute> supportedAttributes =
+            {
+                Attribute_PoolAttribute
+            };
 
-            return supported;
+            std::vector<std::tuple<DType, DType>> supportedTypesMapping =
+            {
+                std::tuple<DType, DType>(DType_FP16, DType_FP16),
+                std::tuple<DType, DType>(DType_FP16, DType_FP32),
+                std::tuple<DType, DType>(DType_FP32, DType_FP32),
+                std::tuple<DType, DType>(DType_INT8, DType_INT32),
+                std::tuple<DType, DType>(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<Attribute> supportedAttributes =
+            {
+                Attribute_PoolAttribute
+            };
+
+            std::vector<DType> 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<Attribute> supportedAttributes =
+            {
+                Attribute_PadAttribute
+            };
+
+            std::vector<DType> 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 @@
         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 54d6db6..fbe1265 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 <doctest/doctest.h>
 
 TEST_SUITE("TosaRefEndToEnd")
 {
-std::vector<armnn::BackendId> tosaDefaultBackends = { "TosaRef" };
+std::vector<BackendId> tosaDefaultBackends = { "TosaRef" };
 
 // Addition
-TEST_CASE("TosaRefEndtoEndTestFloat32")
+TEST_CASE("TosaRefAdditionEndtoEndTestFloat32")
 {
-    AdditionEndToEnd<armnn::DataType::Float32>(tosaDefaultBackends);
+    AdditionEndToEnd<DataType::Float32>(tosaDefaultBackends);
 }
 
-TEST_CASE("TosaRefEndtoEndTestInt32")
+TEST_CASE("TosaRefAdditionEndtoEndTestInt32")
 {
-    AdditionEndToEnd<armnn::DataType::Signed32>(tosaDefaultBackends);
+    AdditionEndToEnd<DataType::Signed32>(tosaDefaultBackends);
 }
 
-TEST_CASE("TosaRefEndtoEndTestFloat16")
+TEST_CASE("TosaRefAdditionEndtoEndTestFloat16")
 {
-    AdditionEndToEndFloat16<armnn::DataType::Float16>(tosaDefaultBackends);
+    AdditionEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
+// Max Pool 2D
+TEST_CASE("TosaRefMaxPool2DEndtoEndTestFloat32")
+{
+    MaxPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefMaxPool2DEndtoEndTestFloat16")
+{
+    MaxPool2dEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefMaxPool2DIgnoreValueEndtoEndTestFloat32")
+{
+    MaxPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends, PaddingMethod::IgnoreValue);
+}
+
+// Average Pool 2D
+TEST_CASE("TosaRefAvgPool2DEndtoEndTestFloat32")
+{
+    AvgPool2dEndToEnd<DataType::Float32>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefAvgPool2DEndtoEndTestFloat16")
+{
+    AvgPool2dEndToEndFloat16<DataType::Float16>(tosaDefaultBackends);
+}
+
+TEST_CASE("TosaRefAvgPool2DIgnoreValueEndtoEndTestFloat32")
+{
+    AvgPool2dEndToEnd<DataType::Float32>(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 47f3138..48eca34 100644
--- a/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
+++ b/src/backends/tosaReference/test/TosaRefLayerSupportTests.cpp
@@ -57,9 +57,131 @@
                                                      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);
 }
 
 }