COMPMID-2235: Extend type support for CL/NEON DequantizationLayer.

Adds support for:
- QSYMM8

Change-Id: Ia0b839fc844ce0f968dad1b69a001f9a660dbcd5
Signed-off-by: Georgios Pinitas <georgios.pinitas@arm.com>
Reviewed-on: https://review.mlplatform.org/c/1378
Comments-Addressed: Arm Jenkins <bsgcomp@arm.com>
Tested-by: Arm Jenkins <bsgcomp@arm.com>
Reviewed-by: Manuel Bottini <manuel.bottini@arm.com>
Reviewed-by: Michalis Spyrou <michalis.spyrou@arm.com>
diff --git a/tests/validation/CL/DequantizationLayer.cpp b/tests/validation/CL/DequantizationLayer.cpp
index b1b0d81..2ef8c60 100644
--- a/tests/validation/CL/DequantizationLayer.cpp
+++ b/tests/validation/CL/DequantizationLayer.cpp
@@ -27,6 +27,7 @@
 #include "arm_compute/runtime/CL/functions/CLDequantizationLayer.h"
 #include "tests/CL/CLAccessor.h"
 #include "tests/PaddingCalculator.h"
+#include "tests/datasets/DatatypeDataset.h"
 #include "tests/datasets/ShapeDatasets.h"
 #include "tests/framework/Asserts.h"
 #include "tests/framework/Macros.h"
@@ -96,16 +97,14 @@
 using CLDequantizationLayerFixture = DequantizationValidationFixture<CLTensor, CLAccessor, CLDequantizationLayer, T>;
 
 TEST_SUITE(FP16)
-FIXTURE_DATA_TEST_CASE(RunSmall, CLDequantizationLayerFixture<half>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(),
-                                                                                                                        framework::dataset::make("DataType", DataType::F16)),
-                                                                                                                framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunSmall, CLDequantizationLayerFixture<half>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), datasets::QuantizedTypes()),
+                                                                                                                framework::dataset::make("DataType", DataType::F16)))
 {
     // Validate output
     validate(CLAccessor(_target), _reference);
 }
-FIXTURE_DATA_TEST_CASE(RunLarge, CLDequantizationLayerFixture<half>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(),
-                                                                                                                      framework::dataset::make("DataType", DataType::F16)),
-                                                                                                              framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunLarge, CLDequantizationLayerFixture<half>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(), datasets::QuantizedTypes()),
+                                                                                                              framework::dataset::make("DataType", DataType::F16)))
 {
     // Validate output
     validate(CLAccessor(_target), _reference);
@@ -113,16 +112,14 @@
 TEST_SUITE_END() // FP16
 
 TEST_SUITE(FP32)
-FIXTURE_DATA_TEST_CASE(RunSmall, CLDequantizationLayerFixture<float>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(),
-                                                                                                                 framework::dataset::make("DataType", DataType::F32)),
-                                                                                                                 framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunSmall, CLDequantizationLayerFixture<float>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), datasets::QuantizedTypes()),
+                                                                                                                 framework::dataset::make("DataType", DataType::F32)))
 {
     // Validate output
     validate(CLAccessor(_target), _reference);
 }
-FIXTURE_DATA_TEST_CASE(RunLarge, CLDequantizationLayerFixture<float>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(),
-                                                                                                                       framework::dataset::make("DataType", DataType::F32)),
-                                                                                                               framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunLarge, CLDequantizationLayerFixture<float>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(), datasets::QuantizedTypes()),
+                                                                                                               framework::dataset::make("DataType", DataType::F32)))
 {
     // Validate output
     validate(CLAccessor(_target), _reference);
diff --git a/tests/validation/CL/UNIT/TensorAllocator.cpp b/tests/validation/CL/UNIT/TensorAllocator.cpp
index 4b8e105..d91f4dd 100644
--- a/tests/validation/CL/UNIT/TensorAllocator.cpp
+++ b/tests/validation/CL/UNIT/TensorAllocator.cpp
@@ -249,9 +249,9 @@
 
     // Check quantization information
     ARM_COMPUTE_EXPECT(!tensor.info()->quantization_info().empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(!tensor.info()->quantization_info().scale.empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(tensor.info()->quantization_info().scale.size() == scale.size(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(tensor.info()->quantization_info().offset.empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(!tensor.info()->quantization_info().scale().empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(tensor.info()->quantization_info().scale().size() == scale.size(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(tensor.info()->quantization_info().offset().empty(), framework::LogLevel::ERRORS);
 
     CLQuantization quantization = tensor.quantization();
     ARM_COMPUTE_ASSERT(quantization.scale != nullptr);
diff --git a/tests/validation/NEON/DequantizationLayer.cpp b/tests/validation/NEON/DequantizationLayer.cpp
index 0ae20b7..a4606fe 100644
--- a/tests/validation/NEON/DequantizationLayer.cpp
+++ b/tests/validation/NEON/DequantizationLayer.cpp
@@ -27,6 +27,7 @@
 #include "arm_compute/runtime/TensorAllocator.h"
 #include "tests/NEON/Accessor.h"
 #include "tests/PaddingCalculator.h"
+#include "tests/datasets/DatatypeDataset.h"
 #include "tests/datasets/ShapeDatasets.h"
 #include "tests/framework/Asserts.h"
 #include "tests/framework/Macros.h"
@@ -106,16 +107,14 @@
 
 #ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC
 TEST_SUITE(FP16)
-FIXTURE_DATA_TEST_CASE(RunSmall, NEDequantizationLayerFixture<half>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(),
-                                                                                                                        framework::dataset::make("DataType", DataType::F16)),
-                                                                                                                framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunSmall, NEDequantizationLayerFixture<half>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), datasets::QuantizedTypes()),
+                                                                                                                framework::dataset::make("DataType", DataType::F16)))
 {
     // Validate output
     validate(Accessor(_target), _reference);
 }
-FIXTURE_DATA_TEST_CASE(RunLarge, NEDequantizationLayerFixture<half>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(),
-                                                                                                                      framework::dataset::make("DataType", DataType::F16)),
-                                                                                                              framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunLarge, NEDequantizationLayerFixture<half>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(), datasets::QuantizedTypes()),
+                                                                                                              framework::dataset::make("DataType", DataType::F16)))
 {
     // Validate output
     validate(Accessor(_target), _reference);
@@ -124,16 +123,14 @@
 #endif           /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */
 
 TEST_SUITE(FP32)
-FIXTURE_DATA_TEST_CASE(RunSmall, NEDequantizationLayerFixture<float>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(),
-                                                                                                                 framework::dataset::make("DataType", DataType::F32)),
-                                                                                                                 framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunSmall, NEDequantizationLayerFixture<float>, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), datasets::QuantizedTypes()),
+                                                                                                                 framework::dataset::make("DataType", DataType::F32)))
 {
     // Validate output
     validate(Accessor(_target), _reference);
 }
-FIXTURE_DATA_TEST_CASE(RunLarge, NEDequantizationLayerFixture<float>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(),
-                                                                                                                       framework::dataset::make("DataType", DataType::F32)),
-                                                                                                               framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.1f, 128.0f) })))
+FIXTURE_DATA_TEST_CASE(RunLarge, NEDequantizationLayerFixture<float>, framework::DatasetMode::NIGHTLY, combine(combine(datasets::LargeShapes(), datasets::QuantizedTypes()),
+                                                                                                               framework::dataset::make("DataType", DataType::F32)))
 {
     // Validate output
     validate(Accessor(_target), _reference);
diff --git a/tests/validation/UNIT/TensorInfo.cpp b/tests/validation/UNIT/TensorInfo.cpp
index 96d07da..009c757 100644
--- a/tests/validation/UNIT/TensorInfo.cpp
+++ b/tests/validation/UNIT/TensorInfo.cpp
@@ -141,9 +141,9 @@
 
     // Check quantization information
     ARM_COMPUTE_EXPECT(!info.quantization_info().empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(!info.quantization_info().scale.empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(info.quantization_info().scale.size() == 1, framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(info.quantization_info().offset.empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(!info.quantization_info().scale().empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(info.quantization_info().scale().size() == 1, framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(info.quantization_info().offset().empty(), framework::LogLevel::ERRORS);
 
     UniformQuantizationInfo qinfo = info.quantization_info().uniform();
     ARM_COMPUTE_EXPECT(qinfo.scale == scale, framework::LogLevel::ERRORS);
@@ -160,10 +160,10 @@
 
     // Check quantization information
     ARM_COMPUTE_EXPECT(!info.quantization_info().empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(!info.quantization_info().scale.empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(info.quantization_info().scale.size() == 1, framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(!info.quantization_info().offset.empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(info.quantization_info().offset.size() == 1, framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(!info.quantization_info().scale().empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(info.quantization_info().scale().size() == 1, framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(!info.quantization_info().offset().empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(info.quantization_info().offset().size() == 1, framework::LogLevel::ERRORS);
 
     UniformQuantizationInfo qinfo = info.quantization_info().uniform();
     ARM_COMPUTE_EXPECT(qinfo.scale == scale, framework::LogLevel::ERRORS);
@@ -179,9 +179,9 @@
 
     // Check quantization information
     ARM_COMPUTE_EXPECT(!info.quantization_info().empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(!info.quantization_info().scale.empty(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(info.quantization_info().scale.size() == scale.size(), framework::LogLevel::ERRORS);
-    ARM_COMPUTE_EXPECT(info.quantization_info().offset.empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(!info.quantization_info().scale().empty(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(info.quantization_info().scale().size() == scale.size(), framework::LogLevel::ERRORS);
+    ARM_COMPUTE_EXPECT(info.quantization_info().offset().empty(), framework::LogLevel::ERRORS);
 }
 
 TEST_SUITE_END() // TensorInfoValidation
diff --git a/tests/validation/fixtures/DequantizationLayerFixture.h b/tests/validation/fixtures/DequantizationLayerFixture.h
index 2e3712d..15f3711 100644
--- a/tests/validation/fixtures/DequantizationLayerFixture.h
+++ b/tests/validation/fixtures/DequantizationLayerFixture.h
@@ -47,10 +47,11 @@
 {
 public:
     template <typename...>
-    void setup(TensorShape shape, DataType data_type, QuantizationInfo qinfo)
+    void setup(TensorShape shape, DataType src_data_type, DataType dst_datatype)
     {
-        _target    = compute_target(shape, data_type, qinfo);
-        _reference = compute_reference(shape, data_type, qinfo);
+        _quantization_info = generate_quantization_info(src_data_type);
+        _target            = compute_target(shape, src_data_type, dst_datatype);
+        _reference         = compute_reference(shape, src_data_type);
     }
 
 protected:
@@ -60,11 +61,11 @@
         library->fill_tensor_uniform(tensor, 0);
     }
 
-    TensorType compute_target(const TensorShape &shape, DataType data_type, QuantizationInfo qinfo)
+    TensorType compute_target(const TensorShape &shape, DataType src_data_type, DataType dst_datatype)
     {
         // Create tensors
-        TensorType src = create_tensor<TensorType>(shape, DataType::QASYMM8, 1, qinfo);
-        TensorType dst = create_tensor<TensorType>(shape, data_type);
+        TensorType src = create_tensor<TensorType>(shape, src_data_type, 1, _quantization_info);
+        TensorType dst = create_tensor<TensorType>(shape, dst_datatype);
 
         // Create and configure function
         FunctionType dequantization_layer;
@@ -89,19 +90,43 @@
         return dst;
     }
 
-    SimpleTensor<T> compute_reference(const TensorShape &shape, DataType data_type, QuantizationInfo qinfo)
+    SimpleTensor<T> compute_reference(const TensorShape &shape, DataType src_data_type)
     {
-        // Create reference
-        SimpleTensor<uint8_t> src{ shape, DataType::QASYMM8, 1, qinfo };
-
-        // Fill reference
-        fill(src);
-
-        return reference::dequantization_layer<T>(src);
+        if(is_data_type_quantized_asymmetric(src_data_type))
+        {
+            SimpleTensor<uint8_t> src{ shape, src_data_type, 1, _quantization_info };
+            fill(src);
+            return reference::dequantization_layer<T>(src);
+        }
+        else
+        {
+            SimpleTensor<int8_t> src{ shape, src_data_type, 1, _quantization_info };
+            fill(src);
+            return reference::dequantization_layer<T>(src);
+        }
     }
 
-    TensorType      _target{};
-    SimpleTensor<T> _reference{};
+protected:
+    QuantizationInfo generate_quantization_info(DataType data_type)
+    {
+        std::uniform_int_distribution<> distribution(1, 127);
+        std::mt19937                    gen(library.get()->seed());
+
+        switch(data_type)
+        {
+            case DataType::QSYMM8:
+                return QuantizationInfo(1.f / distribution(gen));
+            case DataType::QASYMM8:
+                return QuantizationInfo(1.f / distribution(gen), distribution(gen));
+            default:
+                ARM_COMPUTE_ERROR("Unsupported data type");
+        }
+    }
+
+protected:
+    TensorType       _target{};
+    SimpleTensor<T>  _reference{};
+    QuantizationInfo _quantization_info{};
 };
 } // namespace validation
 } // namespace test
diff --git a/tests/validation/reference/DequantizationLayer.cpp b/tests/validation/reference/DequantizationLayer.cpp
index 286a609..d07371c 100644
--- a/tests/validation/reference/DequantizationLayer.cpp
+++ b/tests/validation/reference/DequantizationLayer.cpp
@@ -23,6 +23,8 @@
  */
 #include "DequantizationLayer.h"
 
+#include "Permute.h"
+
 namespace arm_compute
 {
 namespace test
@@ -31,24 +33,82 @@
 {
 namespace reference
 {
-template <typename T>
-SimpleTensor<T> dequantization_layer(const SimpleTensor<uint8_t> &src)
+namespace
 {
-    const DataType                 dst_data_type     = std::is_same<T, float>::value ? DataType::F32 : DataType::F16;
-    const UniformQuantizationInfo &quantization_info = src.quantization_info().uniform();
+template <typename TOut>
+TOut dequantize(int8_t val, const UniformQuantizationInfo qinfo)
+{
+    return static_cast<TOut>(dequantize_qsymm8(val, qinfo));
+}
+template <typename TOut>
+TOut dequantize(uint8_t val, const UniformQuantizationInfo qinfo)
+{
+    return static_cast<TOut>(dequantize_qasymm8(val, qinfo));
+}
 
-    SimpleTensor<T> dst{ src.shape(), dst_data_type };
+template <typename TOut, typename TIn>
+SimpleTensor<TOut> dequantization_layer_nchw(const SimpleTensor<TIn> &src)
+{
+    const DataType src_data_type = src.data_type();
+    const DataType dst_data_type = std::is_same<TOut, float>::value ? DataType::F32 : DataType::F16;
 
-    for(int i = 0; i < src.num_elements(); ++i)
+    SimpleTensor<TOut> dst{ src.shape(), dst_data_type };
+
+    if(src_data_type == DataType::QSYMM8_PER_CHANNEL)
     {
-        dst[i] = static_cast<T>(dequantize_qasymm8(src[i], quantization_info));
+        const int WH = src.shape().x() * src.shape().y();
+        const int C  = src.shape().z();
+        const int N  = src.shape().total_size() / (WH * C);
+
+        const std::vector<float> qscales = src.quantization_info().scale();
+
+        for(int n = 0; n < N; ++n)
+        {
+            for(int c = 0; c < C; ++c)
+            {
+                const size_t                  idx           = n * C * WH + c * WH;
+                const UniformQuantizationInfo channel_qinfo = { qscales[c], 0 };
+
+                // Dequantize slice
+                for(int s = 0; s < WH; ++s)
+                {
+                    dst[idx + s] = dequantize<TOut>(src[idx + s], channel_qinfo);
+                }
+            }
+        }
+    }
+    else
+    {
+        const UniformQuantizationInfo &quantization_info = src.quantization_info().uniform();
+        ARM_COMPUTE_ERROR_ON(quantization_info.offset != 0 && src_data_type == DataType::QSYMM8);
+
+        for(int i = 0; i < src.num_elements(); ++i)
+        {
+            dst[i] = static_cast<TOut>(dequantize<TOut>(src[i], quantization_info));
+        }
     }
 
     return dst;
 }
+} // namespace
+template <typename TOut, typename TIn>
+SimpleTensor<TOut> dequantization_layer(const SimpleTensor<TIn> &src)
+{
+    if(src.data_layout() == DataLayout::NHWC && src.data_type() == DataType::QSYMM8_PER_CHANNEL)
+    {
+        SimpleTensor<TIn> src_nchw = reference::permute<TIn>(src, PermutationVector(1U, 2U, 0U));
+        return reference::permute<TOut>(dequantization_layer_nchw<TOut>(src_nchw), PermutationVector(2U, 0U, 1U));
+    }
+    else
+    {
+        return dequantization_layer_nchw<TOut>(src);
+    }
+}
 
 template SimpleTensor<half> dequantization_layer(const SimpleTensor<uint8_t> &src);
 template SimpleTensor<float> dequantization_layer(const SimpleTensor<uint8_t> &src);
+template SimpleTensor<half> dequantization_layer(const SimpleTensor<int8_t> &src);
+template SimpleTensor<float> dequantization_layer(const SimpleTensor<int8_t> &src);
 } // namespace reference
 } // namespace validation
 } // namespace test
diff --git a/tests/validation/reference/DequantizationLayer.h b/tests/validation/reference/DequantizationLayer.h
index 1d0e54b..8c78084 100644
--- a/tests/validation/reference/DequantizationLayer.h
+++ b/tests/validation/reference/DequantizationLayer.h
@@ -35,8 +35,8 @@
 {
 namespace reference
 {
-template <typename T>
-SimpleTensor<T> dequantization_layer(const SimpleTensor<uint8_t> &src);
+template <typename TOut, typename TIn>
+SimpleTensor<TOut> dequantization_layer(const SimpleTensor<TIn> &src);
 } // namespace reference
 } // namespace validation
 } // namespace test