IVGCVSW-3291 Add L2Normalization epsilon value to serialization

Signed-off-by: Ferran Balaguer <ferran.balaguer@arm.com>
Change-Id: Icfff3fb596a03c126a42b1d0c254a68e498df734
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index db5672f..7947893 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -224,6 +224,7 @@
 
 table L2NormalizationDescriptor {
     dataLayout:DataLayout = NCHW;
+    eps:float = 1e-12;
 }
 
 table MinimumLayer {
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index dabe977..efadbb3 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -380,9 +380,11 @@
 
     // Create the FlatBuffer L2Normalization Descriptor
     auto fbDescriptor = serializer::CreateL2NormalizationDescriptor(
-            m_flatBufferBuilder, GetFlatBufferDataLayout(l2NormalizationDescriptor.m_DataLayout));
+            m_flatBufferBuilder,
+            GetFlatBufferDataLayout(l2NormalizationDescriptor.m_DataLayout),
+            l2NormalizationDescriptor.m_Eps);
 
-    // Create Flatuffer layer
+    // Create FlatBuffer layer
     auto fbLayer = serializer::CreateL2NormalizationLayer(m_flatBufferBuilder, fbBaseLayer, fbDescriptor);
 
     CreateAnyLayer(fbLayer.o, serializer::Layer::Layer_L2NormalizationLayer);
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index 812a478..ddebd14 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -1046,39 +1046,41 @@
     deserializedNetwork->Accept(verifier);
 }
 
+class L2NormalizationLayerVerifier : public LayerVerifierBase
+{
+public:
+    L2NormalizationLayerVerifier(const std::string& layerName,
+                                 const std::vector<armnn::TensorInfo>& inputInfos,
+                                 const std::vector<armnn::TensorInfo>& outputInfos,
+                                 const armnn::L2NormalizationDescriptor& descriptor)
+            : LayerVerifierBase(layerName, inputInfos, outputInfos)
+            , m_Descriptor(descriptor) {}
+
+    void VisitL2NormalizationLayer(const armnn::IConnectableLayer* layer,
+                                   const armnn::L2NormalizationDescriptor& descriptor,
+                                   const char* name) override
+    {
+        VerifyNameAndConnections(layer, name);
+        VerifyDescriptor(descriptor);
+    }
+private:
+    void VerifyDescriptor(const armnn::L2NormalizationDescriptor& descriptor)
+    {
+        BOOST_TEST(descriptor.m_Eps == m_Descriptor.m_Eps);
+        BOOST_TEST(GetDataLayoutName(descriptor.m_DataLayout) == GetDataLayoutName(m_Descriptor.m_DataLayout));
+    }
+
+    armnn::L2NormalizationDescriptor m_Descriptor;
+};
+
 BOOST_AUTO_TEST_CASE(SerializeL2Normalization)
 {
-    class L2NormalizationLayerVerifier : public LayerVerifierBase
-    {
-    public:
-        L2NormalizationLayerVerifier(const std::string& layerName,
-                                     const std::vector<armnn::TensorInfo>& inputInfos,
-                                     const std::vector<armnn::TensorInfo>& outputInfos,
-                                     const armnn::L2NormalizationDescriptor& descriptor)
-        : LayerVerifierBase(layerName, inputInfos, outputInfos)
-        , m_Descriptor(descriptor) {}
-
-        void VisitL2NormalizationLayer(const armnn::IConnectableLayer* layer,
-                                       const armnn::L2NormalizationDescriptor& descriptor,
-                                       const char* name) override
-        {
-            VerifyNameAndConnections(layer, name);
-            VerifyDescriptor(descriptor);
-        }
-    private:
-        void VerifyDescriptor(const armnn::L2NormalizationDescriptor& descriptor)
-        {
-            BOOST_TEST(GetDataLayoutName(descriptor.m_DataLayout) == GetDataLayoutName(m_Descriptor.m_DataLayout));
-        }
-
-        armnn::L2NormalizationDescriptor m_Descriptor;
-    };
-
     const std::string l2NormLayerName("l2Normalization");
     const armnn::TensorInfo info({1, 2, 1, 5}, armnn::DataType::Float32);
 
     armnn::L2NormalizationDescriptor desc;
     desc.m_DataLayout = armnn::DataLayout::NCHW;
+    desc.m_Eps = 0.0001f;
 
     armnn::INetworkPtr network = armnn::INetwork::Create();
     armnn::IConnectableLayer* const inputLayer0 = network->AddInputLayer(0);
@@ -1098,6 +1100,62 @@
     deserializedNetwork->Accept(verifier);
 }
 
+BOOST_AUTO_TEST_CASE(EnsureL2NormalizationBackwardCompatibility)
+{
+    // The hex array below is a flat buffer containing a simple network with one input
+    // a L2Normalization layer and an output layer with dimensions as per the tensor infos below.
+    //
+    // This test verifies that we can still read back these old style
+    // models without the normalization epsilon value.
+    unsigned int size = 508;
+    const unsigned char l2NormalizationModel[] = {
+            0x10,0x00,0x00,0x00,0x00,0x00,0x0A,0x00,0x10,0x00,0x04,0x00,0x08,0x00,0x0C,0x00,0x0A,0x00,0x00,0x00,
+            0x0C,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3C,0x01,0x00,0x00,
+            0x74,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+            0x02,0x00,0x00,0x00,0xE8,0xFE,0xFF,0xFF,0x00,0x00,0x00,0x0B,0x04,0x00,0x00,0x00,0xD6,0xFE,0xFF,0xFF,
+            0x0C,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x08,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,
+            0x9E,0xFF,0xFF,0xFF,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x10,0x00,0x00,0x00,
+            0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,
+            0x00,0x00,0x00,0x00,0x4C,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x44,0xFF,0xFF,0xFF,
+            0x00,0x00,0x00,0x20,0x0C,0x00,0x00,0x00,0x08,0x00,0x0C,0x00,0x04,0x00,0x08,0x00,0x08,0x00,0x00,0x00,
+            0x20,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x06,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,
+            0x18,0x00,0x04,0x00,0x08,0x00,0x0C,0x00,0x10,0x00,0x14,0x00,0x0E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+            0x10,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,
+            0x6C,0x32,0x4E,0x6F,0x72,0x6D,0x61,0x6C,0x69,0x7A,0x61,0x74,0x69,0x6F,0x6E,0x00,0x01,0x00,0x00,0x00,
+            0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x04,0x00,
+            0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x52,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x01,0x08,0x00,0x00,0x00,
+            0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+            0x05,0x00,0x00,0x00,0x08,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+            0x00,0x00,0x00,0x00,0x08,0x00,0x0C,0x00,0x07,0x00,0x08,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x09,
+            0x04,0x00,0x00,0x00,0xF6,0xFF,0xFF,0xFF,0x0C,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x0A,0x00,0x04,0x00,
+            0x06,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x14,0x00,0x00,0x00,0x04,0x00,0x08,0x00,
+            0x0C,0x00,0x10,0x00,0x0E,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x10,0x00,0x00,0x00,
+            0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+            0x0C,0x00,0x00,0x00,0x08,0x00,0x0A,0x00,0x00,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x10,0x00,0x00,0x00,
+            0x00,0x00,0x0A,0x00,0x10,0x00,0x08,0x00,0x07,0x00,0x0C,0x00,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+            0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
+            0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0 };
+
+    std::stringstream ss;
+    for (unsigned int i = 0; i < size; ++i)
+    {
+        ss << l2NormalizationModel[i];
+    }
+    std::string l2NormalizationLayerNetwork = ss.str();
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(l2NormalizationLayerNetwork);
+    BOOST_CHECK(deserializedNetwork);
+    const std::string layerName("l2Normalization");
+    const armnn::TensorInfo inputInfo = armnn::TensorInfo({1, 2, 1, 5}, armnn::DataType::Float32);
+
+    armnn::L2NormalizationDescriptor desc;
+    desc.m_DataLayout = armnn::DataLayout::NCHW;
+    // Since this variable does not exist in the l2NormalizationModel[] dump, the default value will be loaded.
+    desc.m_Eps = 1e-12f;
+
+    L2NormalizationLayerVerifier verifier(layerName, {inputInfo}, {inputInfo}, desc);
+    deserializedNetwork->Accept(verifier);
+}
+
 BOOST_AUTO_TEST_CASE(SerializeMaximum)
 {
     class MaximumLayerVerifier : public LayerVerifierBase