IVGCVSW-3286 Add epsilon Reference workload support

Change-Id: I5cabbf9d1ef3858be68d6820d14845e512128c5b
Signed-off-by: Ferran Balaguer <ferran.balaguer@arm.com>
diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp
index 9479db3..dd1991d 100644
--- a/include/armnn/Descriptors.hpp
+++ b/include/armnn/Descriptors.hpp
@@ -424,9 +424,12 @@
 struct L2NormalizationDescriptor
 {
     L2NormalizationDescriptor()
-        : m_DataLayout(DataLayout::NCHW)
+    : m_Eps(1e-12f)
+    , m_DataLayout(DataLayout::NCHW)
     {}
 
+    /// Used to avoid dividing by zero.
+    float m_Eps;
     /// The data layout to be used (NCHW, NHWC).
     DataLayout m_DataLayout;
 };
diff --git a/src/backends/backendsCommon/test/LayerTests.cpp b/src/backends/backendsCommon/test/LayerTests.cpp
index 55e799e..115b5ec 100644
--- a/src/backends/backendsCommon/test/LayerTests.cpp
+++ b/src/backends/backendsCommon/test/LayerTests.cpp
@@ -5294,7 +5294,8 @@
     float outScale,
     int32_t outOffset,
     const std::vector<float>& expectedOutputValues,
-    const armnn::DataLayout layout)
+    const armnn::DataLayout layout,
+    float epsilon = 1e-12f)
 {
     const armnn::TensorInfo inputTensorInfo(inputOutputTensorShape, ArmnnType, scale, offset);
     const armnn::TensorInfo outputTensorInfo(inputOutputTensorShape, ArmnnType, outScale, outOffset);
@@ -5333,6 +5334,7 @@
     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
 
     armnn::L2NormalizationQueueDescriptor descriptor;
+    descriptor.m_Parameters.m_Eps = epsilon;
     descriptor.m_Parameters.m_DataLayout = layout;
     armnn::WorkloadInfo info;
 
@@ -5798,6 +5800,57 @@
 }
 
 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
+LayerTestResult<T, 4> L2NormalizationEpsilonTestCommon(
+        armnn::IWorkloadFactory& workloadFactory,
+        const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+        float scale,
+        int32_t offset,
+        float outScale,
+        int32_t outOffset,
+        const armnn::DataLayout layout,
+        float epsilon)
+{
+    // Width: 1
+    // Height: 1
+    // Channels: 3
+    // BatchSize: 1
+    unsigned int numberOfBatches = 1;
+    unsigned int numberOfChannels = 3;
+    unsigned int height = 1;
+    unsigned int width = 1;
+
+    const armnn::TensorShape inputOutputShape = armnnUtils::GetTensorShape(
+            numberOfBatches, numberOfChannels, height, width, layout);
+
+    // 0.0000001^2 + 0.00000002^2 + 0.00000003^2 < 1e-12
+    std::vector<float> inputValues
+    {
+        // Batch 0, Channel 0, Height (1) x Width (1)
+        0.00000001f,
+
+        // Batch 0, Channel 1, Height (1) x Width (1)
+        0.00000002f,
+
+        // Batch 0, Channel 2, Height (1) x Width (1)
+        0.00000003f,
+    };
+
+    const float approxInvL2Norm = 1.f / sqrtf(epsilon);
+    std::vector<float> expectedOutputValues
+    {
+        // Batch 0, Channel 0, Height (1) x Width (1)
+        0.00000001f * approxInvL2Norm,
+        0.00000002f * approxInvL2Norm,
+        0.00000003f * approxInvL2Norm,
+    };
+
+    return L2NormalizationTestImpl<ArmnnType>(workloadFactory, memoryManager, inputOutputShape, scale, offset,
+                                              inputValues, outScale, outOffset, expectedOutputValues, layout,
+                                              epsilon);
+}
+
+
+template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
 LayerTestResult<T, 4> L2Normalization1dTestCommon(
         armnn::IWorkloadFactory& workloadFactory,
         const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
@@ -5872,6 +5925,26 @@
                                               inputValues, outScale, outOffset, expectedOutputValues, layout);
 }
 
+LayerTestResult<float, 4> L2NormalizationDefaultEpsilonTest(
+        armnn::IWorkloadFactory& workloadFactory,
+        const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+        const armnn::DataLayout layout)
+{
+    // Dummy descriptor to get the default value of epsilon.
+    armnn::L2NormalizationDescriptor descriptor;
+
+    return L2NormalizationEpsilonTestCommon<armnn::DataType::Float32>(workloadFactory, memoryManager, 0.f, 0, 0.f, 0,
+                                                                      layout, descriptor.m_Eps);
+}
+
+LayerTestResult<float, 4> L2NormalizationNonDefaultEpsilonTest(
+        armnn::IWorkloadFactory& workloadFactory,
+        const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+        const armnn::DataLayout layout)
+{
+    return L2NormalizationEpsilonTestCommon<armnn::DataType::Float32>(workloadFactory, memoryManager, 0.f, 0, 0.f, 0,
+                                                                      layout, 1e-9f);
+}
 
 LayerTestResult<float, 4> L2Normalization1dTest(
     armnn::IWorkloadFactory& workloadFactory,
diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp
index 704e88e..4c34044 100644
--- a/src/backends/backendsCommon/test/LayerTests.hpp
+++ b/src/backends/backendsCommon/test/LayerTests.hpp
@@ -922,6 +922,16 @@
     armnn::IWorkloadFactory& workloadFactory,
     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager);
 
+LayerTestResult<float, 4> L2NormalizationDefaultEpsilonTest(
+        armnn::IWorkloadFactory& workloadFactory,
+        const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+        const armnn::DataLayout layout);
+
+LayerTestResult<float, 4> L2NormalizationNonDefaultEpsilonTest(
+        armnn::IWorkloadFactory& workloadFactory,
+        const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
+        const armnn::DataLayout layout);
+
 LayerTestResult<float, 4> L2Normalization1dTest(
     armnn::IWorkloadFactory& workloadFactory,
     const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp
index 7540f1d..cf4d9fd 100644
--- a/src/backends/reference/test/RefLayerTests.cpp
+++ b/src/backends/reference/test/RefLayerTests.cpp
@@ -528,6 +528,9 @@
 ARMNN_AUTO_TEST_CASE(L2Normalization3dUint8Nhwc, L2Normalization3dUint8Test, armnn::DataLayout::NHWC)
 ARMNN_AUTO_TEST_CASE(L2Normalization4dUint8Nhwc, L2Normalization4dUint8Test, armnn::DataLayout::NHWC)
 
+ARMNN_AUTO_TEST_CASE(L2NormalizationDefaultEpsilon, L2NormalizationDefaultEpsilonTest, armnn::DataLayout::NCHW)
+ARMNN_AUTO_TEST_CASE(L2NormalizationNonDefaultEpsilon, L2NormalizationNonDefaultEpsilonTest, armnn::DataLayout::NCHW)
+
 // Pad
 ARMNN_AUTO_TEST_CASE(PadFloat322d, PadFloat322dTest)
 ARMNN_AUTO_TEST_CASE(PadFloat323d, PadFloat323dTest)
diff --git a/src/backends/reference/workloads/RefL2NormalizationWorkload.cpp b/src/backends/reference/workloads/RefL2NormalizationWorkload.cpp
index ce5699e..3b2ab50 100644
--- a/src/backends/reference/workloads/RefL2NormalizationWorkload.cpp
+++ b/src/backends/reference/workloads/RefL2NormalizationWorkload.cpp
@@ -61,7 +61,9 @@
 
                         unsigned int index = dataLayout.GetIndex(inputInfo.GetShape(), n, c, h, w);
 
-                        const float scale = 1.0f / sqrtf(reduction);
+                        float maximum = reduction < m_Data.m_Parameters.m_Eps ? m_Data.m_Parameters.m_Eps : reduction;
+
+                        const float scale = 1.0f / sqrtf(maximum);
 
                         (*inputDecoder)[index];
                         (*outputEncoder)[index];