IVGCVSW-2702 Add Serializer and Deserializer for Permute

Change-Id: I56322cb935de42cfa25bd1fbb0c09d00c7a5dd2b
Signed-off-by: Nattapat Chaimanowong <nattapat.chaimanowong@arm.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 30e17cf..b792d76 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -559,6 +559,7 @@
                 src/armnnDeserializer/test/DeserializeAdd.cpp
                 src/armnnDeserializer/test/DeserializeConvolution2d.cpp
                 src/armnnDeserializer/test/DeserializeMultiplication.cpp
+                src/armnnDeserializer/test/DeserializePermute.cpp
                 src/armnnDeserializer/test/DeserializePooling2d.cpp
                 src/armnnDeserializer/test/DeserializeReshape.cpp
                 src/armnnDeserializer/test/ParserFlatbuffersSerializeFixture.hpp
diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp
index 2462061..ead4fc5 100644
--- a/src/armnnDeserializer/Deserializer.cpp
+++ b/src/armnnDeserializer/Deserializer.cpp
@@ -175,6 +175,7 @@
     m_ParserFunctions[Layer_Convolution2dLayer]          = &Deserializer::ParseConvolution2d;
     m_ParserFunctions[Layer_DepthwiseConvolution2dLayer] = &Deserializer::ParseDepthwiseConvolution2d;
     m_ParserFunctions[Layer_MultiplicationLayer]         = &Deserializer::ParseMultiplication;
+    m_ParserFunctions[Layer_PermuteLayer]                = &Deserializer::ParsePermute;
     m_ParserFunctions[Layer_Pooling2dLayer]              = &Deserializer::ParsePooling2d;
     m_ParserFunctions[Layer_ReshapeLayer]                = &Deserializer::ParseReshape;
     m_ParserFunctions[Layer_SoftmaxLayer]                = &Deserializer::ParseSoftmax;
@@ -200,6 +201,8 @@
             return graphPtr->layers()->Get(layerIndex)->layer_as_MultiplicationLayer()->base();
         case Layer::Layer_OutputLayer:
             return graphPtr->layers()->Get(layerIndex)->layer_as_OutputLayer()->base()->base();
+        case Layer::Layer_PermuteLayer:
+            return graphPtr->layers()->Get(layerIndex)->layer_as_PermuteLayer()->base();
         case Layer::Layer_Pooling2dLayer:
             return graphPtr->layers()->Get(layerIndex)->layer_as_Pooling2dLayer()->base();
         case Layer::Layer_ReshapeLayer:
@@ -831,8 +834,32 @@
     RegisterOutputSlots(layerIndex, layer);
 }
 
+void Deserializer::ParsePermute(unsigned int layerIndex)
+{
+    CHECK_LAYERS(m_Graph, 0, layerIndex);
+
+    auto dimsMapping =
+        m_Graph->layers()->Get(layerIndex)->layer_as_PermuteLayer()->descriptor()->dimMappings();
+
+    auto inputs = GetInputs(m_Graph, layerIndex);
+    CHECK_VALID_SIZE(inputs.size(), 1);
+
+    auto outputs = GetOutputs(m_Graph, layerIndex);
+    CHECK_VALID_SIZE(outputs.size(), 1);
+    auto outputInfo = ToTensorInfo(outputs[0]);
+
+    m_layerName = boost::str(boost::format("Permute:%1%") % layerIndex);
+    const armnn::PermuteDescriptor descriptor(armnn::PermutationVector(dimsMapping->data(), dimsMapping->Length()));
+
+    IConnectableLayer* layer = m_Network->AddPermuteLayer(descriptor, m_layerName.c_str());
+    layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+    RegisterInputSlots(layerIndex, layer);
+    RegisterOutputSlots(layerIndex, layer);
+}
+
 armnn::Pooling2dDescriptor Deserializer::GetPoolingDescriptor(Deserializer::PoolingDescriptor pooling2dDesc,
-                                                                   unsigned int layerIndex)
+                                                              unsigned int layerIndex)
 {
     armnn::Pooling2dDescriptor desc;
 
diff --git a/src/armnnDeserializer/Deserializer.hpp b/src/armnnDeserializer/Deserializer.hpp
index bf78e10..6af1afb 100644
--- a/src/armnnDeserializer/Deserializer.hpp
+++ b/src/armnnDeserializer/Deserializer.hpp
@@ -73,6 +73,7 @@
     void ParseConvolution2d(unsigned int layerIndex);
     void ParseDepthwiseConvolution2d(unsigned int layerIndex);
     void ParseMultiplication(unsigned int layerIndex);
+    void ParsePermute(unsigned int layerIndex);
     void ParsePooling2d(unsigned int layerIndex);
     void ParseReshape(unsigned int layerIndex);
     void ParseSoftmax(unsigned int layerIndex);
diff --git a/src/armnnDeserializer/DeserializerSupport.md b/src/armnnDeserializer/DeserializerSupport.md
index 86d3d02..14c720a 100644
--- a/src/armnnDeserializer/DeserializerSupport.md
+++ b/src/armnnDeserializer/DeserializerSupport.md
@@ -11,6 +11,7 @@
 * DepthwiseConvolution2d
 * FullyConnected
 * Multiplication
+* Permute
 * Pooling2d
 * Reshape
 * Softmax
diff --git a/src/armnnDeserializer/test/DeserializePermute.cpp b/src/armnnDeserializer/test/DeserializePermute.cpp
new file mode 100644
index 0000000..6d08b5f
--- /dev/null
+++ b/src/armnnDeserializer/test/DeserializePermute.cpp
@@ -0,0 +1,137 @@
+//
+// 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>
+
+BOOST_AUTO_TEST_SUITE(Deserializer)
+
+struct PermuteFixture : public ParserFlatbuffersSerializeFixture
+{
+    explicit PermuteFixture(const std::string &inputShape,
+                            const std::string &dimMappings,
+                            const std::string &outputShape,
+                            const std::string &dataType)
+    {
+        m_JsonString = R"(
+            {
+                inputIds: [0],
+                outputIds: [2],
+                layers: [
+                    {
+                        layer_type: "InputLayer",
+                        layer: {
+                            base: {
+                                layerBindingId: 0,
+                                base: {
+                                    index: 0,
+                                    layerName: "InputLayer",
+                                    layerType: "Input",
+                                    inputSlots: [{
+                                        index: 0,
+                                        connection: {sourceLayerIndex:0, outputSlotIndex:0 },
+                                    }],
+                                    outputSlots: [{
+                                        index: 0,
+                                        tensorInfo: {
+                                            dimensions: )" + inputShape + R"(,
+                                            dataType: )" + dataType + R"(
+                                        }
+                                    }]
+                                }
+                            }
+                        }
+                    },
+                    {
+                        layer_type: "PermuteLayer",
+                        layer: {
+                            base: {
+                                index: 1,
+                                layerName: "PermuteLayer",
+                                layerType: "Permute",
+                                inputSlots: [{
+                                    index: 0,
+                                    connection: {sourceLayerIndex:0, outputSlotIndex:0 },
+                                }],
+                                outputSlots: [{
+                                    index: 0,
+                                    tensorInfo: {
+                                        dimensions: )" + outputShape + R"(,
+                                        dataType: )" + dataType + R"(
+                                    }
+                                }]
+                            },
+                            descriptor: {
+                                dimMappings: )" + dimMappings + R"(,
+                            }
+                        }
+                    },
+                    {
+                        layer_type: "OutputLayer",
+                        layer: {
+                            base:{
+                                layerBindingId: 2,
+                                base: {
+                                    index: 2,
+                                    layerName: "OutputLayer",
+                                    layerType: "Output",
+                                    inputSlots: [{
+                                        index: 0,
+                                        connection: {sourceLayerIndex:1, outputSlotIndex:0 },
+                                    }],
+                                    outputSlots: [{
+                                        index: 0,
+                                        tensorInfo: {
+                                            dimensions: )" + outputShape + R"(,
+                                            dataType: )" + dataType + R"(
+                                        },
+                                    }],
+                                }
+                            }
+                        },
+                    }
+                ]
+            }
+        )";
+        SetupSingleInputSingleOutput("InputLayer", "OutputLayer");
+    }
+};
+
+struct SimplePermute2DFixture : PermuteFixture
+{
+    SimplePermute2DFixture() : PermuteFixture("[ 2, 3 ]",
+                                              "[ 1, 0 ]",
+                                              "[ 3, 2 ]",
+                                              "QuantisedAsymm8") {}
+};
+
+BOOST_FIXTURE_TEST_CASE(SimplePermute2DQuantisedAsymm8, SimplePermute2DFixture)
+{
+    RunTest<2, armnn::DataType::QuantisedAsymm8>(0,
+                                                 { 1, 2, 3, 4, 5, 6 },
+                                                 { 1, 4, 2, 5, 3, 6 });
+}
+
+struct SimplePermute4DFixture : PermuteFixture
+{
+    SimplePermute4DFixture() : PermuteFixture("[ 1, 2, 3, 4 ]",
+                                              "[ 3, 2, 1, 0 ]",
+                                              "[ 4, 3, 2, 1 ]",
+                                              "QuantisedAsymm8") {}
+};
+
+BOOST_FIXTURE_TEST_CASE(SimplePermute4DQuantisedAsymm8, SimplePermute4DFixture)
+{
+    RunTest<4, armnn::DataType::QuantisedAsymm8>(0,
+                                                 {  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
+                                                   13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 },
+                                                 {  1, 13,  5, 17,  9, 21,  2, 14,  6, 18, 10, 22,
+                                                    3, 15,  7, 19, 11, 23,  4, 16,  8, 20, 12, 24 });
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/armnnSerializer/Schema.fbs b/src/armnnSerializer/Schema.fbs
index e813651..94ca23b 100644
--- a/src/armnnSerializer/Schema.fbs
+++ b/src/armnnSerializer/Schema.fbs
@@ -90,7 +90,8 @@
     Softmax = 6,
     Convolution2d = 7,
     DepthwiseConvolution2d = 8,
-    Activation = 9
+    Activation = 9,
+    Permute = 10
 }
 
 // Base layer table to be used as part of other layers
@@ -225,6 +226,15 @@
   targetShape:[uint];
 }
 
+table PermuteLayer {
+    base:LayerBase;
+    descriptor:PermuteDescriptor;
+}
+
+table PermuteDescriptor {
+    dimMappings:[uint];
+}
+
 union Layer {
     ActivationLayer,
     AdditionLayer,
@@ -233,6 +243,7 @@
     InputLayer,
     MultiplicationLayer,
     OutputLayer,
+    PermuteLayer,
     Pooling2dLayer,
     ReshapeLayer,
     SoftmaxLayer
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index bee1a3c..e1d22ec 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -226,6 +226,31 @@
     CreateAnyLayer(flatBufferMultiplicationLayer.o, serializer::Layer::Layer_MultiplicationLayer);
 }
 
+void SerializerVisitor::VisitPermuteLayer(const armnn::IConnectableLayer* layer,
+                                          const armnn::PermuteDescriptor& permuteDescriptor,
+                                          const char* name)
+{
+    // Create FlatBuffer BaseLayer
+    auto flatBufferPermuteBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Permute);
+
+    std::vector<unsigned int> dimMappings;
+    for (auto& v: permuteDescriptor.m_DimMappings)
+    {
+        dimMappings.push_back(v);
+    }
+
+    auto flatBufferPermuteDesc = serializer::CreatePermuteDescriptor(m_flatBufferBuilder,
+                                                                     m_flatBufferBuilder.CreateVector(dimMappings));
+
+    // Create the FlatBuffer PermuteLayer
+    auto flatBufferPermuteLayer = serializer::CreatePermuteLayer(m_flatBufferBuilder,
+                                                                 flatBufferPermuteBaseLayer,
+                                                                 flatBufferPermuteDesc);
+
+    // Add the AnyLayer to the FlatBufferLayers
+    CreateAnyLayer(flatBufferPermuteLayer.o, serializer::Layer::Layer_PermuteLayer);
+}
+
 // Build FlatBuffer for Reshape Layer
 void SerializerVisitor::VisitReshapeLayer(const armnn::IConnectableLayer* layer,
                                           const armnn::ReshapeDescriptor& reshapeDescriptor,
diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp
index 0c442e0..329b005 100644
--- a/src/armnnSerializer/Serializer.hpp
+++ b/src/armnnSerializer/Serializer.hpp
@@ -72,6 +72,10 @@
                           armnn::LayerBindingId id,
                           const char* name = nullptr) override;
 
+    void VisitPermuteLayer(const armnn::IConnectableLayer* layer,
+                           const armnn::PermuteDescriptor& PermuteDescriptor,
+                           const char* name = nullptr) override;
+
     void VisitPooling2dLayer(const armnn::IConnectableLayer* layer,
                              const armnn::Pooling2dDescriptor& pooling2dDescriptor,
                              const char* name = nullptr) override;
diff --git a/src/armnnSerializer/SerializerSupport.md b/src/armnnSerializer/SerializerSupport.md
index 0153669..180180a 100644
--- a/src/armnnSerializer/SerializerSupport.md
+++ b/src/armnnSerializer/SerializerSupport.md
@@ -11,8 +11,9 @@
 * DepthwiseConvolution2d
 * FullyConnected
 * Multiplication
+* Permute
 * Pooling2d
 * Reshape
 * Softmax
 
-More machine learning layers will be supported in future releases.
\ No newline at end of file
+More machine learning layers will be supported in future releases.
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index 7dad6ac..c4c6eed 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -364,4 +364,34 @@
                                             outputInfo.GetShape());
 }
 
-BOOST_AUTO_TEST_SUITE_END()
\ No newline at end of file
+BOOST_AUTO_TEST_CASE(SerializeDeserializePermute)
+{
+    unsigned int inputShape[]  = { 4, 3, 2, 1 };
+    unsigned int outputShape[] = { 1, 2, 3, 4 };
+    unsigned int dimsMapping[] = { 3, 2, 1, 0 };
+
+    auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
+    auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
+
+    armnn::PermuteDescriptor permuteDescriptor(armnn::PermutationVector(dimsMapping, 4));
+
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+    armnn::IConnectableLayer *const inputLayer = network->AddInputLayer(0);
+    armnn::IConnectableLayer *const permuteLayer = network->AddPermuteLayer(permuteDescriptor, "PermuteLayer");
+    armnn::IConnectableLayer *const outputLayer = network->AddOutputLayer(0);
+
+    inputLayer->GetOutputSlot(0).Connect(permuteLayer->GetInputSlot(0));
+    inputLayer->GetOutputSlot(0).SetTensorInfo(inputTensorInfo);
+    permuteLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+    permuteLayer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
+
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network));
+    BOOST_CHECK(deserializedNetwork);
+
+    CheckDeserializedNetworkAgainstOriginal(*network,
+                                            *deserializedNetwork,
+                                            inputTensorInfo.GetShape(),
+                                            outputTensorInfo.GetShape());
+}
+
+BOOST_AUTO_TEST_SUITE_END()