IVGCVSW-7884 - Add Tile to Serializer and Deserializer

* Added parsing functions to the serializer and deserializer
* Added Tile and its Descriptor to the ArmnnSchema.fbs
* Added a Unittest

Signed-off-by: David Monahan <david.monahan@arm.com>
Change-Id: I72e638d26038c9b118cd82f633af462fd19e2b34
diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp
index 1e40c63..eb77f92 100644
--- a/src/armnnDeserializer/Deserializer.cpp
+++ b/src/armnnDeserializer/Deserializer.cpp
@@ -276,6 +276,7 @@
     m_ParserFunctions[Layer_StridedSliceLayer]           = &DeserializerImpl::ParseStridedSlice;
     m_ParserFunctions[Layer_SubtractionLayer]            = &DeserializerImpl::ParseSubtraction;
     m_ParserFunctions[Layer_SwitchLayer]                 = &DeserializerImpl::ParseSwitch;
+    m_ParserFunctions[Layer_TileLayer]                   = &DeserializerImpl::ParseTile;
     m_ParserFunctions[Layer_TransposeConvolution2dLayer] = &DeserializerImpl::ParseTransposeConvolution2d;
     m_ParserFunctions[Layer_TransposeLayer]              = &DeserializerImpl::ParseTranspose;
     m_ParserFunctions[Layer_UnidirectionalSequenceLstmLayer] = &DeserializerImpl::ParseUnidirectionalSequenceLstm;
@@ -423,6 +424,8 @@
             return graphPtr->layers()->Get(layerIndex)->layer_as_SubtractionLayer()->base();
         case Layer::Layer_SwitchLayer:
             return graphPtr->layers()->Get(layerIndex)->layer_as_SwitchLayer()->base();
+        case Layer::Layer_TileLayer:
+            return graphPtr->layers()->Get(layerIndex)->layer_as_TileLayer()->base();
         case Layer::Layer_TransposeConvolution2dLayer:
             return graphPtr->layers()->Get(layerIndex)->layer_as_TransposeConvolution2dLayer()->base();
         case Layer::Layer_TransposeLayer:
@@ -3636,6 +3639,33 @@
     RegisterOutputSlots(graph, layerIndex, layer);
 }
 
+void IDeserializer::DeserializerImpl::ParseTile(GraphPtr graph, unsigned int layerIndex)
+{
+    CHECK_LAYERS(graph, 0, layerIndex);
+    auto inputs = GetInputs(graph, layerIndex);
+    CHECK_LOCATION();
+    CHECK_VALID_SIZE(inputs.size(), 1);
+
+    auto outputs = GetOutputs(graph, layerIndex);
+    CHECK_VALID_SIZE(outputs.size(), 1);
+
+    auto TileLayer             = graph->layers()->Get(layerIndex)->layer_as_TileLayer();
+    auto layerName             = GetLayerName(graph, layerIndex);
+    auto flatBufferDescriptor  = TileLayer->descriptor();
+    auto flatBufferMultiples   = flatBufferDescriptor->m_Multiples();
+
+    armnn::TileDescriptor tileDescriptor;
+    tileDescriptor.m_Multiples = std::vector<unsigned int>(flatBufferMultiples->begin(), flatBufferMultiples->end());
+
+    IConnectableLayer* layer = m_Network->AddTileLayer(tileDescriptor, layerName.c_str());
+
+    armnn::TensorInfo output0TensorInfo = ToTensorInfo(outputs[0]);
+    layer->GetOutputSlot(0).SetTensorInfo(output0TensorInfo);
+
+    RegisterInputSlots(graph, layerIndex, layer);
+    RegisterOutputSlots(graph, layerIndex, layer);
+}
+
 void IDeserializer::DeserializerImpl::ParsePrelu(GraphPtr graph, unsigned int layerIndex)
 {
     CHECK_LAYERS(graph, 0, layerIndex);
diff --git a/src/armnnDeserializer/Deserializer.hpp b/src/armnnDeserializer/Deserializer.hpp
index b4b93e6..7e427d6 100644
--- a/src/armnnDeserializer/Deserializer.hpp
+++ b/src/armnnDeserializer/Deserializer.hpp
@@ -149,6 +149,7 @@
     void ParseStridedSlice(GraphPtr graph, unsigned int layerIndex);
     void ParseSubtraction(GraphPtr graph, unsigned int layerIndex);
     void ParseSwitch(GraphPtr graph, unsigned int layerIndex);
+    void ParseTile(GraphPtr graph, unsigned int layerIndex);
     void ParseTranspose(GraphPtr graph, unsigned int layerIndex);
     void ParseTransposeConvolution2d(GraphPtr graph, unsigned int layerIndex);
     void ParseUnidirectionalSequenceLstm(GraphPtr graph, unsigned int layerIndex);
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index 75dd252..bd0bd03 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -187,6 +187,7 @@
     BatchMatMul = 68,
     ElementwiseBinary = 69,
     ReverseV2 = 70,
+    Tile = 71,
 }
 
 // Base layer table to be used as part of other layers
@@ -1053,6 +1054,15 @@
     descriptor:BatchMatMulDescriptor;
 }
 
+table TileDescriptor {
+   m_Multiples:[uint];
+}
+
+table TileLayer {
+    base:LayerBase;
+    descriptor:TileDescriptor;
+}
+
 union Layer {
     ActivationLayer,
     AdditionLayer,
@@ -1125,6 +1135,7 @@
     BatchMatMulLayer,
     ElementwiseBinaryLayer,
     ReverseV2Layer,
+    TileLayer,
 }
 
 table AnyLayer {
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index 39a4295..6cadb59 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -1440,6 +1440,27 @@
     CreateAnyLayer(fbSwitchLayer.o, serializer::Layer::Layer_SwitchLayer);
 }
 
+void SerializerStrategy::SerializeTileLayer(const armnn::IConnectableLayer* layer,
+                                            const armnn::TileDescriptor& descriptor,
+                                            const char* name)
+{
+    IgnoreUnused(name);
+
+    // Create FlatBuffer BaseLayer
+    auto flatBufferBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Tile);
+
+    auto flatBufferDesc = serializer::CreateTileDescriptor(m_flatBufferBuilder,
+                                                           m_flatBufferBuilder.CreateVector(descriptor.m_Multiples));
+
+    // Create the FlatBuffer TileLayer
+    auto flatBufferLayer = serializer::CreateTileLayer(m_flatBufferBuilder,
+                                                       flatBufferBaseLayer,
+                                                       flatBufferDesc);
+
+    // Add the AnyLayer to the FlatBufferLayers
+    CreateAnyLayer(flatBufferLayer.o, serializer::Layer::Layer_TileLayer);
+}
+
 void SerializerStrategy::SerializeTransposeConvolution2dLayer(
     const armnn::IConnectableLayer* layer,
     const armnn::TransposeConvolution2dDescriptor& descriptor,
@@ -2425,6 +2446,13 @@
             SerializeSwitchLayer(layer, name);
             break;
         }
+        case armnn::LayerType::Tile:
+        {
+            const armnn::TileDescriptor& layerDescriptor =
+                    static_cast<const armnn::TileDescriptor&>(descriptor);
+            SerializeTileLayer(layer, layerDescriptor, name);
+            break;
+        }
         case armnn::LayerType::Transpose:
         {
             const armnn::TransposeDescriptor& layerDescriptor =
diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp
index 9e9eca8..afff66e 100644
--- a/src/armnnSerializer/Serializer.hpp
+++ b/src/armnnSerializer/Serializer.hpp
@@ -337,6 +337,10 @@
     void SerializeSwitchLayer(const armnn::IConnectableLayer* layer,
                               const char* name = nullptr);
 
+    void SerializeTileLayer(const armnn::IConnectableLayer* layer,
+                            const armnn::TileDescriptor& descriptor,
+                            const char* name);
+
     void SerializeTransposeConvolution2dLayer(const armnn::IConnectableLayer* layer,
                                               const armnn::TransposeConvolution2dDescriptor& descriptor,
                                               const std::vector<armnn::ConstTensor>& constants,
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index 163e5c8..b2590ea 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -3038,5 +3038,30 @@
     deserializedNetwork->ExecuteStrategy(verifier);
 }
 
+TEST_CASE("SerializeTile")
+{
+    const std::string layerName("Tile");
+    const armnn::TensorInfo inputInfo  = armnn::TensorInfo({ 2, 3 }, armnn::DataType::Float32);
+    const armnn::TensorInfo outputInfo = armnn::TensorInfo({ 4, 6 }, armnn::DataType::Float32);
+
+    armnn::TileDescriptor desc = armnn::TileDescriptor(std::vector<uint32_t>{ 2, 2 });
+
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+    armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
+    armnn::IConnectableLayer* const tileLayer = network->AddTileLayer(desc, layerName.c_str());
+    armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
+
+    inputLayer->GetOutputSlot(0).Connect(tileLayer->GetInputSlot(0));
+    tileLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+
+    inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
+    tileLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network));
+    CHECK(deserializedNetwork);
+
+    LayerVerifierBaseWithDescriptor<armnn::TileDescriptor> verifier(layerName, {inputInfo}, {outputInfo}, desc);
+    deserializedNetwork->ExecuteStrategy(verifier);
+}
 
 }