IVGCVSW-2875 Reference implementation and unit tests for Dequantize

Change-Id: Ie4ade0519cb0bbe35dc36be6c9cd749b9171c74b
Signed-off-by: Nattapat Chaimanowong <nattapat.chaimanowong@arm.com>
diff --git a/src/backends/backendsCommon/MakeWorkloadHelper.hpp b/src/backends/backendsCommon/MakeWorkloadHelper.hpp
index 2d54335..9cd8774 100644
--- a/src/backends/backendsCommon/MakeWorkloadHelper.hpp
+++ b/src/backends/backendsCommon/MakeWorkloadHelper.hpp
@@ -47,11 +47,6 @@
         info.m_InputTensorInfos[0].GetDataType()
         : info.m_OutputTensorInfos[0].GetDataType();
 
-    BOOST_ASSERT(info.m_InputTensorInfos.empty()  ||
-                 info.m_OutputTensorInfos.empty() ||
-                 ((info.m_InputTensorInfos[0].GetDataType() == info.m_OutputTensorInfos[0].GetDataType()) ||
-                   info.m_OutputTensorInfos[0].GetDataType() == armnn::DataType::Boolean));
-
     switch (dataType)
     {
         case DataType::Float16:
diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt
index 124d8f2..47d5231 100644
--- a/src/backends/backendsCommon/test/CMakeLists.txt
+++ b/src/backends/backendsCommon/test/CMakeLists.txt
@@ -14,6 +14,7 @@
     ConvertFp16ToFp32TestImpl.hpp
     ConvertFp32ToFp16TestImpl.hpp
     DebugTestImpl.hpp
+    DequantizeTestImpl.hpp
     DetectionPostProcessLayerTestImpl.hpp
     DetectionPostProcessTestImpl.hpp
     EndToEndTestImpl.hpp
diff --git a/src/backends/backendsCommon/test/DequantizeTestImpl.hpp b/src/backends/backendsCommon/test/DequantizeTestImpl.hpp
new file mode 100644
index 0000000..5c10c75
--- /dev/null
+++ b/src/backends/backendsCommon/test/DequantizeTestImpl.hpp
@@ -0,0 +1,95 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include "WorkloadTestUtils.hpp"
+
+#include <armnn/ArmNN.hpp>
+#include <armnn/Tensor.hpp>
+#include <armnn/TypesUtils.hpp>
+
+#include <backendsCommon/CpuTensorHandle.hpp>
+#include <backendsCommon/IBackendInternal.hpp>
+#include <backendsCommon/WorkloadFactory.hpp>
+
+#include <test/TensorHelpers.hpp>
+
+namespace
+{
+
+template<typename T, std::size_t Dim>
+LayerTestResult<float, Dim> DequantizeTestImpl(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+    const armnn::TensorInfo& inputTensorInfo,
+    const armnn::TensorInfo& outputTensorInfo,
+    const std::vector<T>& inputData,
+    const std::vector<float>& expectedOutputData,
+    armnn::DequantizeQueueDescriptor descriptor)
+{
+    boost::multi_array<T, Dim> input = MakeTensor<T, Dim>(inputTensorInfo, inputData);
+
+    LayerTestResult<float, Dim> ret(outputTensorInfo);
+    ret.outputExpected = MakeTensor<float, Dim>(outputTensorInfo, expectedOutputData);
+
+    std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
+    std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
+
+    armnn::WorkloadInfo info;
+    AddInputToWorkload(descriptor, info, inputTensorInfo, inputHandle.get());
+    AddOutputToWorkload(descriptor, info, outputTensorInfo, outputHandle.get());
+
+    std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDequantize(descriptor, info);
+
+    inputHandle->Allocate();
+    outputHandle->Allocate();
+
+    CopyDataToITensorHandle(inputHandle.get(), input.data());
+
+    ExecuteWorkload(*workload, memoryManager);
+
+    CopyDataFromITensorHandle(ret.output.data(), outputHandle.get());
+
+    return ret;
+}
+
+template <armnn::DataType ArmnnInputType>
+LayerTestResult<float, 4> DequantizeSimpleTest(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    using T = armnn::ResolveType<ArmnnInputType>;
+
+    armnn::DequantizeQueueDescriptor desc;
+
+    const armnn::TensorInfo inputTensorInfo({1, 2, 2, 3}, ArmnnInputType, 0.5f, 1);
+    const armnn::TensorInfo outputTensorInfo({1, 2, 2, 3}, armnn::DataType::Float32);
+
+    std::vector<T> inputData = std::vector<T>(
+    {
+         3,  5,  7,
+         9, 11, 13,
+        15, 17, 19,
+        21, 23, 25,
+    });
+
+    std::vector<float> expectedOutputData = std::vector<float>(
+    {
+        1.0f,   2.0f,  3.0f,
+        4.0f,   5.0f,  6.0f,
+        7.0f,   8.0f,  9.0f,
+        10.0f, 11.0f, 12.0f,
+    });
+
+    return DequantizeTestImpl<T, 4>(workloadFactory,
+                                    memoryManager,
+                                    inputTensorInfo,
+                                    outputTensorInfo,
+                                    inputData,
+                                    expectedOutputData,
+                                    desc);
+}
+
+} // anonymous namespace
diff --git a/src/backends/backendsCommon/test/LayerTests.cpp b/src/backends/backendsCommon/test/LayerTests.cpp
index a088aaa..be1d435 100644
--- a/src/backends/backendsCommon/test/LayerTests.cpp
+++ b/src/backends/backendsCommon/test/LayerTests.cpp
@@ -43,6 +43,7 @@
 #include "ConvertFp16ToFp32TestImpl.hpp"
 #include "ConvertFp32ToFp16TestImpl.hpp"
 #include "DebugTestImpl.hpp"
+#include "DequantizeTestImpl.hpp"
 
 // 3-channel 16x8 image used as common input data for a number of Conv2d tests.
 static std::vector<float> ConvInput3x8x16({
@@ -8969,3 +8970,10 @@
     return GatherMultiDimParamsMultiDimIndicesTestImpl<armnn::DataType::QuantisedAsymm8>(
         workloadFactory, memoryManager);
 }
+
+LayerTestResult<float, 4> DequantizeUint8Test(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager)
+{
+    return DequantizeSimpleTest<armnn::DataType::QuantisedAsymm8>(workloadFactory, memoryManager);
+}
diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp
index a8cb553..c14ba7b 100644
--- a/src/backends/backendsCommon/test/LayerTests.hpp
+++ b/src/backends/backendsCommon/test/LayerTests.hpp
@@ -1439,3 +1439,7 @@
 LayerTestResult<uint8_t, 4> GatherMultiDimParamsMultiDimIndicesUint8Test(
     armnn::IWorkloadFactory& workloadFactory,
     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
+
+LayerTestResult<float, 4> DequantizeUint8Test(
+    armnn::IWorkloadFactory& workloadFactory,
+    const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp
index 820f36b..532c8ea 100644
--- a/src/backends/reference/RefLayerSupport.cpp
+++ b/src/backends/reference/RefLayerSupport.cpp
@@ -380,6 +380,16 @@
                                      &TrueFunc<>);
 }
 
+bool RefLayerSupport::IsDequantizeSupported(const TensorInfo& input,
+                                            const TensorInfo& output,
+                                            Optional<std::string&> reasonIfUnsupported) const
+{
+    return IsSupportedForDataTypeRef(reasonIfUnsupported,
+                                     input.GetDataType(),
+                                     &FalseFunc<>,
+                                     &TrueFunc<>);
+}
+
 bool RefLayerSupport::IsDetectionPostProcessSupported(const armnn::TensorInfo& input0,
                                                       const armnn::TensorInfo& input1,
                                                       const armnn::DetectionPostProcessDescriptor& descriptor,
diff --git a/src/backends/reference/RefLayerSupport.hpp b/src/backends/reference/RefLayerSupport.hpp
index c0d7fcf..42a5a44 100644
--- a/src/backends/reference/RefLayerSupport.hpp
+++ b/src/backends/reference/RefLayerSupport.hpp
@@ -65,6 +65,10 @@
                                          const Optional<TensorInfo>& biases,
                                          Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
 
+    bool IsDequantizeSupported(const TensorInfo& input,
+                               const TensorInfo& output,
+                               Optional<std::string&> reasonIfUnsupported = EmptyOptional()) const override;
+
     bool IsDetectionPostProcessSupported(const TensorInfo& input0,
                                          const TensorInfo& input1,
                                          const DetectionPostProcessDescriptor& descriptor,
diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp
index 3bf83bd..dda1819 100644
--- a/src/backends/reference/RefWorkloadFactory.cpp
+++ b/src/backends/reference/RefWorkloadFactory.cpp
@@ -348,4 +348,10 @@
     return nullptr;
 }
 
+std::unique_ptr<IWorkload> RefWorkloadFactory::CreateDequantize(const DequantizeQueueDescriptor& descriptor,
+                                                                const WorkloadInfo& info) const
+{
+    return MakeWorkload<NullWorkload, RefDequantizeWorkload>(descriptor, info);
+}
+
 } // namespace armnn
diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp
index 211f93b..14d3178 100644
--- a/src/backends/reference/RefWorkloadFactory.hpp
+++ b/src/backends/reference/RefWorkloadFactory.hpp
@@ -177,6 +177,9 @@
     std::unique_ptr<IWorkload> CreateGather(const GatherQueueDescriptor& descriptor,
                                             const WorkloadInfo& info) const override;
 
+    std::unique_ptr<IWorkload> CreateDequantize(const DequantizeQueueDescriptor& descriptor,
+                                                const WorkloadInfo& info) const override;
+
 private:
 
     template <typename F32Workload, typename U8Workload, typename QueueDescriptorType>
diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk
index a75146b..90aa63a 100644
--- a/src/backends/reference/backend.mk
+++ b/src/backends/reference/backend.mk
@@ -39,6 +39,7 @@
         workloads/RefDebugWorkload.cpp \
         workloads/RefDepthwiseConvolution2dFloat32Workload.cpp \
         workloads/RefDepthwiseConvolution2dUint8Workload.cpp \
+        workloads/RefDequantizeWorkload.cpp \
         workloads/RefDetectionPostProcessFloat32Workload.cpp \
         workloads/RefDetectionPostProcessUint8Workload.cpp \
         workloads/RefElementwiseWorkload.cpp \
diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp
index 13ea82d..c0e2e78 100644
--- a/src/backends/reference/test/RefLayerTests.cpp
+++ b/src/backends/reference/test/RefLayerTests.cpp
@@ -530,5 +530,7 @@
     DetectionPostProcessFastNmsUint8Test<armnn::RefWorkloadFactory>();
 }
 
+// Dequantize
+ARMNN_AUTO_TEST_CASE(DequantizeUint8, DequantizeUint8Test)
 
 BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt
index 89aed91..c4fc202 100644
--- a/src/backends/reference/workloads/CMakeLists.txt
+++ b/src/backends/reference/workloads/CMakeLists.txt
@@ -63,6 +63,8 @@
     RefDepthwiseConvolution2dFloat32Workload.hpp
     RefDepthwiseConvolution2dUint8Workload.cpp
     RefDepthwiseConvolution2dUint8Workload.hpp
+    RefDequantizeWorkload.cpp
+    RefDequantizeWorkload.hpp
     RefDetectionPostProcessUint8Workload.cpp
     RefDetectionPostProcessUint8Workload.hpp
     RefDetectionPostProcessFloat32Workload.cpp
diff --git a/src/backends/reference/workloads/RefDequantizeWorkload.cpp b/src/backends/reference/workloads/RefDequantizeWorkload.cpp
new file mode 100644
index 0000000..d861c50
--- /dev/null
+++ b/src/backends/reference/workloads/RefDequantizeWorkload.cpp
@@ -0,0 +1,34 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "RefDequantizeWorkload.hpp"
+#include "RefWorkloadUtils.hpp"
+
+namespace armnn
+{
+
+void RefDequantizeWorkload::Execute() const
+{
+    ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefDequantizeWorkload_Execute");
+
+    const TensorInfo& inputInfo = GetTensorInfo(m_Data.m_Inputs[0]);
+    const DataType& inputDataType = inputInfo.GetDataType();
+
+    float* outputData = GetOutputTensorData<float>(0, m_Data);
+
+    switch (inputDataType)
+    {
+        case DataType::QuantisedAsymm8:
+            Dequantize<uint8_t>(GetInputTensorData<uint8_t>(0, m_Data), outputData, inputInfo);
+            break;
+        case DataType::QuantisedSymm16:
+            Dequantize<int16_t>(GetInputTensorData<int16_t>(0, m_Data), outputData, inputInfo);
+            break;
+        default:
+            throw InvalidArgumentException("RefDequantizeWorkload: Unsupported input data type");
+    }
+}
+
+} // namespace armnn
diff --git a/src/backends/reference/workloads/RefDequantizeWorkload.hpp b/src/backends/reference/workloads/RefDequantizeWorkload.hpp
new file mode 100644
index 0000000..8d019e3
--- /dev/null
+++ b/src/backends/reference/workloads/RefDequantizeWorkload.hpp
@@ -0,0 +1,22 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <backendsCommon/Workload.hpp>
+
+namespace armnn
+{
+
+class RefDequantizeWorkload : public BaseWorkload<DequantizeQueueDescriptor>
+{
+public:
+    using BaseWorkload<DequantizeQueueDescriptor>::m_Data;
+    using BaseWorkload<DequantizeQueueDescriptor>::BaseWorkload;
+
+    void Execute() const override;
+};
+
+} // namespace armnn
diff --git a/src/backends/reference/workloads/RefWorkloadUtils.hpp b/src/backends/reference/workloads/RefWorkloadUtils.hpp
index feb4329..ce79616 100644
--- a/src/backends/reference/workloads/RefWorkloadUtils.hpp
+++ b/src/backends/reference/workloads/RefWorkloadUtils.hpp
@@ -127,6 +127,15 @@
     return ret;
 }
 
+template<typename T>
+inline void Dequantize(const T* inputData, float* outputData, const TensorInfo& info)
+{
+    for (unsigned int i = 0; i < info.GetNumElements(); i++)
+    {
+        outputData[i] = Dequantize<T>(inputData[i], info.GetQuantizationScale(), info.GetQuantizationOffset());
+    }
+}
+
 inline void Quantize(uint8_t* quant, const float* dequant, const TensorInfo& info)
 {
     for (size_t i = 0; i < info.GetNumElements(); i++)
diff --git a/src/backends/reference/workloads/RefWorkloads.hpp b/src/backends/reference/workloads/RefWorkloads.hpp
index 2156388..7d2e813 100644
--- a/src/backends/reference/workloads/RefWorkloads.hpp
+++ b/src/backends/reference/workloads/RefWorkloads.hpp
@@ -63,3 +63,4 @@
 #include "RefDebugWorkload.hpp"
 #include "RefRsqrtFloat32Workload.hpp"
 #include "RefComparisonWorkload.hpp"
+#include "RefDequantizeWorkload.hpp"