IVGCVSW-3705 Add Channel Shuffle Front end and Ref Implementation

* Add front end
* Add reference workload
* Add unit tests
* Add Serializer and Deserializer
* Update ArmNN Versioning

Signed-off-by: Simon Obute <simon.obute@arm.com>
Change-Id: I9ac1f953af3974382eac8e8d62d794d2344e8f47
diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs
index a285a11..740090b 100644
--- a/src/armnnSerializer/ArmnnSchema.fbs
+++ b/src/armnnSerializer/ArmnnSchema.fbs
@@ -176,6 +176,7 @@
     Cast = 61,
     Shape = 62,
     UnidirectionalSequenceLstm = 63,
+    ChannelShuffle = 64,
 }
 
 // Base layer table to be used as part of other layers
@@ -228,6 +229,16 @@
     base:LayerBase;
 }
 
+table ChannelShuffleLayer {
+    base:LayerBase;
+    descriptor:ChannelShuffleDescriptor;
+}
+
+table ChannelShuffleDescriptor {
+    axis:uint = 0;
+    numGroups:uint = 0;
+}
+
 enum ComparisonOperation : byte {
     Equal = 0,
     Greater = 1,
@@ -1000,6 +1011,7 @@
     CastLayer,
     ShapeLayer,
     UnidirectionalSequenceLstmLayer,
+    ChannelShuffleLayer,
 }
 
 table AnyLayer {
diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h
index cf28a7a..653ea6a 100644
--- a/src/armnnSerializer/ArmnnSchema_generated.h
+++ b/src/armnnSerializer/ArmnnSchema_generated.h
@@ -65,6 +65,12 @@
 struct CastLayer;
 struct CastLayerBuilder;
 
+struct ChannelShuffleLayer;
+struct ChannelShuffleLayerBuilder;
+
+struct ChannelShuffleDescriptor;
+struct ChannelShuffleDescriptorBuilder;
+
 struct ComparisonDescriptor;
 struct ComparisonDescriptorBuilder;
 
@@ -588,7 +594,7 @@
 }
 
 inline const char *EnumNameReduceOperation(ReduceOperation e) {
-  if (flatbuffers::IsOutRange(e, ReduceOperation_Sum, ReduceOperation_Min)) return "";
+  if (flatbuffers::IsOutRange(e, ReduceOperation_Sum, ReduceOperation_Prod)) return "";
   const size_t index = static_cast<size_t>(e);
   return EnumNamesReduceOperation()[index];
 }
@@ -750,11 +756,12 @@
   LayerType_Cast = 61,
   LayerType_Shape = 62,
   LayerType_UnidirectionalSequenceLstm = 63,
+  LayerType_ChannelShuffle = 64,
   LayerType_MIN = LayerType_Addition,
-  LayerType_MAX = LayerType_UnidirectionalSequenceLstm
+  LayerType_MAX = LayerType_ChannelShuffle
 };
 
-inline const LayerType (&EnumValuesLayerType())[64] {
+inline const LayerType (&EnumValuesLayerType())[65] {
   static const LayerType values[] = {
     LayerType_Addition,
     LayerType_Input,
@@ -819,13 +826,14 @@
     LayerType_Reduce,
     LayerType_Cast,
     LayerType_Shape,
-    LayerType_UnidirectionalSequenceLstm
+    LayerType_UnidirectionalSequenceLstm,
+    LayerType_ChannelShuffle
   };
   return values;
 }
 
 inline const char * const *EnumNamesLayerType() {
-  static const char * const names[65] = {
+  static const char * const names[66] = {
     "Addition",
     "Input",
     "Multiplication",
@@ -890,13 +898,14 @@
     "Cast",
     "Shape",
     "UnidirectionalSequenceLstm",
+    "ChannelShuffle",
     nullptr
   };
   return names;
 }
 
 inline const char *EnumNameLayerType(LayerType e) {
-  if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_UnidirectionalSequenceLstm)) return "";
+  if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_ChannelShuffle)) return "";
   const size_t index = static_cast<size_t>(e);
   return EnumNamesLayerType()[index];
 }
@@ -1240,11 +1249,12 @@
   Layer_CastLayer = 62,
   Layer_ShapeLayer = 63,
   Layer_UnidirectionalSequenceLstmLayer = 64,
+  Layer_ChannelShuffleLayer = 65,
   Layer_MIN = Layer_NONE,
-  Layer_MAX = Layer_UnidirectionalSequenceLstmLayer
+  Layer_MAX = Layer_ChannelShuffleLayer
 };
 
-inline const Layer (&EnumValuesLayer())[65] {
+inline const Layer (&EnumValuesLayer())[66] {
   static const Layer values[] = {
     Layer_NONE,
     Layer_ActivationLayer,
@@ -1310,13 +1320,14 @@
     Layer_ReduceLayer,
     Layer_CastLayer,
     Layer_ShapeLayer,
-    Layer_UnidirectionalSequenceLstmLayer
+    Layer_UnidirectionalSequenceLstmLayer,
+    Layer_ChannelShuffleLayer
   };
   return values;
 }
 
 inline const char * const *EnumNamesLayer() {
-  static const char * const names[66] = {
+  static const char * const names[67] = {
     "NONE",
     "ActivationLayer",
     "AdditionLayer",
@@ -1382,13 +1393,14 @@
     "CastLayer",
     "ShapeLayer",
     "UnidirectionalSequenceLstmLayer",
+    "ChannelShuffleLayer",
     nullptr
   };
   return names;
 }
 
 inline const char *EnumNameLayer(Layer e) {
-  if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_UnidirectionalSequenceLstmLayer)) return "";
+  if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_ChannelShuffleLayer)) return "";
   const size_t index = static_cast<size_t>(e);
   return EnumNamesLayer()[index];
 }
@@ -1653,6 +1665,10 @@
   static const Layer enum_value = Layer_UnidirectionalSequenceLstmLayer;
 };
 
+template<> struct LayerTraits<armnnSerializer::ChannelShuffleLayer> {
+  static const Layer enum_value = Layer_ChannelShuffleLayer;
+};
+
 bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer type);
 bool VerifyLayerVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector<flatbuffers::Offset<void>> *values, const flatbuffers::Vector<uint8_t> *types);
 
@@ -2747,6 +2763,112 @@
   return builder_.Finish();
 }
 
+struct ChannelShuffleLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef ChannelShuffleLayerBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_BASE = 4,
+    VT_DESCRIPTOR = 6
+  };
+  const armnnSerializer::LayerBase *base() const {
+    return GetPointer<const armnnSerializer::LayerBase *>(VT_BASE);
+  }
+  const armnnSerializer::ChannelShuffleDescriptor *descriptor() const {
+    return GetPointer<const armnnSerializer::ChannelShuffleDescriptor *>(VT_DESCRIPTOR);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyOffset(verifier, VT_BASE) &&
+           verifier.VerifyTable(base()) &&
+           VerifyOffset(verifier, VT_DESCRIPTOR) &&
+           verifier.VerifyTable(descriptor()) &&
+           verifier.EndTable();
+  }
+};
+
+struct ChannelShuffleLayerBuilder {
+  typedef ChannelShuffleLayer Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_base(flatbuffers::Offset<armnnSerializer::LayerBase> base) {
+    fbb_.AddOffset(ChannelShuffleLayer::VT_BASE, base);
+  }
+  void add_descriptor(flatbuffers::Offset<armnnSerializer::ChannelShuffleDescriptor> descriptor) {
+    fbb_.AddOffset(ChannelShuffleLayer::VT_DESCRIPTOR, descriptor);
+  }
+  explicit ChannelShuffleLayerBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  ChannelShuffleLayerBuilder &operator=(const ChannelShuffleLayerBuilder &);
+  flatbuffers::Offset<ChannelShuffleLayer> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<ChannelShuffleLayer>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<ChannelShuffleLayer> CreateChannelShuffleLayer(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    flatbuffers::Offset<armnnSerializer::LayerBase> base = 0,
+    flatbuffers::Offset<armnnSerializer::ChannelShuffleDescriptor> descriptor = 0) {
+  ChannelShuffleLayerBuilder builder_(_fbb);
+  builder_.add_descriptor(descriptor);
+  builder_.add_base(base);
+  return builder_.Finish();
+}
+
+struct ChannelShuffleDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
+  typedef ChannelShuffleDescriptorBuilder Builder;
+  enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
+    VT_AXIS = 4,
+    VT_NUMGROUPS = 6
+  };
+  uint32_t axis() const {
+    return GetField<uint32_t>(VT_AXIS, 0);
+  }
+  uint32_t numGroups() const {
+    return GetField<uint32_t>(VT_NUMGROUPS, 0);
+  }
+  bool Verify(flatbuffers::Verifier &verifier) const {
+    return VerifyTableStart(verifier) &&
+           VerifyField<uint32_t>(verifier, VT_AXIS) &&
+           VerifyField<uint32_t>(verifier, VT_NUMGROUPS) &&
+           verifier.EndTable();
+  }
+};
+
+struct ChannelShuffleDescriptorBuilder {
+  typedef ChannelShuffleDescriptor Table;
+  flatbuffers::FlatBufferBuilder &fbb_;
+  flatbuffers::uoffset_t start_;
+  void add_axis(uint32_t axis) {
+    fbb_.AddElement<uint32_t>(ChannelShuffleDescriptor::VT_AXIS, axis, 0);
+  }
+  void add_numGroups(uint32_t numGroups) {
+    fbb_.AddElement<uint32_t>(ChannelShuffleDescriptor::VT_NUMGROUPS, numGroups, 0);
+  }
+  explicit ChannelShuffleDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb)
+        : fbb_(_fbb) {
+    start_ = fbb_.StartTable();
+  }
+  ChannelShuffleDescriptorBuilder &operator=(const ChannelShuffleDescriptorBuilder &);
+  flatbuffers::Offset<ChannelShuffleDescriptor> Finish() {
+    const auto end = fbb_.EndTable(start_);
+    auto o = flatbuffers::Offset<ChannelShuffleDescriptor>(end);
+    return o;
+  }
+};
+
+inline flatbuffers::Offset<ChannelShuffleDescriptor> CreateChannelShuffleDescriptor(
+    flatbuffers::FlatBufferBuilder &_fbb,
+    uint32_t axis = 0,
+    uint32_t numGroups = 0) {
+  ChannelShuffleDescriptorBuilder builder_(_fbb);
+  builder_.add_numGroups(numGroups);
+  builder_.add_axis(axis);
+  return builder_.Finish();
+}
+
 struct ComparisonDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table {
   typedef ComparisonDescriptorBuilder Builder;
   enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE {
@@ -9838,6 +9960,9 @@
   const armnnSerializer::UnidirectionalSequenceLstmLayer *layer_as_UnidirectionalSequenceLstmLayer() const {
     return layer_type() == armnnSerializer::Layer_UnidirectionalSequenceLstmLayer ? static_cast<const armnnSerializer::UnidirectionalSequenceLstmLayer *>(layer()) : nullptr;
   }
+  const armnnSerializer::ChannelShuffleLayer *layer_as_ChannelShuffleLayer() const {
+    return layer_type() == armnnSerializer::Layer_ChannelShuffleLayer ? static_cast<const armnnSerializer::ChannelShuffleLayer *>(layer()) : nullptr;
+  }
   bool Verify(flatbuffers::Verifier &verifier) const {
     return VerifyTableStart(verifier) &&
            VerifyField<uint8_t>(verifier, VT_LAYER_TYPE) &&
@@ -10103,6 +10228,10 @@
   return layer_as_UnidirectionalSequenceLstmLayer();
 }
 
+template<> inline const armnnSerializer::ChannelShuffleLayer *AnyLayer::layer_as<armnnSerializer::ChannelShuffleLayer>() const {
+  return layer_as_ChannelShuffleLayer();
+}
+
 struct AnyLayerBuilder {
   typedef AnyLayer Table;
   flatbuffers::FlatBufferBuilder &fbb_;
@@ -10589,6 +10718,10 @@
       auto ptr = reinterpret_cast<const armnnSerializer::UnidirectionalSequenceLstmLayer *>(obj);
       return verifier.VerifyTable(ptr);
     }
+    case Layer_ChannelShuffleLayer: {
+      auto ptr = reinterpret_cast<const armnnSerializer::ChannelShuffleLayer *>(obj);
+      return verifier.VerifyTable(ptr);
+    }
     default: return true;
   }
 }
diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp
index 195b416..9a3a270 100644
--- a/src/armnnSerializer/Serializer.cpp
+++ b/src/armnnSerializer/Serializer.cpp
@@ -302,6 +302,19 @@
     CreateAnyLayer(fbCastLayer.o, serializer::Layer::Layer_CastLayer);
 }
 
+void SerializerStrategy::SerializeChannelShuffleLayer(const armnn::IConnectableLayer* layer,
+                                                      const armnn::ChannelShuffleDescriptor& descriptor,
+                                                      const char* name)
+{
+    IgnoreUnused(name);
+    auto fbDescriptor = CreateChannelShuffleDescriptor(m_flatBufferBuilder,
+                                                       descriptor.m_Axis,
+                                                       descriptor.m_NumGroups);
+    auto fbBaseLayer  = CreateLayerBase(layer, serializer::LayerType::LayerType_ChannelShuffle);
+    auto fbChannelShuffleLayer = serializer::CreateChannelShuffleLayer(m_flatBufferBuilder, fbBaseLayer, fbDescriptor);
+    CreateAnyLayer(fbChannelShuffleLayer.o, serializer::Layer::Layer_ChannelShuffleLayer);
+}
+
 void SerializerStrategy::SerializeComparisonLayer(const armnn::IConnectableLayer* layer,
                                              const armnn::ComparisonDescriptor& descriptor,
                                              const char* name)
@@ -1997,6 +2010,15 @@
             SerializeCastLayer(layer, name);
             break;
         }
+        case armnn::LayerType::ChannelShuffle :
+        {
+            const armnn::ChannelShuffleDescriptor& layerDescriptor =
+                                                     static_cast<const armnn::ChannelShuffleDescriptor&>(descriptor);
+            SerializeChannelShuffleLayer(layer,
+                                         layerDescriptor,
+                                         name);
+            break;
+        }
         case armnn::LayerType::Comparison :
         {
             const armnn::ComparisonDescriptor& layerDescriptor =
diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp
index 18b2cc7..43fb0f4 100644
--- a/src/armnnSerializer/Serializer.hpp
+++ b/src/armnnSerializer/Serializer.hpp
@@ -131,6 +131,10 @@
     void SerializeCastLayer(const armnn::IConnectableLayer* layer,
                             const char* name = nullptr);
 
+    void SerializeChannelShuffleLayer(const armnn::IConnectableLayer* layer,
+                                      const armnn::ChannelShuffleDescriptor& descriptor,
+                                      const char* name = nullptr);
+
     void SerializeComparisonLayer(const armnn::IConnectableLayer* layer,
                                   const armnn::ComparisonDescriptor& descriptor,
                                   const char* name = nullptr);
diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp
index 9e9df0d..cd7fd5c 100644
--- a/src/armnnSerializer/test/SerializerTests.cpp
+++ b/src/armnnSerializer/test/SerializerTests.cpp
@@ -202,6 +202,34 @@
         deserializedNetwork->ExecuteStrategy(verifier);
 }
 
+TEST_CASE("SerializeChannelShuffle")
+{
+    const std::string layerName("channelShuffle");
+    const armnn::TensorInfo inputInfo({1, 9}, armnn::DataType::Float32);
+    const armnn::TensorInfo outputInfo({1, 9}, armnn::DataType::Float32);
+
+    armnn::ChannelShuffleDescriptor descriptor({3, 1});
+
+    armnn::INetworkPtr network = armnn::INetwork::Create();
+    armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0);
+    armnn::IConnectableLayer* const ChannelShuffleLayer =
+            network->AddChannelShuffleLayer(descriptor, layerName.c_str());
+    armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0);
+
+    inputLayer->GetOutputSlot(0).Connect(ChannelShuffleLayer->GetInputSlot(0));
+    ChannelShuffleLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
+
+    inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
+    ChannelShuffleLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
+
+    armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network));
+    CHECK(deserializedNetwork);
+
+    LayerVerifierBaseWithDescriptor<armnn::ChannelShuffleDescriptor> verifier(
+            layerName, {inputInfo}, {outputInfo}, descriptor);
+    deserializedNetwork->ExecuteStrategy(verifier);
+}
+
 TEST_CASE("SerializeComparison")
 {
     const std::string layerName("comparison");