IVGCVSW-2683 Add Serializer & Deserializer for Constant

Change-Id: Iad7d89dfa963d9015cbe044f67aecc8bf6634b10
Signed-off-by: Conor Kennedy <conor.kennedy@arm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7db728c..a400776 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -597,6 +597,7 @@
                 src/armnnSerializer/test/SerializerTests.cpp
                 src/armnnDeserializer/test/DeserializeActivation.cpp
                 src/armnnDeserializer/test/DeserializeAdd.cpp
+                src/armnnDeserializer/test/DeserializeConstant.cpp
                 src/armnnDeserializer/test/DeserializeConvolution2d.cpp
                 src/armnnDeserializer/test/DeserializeFullyConnected.cpp
                 src/armnnDeserializer/test/DeserializeMultiplication.cpp
diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp
index 09c0502..b263c3a 100644
--- a/src/armnnDeserializer/Deserializer.cpp
+++ b/src/armnnDeserializer/Deserializer.cpp
@@ -187,6 +187,7 @@
     // register supported layers
     m_ParserFunctions[Layer_ActivationLayer]             = &Deserializer::ParseActivation;
     m_ParserFunctions[Layer_AdditionLayer]               = &Deserializer::ParseAdd;
+    m_ParserFunctions[Layer_ConstantLayer]               = &Deserializer::ParseConstant;
     m_ParserFunctions[Layer_Convolution2dLayer]          = &Deserializer::ParseConvolution2d;
     m_ParserFunctions[Layer_DepthwiseConvolution2dLayer] = &Deserializer::ParseDepthwiseConvolution2d;
     m_ParserFunctions[Layer_FullyConnectedLayer]         = &Deserializer::ParseFullyConnected;
@@ -207,6 +208,8 @@
             return graphPtr->layers()->Get(layerIndex)->layer_as_ActivationLayer()->base();
         case Layer::Layer_AdditionLayer:
             return graphPtr->layers()->Get(layerIndex)->layer_as_AdditionLayer()->base();
+        case Layer::Layer_ConstantLayer:
+            return graphPtr->layers()->Get(layerIndex)->layer_as_ConstantLayer()->base();
         case Layer::Layer_Convolution2dLayer:
             return graphPtr->layers()->Get(layerIndex)->layer_as_Convolution2dLayer()->base();
         case Layer::Layer_DepthwiseConvolution2dLayer:
@@ -772,6 +775,29 @@
     RegisterOutputSlots(graph, layerIndex, layer);
 }
 
+void Deserializer::ParseConstant(GraphPtr graph, unsigned int layerIndex)
+{
+    CHECK_LAYERS(graph, 0, layerIndex);
+    CHECK_LOCATION();
+
+    auto outputs = GetOutputs(graph, layerIndex);
+    CHECK_VALID_SIZE(outputs.size(), 1);
+
+    auto layerName = GetLayerName(graph, layerIndex);
+
+    auto serializerLayer = graph->layers()->Get(layerIndex)->layer_as_ConstantLayer();
+    auto serializerInput = serializerLayer->input();
+
+    armnn::ConstTensor input = ToConstTensor(serializerInput);
+
+    IConnectableLayer* layer = m_Network->AddConstantLayer(input, layerName.c_str());
+
+    armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
+    layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+    RegisterOutputSlots(graph, layerIndex, layer);
+}
+
 void Deserializer::ParseConvolution2d(GraphPtr graph, unsigned int layerIndex)
 {
     CHECK_LAYERS(graph, 0, layerIndex);
diff --git a/src/armnnDeserializer/Deserializer.hpp b/src/armnnDeserializer/Deserializer.hpp
index 94318e4..9159ff2 100644
--- a/src/armnnDeserializer/Deserializer.hpp
+++ b/src/armnnDeserializer/Deserializer.hpp
@@ -70,6 +70,7 @@
     void ParseUnsupportedLayer(GraphPtr graph, unsigned int layerIndex);
     void ParseActivation(GraphPtr graph, unsigned int layerIndex);
     void ParseAdd(GraphPtr graph, unsigned int layerIndex);
+    void ParseConstant(GraphPtr graph, unsigned int layerIndex);
     void ParseConvolution2d(GraphPtr graph, unsigned int layerIndex);
     void ParseDepthwiseConvolution2d(GraphPtr graph, unsigned int layerIndex);
     void ParseFullyConnected(GraphPtr graph, unsigned int layerIndex);
diff --git a/src/armnnDeserializer/DeserializerSupport.md b/src/armnnDeserializer/DeserializerSupport.md
index 7e7ee06..9881444 100644
--- a/src/armnnDeserializer/DeserializerSupport.md
+++ b/src/armnnDeserializer/DeserializerSupport.md
@@ -8,6 +8,7 @@
 
 * Activation
 * Addition
+* Constant
 * Convolution2d
 * DepthwiseConvolution2d
 * FullyConnected
diff --git a/src/armnnDeserializer/test/DeserializeConstant.cpp b/src/armnnDeserializer/test/DeserializeConstant.cpp
new file mode 100644
index 0000000..0abe5e6
--- /dev/null
+++ b/src/armnnDeserializer/test/DeserializeConstant.cpp
@@ -0,0 +1,152 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <boost/test/unit_test.hpp>
+#include "ParserFlatbuffersSerializeFixture.hpp"
+#include "../Deserializer.hpp"
+
+#include <string>
+#include <iostream>
+
+BOOST_AUTO_TEST_SUITE(DeserializeParser)
+
+struct ConstantAddFixture : public ParserFlatbuffersSerializeFixture
+{
+    explicit ConstantAddFixture(const std::string & shape,
+                                const std::string & constTensorDatatype,
+                                const std::string & constData,
+                                const std::string & dataType)
+    {
+        m_JsonString = R"(
+        {
+                inputIds: [0],
+                outputIds: [3],
+                layers: [
+                {
+                    layer_type: "InputLayer",
+                    layer: {
+                          base: {
+                                layerBindingId: 0,
+                                base: {
+                                    index: 0,
+                                    layerName: "InputLayer1",
+                                    layerType: "Input",
+                                    inputSlots: [{
+                                        index: 0,
+                                        connection: {sourceLayerIndex:0, outputSlotIndex:0 },
+                                    }],
+                                    outputSlots: [ {
+                                        index: 0,
+                                        tensorInfo: {
+                                            dimensions: )" + shape + R"(,
+                                            dataType: )" + dataType + R"(
+                                        },
+                                    }],
+                                 },}},
+                },
+                {
+                layer_type: "ConstantLayer",
+                layer: {
+                            base: {
+                                  index:1,
+                                  layerName: "ConstantLayer",
+                                  layerType: "Constant",
+                                  outputSlots: [ {
+                                      index: 0,
+                                      tensorInfo: {
+                                          dimensions: )" + shape + R"(,
+                                          dataType: )" + dataType + R"(,
+                                      },
+                                  }],
+                                  inputSlots: [{
+                                        index: 0,
+                                        connection: {sourceLayerIndex:0, outputSlotIndex:0 },
+                                    }],
+                                },
+                            input: {
+                                info: {
+                                         dimensions: )" + shape + R"(,
+                                         dataType: )" + dataType + R"(
+                                     },
+                                data_type: )" + constTensorDatatype + R"(,
+                                data: {
+                                    data: )" + constData + R"(,
+                                    } }
+                        },
+                },
+                {
+                layer_type: "AdditionLayer",
+                layer : {
+                        base: {
+                             index:2,
+                             layerName: "AdditionLayer",
+                             layerType: "Addition",
+                             inputSlots: [
+                                            {
+                                             index: 0,
+                                             connection: {sourceLayerIndex:0, outputSlotIndex:0 },
+                                            },
+                                            {
+                                             index: 1,
+                                             connection: {sourceLayerIndex:1, outputSlotIndex:0 },
+                                            }
+                             ],
+                             outputSlots: [ {
+                                 index: 0,
+                                 tensorInfo: {
+                                     dimensions: )" + shape + R"(,
+                                     dataType: )" + dataType + R"(
+                                 },
+                             }],
+                            }},
+                },
+                {
+                layer_type: "OutputLayer",
+                layer: {
+                        base:{
+                              layerBindingId: 0,
+                              base: {
+                                    index: 3,
+                                    layerName: "OutputLayer",
+                                    layerType: "Output",
+                                    inputSlots: [{
+                                        index: 0,
+                                        connection: {sourceLayerIndex:2, outputSlotIndex:0 },
+                                    }],
+                                    outputSlots: [ {
+                                        index: 0,
+                                        tensorInfo: {
+                                            dimensions: )" + shape + R"(,
+                                            dataType: )" + dataType + R"(
+                                        },
+                                }],
+                            }}},
+                }]
+         }
+        )";
+        SetupSingleInputSingleOutput("InputLayer1", "OutputLayer");
+    }
+};
+
+struct SimpleConstantAddFixture : ConstantAddFixture
+{
+    SimpleConstantAddFixture()
+            : ConstantAddFixture("[ 2, 3 ]",             // shape
+                                 "ByteData",             // constDataType
+                                 "[ 1, 2, 3, 4, 5, 6 ]", // constData
+                                 "QuantisedAsymm8")      // datatype
+
+    {}
+};
+
+BOOST_FIXTURE_TEST_CASE(SimpleConstantAddQuantisedAsymm8, SimpleConstantAddFixture)
+{
+    RunTest<2, armnn::DataType::QuantisedAsymm8>(
+            0,
+            { 1, 2, 3, 4, 5, 6  },
+            { 2, 4, 6, 8, 10, 12 });
+}
+
+BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index dc14069..db70e7b 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -92,7 +92,8 @@
     DepthwiseConvolution2d = 8,
     Activation = 9,
     Permute = 10,
-    FullyConnected = 11
+    FullyConnected = 11,
+    Constant = 12
 }
 
 // Base layer table to be used as part of other layers
@@ -125,6 +126,11 @@
     base:LayerBase;
 }
 
+table ConstantLayer {
+    base:LayerBase;
+    input:ConstTensor;
+}
+
 table Convolution2dLayer {
     base:LayerBase;
     descriptor:Convolution2dDescriptor;
@@ -251,6 +257,7 @@
 union Layer {
     ActivationLayer,
     AdditionLayer,
+    ConstantLayer,
     Convolution2dLayer,
     DepthwiseConvolution2dLayer,
     FullyConnectedLayer,
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index a0a640e..b8f5c3b 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -141,6 +141,25 @@
     CreateAnyLayer(flatBufferAdditionLayer.o, serializer::Layer::Layer_AdditionLayer);
 }
 
+// Build FlatBuffer for Constant Layer
+void SerializerVisitor::VisitConstantLayer(const armnn::IConnectableLayer* layer,
+                                           const armnn::ConstTensor& input,
+                                           const char* name)
+{
+    // Create FlatBuffer BaseLayer
+    auto flatBufferConstantBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Constant);
+
+    auto flatBufferConstTensorInfo = CreateConstTensorInfo(input);
+
+    // Create the FlatBuffer ConstantLayer
+    auto flatBufferLayer = CreateConstantLayer(m_flatBufferBuilder,
+                                               flatBufferConstantBaseLayer,
+                                               flatBufferConstTensorInfo);
+
+    // Add the AnyLayer to the FlatBufferLayers
+    CreateAnyLayer(flatBufferLayer.o, serializer::Layer::Layer_ConstantLayer);
+}
+
 // Build FlatBuffer for Convolution2dLayer
 void SerializerVisitor::VisitConvolution2dLayer(const armnn::IConnectableLayer* layer,
                                                 const armnn::Convolution2dDescriptor& descriptor,
diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp
index a423aa8..781648f 100644
--- a/src/armnnSerializer/Serializer.hpp
+++ b/src/armnnSerializer/Serializer.hpp
@@ -49,6 +49,10 @@
     void VisitAdditionLayer(const armnn::IConnectableLayer* layer,
                             const char* name = nullptr) override;
 
+    void VisitConstantLayer(const armnn::IConnectableLayer* layer,
+                            const armnn::ConstTensor& input,
+                            const char* = nullptr) override;
+
     void VisitConvolution2dLayer(const armnn::IConnectableLayer* layer,
                                  const armnn::Convolution2dDescriptor& descriptor,
                                  const armnn::ConstTensor& weights,
diff --git a/src/armnnSerializer/SerializerSupport.md b/src/armnnSerializer/SerializerSupport.md
index 18e9f53..9e84b63 100644
--- a/src/armnnSerializer/SerializerSupport.md
+++ b/src/armnnSerializer/SerializerSupport.md
@@ -8,6 +8,7 @@
 
 * Activation
 * Addition
+* Constant
 * Convolution2d
 * DepthwiseConvolution2d
 * FullyConnected
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index bb05052..4e90dbe 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -174,6 +174,71 @@
     deserializedNetwork->Accept(nameChecker);
 }
 
+BOOST_AUTO_TEST_CASE(SerializeConstant)
+{
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+
+    armnn::ConstTensor inputTensor;
+
+    armnn::IConnectableLayer* const inputLayer0 = network->AddConstantLayer(inputTensor, "constant");
+    armnn::IConnectableLayer* const outputLayer0 = network->AddOutputLayer(0);
+
+    inputLayer0->GetOutputSlot(0).Connect(outputLayer0->GetInputSlot(0));
+
+    armnnSerializer::Serializer serializer;
+    serializer.Serialize(*network);
+
+    std::stringstream stream;
+    serializer.SaveSerializedToStream(stream);
+    BOOST_TEST(stream.str().length() > 0);
+    BOOST_TEST(stream.str().find("constant") != stream.str().npos);
+}
+
+BOOST_AUTO_TEST_CASE(SerializeDeserializeConstant)
+{
+    class VerifyConstantName : public armnn::LayerVisitorBase<armnn::VisitorNoThrowPolicy>
+    {
+    public:
+        void VisitConstantLayer(const armnn::IConnectableLayer*, const armnn::ConstTensor&, const char* name) override
+        {
+            BOOST_TEST(name == "constant");
+        }
+    };
+
+    armnn::TensorInfo commonTensorInfo({ 2, 3 }, armnn::DataType::Float32);
+
+    std::vector<float> constantData = GenerateRandomData<float>(commonTensorInfo.GetNumElements());
+    armnn::ConstTensor constTensor(commonTensorInfo, constantData);
+
+    // Builds up the structure of the network.
+    armnn::INetworkPtr net(armnn::INetwork::Create());
+
+    armnn::IConnectableLayer* input = net->AddInputLayer(0);
+    armnn::IConnectableLayer* constant = net->AddConstantLayer(constTensor, "constant");
+    armnn::IConnectableLayer* add = net->AddAdditionLayer();
+    armnn::IConnectableLayer* output = net->AddOutputLayer(0);
+
+    input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
+    constant->GetOutputSlot(0).Connect(add->GetInputSlot(1));
+    add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+    // Sets the tensors in the network.
+    input->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
+    constant->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
+    add->GetOutputSlot(0).SetTensorInfo(commonTensorInfo);
+
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*net));
+    BOOST_CHECK(deserializedNetwork);
+
+    VerifyConstantName nameChecker;
+    deserializedNetwork->Accept(nameChecker);
+
+    CheckDeserializedNetworkAgainstOriginal(*net,
+                                            *deserializedNetwork,
+                                            commonTensorInfo.GetShape(),
+                                            commonTensorInfo.GetShape());
+}
+
 BOOST_AUTO_TEST_CASE(SerializeMultiplication)
 {
     class VerifyMultiplicationName : public armnn::LayerVisitorBase<armnn::VisitorNoThrowPolicy>