From b2d3ec5b1e938ef34facfdbcff83fc8e845d5f7c Mon Sep 17 00:00:00 2001 From: Teresa Charlin Date: Tue, 12 Apr 2022 22:07:09 +0100 Subject: IVGCVSW-6856 Add GATHERNd FrontEnd and Ref Implementation * Add front end * Add reference workload * Add unit tests * Add EndToEnd test Signed-off-by: Teresa Charlin Change-Id: I4cebd17b18476df86162e2dda3366c10e80bd2f8 --- src/armnn/BackendHelper.cpp | 15 ++++++ src/armnn/LayersFwd.hpp | 2 + src/armnn/Network.cpp | 14 ++++- src/armnn/Network.hpp | 2 + src/armnn/layers/GatherNdLayer.cpp | 104 +++++++++++++++++++++++++++++++++++++ src/armnn/layers/GatherNdLayer.hpp | 48 +++++++++++++++++ 6 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 src/armnn/layers/GatherNdLayer.cpp create mode 100644 src/armnn/layers/GatherNdLayer.hpp (limited to 'src/armnn') diff --git a/src/armnn/BackendHelper.cpp b/src/armnn/BackendHelper.cpp index e5c975994e..056fbb08fa 100644 --- a/src/armnn/BackendHelper.cpp +++ b/src/armnn/BackendHelper.cpp @@ -655,6 +655,21 @@ bool LayerSupportHandle::IsGatherSupported(const TensorInfo& input0, reasonIfUnsupported); } +bool LayerSupportHandle::IsGatherNdSupported(const TensorInfo& input0, + const TensorInfo& input1, + const TensorInfo& output, + Optional reasonIfUnsupported) +{ + TensorInfos infos{input0, input1, output}; + + return m_LayerSupport->IsLayerSupported(LayerType::GatherNd, + infos, + BaseDescriptor(), + EmptyOptional(), + EmptyOptional(), + reasonIfUnsupported); +} + bool LayerSupportHandle::IsInputSupported(const TensorInfo& input, Optional reasonIfUnsupported) { diff --git a/src/armnn/LayersFwd.hpp b/src/armnn/LayersFwd.hpp index 607c83b5fa..dcfb91b65a 100644 --- a/src/armnn/LayersFwd.hpp +++ b/src/armnn/LayersFwd.hpp @@ -34,6 +34,7 @@ #include "layers/FloorLayer.hpp" #include "layers/FullyConnectedLayer.hpp" #include "layers/GatherLayer.hpp" +#include "layers/GatherNdLayer.hpp" #include "layers/InputLayer.hpp" #include "layers/InstanceNormalizationLayer.hpp" #include "layers/L2NormalizationLayer.hpp" @@ -134,6 +135,7 @@ DECLARE_LAYER(Fill) DECLARE_LAYER(Floor) DECLARE_LAYER(FullyConnected) DECLARE_LAYER(Gather) +DECLARE_LAYER(GatherNd) DECLARE_LAYER(Input) DECLARE_LAYER(InstanceNormalization) DECLARE_LAYER(L2Normalization) diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index a3655509fb..226d478110 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -379,6 +379,11 @@ IConnectableLayer* INetwork::AddGatherLayer(const GatherDescriptor& descriptor, return pNetworkImpl->AddGatherLayer(descriptor, name); } +IConnectableLayer* INetwork::AddGatherNdLayer(const char* name) +{ + return pNetworkImpl->AddGatherNdLayer(name); +} + IConnectableLayer* INetwork::AddSwitchLayer(const char* name) { return pNetworkImpl->AddSwitchLayer(name); @@ -2442,17 +2447,22 @@ IConnectableLayer* NetworkImpl::AddDequantizeLayer(const char* name) } IConnectableLayer* NetworkImpl::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor, - const char* name) + const char* name) { return m_Graph->AddLayer(stridedSliceDescriptor, name); } IConnectableLayer* NetworkImpl::AddGatherLayer(const GatherDescriptor& gatherDescriptor, - const char* name) + const char* name) { return m_Graph->AddLayer(gatherDescriptor, name); } +IConnectableLayer* NetworkImpl::AddGatherNdLayer(const char* name) +{ + return m_Graph->AddLayer(name); +} + IConnectableLayer* NetworkImpl::AddMergeLayer(const char* name) { return m_Graph->AddLayer(name); diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp index fffad86b80..6e4d29e490 100644 --- a/src/armnn/Network.hpp +++ b/src/armnn/Network.hpp @@ -130,6 +130,8 @@ public: IConnectableLayer* AddGatherLayer(const GatherDescriptor& gatherDescriptor, const char* name = nullptr); + IConnectableLayer* AddGatherNdLayer(const char* name = nullptr); + IConnectableLayer* AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc, const char* name = nullptr); diff --git a/src/armnn/layers/GatherNdLayer.cpp b/src/armnn/layers/GatherNdLayer.cpp new file mode 100644 index 0000000000..1ca2cbbae3 --- /dev/null +++ b/src/armnn/layers/GatherNdLayer.cpp @@ -0,0 +1,104 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "GatherNdLayer.hpp" +#include "LayerCloneBase.hpp" + +#include +#include +#include + +namespace armnn +{ + +GatherNdLayer::GatherNdLayer(const char* name) + : Layer(2, 1, LayerType::GatherNd, name) +{ +} + +std::unique_ptr GatherNdLayer::CreateWorkload(const armnn::IWorkloadFactory& factory) const +{ + GatherNdQueueDescriptor descriptor; + SetAdditionalInfo(descriptor); + + return factory.CreateWorkload(LayerType::GatherNd, descriptor, PrepInfoAndDesc(descriptor)); +} + +GatherNdLayer* GatherNdLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, GetName()); +} + +std::vector GatherNdLayer::InferOutputShapes(const std::vector& inputShapes) const +{ + ARMNN_ASSERT(inputShapes.size() == 2); + const TensorShape& params = inputShapes[0]; + const TensorShape& indices = inputShapes[1]; + + if (indices.GetDimensionality() == Dimensionality::Scalar && indices.GetNumDimensions() == 1) + { + return std::vector({ TensorShape(Dimensionality::Scalar)}); + } + + const unsigned int paramsDim = params.GetNumDimensions(); + const unsigned int indicesDim = indices.GetNumDimensions(); + + // last dimension of indices + unsigned int index_depth = indices[indicesDim - 1]; + ARMNN_ASSERT(index_depth <= paramsDim); + + // all but the last dimension of indices + std::vector outer_shape; + outer_shape.reserve(indicesDim - 1); + for (unsigned int i = 0; i < indicesDim - 1; ++i) + { + outer_shape.emplace_back(indices[i]); + } + + // elements after index_depth + std::vector inner_shape; + inner_shape.reserve(paramsDim - index_depth); + for (unsigned int i = index_depth; i < paramsDim; ++i) + { + inner_shape.emplace_back(params[i]); + } + + // concatenate outer_shape + inner_shape + std::vector output_shape; + output_shape.reserve( outer_shape.size() + inner_shape.size() ); + output_shape.insert( output_shape.end(), outer_shape.begin(), outer_shape.end() ); + output_shape.insert( output_shape.end(), inner_shape.begin(), inner_shape.end() ); + + const auto outputDim = static_cast(output_shape.size()); + return std::vector({ TensorShape({outputDim, output_shape.data()})}); +} + +void GatherNdLayer::ValidateTensorShapesFromInputs() +{ + VerifyLayerConnections(2, CHECK_LOCATION()); + + const TensorShape& outputShape = GetOutputSlot(0).GetTensorInfo().GetShape(); + + VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod); + + std::vector inferredShapes = InferOutputShapes( + {GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), + GetInputSlot(1).GetConnection()->GetTensorInfo().GetShape()}); + ARMNN_ASSERT(inferredShapes.size() == 1); + ARMNN_ASSERT(inferredShapes[0].GetDimensionality() == Dimensionality::Specified || + inferredShapes[0].GetDimensionality() == Dimensionality::Scalar); + + ValidateAndCopyShape(outputShape, inferredShapes[0], m_ShapeInferenceMethod, "GatherNdLayer"); +} + +ARMNN_NO_DEPRECATE_WARN_BEGIN +void GatherNdLayer::Accept(ILayerVisitor& visitor) const +{ + IgnoreUnused(visitor); + throw armnn::Exception("GatherNdLayer VisitGatherNdLayer is not implemented"); +} +ARMNN_NO_DEPRECATE_WARN_END + +} // namespace armnn diff --git a/src/armnn/layers/GatherNdLayer.hpp b/src/armnn/layers/GatherNdLayer.hpp new file mode 100644 index 0000000000..9e07715f90 --- /dev/null +++ b/src/armnn/layers/GatherNdLayer.hpp @@ -0,0 +1,48 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "Layer.hpp" + +namespace armnn +{ + +/// This layer represents a GatherNd operator. +class GatherNdLayer : public Layer +{ +public: + /// Makes a workload for the Gather type. + /// @param [in] factory The workload factory which will create the workload. + /// @return A pointer to the created workload, or nullptr if not created. + virtual std::unique_ptr CreateWorkload(const IWorkloadFactory& factory) const override; + + /// Creates a dynamically-allocated copy of this layer. + /// @param [in] graph The graph into which this layer is being cloned. + GatherNdLayer* Clone(Graph& graph) const override; + + /// Infers the output shapes from given input shapes and layer properties. + /// @param [in] inputShapes The input shapes layer has. + /// @return A vector to the inferred output shape. + std::vector InferOutputShapes(const std::vector& inputShapes) const override; + + /// Check if the input tensor shape(s) + /// will lead to a valid configuration of @ref GatherNdLayer. + void ValidateTensorShapesFromInputs() override; + + ARMNN_NO_DEPRECATE_WARN_BEGIN + void Accept(ILayerVisitor& visitor) const override; + ARMNN_NO_DEPRECATE_WARN_END + +protected: + /// Constructor to create a GatherNdLayer. + /// @param [in] name Optional name for the layer. + GatherNdLayer(const char* name); + + /// Default destructor + ~GatherNdLayer() = default; +}; + +} // namespace armnn -- cgit v1.2.1