IVGCVSW-2531 Serialize a simple ArmNN Network

Change-Id: I68cf5072aca6e3a8b3b8c57e19b6d417cd5813fc
Signed-off-by: Mike Kelly <mike.kelly@arm.com>
diff --git a/src/armnnSerializer/README.md b/src/armnnSerializer/README.md
new file mode 100644
index 0000000..61478b1
--- /dev/null
+++ b/src/armnnSerializer/README.md
@@ -0,0 +1,6 @@
+# The Arm NN Serializer
+
+The `armnnSerializer` is a library for serializing an Arm NN network to a stream.
+
+For more information about the layers that are supported, and the networks that have been tested,
+see [SerializerSupport.md](./SerializerSupport.md)
diff --git a/src/armnnSerializer/Schema.fbs b/src/armnnSerializer/Schema.fbs
index 2a5fbcd..2527f6d 100644
--- a/src/armnnSerializer/Schema.fbs
+++ b/src/armnnSerializer/Schema.fbs
@@ -108,8 +108,8 @@
 // Root type for serialized data is the graph of the network
 table SerializedGraph {
     layers:[AnyLayer];
-    inputIds:[int];
-    outputIds:[int];
+    inputIds:[uint];
+    outputIds:[uint];
 }
 
 root_type SerializedGraph;
diff --git a/src/armnnSerializer/SeralizerSupport.md b/src/armnnSerializer/SeralizerSupport.md
new file mode 100644
index 0000000..16d1940
--- /dev/null
+++ b/src/armnnSerializer/SeralizerSupport.md
@@ -0,0 +1,11 @@
+# The layers that ArmNN SDK Serializer currently supports.
+
+This reference guide provides a list of layers which can be serialized currently by the Arm NN SDK.
+
+## Fully supported
+
+The Arm NN SDK Serializer currently supports the following layers:
+
+* Addition
+
+More machine learning layers will be supported in future releases.
\ No newline at end of file
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
new file mode 100644
index 0000000..57baf0e
--- /dev/null
+++ b/src/armnnSerializer/Serializer.cpp
@@ -0,0 +1,186 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "Serializer.hpp"
+#include <armnn/ArmNN.hpp>
+#include <iostream>
+#include <Schema_generated.h>
+#include <flatbuffers/util.h>
+
+using namespace armnn;
+namespace fb = flatbuffers;
+namespace serializer = armnn::armnnSerializer;
+
+namespace armnnSerializer
+{
+
+serializer::DataType GetFlatBufferDataType(DataType dataType)
+{
+    switch (dataType)
+    {
+        case DataType::Float32:
+            return serializer::DataType::DataType_Float32;
+        case DataType::Float16:
+            return serializer::DataType::DataType_Float16;
+        case DataType::Signed32:
+            return serializer::DataType::DataType_Signed32;
+        case DataType::QuantisedAsymm8:
+            return serializer::DataType::DataType_QuantisedAsymm8;
+        case DataType::Boolean:
+            return serializer::DataType::DataType_Boolean;
+        default:
+            return serializer::DataType::DataType_Float16;
+    }
+}
+
+// Build FlatBuffer for Input Layer
+void Serializer::VisitInputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name)
+{
+    // Create FlatBuffer BaseLayer
+    auto flatBufferInputBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Input);
+
+    // Create FlatBuffer BindableBaseLayer
+    auto flatBufferInputBindableBaseLayer = serializer::CreateBindableLayerBase(m_flatBufferBuilder,
+                                                                                flatBufferInputBaseLayer,
+                                                                                id);
+
+    // Push layer Guid to outputIds.
+    m_inputIds.push_back(layer->GetGuid());
+
+    // Create the FlatBuffer InputLayer
+    auto flatBufferInputLayer = serializer::CreateInputLayer(m_flatBufferBuilder, flatBufferInputBindableBaseLayer);
+
+    // Add the AnyLayer to the FlatBufferLayers
+    CreateAnyLayer(flatBufferInputLayer.o, serializer::Layer::Layer_InputLayer);
+}
+
+// Build FlatBuffer for Output Layer
+void Serializer::VisitOutputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name)
+{
+    // Create FlatBuffer BaseLayer
+    auto flatBufferOutputBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Output);
+
+    // Create FlatBuffer BindableBaseLayer
+    auto flatBufferOutputBindableBaseLayer = serializer::CreateBindableLayerBase(m_flatBufferBuilder,
+                                                                                 flatBufferOutputBaseLayer,
+                                                                                 id);
+    // Push layer Guid to outputIds.
+    m_outputIds.push_back(layer->GetGuid());
+
+    // Create the FlatBuffer OutputLayer
+    auto flatBufferOutputLayer = serializer::CreateOutputLayer(m_flatBufferBuilder, flatBufferOutputBindableBaseLayer);
+    // Add the AnyLayer to the FlatBufferLayers
+    CreateAnyLayer(flatBufferOutputLayer.o, serializer::Layer::Layer_OutputLayer);
+}
+
+// Build FlatBuffer for Addition Layer
+void Serializer::VisitAdditionLayer(const IConnectableLayer* layer, const char* name)
+{
+    // Create FlatBuffer BaseLayer
+    auto flatBufferAdditionBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Addition);
+
+    // Create the FlatBuffer AdditionLayer
+    auto flatBufferAdditionLayer = serializer::CreateAdditionLayer(m_flatBufferBuilder, flatBufferAdditionBaseLayer);
+
+    // Add the AnyLayer to the FlatBufferLayers
+    CreateAnyLayer(flatBufferAdditionLayer.o, serializer::Layer::Layer_AdditionLayer);
+}
+
+void Serializer::Serialize(const INetwork& inNetwork)
+{
+    // Iterate through to network
+    inNetwork.Accept(*this);
+
+    // Create FlatBuffer SerializedGraph
+    auto serializedGraph = serializer::CreateSerializedGraph(m_flatBufferBuilder,
+                                                             m_flatBufferBuilder.CreateVector(m_serializedLayers),
+                                                             m_flatBufferBuilder.CreateVector(m_inputIds),
+                                                             m_flatBufferBuilder.CreateVector(m_outputIds));
+
+    // Serialize the graph
+    m_flatBufferBuilder.Finish(serializedGraph);
+}
+
+bool Serializer::SaveSerializedToStream(std::ostream& stream)
+{
+    stream.write(reinterpret_cast<const char*>(m_flatBufferBuilder.GetBufferPointer()), m_flatBufferBuilder.GetSize());
+    return !stream.bad();
+}
+
+fb::Offset<serializer::LayerBase> Serializer::CreateLayerBase(const IConnectableLayer* layer,
+                                                              const serializer::LayerType layerType)
+{
+    std::vector<fb::Offset<serializer::InputSlot>> inputSlots = CreateInputSlots(layer);
+    std::vector<fb::Offset<serializer::OutputSlot>> outputSlots = CreateOutputSlots(layer);
+
+    return serializer::CreateLayerBase(m_flatBufferBuilder,
+                                       layer->GetGuid(),
+                                       m_flatBufferBuilder.CreateString(layer->GetName()),
+                                       layerType,
+                                       m_flatBufferBuilder.CreateVector(inputSlots),
+                                       m_flatBufferBuilder.CreateVector(outputSlots));
+}
+
+void Serializer::CreateAnyLayer(const flatbuffers::Offset<void>& layer, const serializer::Layer serializerLayer)
+{
+    auto anyLayer = armnn::armnnSerializer::CreateAnyLayer(m_flatBufferBuilder,
+                                                           serializerLayer,
+                                                           layer);
+    m_serializedLayers.push_back(anyLayer);
+}
+
+std::vector<fb::Offset<serializer::InputSlot>> Serializer::CreateInputSlots(const IConnectableLayer* layer)
+{
+    std::vector<fb::Offset <serializer::InputSlot>> inputSlots;
+
+    // Get the InputSlots
+    for (unsigned int slotIndex = 0; slotIndex<layer->GetNumInputSlots(); ++slotIndex)
+    {
+        const IInputSlot& inputSlot = layer->GetInputSlot(slotIndex);
+
+        // Get the Connection for the InputSlot
+        const IOutputSlot* connection = inputSlot.GetConnection();
+
+        // Create FlatBuffer Connection
+        serializer::Connection conn(connection->GetOwningLayerGuid(), connection->CalculateIndexOnOwner());
+        // Create FlatBuffer InputSlot
+        inputSlots.push_back(serializer::CreateInputSlot(m_flatBufferBuilder, slotIndex, &conn));
+    }
+    return inputSlots;
+}
+
+std::vector<fb::Offset<serializer::OutputSlot>> Serializer::CreateOutputSlots(const IConnectableLayer* layer)
+{
+    std::vector<fb::Offset<serializer::OutputSlot>> outputSlots;
+
+    // Get the OutputSlots
+    for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
+    {
+        const IOutputSlot& outputSlot = layer->GetOutputSlot(slotIndex);
+        const TensorInfo& tensorInfo = outputSlot.GetTensorInfo();
+
+        // Get the dimensions
+        std::vector<unsigned int> shape;
+        for(unsigned int dim = 0; dim < tensorInfo.GetShape().GetNumDimensions(); ++dim)
+        {
+            shape.push_back(tensorInfo.GetShape()[dim]);
+        }
+
+        // Create FlatBuffer TensorInfo
+        auto flatBufferTensorInfo = serializer::CreateTensorInfo(m_flatBufferBuilder,
+                                                                 m_flatBufferBuilder.CreateVector(shape),
+                                                                 GetFlatBufferDataType(tensorInfo.GetDataType()),
+                                                                 tensorInfo.GetQuantizationScale(),
+                                                                 tensorInfo.GetQuantizationOffset());
+
+        // Create FlatBuffer Outputslot
+        outputSlots.push_back(serializer::CreateOutputSlot(m_flatBufferBuilder,
+                                                           slotIndex,
+                                                           flatBufferTensorInfo));
+    }
+    return outputSlots;
+}
+
+} //namespace armnnSerializer
diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp
new file mode 100644
index 0000000..697e5cf
--- /dev/null
+++ b/src/armnnSerializer/Serializer.hpp
@@ -0,0 +1,72 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+#pragma once
+
+#include <armnn/ILayerVisitor.hpp>
+#include <armnn/LayerVisitorBase.hpp>
+#include <iostream>
+#include <Schema_generated.h>
+
+namespace armnnSerializer
+{
+
+class Serializer : public armnn::LayerVisitorBase<armnn::VisitorNoThrowPolicy>
+{
+public:
+    Serializer() {};
+    ~Serializer() {};
+
+    void VisitAdditionLayer(const armnn::IConnectableLayer* layer,
+                            const char* name = nullptr) override;
+
+    void VisitInputLayer(const armnn::IConnectableLayer* layer,
+                         armnn::LayerBindingId id,
+                         const char* name = nullptr) override;
+
+    void VisitOutputLayer(const armnn::IConnectableLayer* layer,
+                          armnn::LayerBindingId id,
+                          const char* name = nullptr) override;
+
+    /// Serializes the network to ArmNN SerializedGraph.
+    /// @param [in] inNetwork The network to be serialized.
+    void Serialize(const armnn::INetwork& inNetwork);
+
+    /// Serializes the SerializedGraph to the stream.
+    /// @param [stream] the stream to save to
+    /// @return true if graph is Serialized to the Stream, false otherwise
+    bool SaveSerializedToStream(std::ostream& stream);
+
+private:
+
+    /// Creates the Input Slots and Output Slots and LayerBase for the layer.
+    flatbuffers::Offset<armnn::armnnSerializer::LayerBase> CreateLayerBase(
+            const armnn::IConnectableLayer* layer,
+            const armnn::armnnSerializer::LayerType layerType);
+
+    /// Creates the serializer AnyLayer for the layer and adds it to m_serializedLayers.
+    void CreateAnyLayer(const flatbuffers::Offset<void>& layer, const armnn::armnnSerializer::Layer serializerLayer);
+
+    /// Creates the serializer InputSlots for the layer.
+    std::vector<flatbuffers::Offset<armnn::armnnSerializer::InputSlot>> CreateInputSlots(
+            const armnn::IConnectableLayer* layer);
+
+    /// Creates the serializer OutputSlots for the layer.
+    std::vector<flatbuffers::Offset<armnn::armnnSerializer::OutputSlot>> CreateOutputSlots(
+            const armnn::IConnectableLayer* layer);
+
+    /// FlatBufferBuilder to create our layers' FlatBuffers.
+    flatbuffers::FlatBufferBuilder m_flatBufferBuilder;
+
+    /// AnyLayers required by the SerializedGraph.
+    std::vector<flatbuffers::Offset<armnn::armnnSerializer::AnyLayer>> m_serializedLayers;
+
+    /// Guids of all Input Layers required by the SerializedGraph.
+    std::vector<unsigned int> m_inputIds;
+
+    /// Guids of all Output Layers required by the SerializedGraph.
+    std::vector<unsigned int> m_outputIds;
+};
+
+} //namespace armnnSerializer
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
new file mode 100644
index 0000000..17ad6e3
--- /dev/null
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -0,0 +1,35 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <armnn/ArmNN.hpp>
+#include <armnn/INetwork.hpp>
+#include "../Serializer.hpp"
+#include <sstream>
+#include <boost/test/unit_test.hpp>
+
+BOOST_AUTO_TEST_SUITE(SerializerTests)
+
+BOOST_AUTO_TEST_CASE(SimpleNetworkSerialization)
+{
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+    armnn::IConnectableLayer* const inputLayer0 = network->AddInputLayer(0);
+    armnn::IConnectableLayer* const inputLayer1 = network->AddInputLayer(1);
+
+    armnn::IConnectableLayer* const additionLayer0 = network->AddAdditionLayer();
+    inputLayer0->GetOutputSlot(0).Connect(additionLayer0->GetInputSlot(0));
+    inputLayer1->GetOutputSlot(0).Connect(additionLayer0->GetInputSlot(1));
+
+    armnn::IConnectableLayer* const outputLayer0 = network->AddOutputLayer(0);
+    additionLayer0->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_AUTO_TEST_SUITE_END()