From 0c3ea5b8ac5ad8ca516930a0491afb1d1074e45b Mon Sep 17 00:00:00 2001 From: Sadik Armagan Date: Wed, 3 Feb 2021 09:29:30 +0000 Subject: backends/reference: Add ReduceSum operation support This patch addes ReduceSum operation support for reference backend, which computes the sum of elements across dimensions of a tensor. Changelog v1: - Fix file header descriptions. Chagelog v2: - Fix line limit issue. - Fix type conversion issue. Changelog v3: - Remove tabs. - Modify newly added file headers. Changelog v4: - Symbol on header isn't allowed so drop it from newly added file headers. Changelog v5: - Remove tabs, fix the use of brackets and align lines correctly. Changelog v6: - Add serializer and deserializer support. Changelog v7: - Fix build error add missed code. Changelog v8: - Rename ReduceSumDecriptor to ReduceDescriptor - Update m_KeepDims field data type to bool on ReduceDescriptor - Add ReduceOperation field to ReduceDescriptor - Rename ReduceSumLayer to ReduceLayer - Update ReduceLayer to use ReduceDescriptor - Update ReduceLayer::ValidateTensorShapesFromInputs() function - Rename RefReduceSumWokload to RefReduceWorkload - Update workload to use ReduceDescriptor - Update workload to use Decoders and Encoders - Remove ReduceSum.hpp and ReduceSum.cpp - Added Reduce.hpp and Reduce.cpp - Move Mean.cpp (which is implementing REDUCE_MEAN) functionality to Reduce.cpp - Update RefMeanWorkload to call Reduce function with ReduceOperation::Mean argument - Remove Mean.hpp and Mean.cpp - Update the Serializer/Deserializer ArmnnSchema.fbs for ReduceLayer, ReduceDescriptor, and ReduceOperation - Update Serializer and Deserializer for serializing/parsing ReduceLayer - Added TfLiter parser Sum test for REDUCE_SUM operator - Make corresponding changes on front-end and Ref backend to support REDUCE_SUM operator Changelog v9: - Fixed build errors. Change-Id: I8c8e034f3df73f9565b3c18eff51ecca6c542195 Signed-off-by: Inki Dae Signed-off-by: Sadik Armagan --- Android.mk | 1 + CMakeLists.txt | 4 + include/armnn/Descriptors.hpp | 32 ++ include/armnn/DescriptorsFwd.hpp | 1 + include/armnn/ILayerSupport.hpp | 5 + include/armnn/ILayerVisitor.hpp | 8 + include/armnn/INetwork.hpp | 7 + include/armnn/LayerSupport.hpp | 9 + include/armnn/LayerVisitorBase.hpp | 4 + include/armnn/Types.hpp | 8 + src/armnn/InternalTypes.hpp | 1 + src/armnn/LayerSupport.cpp | 11 + src/armnn/LayersFwd.hpp | 2 + src/armnn/Network.cpp | 6 + src/armnn/Network.hpp | 3 + src/armnn/layers/ReduceLayer.cpp | 100 ++++++ src/armnn/layers/ReduceLayer.hpp | 42 +++ .../test/TestNameAndDescriptorLayerVisitor.hpp | 1 + src/armnnDeserializer/Deserializer.cpp | 52 ++++ src/armnnDeserializer/Deserializer.hpp | 1 + .../test/DeserializeReduceSum.cpp | 126 ++++++++ src/armnnSerializer/ArmnnSchema.fbs | 28 +- src/armnnSerializer/ArmnnSchema_generated.h | 238 +++++++++++++- src/armnnSerializer/Serializer.cpp | 19 ++ src/armnnSerializer/Serializer.hpp | 4 + src/armnnSerializer/SerializerUtils.cpp | 17 + src/armnnSerializer/SerializerUtils.hpp | 2 + src/armnnSerializer/test/SerializerTests.cpp | 30 ++ src/armnnTfLiteParser/TfLiteParser.cpp | 54 ++++ src/armnnTfLiteParser/TfLiteParser.hpp | 1 + src/armnnTfLiteParser/test/Sum.cpp | 110 +++++++ src/backends/backendsCommon/LayerSupportBase.cpp | 8 + src/backends/backendsCommon/LayerSupportBase.hpp | 5 + src/backends/backendsCommon/WorkloadData.cpp | 27 ++ src/backends/backendsCommon/WorkloadData.hpp | 5 + src/backends/backendsCommon/WorkloadFactory.cpp | 18 ++ src/backends/backendsCommon/WorkloadFactory.hpp | 3 + src/backends/backendsCommon/common.mk | 1 + src/backends/backendsCommon/test/CMakeLists.txt | 2 + .../test/IsLayerSupportedTestImpl.hpp | 2 + src/backends/backendsCommon/test/LayerTests.hpp | 1 + .../test/layerTests/ReduceSumTestImpl.cpp | 344 +++++++++++++++++++++ .../test/layerTests/ReduceSumTestImpl.hpp | 43 +++ src/backends/reference/RefLayerSupport.cpp | 30 ++ src/backends/reference/RefLayerSupport.hpp | 5 + src/backends/reference/RefWorkloadFactory.cpp | 6 + src/backends/reference/RefWorkloadFactory.hpp | 3 + src/backends/reference/backend.mk | 3 +- src/backends/reference/test/RefLayerTests.cpp | 7 + src/backends/reference/workloads/CMakeLists.txt | 6 +- src/backends/reference/workloads/Mean.cpp | 143 --------- src/backends/reference/workloads/Mean.hpp | 22 -- src/backends/reference/workloads/Reduce.cpp | 151 +++++++++ src/backends/reference/workloads/Reduce.hpp | 24 ++ .../reference/workloads/RefMeanWorkload.cpp | 9 +- .../reference/workloads/RefReduceWorkload.cpp | 42 +++ .../reference/workloads/RefReduceWorkload.hpp | 23 ++ src/backends/reference/workloads/RefWorkloads.hpp | 1 + 58 files changed, 1678 insertions(+), 183 deletions(-) create mode 100644 src/armnn/layers/ReduceLayer.cpp create mode 100644 src/armnn/layers/ReduceLayer.hpp create mode 100644 src/armnnDeserializer/test/DeserializeReduceSum.cpp create mode 100644 src/armnnTfLiteParser/test/Sum.cpp create mode 100644 src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.cpp create mode 100644 src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.hpp delete mode 100644 src/backends/reference/workloads/Mean.cpp delete mode 100644 src/backends/reference/workloads/Mean.hpp create mode 100644 src/backends/reference/workloads/Reduce.cpp create mode 100644 src/backends/reference/workloads/Reduce.hpp create mode 100644 src/backends/reference/workloads/RefReduceWorkload.cpp create mode 100644 src/backends/reference/workloads/RefReduceWorkload.hpp diff --git a/Android.mk b/Android.mk index 2ffddf49ca..6ada126893 100644 --- a/Android.mk +++ b/Android.mk @@ -198,6 +198,7 @@ LOCAL_SRC_FILES := \ src/armnn/layers/QuantizeLayer.cpp \ src/armnn/layers/QuantizedLstmLayer.cpp \ src/armnn/layers/RankLayer.cpp \ + src/armnn/layers/ReduceLayer.cpp \ src/armnn/layers/ReshapeLayer.cpp \ src/armnn/layers/ResizeLayer.cpp \ src/armnn/layers/SliceLayer.cpp \ diff --git a/CMakeLists.txt b/CMakeLists.txt index b071bf0865..789e2d9768 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,6 +368,8 @@ list(APPEND armnn_sources src/armnn/layers/PreluLayer.cpp src/armnn/layers/RankLayer.hpp src/armnn/layers/RankLayer.cpp + src/armnn/layers/ReduceLayer.hpp + src/armnn/layers/ReduceLayer.cpp src/armnn/layers/ReshapeLayer.hpp src/armnn/layers/ReshapeLayer.cpp src/armnn/layers/ResizeLayer.hpp @@ -829,6 +831,7 @@ if(BUILD_UNIT_TESTS) src/armnnTfLiteParser/test/Squeeze.cpp src/armnnTfLiteParser/test/StridedSlice.cpp src/armnnTfLiteParser/test/Sub.cpp + src/armnnTfLiteParser/test/Sum.cpp src/armnnTfLiteParser/test/TransposeConv.cpp src/armnnTfLiteParser/test/Transpose.cpp src/armnnTfLiteParser/test/Unpack.cpp @@ -925,6 +928,7 @@ if(BUILD_UNIT_TESTS) src/armnnDeserializer/test/DeserializePermute.cpp src/armnnDeserializer/test/DeserializePooling2d.cpp src/armnnDeserializer/test/DeserializeRank.cpp + src/armnnDeserializer/test/DeserializeReduceSum.cpp src/armnnDeserializer/test/DeserializeReshape.cpp src/armnnDeserializer/test/DeserializeResizeBilinear.cpp src/armnnDeserializer/test/DeserializeRsqrt.cpp diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp index f023e00e93..d6e37e535e 100644 --- a/include/armnn/Descriptors.hpp +++ b/include/armnn/Descriptors.hpp @@ -1286,4 +1286,36 @@ struct LogicalBinaryDescriptor LogicalBinaryOperation m_Operation; }; +/// A ReduceDescriptor for the REDUCE operators. +struct ReduceDescriptor +{ + ReduceDescriptor() + : m_TargetHeight(0) + , m_TargetWidth(0) + , m_KeepDims(false) + , m_vAxis() + , m_ReduceOperation(ReduceOperation::Sum) + {} + + bool operator ==(const ReduceDescriptor& rhs) const + { + return m_TargetHeight == rhs.m_TargetHeight && + m_TargetWidth == rhs.m_TargetWidth && + m_KeepDims == rhs.m_KeepDims && + m_vAxis == rhs.m_vAxis && + m_ReduceOperation == rhs.m_ReduceOperation; + } + + /// Target height value. + uint32_t m_TargetHeight; + /// Target width value. + uint32_t m_TargetWidth; + /// if true then output shape has no change. + bool m_KeepDims; + /// The indices of the dimensions to reduce. + std::vector m_vAxis; + /// Specifies the reduction operation to execute + ReduceOperation m_ReduceOperation; +}; + } // namespace armnn diff --git a/include/armnn/DescriptorsFwd.hpp b/include/armnn/DescriptorsFwd.hpp index dff5ec73ad..054ce51144 100644 --- a/include/armnn/DescriptorsFwd.hpp +++ b/include/armnn/DescriptorsFwd.hpp @@ -36,6 +36,7 @@ struct QLstmDescriptor; struct ReshapeDescriptor; struct ResizeBilinearDescriptor; struct ResizeDescriptor; +struct ReduceDescriptor; struct SoftmaxDescriptor; struct SpaceToBatchNdDescriptor; struct SpaceToDepthDescriptor; diff --git a/include/armnn/ILayerSupport.hpp b/include/armnn/ILayerSupport.hpp index 5bcfc7d3cd..602e4e5b57 100644 --- a/include/armnn/ILayerSupport.hpp +++ b/include/armnn/ILayerSupport.hpp @@ -324,6 +324,11 @@ public: const TensorInfo& output, Optional reasonIfUnsupported = EmptyOptional()) const = 0; + virtual bool IsReduceSupported(const TensorInfo& input, + const TensorInfo& output, + const ReduceDescriptor& descriptor, + Optional reasonIfUnsupported = EmptyOptional()) const = 0; + virtual bool IsReshapeSupported(const TensorInfo& input, const TensorInfo& output, const ReshapeDescriptor& descriptor, diff --git a/include/armnn/ILayerVisitor.hpp b/include/armnn/ILayerVisitor.hpp index b8e741d01e..b5112a8f0b 100644 --- a/include/armnn/ILayerVisitor.hpp +++ b/include/armnn/ILayerVisitor.hpp @@ -409,6 +409,14 @@ public: virtual void VisitRankLayer(const IConnectableLayer* layer, const char* name = nullptr) = 0; + /// Function that a reduce layer should call back to when its Accept(ILayerVisitor&) function is invoked. + /// @param layer - pointer to the layer which is calling back to this visit function. + /// @param ReduceDescriptor - Parameters for the reduce max operation. + /// @param name - Optional name for the layer. + virtual void VisitReduceLayer(const IConnectableLayer* layer, + const ReduceDescriptor& reduceDescriptor, + const char* name = nullptr) = 0; + /// Function a reshape layer should call back to when its Accept(ILayerVisitor&) function is invoked. /// @param layer - pointer to the layer which is calling back to this visit function. /// @param reshapeDescriptor - Parameters for the reshape operation. diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp index b89df63aa4..ca1b725d48 100644 --- a/include/armnn/INetwork.hpp +++ b/include/armnn/INetwork.hpp @@ -372,6 +372,13 @@ public: virtual IConnectableLayer* AddResizeLayer(const ResizeDescriptor& resizeDescriptor, const char* name = nullptr) = 0; + /// Adds a reduce layer to the network. + /// @param ReduceDescriptor - Parameters for the reduce operation. + /// @param name - Optional name for the layer. + /// @return - Interface for configuring the layer. + virtual IConnectableLayer* AddReduceLayer(const ReduceDescriptor& reduceDescriptor, + const char* name = nullptr) = 0; + /// Adds an instance normalization layer to the network. /// @param desc - Parameters for the instance normalization operation. /// @param name - Optional name for the layer. diff --git a/include/armnn/LayerSupport.hpp b/include/armnn/LayerSupport.hpp index 2ec086b185..6f1eb0347b 100644 --- a/include/armnn/LayerSupport.hpp +++ b/include/armnn/LayerSupport.hpp @@ -302,6 +302,14 @@ bool IsQuantizedLstmSupported(const BackendId& backend, char* reasonIfUnsupported = nullptr, size_t reasonIfUnsupportedMaxLength = 1024); +/// Deprecated in favor of IBackend and ILayerSupport interfaces +bool IsReduceSupported(const BackendId& backend, + const TensorInfo& input, + const TensorInfo& output, + const ReduceDescriptor& descriptor, + char* reasonIfUnsupported = nullptr, + size_t reasonIfUnsupportedMaxLength = 1024); + /// Deprecated in favor of IBackend and ILayerSupport interfaces bool IsReshapeSupported(const BackendId& backend, const TensorInfo& input, @@ -413,4 +421,5 @@ bool IsTransposeConvolution2dSupported(const BackendId& backend, const Optional& biases, char* reasonIfUnsupported = nullptr, size_t reasonIfUnsupportedMaxLength = 1024); + } diff --git a/include/armnn/LayerVisitorBase.hpp b/include/armnn/LayerVisitorBase.hpp index a97530ec40..fb88d559bb 100644 --- a/include/armnn/LayerVisitorBase.hpp +++ b/include/armnn/LayerVisitorBase.hpp @@ -208,6 +208,10 @@ public: void VisitRankLayer(const IConnectableLayer*, const char*) override { DefaultPolicy::Apply(__func__); } + void VisitReduceLayer(const IConnectableLayer*, + const ReduceDescriptor&, + const char*) override { DefaultPolicy::Apply(__func__); } + void VisitReshapeLayer(const IConnectableLayer*, const ReshapeDescriptor&, const char*) override { DefaultPolicy::Apply(__func__); } diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp index 46f246f46d..22004bd0a4 100644 --- a/include/armnn/Types.hpp +++ b/include/armnn/Types.hpp @@ -108,6 +108,14 @@ enum class PoolingAlgorithm L2 = 2 }; +enum class ReduceOperation +{ + Sum = 0, + Max = 1, + Mean = 2, + Min = 3 +}; + enum class ResizeMethod { Bilinear = 0, diff --git a/src/armnn/InternalTypes.hpp b/src/armnn/InternalTypes.hpp index 6e47399871..6e6559137c 100644 --- a/src/armnn/InternalTypes.hpp +++ b/src/armnn/InternalTypes.hpp @@ -63,6 +63,7 @@ X(QuantizedLstm) \ X(Reshape) \ X(Rank) \ + X(Reduce) \ X(Resize) \ X(Slice) \ X(Softmax) \ diff --git a/src/armnn/LayerSupport.cpp b/src/armnn/LayerSupport.cpp index 197e1afe18..8812e0ea77 100644 --- a/src/armnn/LayerSupport.cpp +++ b/src/armnn/LayerSupport.cpp @@ -528,6 +528,7 @@ bool IsQuantizedLstmSupported(const BackendId& backend, cellStateOut, output, paramsInfo); } + bool IsPermuteSupported(const BackendId& backend, const TensorInfo& input, const TensorInfo& output, @@ -558,6 +559,16 @@ bool IsPreluSupported(const BackendId& backend, FORWARD_LAYER_SUPPORT_FUNC(backend, IsPreluSupported, input, alpha, output); } +bool IsReduceSupported(const BackendId& backend, + const TensorInfo& input, + const TensorInfo& output, + const ReduceDescriptor& descriptor, + char* reasonIfUnsupported, + size_t reasonIfUnsupportedMaxLength) +{ + FORWARD_LAYER_SUPPORT_FUNC(backend, IsReduceSupported, input, output, descriptor); +} + bool IsReshapeSupported(const BackendId& backend, const TensorInfo& input, const TensorInfo& output, diff --git a/src/armnn/LayersFwd.hpp b/src/armnn/LayersFwd.hpp index b9ca61a70b..6782fb5eb7 100644 --- a/src/armnn/LayersFwd.hpp +++ b/src/armnn/LayersFwd.hpp @@ -56,6 +56,7 @@ #include "layers/QLstmLayer.hpp" #include "layers/QuantizedLstmLayer.hpp" #include "layers/RankLayer.hpp" +#include "layers/ReduceLayer.hpp" #include "layers/ReshapeLayer.hpp" #include "layers/ResizeLayer.hpp" #include "layers/SliceLayer.hpp" @@ -149,6 +150,7 @@ DECLARE_LAYER(Quantize) DECLARE_LAYER(QLstm) DECLARE_LAYER(QuantizedLstm) DECLARE_LAYER(Rank) +DECLARE_LAYER(Reduce) DECLARE_LAYER(Reshape) DECLARE_LAYER(Resize) DECLARE_LAYER(Slice) diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index d41f2f6fa7..f8b0675f0d 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -1491,6 +1491,12 @@ IConnectableLayer* Network::AddRankLayer(const char* name) return m_Graph->AddLayer(name); } +IConnectableLayer* Network::AddReduceLayer(const ReduceDescriptor& reduceDescriptor, + const char* name) +{ + return m_Graph->AddLayer(reduceDescriptor, name); +} + IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor, const char* name) { diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp index c652edb416..1205bd847e 100644 --- a/src/armnn/Network.hpp +++ b/src/armnn/Network.hpp @@ -169,6 +169,9 @@ public: IConnectableLayer* AddResizeLayer(const ResizeDescriptor& resizeDescriptor, const char* name = nullptr) override; + IConnectableLayer* AddReduceLayer(const ReduceDescriptor& reduceDescriptor, + const char* name = nullptr) override; + IConnectableLayer* AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc, const char* name = nullptr) override; diff --git a/src/armnn/layers/ReduceLayer.cpp b/src/armnn/layers/ReduceLayer.cpp new file mode 100644 index 0000000000..b68cd2eabc --- /dev/null +++ b/src/armnn/layers/ReduceLayer.cpp @@ -0,0 +1,100 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "ReduceLayer.hpp" +#include "LayerCloneBase.hpp" + +#include + +#include +#include + +namespace armnn +{ + +ReduceLayer::ReduceLayer(const ReduceDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Reduce, param, name) +{ +} + +std::unique_ptr ReduceLayer::CreateWorkload(const IWorkloadFactory& factory) const +{ + ReduceQueueDescriptor descriptor; + return factory.CreateReduce(descriptor, PrepInfoAndDesc(descriptor)); +} + +ReduceLayer* ReduceLayer::Clone(Graph& graph) const +{ + return CloneBase(graph, m_Param, GetName()); +} + +void ReduceLayer::ValidateTensorShapesFromInputs() +{ + VerifyLayerConnections(1, CHECK_LOCATION()); + + const TensorShape& outputShape = GetOutputSlot(0).GetTensorInfo().GetShape(); + + VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod); + + const TensorInfo& input = GetInputSlot(0).GetConnection()->GetTensorInfo(); + + ARMNN_ASSERT_MSG(input.GetNumDimensions() > 0 && input.GetNumDimensions() <= 4, + "ReduceLayer: Reduce supports up to 4D input."); + + unsigned int rank = input.GetNumDimensions(); + unsigned int outputRank = 0; + + // Calculate output dimension + if (m_Param.m_KeepDims) + { + outputRank = rank; + } + else if (m_Param.m_vAxis.empty()) + { + outputRank = 1; + } + else if (m_Param.m_vAxis.size() > input.GetNumDimensions()) + { + throw LayerValidationException("ReduceLayer: Dimensions to reduce can not be bigger than input dimensions"); + } + else + { + outputRank = input.GetNumDimensions() - armnn::numeric_cast(m_Param.m_vAxis.size()); + if (outputRank == 0) + { + outputRank = 1; + } + } + + std::vector dimSizes(outputRank, 1); + if (!m_Param.m_vAxis.empty()) + { + // Skip the dimension that has been reduced unless keepDims is true. + unsigned int outputIndex = 0; + for (unsigned int i = 0; i < input.GetNumDimensions(); ++i) + { + if (std::find(m_Param.m_vAxis.begin(), m_Param.m_vAxis.end(), i) == m_Param.m_vAxis.end()) + { + dimSizes[outputIndex] = armnn::numeric_cast(input.GetShape()[i]); + ++outputIndex; + } + else if (m_Param.m_KeepDims) + { + dimSizes[outputIndex] = 1; + ++outputIndex; + } + } + } + const TensorShape& inferredShape = TensorShape(outputRank, dimSizes.data()); + + ValidateAndCopyShape(outputShape, inferredShape, m_ShapeInferenceMethod, "ReduceLayer"); +} + +void ReduceLayer::Accept(ILayerVisitor& visitor) const +{ + visitor.VisitReduceLayer(this, GetParameters(), GetName()); +} + +} // namespace armnn diff --git a/src/armnn/layers/ReduceLayer.hpp b/src/armnn/layers/ReduceLayer.hpp new file mode 100644 index 0000000000..fd4f2073f1 --- /dev/null +++ b/src/armnn/layers/ReduceLayer.hpp @@ -0,0 +1,42 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + +#include "LayerWithParameters.hpp" + +namespace armnn +{ + +/// This layer represents a reduction operation. +class ReduceLayer : public LayerWithParameters +{ +public: + /// Makes a workload for the Reduce type. + /// @param [in] graph The graph where this layer can be found. + /// @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_ptrCreateWorkload(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. + ReduceLayer* Clone(Graph& graph) const override; + + /// Check if the input tensor shape(s) + /// will lead to a valid configuration of @ref ReduceLayer. + void ValidateTensorShapesFromInputs() override; + + void Accept(ILayerVisitor& visitor) const override; + +protected: + /// Constructor to create a ReduceLayer. + /// @param [in] param ReduceDescriptor to configure the reduction operation. + /// @param [in] name Optional name for the layer. + ReduceLayer(const ReduceDescriptor& param, const char* name); + + /// Default destructor + ~ReduceLayer() = default; +}; + +} // namespace armnn diff --git a/src/armnn/test/TestNameAndDescriptorLayerVisitor.hpp b/src/armnn/test/TestNameAndDescriptorLayerVisitor.hpp index dc6d11440f..c911caa699 100644 --- a/src/armnn/test/TestNameAndDescriptorLayerVisitor.hpp +++ b/src/armnn/test/TestNameAndDescriptorLayerVisitor.hpp @@ -60,6 +60,7 @@ DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Normalization) DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Pad) DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Permute) DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Pooling2d) +DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Reduce) DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Reshape) DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Resize) DECLARE_TEST_NAME_AND_DESCRIPTOR_LAYER_VISITOR_CLASS(Slice) diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp index 14c2af4fe1..e98ff15aa9 100644 --- a/src/armnnDeserializer/Deserializer.cpp +++ b/src/armnnDeserializer/Deserializer.cpp @@ -251,6 +251,7 @@ m_ParserFunctions(Layer_MAX+1, &IDeserializer::DeserializerImpl::ParseUnsupporte m_ParserFunctions[Layer_QuantizeLayer] = &DeserializerImpl::ParseQuantize; m_ParserFunctions[Layer_QuantizedLstmLayer] = &DeserializerImpl::ParseQuantizedLstm; m_ParserFunctions[Layer_RankLayer] = &DeserializerImpl::ParseRank; + m_ParserFunctions[Layer_ReduceLayer] = &DeserializerImpl::ParseReduce; m_ParserFunctions[Layer_ReshapeLayer] = &DeserializerImpl::ParseReshape; m_ParserFunctions[Layer_ResizeBilinearLayer] = &DeserializerImpl::ParseResizeBilinear; m_ParserFunctions[Layer_ResizeLayer] = &DeserializerImpl::ParseResize; @@ -363,6 +364,8 @@ LayerBaseRawPtr IDeserializer::DeserializerImpl::GetBaseLayer(const GraphPtr& gr return graphPtr->layers()->Get(layerIndex)->layer_as_QuantizedLstmLayer()->base(); case Layer::Layer_RankLayer: return graphPtr->layers()->Get(layerIndex)->layer_as_RankLayer()->base(); + case Layer::Layer_ReduceLayer: + return graphPtr->layers()->Get(layerIndex)->layer_as_ReduceLayer()->base(); case Layer::Layer_ReshapeLayer: return graphPtr->layers()->Get(layerIndex)->layer_as_ReshapeLayer()->base(); case Layer::Layer_ResizeBilinearLayer: @@ -498,6 +501,23 @@ armnn::ComparisonOperation ToComparisonOperation(armnnSerializer::ComparisonOper } } +armnn::ReduceOperation ToReduceOperation(armnnSerializer::ReduceOperation operation) +{ + switch (operation) + { + case armnnSerializer::ReduceOperation::ReduceOperation_Sum: + return armnn::ReduceOperation::Sum; + case armnnSerializer::ReduceOperation::ReduceOperation_Max: + return armnn::ReduceOperation::Max; + case armnnSerializer::ReduceOperation::ReduceOperation_Mean: + return armnn::ReduceOperation::Mean; + case armnnSerializer::ReduceOperation::ReduceOperation_Min: + return armnn::ReduceOperation::Min; + default: + return armnn::ReduceOperation::Sum; + } +} + armnn::LogicalBinaryOperation ToLogicalBinaryOperation(armnnSerializer::LogicalBinaryOperation operation) { switch (operation) @@ -2082,6 +2102,38 @@ void IDeserializer::DeserializerImpl::ParseRank(GraphPtr graph, unsigned int lay RegisterOutputSlots(graph, layerIndex, layer); } +void IDeserializer::DeserializerImpl::ParseReduce(GraphPtr graph, unsigned int layerIndex) +{ + CHECK_LAYERS(graph, 0, layerIndex); + CHECK_LOCATION(); + + auto inputs = GetInputs(graph, layerIndex); + CHECK_VALID_SIZE(inputs.size(), 1); + + auto outputs = GetOutputs(graph, layerIndex); + CHECK_VALID_SIZE(outputs.size(), 1); + + auto fbLayer = graph->layers()->Get(layerIndex)->layer_as_ReduceLayer(); + auto fbDescriptor = fbLayer->descriptor(); + auto flatBufferAxis = fbDescriptor->axis(); + + armnn::ReduceDescriptor descriptor; + descriptor.m_TargetHeight = fbDescriptor->targetHeight(); + descriptor.m_TargetWidth = fbDescriptor->targetWidth(); + descriptor.m_KeepDims = fbDescriptor->keepDims(); + descriptor.m_vAxis = std::vector(flatBufferAxis->begin(), flatBufferAxis->end()); + descriptor.m_ReduceOperation = ToReduceOperation(fbDescriptor->reduceOperation()); + + const std::string& layerName = GetLayerName(graph, layerIndex); + IConnectableLayer* layer = m_Network->AddReduceLayer(descriptor, layerName.c_str()); + + armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]); + layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + + RegisterInputSlots(graph, layerIndex, layer); + RegisterOutputSlots(graph, layerIndex, layer); +} + void IDeserializer::DeserializerImpl::ParseReshape(GraphPtr graph, unsigned int layerIndex) { CHECK_LAYERS(graph, 0, layerIndex); diff --git a/src/armnnDeserializer/Deserializer.hpp b/src/armnnDeserializer/Deserializer.hpp index e232feed9b..f4f64240a0 100644 --- a/src/armnnDeserializer/Deserializer.hpp +++ b/src/armnnDeserializer/Deserializer.hpp @@ -119,6 +119,7 @@ private: void ParseQLstm(GraphPtr graph, unsigned int layerIndex); void ParseQuantize(GraphPtr graph, unsigned int layerIndex); void ParseRank(GraphPtr graph, unsigned int layerIndex); + void ParseReduce(GraphPtr graph, unsigned int layerIndex); void ParseReshape(GraphPtr graph, unsigned int layerIndex); void ParseResize(GraphPtr graph, unsigned int layerIndex); void ParseResizeBilinear(GraphPtr graph, unsigned int layerIndex); diff --git a/src/armnnDeserializer/test/DeserializeReduceSum.cpp b/src/armnnDeserializer/test/DeserializeReduceSum.cpp new file mode 100644 index 0000000000..d88613e593 --- /dev/null +++ b/src/armnnDeserializer/test/DeserializeReduceSum.cpp @@ -0,0 +1,126 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include +#include "ParserFlatbuffersSerializeFixture.hpp" +#include "../Deserializer.hpp" + +#include +#include + +BOOST_AUTO_TEST_SUITE(Deserializer) + +struct ReduceSumFixture : public ParserFlatbuffersSerializeFixture +{ + explicit ReduceSumFixture(const std::string& inputShape, + const std::string& outputShape, + const std::string& axis, + 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: "ReduceLayer", + layer: { + base: { + index: 1, + layerName: "ReduceSumLayer", + layerType: "Reduce", + inputSlots: [{ + index: 0, + connection: {sourceLayerIndex:0, outputSlotIndex:0 }, + }], + outputSlots: [{ + index: 0, + tensorInfo: { + dimensions: )" + outputShape + R"(, + dataType: )" + dataType + R"( + } + }] + }, + descriptor: { + axis: )" + axis + R"(, + keepDims: true, + reduceOperation: Sum + } + } + }, + { + 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"( + }, + }], + } + } + }, + } + ] + } + )"; + Setup(); + } +}; + +struct SimpleReduceSumFixture : ReduceSumFixture +{ + SimpleReduceSumFixture() + : ReduceSumFixture("[ 1, 1, 3, 2 ]", // inputShape + "[ 1, 1, 1, 2 ]", // outputShape + "[ 2 ]", // axis + "Float32") // dataType + {} +}; + +BOOST_FIXTURE_TEST_CASE(SimpleReduceSum, SimpleReduceSumFixture) +{ + RunTest<4, armnn::DataType::Float32>( + 0, + {{"InputLayer", { 1.0f, 1.0f, 2.0f, 2.0f, 3.0f, 3.0f }}}, + {{"OutputLayer", { 6.0f, 6.0f }}}); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs index 1f71ce19f2..aa539b188f 100644 --- a/src/armnnSerializer/ArmnnSchema.fbs +++ b/src/armnnSerializer/ArmnnSchema.fbs @@ -47,6 +47,13 @@ enum DataLayout : byte { NCHW = 1 } +enum ReduceOperation: byte { + Sum = 0, + Max = 1, + Mean = 2, + Min = 3 +} + enum ResizeMethod: byte { NearestNeighbor = 0, Bilinear = 1, @@ -160,7 +167,8 @@ enum LayerType : uint { QLstm = 56, Fill = 57, Rank = 58, - LogicalBinary = 59 + LogicalBinary = 59, + Reduce = 60 } // Base layer table to be used as part of other layers @@ -881,6 +889,19 @@ table RankLayer { base:LayerBase; } +table ReduceLayer { + base:LayerBase; + descriptor:ReduceDescriptor; +} + +table ReduceDescriptor { + targetHeight:uint; + targetWidth:uint; + keepDims:bool = false; + axis:[uint]; + reduceOperation:ReduceOperation = Sum; +} + union Layer { ActivationLayer, AdditionLayer, @@ -941,7 +962,8 @@ union Layer { QLstmLayer, FillLayer, RankLayer, - LogicalBinaryLayer + LogicalBinaryLayer, + ReduceLayer } table AnyLayer { @@ -960,4 +982,4 @@ table SerializedGraph { featureVersions:FeatureCompatibilityVersions; } -root_type SerializedGraph; \ No newline at end of file +root_type SerializedGraph; diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h index 031da5d6fa..32548b2144 100644 --- a/src/armnnSerializer/ArmnnSchema_generated.h +++ b/src/armnnSerializer/ArmnnSchema_generated.h @@ -4,6 +4,7 @@ // // automatically generated by the FlatBuffers compiler, do not modify + #ifndef FLATBUFFERS_GENERATED_ARMNNSCHEMA_ARMNNSERIALIZER_H_ #define FLATBUFFERS_GENERATED_ARMNNSCHEMA_ARMNNSERIALIZER_H_ @@ -349,6 +350,12 @@ struct StandInLayerBuilder; struct RankLayer; struct RankLayerBuilder; +struct ReduceLayer; +struct ReduceLayerBuilder; + +struct ReduceDescriptor; +struct ReduceDescriptorBuilder; + struct AnyLayer; struct AnyLayerBuilder; @@ -532,6 +539,42 @@ inline const char *EnumNameDataLayout(DataLayout e) { return EnumNamesDataLayout()[index]; } +enum ReduceOperation { + ReduceOperation_Sum = 0, + ReduceOperation_Max = 1, + ReduceOperation_Mean = 2, + ReduceOperation_Min = 3, + ReduceOperation_MIN = ReduceOperation_Sum, + ReduceOperation_MAX = ReduceOperation_Min +}; + +inline const ReduceOperation (&EnumValuesReduceOperation())[4] { + static const ReduceOperation values[] = { + ReduceOperation_Sum, + ReduceOperation_Max, + ReduceOperation_Mean, + ReduceOperation_Min + }; + return values; +} + +inline const char * const *EnumNamesReduceOperation() { + static const char * const names[5] = { + "Sum", + "Max", + "Mean", + "Min", + nullptr + }; + return names; +} + +inline const char *EnumNameReduceOperation(ReduceOperation e) { + if (flatbuffers::IsOutRange(e, ReduceOperation_Sum, ReduceOperation_Min)) return ""; + const size_t index = static_cast(e); + return EnumNamesReduceOperation()[index]; +} + enum ResizeMethod { ResizeMethod_NearestNeighbor = 0, ResizeMethod_Bilinear = 1, @@ -685,11 +728,12 @@ enum LayerType { LayerType_Fill = 57, LayerType_Rank = 58, LayerType_LogicalBinary = 59, + LayerType_Reduce = 60, LayerType_MIN = LayerType_Addition, - LayerType_MAX = LayerType_LogicalBinary + LayerType_MAX = LayerType_Reduce }; -inline const LayerType (&EnumValuesLayerType())[60] { +inline const LayerType (&EnumValuesLayerType())[61] { static const LayerType values[] = { LayerType_Addition, LayerType_Input, @@ -750,13 +794,14 @@ inline const LayerType (&EnumValuesLayerType())[60] { LayerType_QLstm, LayerType_Fill, LayerType_Rank, - LayerType_LogicalBinary + LayerType_LogicalBinary, + LayerType_Reduce }; return values; } inline const char * const *EnumNamesLayerType() { - static const char * const names[61] = { + static const char * const names[62] = { "Addition", "Input", "Multiplication", @@ -817,13 +862,14 @@ inline const char * const *EnumNamesLayerType() { "Fill", "Rank", "LogicalBinary", + "Reduce", nullptr }; return names; } inline const char *EnumNameLayerType(LayerType e) { - if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_LogicalBinary)) return ""; + if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_Reduce)) return ""; const size_t index = static_cast(e); return EnumNamesLayerType()[index]; } @@ -1157,11 +1203,12 @@ enum Layer { Layer_FillLayer = 58, Layer_RankLayer = 59, Layer_LogicalBinaryLayer = 60, + Layer_ReduceLayer = 61, Layer_MIN = Layer_NONE, - Layer_MAX = Layer_LogicalBinaryLayer + Layer_MAX = Layer_ReduceLayer }; -inline const Layer (&EnumValuesLayer())[61] { +inline const Layer (&EnumValuesLayer())[62] { static const Layer values[] = { Layer_NONE, Layer_ActivationLayer, @@ -1223,13 +1270,14 @@ inline const Layer (&EnumValuesLayer())[61] { Layer_QLstmLayer, Layer_FillLayer, Layer_RankLayer, - Layer_LogicalBinaryLayer + Layer_LogicalBinaryLayer, + Layer_ReduceLayer }; return values; } inline const char * const *EnumNamesLayer() { - static const char * const names[62] = { + static const char * const names[63] = { "NONE", "ActivationLayer", "AdditionLayer", @@ -1291,13 +1339,14 @@ inline const char * const *EnumNamesLayer() { "FillLayer", "RankLayer", "LogicalBinaryLayer", + "ReduceLayer", nullptr }; return names; } inline const char *EnumNameLayer(Layer e) { - if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_LogicalBinaryLayer)) return ""; + if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_ReduceLayer)) return ""; const size_t index = static_cast(e); return EnumNamesLayer()[index]; } @@ -1546,6 +1595,10 @@ template<> struct LayerTraits { static const Layer enum_value = Layer_LogicalBinaryLayer; }; +template<> struct LayerTraits { + static const Layer enum_value = Layer_ReduceLayer; +}; + bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer type); bool VerifyLayerVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types); @@ -9097,6 +9150,160 @@ inline flatbuffers::Offset CreateRankLayer( return builder_.Finish(); } +struct ReduceLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef ReduceLayerBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BASE = 4, + VT_DESCRIPTOR = 6 + }; + const armnnSerializer::LayerBase *base() const { + return GetPointer(VT_BASE); + } + const armnnSerializer::ReduceDescriptor *descriptor() const { + return GetPointer(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 ReduceLayerBuilder { + typedef ReduceLayer Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_base(flatbuffers::Offset base) { + fbb_.AddOffset(ReduceLayer::VT_BASE, base); + } + void add_descriptor(flatbuffers::Offset descriptor) { + fbb_.AddOffset(ReduceLayer::VT_DESCRIPTOR, descriptor); + } + explicit ReduceLayerBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ReduceLayerBuilder &operator=(const ReduceLayerBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateReduceLayer( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset base = 0, + flatbuffers::Offset descriptor = 0) { + ReduceLayerBuilder builder_(_fbb); + builder_.add_descriptor(descriptor); + builder_.add_base(base); + return builder_.Finish(); +} + +struct ReduceDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef ReduceDescriptorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TARGETHEIGHT = 4, + VT_TARGETWIDTH = 6, + VT_KEEPDIMS = 8, + VT_AXIS = 10, + VT_REDUCEOPERATION = 12 + }; + uint32_t targetHeight() const { + return GetField(VT_TARGETHEIGHT, 0); + } + uint32_t targetWidth() const { + return GetField(VT_TARGETWIDTH, 0); + } + bool keepDims() const { + return GetField(VT_KEEPDIMS, 0) != 0; + } + const flatbuffers::Vector *axis() const { + return GetPointer *>(VT_AXIS); + } + armnnSerializer::ReduceOperation reduceOperation() const { + return static_cast(GetField(VT_REDUCEOPERATION, 0)); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_TARGETHEIGHT) && + VerifyField(verifier, VT_TARGETWIDTH) && + VerifyField(verifier, VT_KEEPDIMS) && + VerifyOffset(verifier, VT_AXIS) && + verifier.VerifyVector(axis()) && + VerifyField(verifier, VT_REDUCEOPERATION) && + verifier.EndTable(); + } +}; + +struct ReduceDescriptorBuilder { + typedef ReduceDescriptor Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_targetHeight(uint32_t targetHeight) { + fbb_.AddElement(ReduceDescriptor::VT_TARGETHEIGHT, targetHeight, 0); + } + void add_targetWidth(uint32_t targetWidth) { + fbb_.AddElement(ReduceDescriptor::VT_TARGETWIDTH, targetWidth, 0); + } + void add_keepDims(bool keepDims) { + fbb_.AddElement(ReduceDescriptor::VT_KEEPDIMS, static_cast(keepDims), 0); + } + void add_axis(flatbuffers::Offset> axis) { + fbb_.AddOffset(ReduceDescriptor::VT_AXIS, axis); + } + void add_reduceOperation(armnnSerializer::ReduceOperation reduceOperation) { + fbb_.AddElement(ReduceDescriptor::VT_REDUCEOPERATION, static_cast(reduceOperation), 0); + } + explicit ReduceDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + ReduceDescriptorBuilder &operator=(const ReduceDescriptorBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateReduceDescriptor( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t targetHeight = 0, + uint32_t targetWidth = 0, + bool keepDims = false, + flatbuffers::Offset> axis = 0, + armnnSerializer::ReduceOperation reduceOperation = armnnSerializer::ReduceOperation_Sum) { + ReduceDescriptorBuilder builder_(_fbb); + builder_.add_axis(axis); + builder_.add_targetWidth(targetWidth); + builder_.add_targetHeight(targetHeight); + builder_.add_reduceOperation(reduceOperation); + builder_.add_keepDims(keepDims); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateReduceDescriptorDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t targetHeight = 0, + uint32_t targetWidth = 0, + bool keepDims = false, + const std::vector *axis = nullptr, + armnnSerializer::ReduceOperation reduceOperation = armnnSerializer::ReduceOperation_Sum) { + auto axis__ = axis ? _fbb.CreateVector(*axis) : 0; + return armnnSerializer::CreateReduceDescriptor( + _fbb, + targetHeight, + targetWidth, + keepDims, + axis__, + reduceOperation); +} + struct AnyLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef AnyLayerBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -9290,6 +9497,9 @@ struct AnyLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const armnnSerializer::LogicalBinaryLayer *layer_as_LogicalBinaryLayer() const { return layer_type() == armnnSerializer::Layer_LogicalBinaryLayer ? static_cast(layer()) : nullptr; } + const armnnSerializer::ReduceLayer *layer_as_ReduceLayer() const { + return layer_type() == armnnSerializer::Layer_ReduceLayer ? static_cast(layer()) : nullptr; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_LAYER_TYPE) && @@ -9539,6 +9749,10 @@ template<> inline const armnnSerializer::LogicalBinaryLayer *AnyLayer::layer_as< return layer_as_LogicalBinaryLayer(); } +template<> inline const armnnSerializer::ReduceLayer *AnyLayer::layer_as() const { + return layer_as_ReduceLayer(); +} + struct AnyLayerBuilder { typedef AnyLayer Table; flatbuffers::FlatBufferBuilder &fbb_; @@ -9989,6 +10203,10 @@ inline bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } + case Layer_ReduceLayer: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp index a0c99b9cca..a2217a3dc4 100644 --- a/src/armnnSerializer/Serializer.cpp +++ b/src/armnnSerializer/Serializer.cpp @@ -904,6 +904,25 @@ void SerializerVisitor::VisitRankLayer(const armnn::IConnectableLayer* layer, CreateAnyLayer(flatBufferRankLayer.o, serializer::Layer::Layer_RankLayer); } + +void SerializerVisitor::VisitReduceLayer(const armnn::IConnectableLayer* layer, + const armnn::ReduceDescriptor& reduceDescriptor, + const char*) +{ + auto fbReduceBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Reduce); + auto fbDescriptor = CreateReduceDescriptor(m_flatBufferBuilder, + reduceDescriptor.m_TargetHeight, + reduceDescriptor.m_TargetWidth, + reduceDescriptor.m_KeepDims, + m_flatBufferBuilder.CreateVector(reduceDescriptor.m_vAxis), + GetFlatBufferReduceOperation(reduceDescriptor.m_ReduceOperation)); + auto fbReduceLayer = serializer::CreateReduceLayer(m_flatBufferBuilder, + fbReduceBaseLayer, + fbDescriptor); + + CreateAnyLayer(fbReduceLayer.o, serializer::Layer::Layer_ReduceLayer); +} + // 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 f28be09036..10971fddc8 100644 --- a/src/armnnSerializer/Serializer.hpp +++ b/src/armnnSerializer/Serializer.hpp @@ -226,6 +226,10 @@ public: void VisitRankLayer(const armnn::IConnectableLayer* layer, const char* name = nullptr) override; + void VisitReduceLayer(const armnn::IConnectableLayer* layer, + const armnn::ReduceDescriptor& reduceDescriptor, + const char* name = nullptr) override; + void VisitReshapeLayer(const armnn::IConnectableLayer* layer, const armnn::ReshapeDescriptor& reshapeDescriptor, const char* name = nullptr) override; diff --git a/src/armnnSerializer/SerializerUtils.cpp b/src/armnnSerializer/SerializerUtils.cpp index 045d6aac5c..32ac75e024 100644 --- a/src/armnnSerializer/SerializerUtils.cpp +++ b/src/armnnSerializer/SerializerUtils.cpp @@ -198,4 +198,21 @@ armnnSerializer::ResizeMethod GetFlatBufferResizeMethod(armnn::ResizeMethod meth } } +armnnSerializer::ReduceOperation GetFlatBufferReduceOperation(armnn::ReduceOperation reduceOperation) +{ + switch (reduceOperation) + { + case armnn::ReduceOperation::Sum: + return armnnSerializer::ReduceOperation::ReduceOperation_Sum; + case armnn::ReduceOperation::Max: + return armnnSerializer::ReduceOperation::ReduceOperation_Max; + case armnn::ReduceOperation::Mean: + return armnnSerializer::ReduceOperation::ReduceOperation_Mean; + case armnn::ReduceOperation::Min: + return armnnSerializer::ReduceOperation::ReduceOperation_Min; + default: + return armnnSerializer::ReduceOperation::ReduceOperation_Sum; + } +} + } // namespace armnnSerializer diff --git a/src/armnnSerializer/SerializerUtils.hpp b/src/armnnSerializer/SerializerUtils.hpp index a3cf5ba3c1..55179864e8 100644 --- a/src/armnnSerializer/SerializerUtils.hpp +++ b/src/armnnSerializer/SerializerUtils.hpp @@ -38,4 +38,6 @@ armnnSerializer::ResizeMethod GetFlatBufferResizeMethod(armnn::ResizeMethod meth armnnSerializer::LogicalBinaryOperation GetFlatBufferLogicalBinaryOperation( armnn::LogicalBinaryOperation logicalBinaryOperation); +armnnSerializer::ReduceOperation GetFlatBufferReduceOperation(armnn::ReduceOperation reduceOperation); + } // namespace armnnSerializer diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp index 11177f5d04..44e8a3898e 100644 --- a/src/armnnSerializer/test/SerializerTests.cpp +++ b/src/armnnSerializer/test/SerializerTests.cpp @@ -2297,6 +2297,36 @@ BOOST_AUTO_TEST_CASE(SerializeRank) deserializedNetwork->Accept(verifier); } +BOOST_AUTO_TEST_CASE(SerializeReduceSum) +{ + DECLARE_LAYER_VERIFIER_CLASS_WITH_DESCRIPTOR(Reduce) + + const std::string layerName("Reduce_Sum"); + const armnn::TensorInfo inputInfo({1, 1, 3, 2}, armnn::DataType::Float32); + const armnn::TensorInfo outputInfo({1, 1, 1, 2}, armnn::DataType::Float32); + + armnn::ReduceDescriptor descriptor; + descriptor.m_vAxis = { 2 }; + descriptor.m_ReduceOperation = armnn::ReduceOperation::Sum; + + armnn::INetworkPtr network = armnn::INetwork::Create(); + armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0); + armnn::IConnectableLayer* const reduceSumLayer = network->AddReduceLayer(descriptor, layerName.c_str()); + armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0); + + inputLayer->GetOutputSlot(0).Connect(reduceSumLayer->GetInputSlot(0)); + reduceSumLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo); + reduceSumLayer->GetOutputSlot(0).SetTensorInfo(outputInfo); + + armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network)); + BOOST_CHECK(deserializedNetwork); + + ReduceLayerVerifier verifier(layerName, {inputInfo}, {outputInfo}, descriptor); + deserializedNetwork->Accept(verifier); +} + BOOST_AUTO_TEST_CASE(SerializeReshape) { DECLARE_LAYER_VERIFIER_CLASS_WITH_DESCRIPTOR(Reshape) diff --git a/src/armnnTfLiteParser/TfLiteParser.cpp b/src/armnnTfLiteParser/TfLiteParser.cpp index 1a1e854395..db60224999 100644 --- a/src/armnnTfLiteParser/TfLiteParser.cpp +++ b/src/armnnTfLiteParser/TfLiteParser.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -580,6 +581,7 @@ TfLiteParser::TfLiteParser(const Optional& o m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze; m_ParserFunctions[tflite::BuiltinOperator_STRIDED_SLICE] = &TfLiteParser::ParseStridedSlice; m_ParserFunctions[tflite::BuiltinOperator_SUB] = &TfLiteParser::ParseSub; + m_ParserFunctions[tflite::BuiltinOperator_SUM] = &TfLiteParser::ParseSum; m_ParserFunctions[tflite::BuiltinOperator_TANH] = &TfLiteParser::ParseTanH; m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE] = &TfLiteParser::ParseTranspose; m_ParserFunctions[tflite::BuiltinOperator_TRANSPOSE_CONV] = &TfLiteParser::ParseTransposeConv; @@ -2994,6 +2996,58 @@ void TfLiteParser::ParseDepthToSpace(size_t subgraphIndex, size_t operatorIndex) RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]}); } +void TfLiteParser::ParseSum(size_t subgraphIndex, size_t operatorIndex) +{ + CHECK_MODEL(m_Model, subgraphIndex, operatorIndex); + + const auto &operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex]; + const auto *options = operatorPtr->builtin_options.AsReducerOptions(); + + auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex); + CHECK_VALID_SIZE(inputs.size(), 2); + + auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex); + CHECK_VALID_SIZE(outputs.size(), 1); + + auto layerName = fmt::format("Sum:{}:{}", subgraphIndex, operatorIndex); + + armnn::TensorInfo inputTensorInfo0 = ToTensorInfo(inputs[0]); + armnn::TensorInfo inputTensorInfo1 = ToTensorInfo(inputs[1]); + TensorShape input0Shape = inputTensorInfo0.GetShape(); + + ReduceDescriptor desc; + + BufferRawPtr axisBufferPtr = GetBuffer(m_Model, inputs[1]->buffer); + // Get const axis value from model and set it to descriptor. + if (axisBufferPtr != nullptr) + { + for (uint32_t i = 0; i < inputTensorInfo1.GetNumElements(); ++i) + { + desc.m_vAxis.push_back(armnnUtils::GetUnsignedAxis(inputTensorInfo0.GetNumDimensions(), + axisBufferPtr->data.data()[i])); + } + } + + desc.m_TargetHeight = input0Shape[1]; + desc.m_TargetWidth = input0Shape[2]; + desc.m_KeepDims = options->keep_dims; + desc.m_ReduceOperation = armnn::ReduceOperation::Sum; + + // Register a new layer object, Sum. + IConnectableLayer *layer = m_Network->AddReduceLayer(desc, layerName.c_str()); + + armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]); + layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + + // Register input tensor to the layer. + auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex)); + RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]}); + + // Register output tensor to the layer. + auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex)); + RegisterOutputSlots(subgraphIndex, operatorIndex, layer, outputTensorIndexes); +} + armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer, unsigned int outputSlot, tflite::ActivationFunctionType activationType) diff --git a/src/armnnTfLiteParser/TfLiteParser.hpp b/src/armnnTfLiteParser/TfLiteParser.hpp index 418180fd25..5f180603f0 100644 --- a/src/armnnTfLiteParser/TfLiteParser.hpp +++ b/src/armnnTfLiteParser/TfLiteParser.hpp @@ -135,6 +135,7 @@ private: void ParseSqueeze(size_t subgraphIndex, size_t operatorIndex); void ParseStridedSlice(size_t subgraphIndex, size_t operatorIndex); void ParseSub(size_t subgraphIndex, size_t operatorIndex); + void ParseSum(size_t subgraphIndex, size_t operatorIndex); void ParseDiv(size_t subgraphIndex, size_t operatorIndex); void ParseTanH(size_t subgraphIndex, size_t operatorIndex); void ParseTranspose(size_t subgraphIndex, size_t operatorIndex); diff --git a/src/armnnTfLiteParser/test/Sum.cpp b/src/armnnTfLiteParser/test/Sum.cpp new file mode 100644 index 0000000000..22b19ae058 --- /dev/null +++ b/src/armnnTfLiteParser/test/Sum.cpp @@ -0,0 +1,110 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include +#include "ParserFlatbuffersFixture.hpp" +#include "../TfLiteParser.hpp" + +#include +#include + +BOOST_AUTO_TEST_SUITE(TensorflowLiteParser) + +struct SumFixture : public ParserFlatbuffersFixture +{ + explicit SumFixture(const std::string& inputShape, + const std::string& outputShape, + const std::string& axisShape, + const std::string& axisData) + { + m_JsonString = R"( + { + "version": 3, + "operator_codes": [ { "builtin_code": "SUM" } ], + "subgraphs": [ { + "tensors": [ + { + "shape": )" + inputShape + R"(, + "type": "FLOAT32", + "buffer": 0, + "name": "inputTensor", + "quantization": { + "min": [ 0.0 ], + "max": [ 255.0 ], + "scale": [ 1.0 ], + "zero_point": [ 0 ], + } + }, + { + "shape": )" + outputShape + R"( , + "type": "FLOAT32", + "buffer": 1, + "name": "outputTensor", + "quantization": { + "min": [ 0.0 ], + "max": [ 255.0 ], + "scale": [ 1.0 ], + "zero_point": [ 0 ], + } + }, + { + "shape": )" + axisShape + R"( , + "type": "INT32", + "buffer": 2, + "name": "axis", + "quantization": { + "min": [ 0.0 ], + "max": [ 255.0 ], + "scale": [ 1.0 ], + "zero_point": [ 0 ], + } + } + ], + "inputs": [ 0 ], + "outputs": [ 1 ], + "operators": [ + { + "opcode_index": 0, + "inputs": [ 0 , 2 ], + "outputs": [ 1 ], + "builtin_options_type": "ReducerOptions", + "builtin_options": { + "keep_dims": true, + }, + "custom_options_format": "FLEXBUFFERS" + } + ], + } ], + "buffers" : [ + { }, + { }, + { "data": )" + axisData + R"(, }, + ] + } + )"; + SetupSingleInputSingleOutput("inputTensor", "outputTensor"); + } +}; + +struct SimpleSumFixture : public SumFixture +{ + SimpleSumFixture() : SumFixture("[ 1, 3, 2, 4 ]", "[ 1, 1, 1, 4 ]", "[ 2 ]", "[ 1, 2 ]") {} +}; + +BOOST_FIXTURE_TEST_CASE(ParseSum, SimpleSumFixture) +{ + RunTest<4, armnn::DataType::Float32, armnn::DataType::Float32> + (0, {{ "inputTensor", { 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + + 10.0f, 20.0f, 30.0f, 40.0f, + 50.0f, 60.0f, 70.0f, 80.0f, + + 100.0f, 200.0f, 300.0f, 400.0f, + 500.0f, 600.0f, 700.0f, 800.0f } } }, + {{ "outputTensor", { 666.0f, 888.0f, 1110.0f, 1332.0f } } }); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/backends/backendsCommon/LayerSupportBase.cpp b/src/backends/backendsCommon/LayerSupportBase.cpp index 543591091b..77067d9c6c 100644 --- a/src/backends/backendsCommon/LayerSupportBase.cpp +++ b/src/backends/backendsCommon/LayerSupportBase.cpp @@ -512,6 +512,14 @@ bool LayerSupportBase::IsRankSupported(const TensorInfo&, // input return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported); } +bool LayerSupportBase::IsReduceSupported(const TensorInfo& /*input*/, + const TensorInfo& /*output*/, + const ReduceDescriptor& /*descriptor*/, + Optional reasonIfUnsupported) const +{ + return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported); +} + bool LayerSupportBase::IsReshapeSupported(const TensorInfo&, // input const TensorInfo&, // output const ReshapeDescriptor&, // descriptor diff --git a/src/backends/backendsCommon/LayerSupportBase.hpp b/src/backends/backendsCommon/LayerSupportBase.hpp index 7b873e3d6c..e04d657716 100644 --- a/src/backends/backendsCommon/LayerSupportBase.hpp +++ b/src/backends/backendsCommon/LayerSupportBase.hpp @@ -315,6 +315,11 @@ public: const TensorInfo& output, Optional reasonIfUnsupported) const override; + bool IsReduceSupported(const TensorInfo& input, + const TensorInfo& output, + const ReduceDescriptor& descriptor, + Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsReshapeSupported(const TensorInfo& input, const TensorInfo& output, const ReshapeDescriptor& descriptor, diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp index d795e32e4b..b51099ff79 100644 --- a/src/backends/backendsCommon/WorkloadData.cpp +++ b/src/backends/backendsCommon/WorkloadData.cpp @@ -3633,4 +3633,31 @@ void LogicalBinaryQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) co } } +void ReduceQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const +{ + const std::string descriptorName{"ReduceQueueDescriptor"}; + + ValidateNumInputs(workloadInfo, descriptorName, 1); + ValidateNumOutputs(workloadInfo, descriptorName, 1); + + const TensorInfo& inputTensorInfo = workloadInfo.m_InputTensorInfos[0]; + const TensorInfo& outputTensorInfo = workloadInfo.m_OutputTensorInfos[0]; + + ValidateTensorNumDimensions(inputTensorInfo, descriptorName, 4, "input"); + + std::vector supportedTypes = + { + DataType::BFloat16, + DataType::Float16, + DataType::Float32, + DataType::QAsymmS8, + DataType::QAsymmU8, + DataType::QSymmS16, + DataType::Signed32 + }; + + ValidateDataTypes(inputTensorInfo, supportedTypes, descriptorName); + ValidateTensorDataTypesMatch(inputTensorInfo, outputTensorInfo, descriptorName, "input", "output"); +} + } // namespace armnn diff --git a/src/backends/backendsCommon/WorkloadData.hpp b/src/backends/backendsCommon/WorkloadData.hpp index 0a232dc515..8a2dd1fe78 100644 --- a/src/backends/backendsCommon/WorkloadData.hpp +++ b/src/backends/backendsCommon/WorkloadData.hpp @@ -668,4 +668,9 @@ struct LogicalBinaryQueueDescriptor : QueueDescriptorWithParameters +{ + void Validate(const WorkloadInfo& workloadInfo) const; +}; + } // namespace armnn diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp index 3a8a2ae18f..19281a82e9 100644 --- a/src/backends/backendsCommon/WorkloadFactory.cpp +++ b/src/backends/backendsCommon/WorkloadFactory.cpp @@ -1220,6 +1220,18 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, break; } + case LayerType::Reduce: + { + auto cLayer = PolymorphicDowncast(&layer); + const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); + const TensorInfo& output = layer.GetOutputSlot(0).GetTensorInfo(); + + result = layerSupportObject->IsReduceSupported(OverrideDataType(input, dataType), + OverrideDataType(output, dataType), + cLayer->GetParameters(), + reason); + break; + } default: { ARMNN_ASSERT_MSG(false, "WorkloadFactory did not recognise type of layer."); @@ -1593,6 +1605,12 @@ std::unique_ptr IWorkloadFactory::CreateRank(const RankQueueDescripto return std::unique_ptr(); } +std::unique_ptr IWorkloadFactory::CreateReduce(const ReduceQueueDescriptor& /*descriptor*/, + const WorkloadInfo& /*info*/) const +{ + return std::unique_ptr(); +} + std::unique_ptr IWorkloadFactory::CreateReshape(const ReshapeQueueDescriptor& /*descriptor*/, const WorkloadInfo& /*info*/) const { diff --git a/src/backends/backendsCommon/WorkloadFactory.hpp b/src/backends/backendsCommon/WorkloadFactory.hpp index 2e813e9945..6ab6d2c8ac 100644 --- a/src/backends/backendsCommon/WorkloadFactory.hpp +++ b/src/backends/backendsCommon/WorkloadFactory.hpp @@ -231,6 +231,9 @@ public: virtual std::unique_ptr CreateRank(const RankQueueDescriptor& descriptor, const WorkloadInfo& info) const; + virtual std::unique_ptr CreateReduce(const ReduceQueueDescriptor& descriptor, + const WorkloadInfo& info) const; + virtual std::unique_ptr CreateReshape(const ReshapeQueueDescriptor& descriptor, const WorkloadInfo& info) const; diff --git a/src/backends/backendsCommon/common.mk b/src/backends/backendsCommon/common.mk index 7254d21f05..3b6299daf3 100644 --- a/src/backends/backendsCommon/common.mk +++ b/src/backends/backendsCommon/common.mk @@ -75,6 +75,7 @@ COMMON_TEST_SOURCES := \ test/layerTests/PadTestImpl.cpp \ test/layerTests/Pooling2dTestImpl.cpp \ test/layerTests/RankTestImpl.cpp \ + test/layerTests/ReduceSumTestImpl.cpp \ test/layerTests/ReshapeTestImpl.cpp \ test/layerTests/ResizeTestImpl.cpp \ test/layerTests/RsqrtTestImpl.cpp \ diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt index 7894895c39..b20ef2dd25 100644 --- a/src/backends/backendsCommon/test/CMakeLists.txt +++ b/src/backends/backendsCommon/test/CMakeLists.txt @@ -137,6 +137,8 @@ list(APPEND armnnBackendsCommonUnitTests_sources layerTests/QuantizeTestImpl.hpp layerTests/RankTestImpl.cpp layerTests/RankTestImpl.hpp + layerTests/ReduceSumTestImpl.cpp + layerTests/ReduceSumTestImpl.hpp layerTests/ReshapeTestImpl.cpp layerTests/ReshapeTestImpl.hpp layerTests/ResizeTestImpl.cpp diff --git a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp index 1492a8092f..c7d1dd2182 100644 --- a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp +++ b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp @@ -677,6 +677,8 @@ DECLARE_LAYER_POLICY_2_PARAM(StridedSlice) DECLARE_LAYER_POLICY_1_PARAM(Subtraction) +DECLARE_LAYER_POLICY_2_PARAM(Reduce) + DECLARE_LAYER_POLICY_1_PARAM(Switch) DECLARE_LAYER_POLICY_2_PARAM(Transpose) diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp index e9eb5b9553..d87a3b08ab 100644 --- a/src/backends/backendsCommon/test/LayerTests.hpp +++ b/src/backends/backendsCommon/test/LayerTests.hpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include diff --git a/src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.cpp new file mode 100644 index 0000000000..4edbd1108a --- /dev/null +++ b/src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.cpp @@ -0,0 +1,344 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "ReduceSumTestImpl.hpp" + +#include +#include +#include + +#include + +namespace +{ + +template> +LayerTestResult ReduceTestCommon( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + const armnn::TensorInfo inputTensorInfo, + const armnn::TensorInfo outputTensorInfo, + const std::vector& inputData, + const std::vector& outputData, + const std::vector vAxis, + const armnn::ReduceOperation reduceOperation) +{ + IgnoreUnused(memoryManager); + auto inputTensor = MakeTensor(inputTensorInfo, ConvertToDataType(inputData, inputTensorInfo)); + + LayerTestResult result(outputTensorInfo); + result.outputExpected = MakeTensor(outputTensorInfo, outputData); + + std::unique_ptr inputHandle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo); + std::unique_ptr outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo); + + armnn::ReduceQueueDescriptor descriptor; + std::vector updated_idx; + uint32_t resolvedAxis = 0; + for (uint32_t i = 0; i < vAxis.size(); ++i) + { + if (vAxis[i] < 0) + { + resolvedAxis = inputTensorInfo.GetNumDimensions() + static_cast(vAxis[i]); + } else + { + resolvedAxis = static_cast(vAxis[i]); + } + + updated_idx.push_back(resolvedAxis); + } + + descriptor.m_Parameters.m_vAxis = updated_idx; + descriptor.m_Parameters.m_ReduceOperation = reduceOperation; + armnn::WorkloadInfo info; + + AddInputToWorkload(descriptor, info, inputTensorInfo, inputHandle.get()); + AddOutputToWorkload(descriptor, info, outputTensorInfo, outputHandle.get()); + + std::unique_ptr workload = workloadFactory.CreateReduce(descriptor, info); + + inputHandle->Allocate(); + outputHandle->Allocate(); + + CopyDataToITensorHandle(inputHandle.get(), inputTensor.origin()); + + workload->Execute(); + + CopyDataFromITensorHandle(result.output.origin(), outputHandle.get()); + + return result; +} + +} // namespace + +template +LayerTestResult ReduceSumSimpleTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory) +{ + const armnn::TensorShape inputShape{ 1, 1, 1, 5 }; + const armnn::TensorShape outputShape{ 1, 1, 1, 1}; + + armnn::TensorInfo inputTensorInfo(inputShape, ArmnnType); + + if (armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(1.0f); + inputTensorInfo.SetQuantizationOffset(0); + } + + armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); + + std::vector inputValues({ 5.0f, 2.0f, 8.0f, 10.0f, 9.0f }); + std::vector outputValues({ 34.0f }); + + return ReduceTestCommon(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + inputValues, + outputValues, + { -1 }, + armnn::ReduceOperation::Sum); +} + +template +LayerTestResult ReduceSumSingleAxisTest1( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory) +{ + const armnn::TensorShape inputShape{ 1, 3, 2, 4 }; + const armnn::TensorShape outputShape{ 1, 1, 2, 4}; + + armnn::TensorInfo inputTensorInfo(inputShape, ArmnnType); + + if (armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(1.0f); + inputTensorInfo.SetQuantizationOffset(0); + } + + armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); + + std::vector inputValues({ 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + + 10.0f, 20.0f, 30.0f, 40.0f, + 50.0f, 60.0f, 70.0f, 80.0f, + + 100.0f, 200.0f, 300.0f, 400.0f, + 500.0f, 600.0f, 700.0f, 800.0f }); + std::vector outputValues({ 111.0f, 222.0f, 333.0f, 444.0f, + 555.0f, 666.0f, 777.0f, 888.0f }); + + return ReduceTestCommon(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + inputValues, + outputValues, + { 1 }, + armnn::ReduceOperation::Sum); +} + +template +LayerTestResult ReduceSumSingleAxisTest2( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory) +{ + const armnn::TensorShape inputShape{ 1, 6, 3, 4 }; + const armnn::TensorShape outputShape{ 1, 1, 3, 4}; + + armnn::TensorInfo inputTensorInfo(inputShape, ArmnnType); + + if (armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(1.0f); + inputTensorInfo.SetQuantizationOffset(0); + } + + armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); + + std::vector inputValues( {7, 8, 6, 1, + 1, 1, 8, 7, + 3, 7, 7, 7, + + 6, 8, 4, 7, + 3, 8, 7, 3, + 5, 8, 8, 8, + + + 7, 8, 2, 7, + 3, 8, 5, 6, + 8, 4, 2, 7, + + 1, 6, 7, 2, + 8, 3, 3, 1, + 7, 6, 2, 6, + + + 5, 3, 4, 8, + 7, 8, 2, 4, + 6, 6, 2, 8, + + 2, 2, 7, 2, + 5, 3, 6, 3, + 6, 1, 8, 8}); + std::vector outputValues({ 28.0f, 35.0f, 30.0f, 27.0f, + 27.0f, 31.0f, 31.0f, 24.0f, + 35.0f, 32.0f, 29.0f, 44.0f}); + + return ReduceTestCommon(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + inputValues, + outputValues, + { 1 }, + armnn::ReduceOperation::Sum); +} + +template +LayerTestResult ReduceSumSingleAxisTest3( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory) +{ + const armnn::TensorShape inputShape{ 1, 6, 3, 4 }; + const armnn::TensorShape outputShape{ 1, 6, 3, 1}; + + armnn::TensorInfo inputTensorInfo(inputShape, ArmnnType); + + if (armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(1.0f); + inputTensorInfo.SetQuantizationOffset(0); + } + + armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); + + std::vector inputValues( {7, 8, 6, 1, + 1, 1, 8, 7, + 3, 7, 7, 7, + + 6, 8, 4, 7, + 3, 8, 7, 3, + 5, 8, 8, 8, + + + 7, 8, 2, 7, + 3, 8, 5, 6, + 8, 4, 2, 7, + + 1, 6, 7, 2, + 8, 3, 3, 1, + 7, 6, 2, 6, + + + 5, 3, 4, 8, + 7, 8, 2, 4, + 6, 6, 2, 8, + + 2, 2, 7, 2, + 5, 3, 6, 3, + 6, 1, 8, 8}); + std::vector outputValues({ 22.0f, 17.0f, 24.0f, + 25.0f, 21.0f, 29.0f, + + 24.0f, 22.0f, 21.0f, + 16.0f, 15.0f, 21.0f, + + 20.0f, 21.0f, 22.0f, + 13.0f, 17.0f, 23.0f}); + + return ReduceTestCommon(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + inputValues, + outputValues, + { 3 }, + armnn::ReduceOperation::Sum); +} + +template +LayerTestResult ReduceSumMultipleAxisTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory) +{ + const armnn::TensorShape inputShape{ 1, 3, 2, 4 }; + const armnn::TensorShape outputShape{ 1, 1, 1, 4}; + + armnn::TensorInfo inputTensorInfo(inputShape, ArmnnType); + + if (armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(1.0f); + inputTensorInfo.SetQuantizationOffset(0); + } + + armnn::TensorInfo outputTensorInfo(outputShape, armnn::DataType::Float32); + + std::vector inputValues({ 1.0f, 2.0f, 3.0f, 4.0f, + 5.0f, 6.0f, 7.0f, 8.0f, + + 10.0f, 20.0f, 30.0f, 40.0f, + 50.0f, 60.0f, 70.0f, 80.0f, + + 100.0f, 200.0f, 300.0f, 400.0f, + 500.0f, 600.0f, 700.0f, 800.0f }); + std::vector outputValues({ 666.0f, 888.0f, 1110.0f, 1332.0f }); + + return ReduceTestCommon(workloadFactory, + memoryManager, + tensorHandleFactory, + inputTensorInfo, + outputTensorInfo, + inputValues, + outputValues, + { 1, 2 }, + armnn::ReduceOperation::Sum); +} + +// Explicit template specializations + +template LayerTestResult +ReduceSumSimpleTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template LayerTestResult +ReduceSumSingleAxisTest1( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template LayerTestResult +ReduceSumSingleAxisTest2( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template LayerTestResult +ReduceSumSingleAxisTest3( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template LayerTestResult +ReduceSumMultipleAxisTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); diff --git a/src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.hpp b/src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.hpp new file mode 100644 index 0000000000..db23240958 --- /dev/null +++ b/src/backends/backendsCommon/test/layerTests/ReduceSumTestImpl.hpp @@ -0,0 +1,43 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "LayerTestResult.hpp" + +#include + +#include +#include + +template> +LayerTestResult ReduceSumSimpleTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template> +LayerTestResult ReduceSumSingleAxisTest1( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template> +LayerTestResult ReduceSumSingleAxisTest2( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template> +LayerTestResult ReduceSumSingleAxisTest3( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); + +template> +LayerTestResult ReduceSumMultipleAxisTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory); diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp index bdaaafb0af..992ae71f97 100644 --- a/src/backends/reference/RefLayerSupport.cpp +++ b/src/backends/reference/RefLayerSupport.cpp @@ -1706,6 +1706,36 @@ bool RefLayerSupport::IsRankSupported(const TensorInfo& input, "Reference rank: input type not supported."); } +bool RefLayerSupport::IsReduceSupported(const TensorInfo& input, + const TensorInfo& output, + const ReduceDescriptor& descriptor, + Optional reasonIfUnsupported) const +{ + IgnoreUnused(descriptor); + bool supported = true; + std::array supportedTypes = + { + DataType::BFloat16, + DataType::Float32, + DataType::Float16, + DataType::QAsymmS8, + DataType::QAsymmU8, + DataType::QSymmS16, + DataType::Signed32 + }; + + supported &= CheckSupportRule(TypeAnyOf(input, supportedTypes), reasonIfUnsupported, + "Reference Reduce: input type not supported"); + + supported &= CheckSupportRule(TypeAnyOf(output, supportedTypes), reasonIfUnsupported, + "Reference Reduce: output type not supported"); + + supported &= CheckSupportRule(TypesAreEqual(input, output), reasonIfUnsupported, + "Reference Reduce: input and output types not matching"); + + return supported; +} + bool RefLayerSupport::IsReshapeSupported(const TensorInfo& input, const TensorInfo& output, const ReshapeDescriptor& descriptor, diff --git a/src/backends/reference/RefLayerSupport.hpp b/src/backends/reference/RefLayerSupport.hpp index 6b6440833e..b75b778f7a 100644 --- a/src/backends/reference/RefLayerSupport.hpp +++ b/src/backends/reference/RefLayerSupport.hpp @@ -275,6 +275,11 @@ public: const TensorInfo& output, Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsReduceSupported(const TensorInfo& input, + const TensorInfo& output, + const ReduceDescriptor& descriptor, + Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsReshapeSupported(const TensorInfo& input, const TensorInfo& output, const ReshapeDescriptor& descriptor, diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp index 468aeb3877..fde6c863c3 100644 --- a/src/backends/reference/RefWorkloadFactory.cpp +++ b/src/backends/reference/RefWorkloadFactory.cpp @@ -580,6 +580,12 @@ std::unique_ptr RefWorkloadFactory::CreateRank(const RankQueueDescrip return std::make_unique(descriptor, info); } +std::unique_ptr RefWorkloadFactory::CreateReduce(const ReduceQueueDescriptor& descriptor, + const WorkloadInfo& info) const +{ + return std::make_unique(descriptor, info); +} + std::unique_ptr RefWorkloadFactory::CreateReshape(const ReshapeQueueDescriptor& descriptor, const WorkloadInfo& info) const { diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp index 41cefd34ce..c22d87fa43 100644 --- a/src/backends/reference/RefWorkloadFactory.hpp +++ b/src/backends/reference/RefWorkloadFactory.hpp @@ -223,6 +223,9 @@ public: std::unique_ptr CreateRank(const RankQueueDescriptor& descriptor, const WorkloadInfo& info) const override; + std::unique_ptr CreateReduce(const ReduceQueueDescriptor& descriptor, + const WorkloadInfo& info) const override; + std::unique_ptr CreateReshape(const ReshapeQueueDescriptor& descriptor, const WorkloadInfo& info) const override; diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk index b4aa3a0953..96765097e4 100644 --- a/src/backends/reference/backend.mk +++ b/src/backends/reference/backend.mk @@ -38,11 +38,11 @@ BACKEND_SOURCES := \ workloads/InstanceNorm.cpp \ workloads/LogSoftmax.cpp \ workloads/LstmUtils.cpp \ - workloads/Mean.cpp \ workloads/Concatenate.cpp \ workloads/Pad.cpp \ workloads/Pooling2d.cpp \ workloads/PreluImpl.cpp \ + workloads/Reduce.cpp \ workloads/RefActivationWorkload.cpp \ workloads/RefArgMinMaxWorkload.cpp \ workloads/RefBatchNormalizationWorkload.cpp \ @@ -81,6 +81,7 @@ BACKEND_SOURCES := \ workloads/RefPreluWorkload.cpp \ workloads/RefQLstmWorkload.cpp \ workloads/RefQuantizeWorkload.cpp \ + workloads/RefReduceWorkload.cpp \ workloads/RefReshapeWorkload.cpp \ workloads/RefResizeBilinearWorkload.cpp \ workloads/RefResizeWorkload.cpp \ diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp index 502e0cb84d..d5e0f8290b 100644 --- a/src/backends/reference/test/RefLayerTests.cpp +++ b/src/backends/reference/test/RefLayerTests.cpp @@ -2234,4 +2234,11 @@ ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOrBroadcast2, LogicalOrBroadcast2Test) ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalAndBroadcast3, LogicalAndBroadcast3Test) ARMNN_AUTO_TEST_CASE_WITH_THF(LogicalOrBroadcast3, LogicalOrBroadcast3Test) +// ReduceSum +ARMNN_AUTO_TEST_CASE_WITH_THF(ReduceSumFloat32, ReduceSumSimpleTest) +ARMNN_AUTO_TEST_CASE_WITH_THF(ReduceSumSingleAxisFloat32_1, ReduceSumSingleAxisTest1) +ARMNN_AUTO_TEST_CASE_WITH_THF(ReduceSumSingleAxisFloat32_2, ReduceSumSingleAxisTest2) +ARMNN_AUTO_TEST_CASE_WITH_THF(ReduceSumSingleAxisFloat32_3, ReduceSumSingleAxisTest3) +ARMNN_AUTO_TEST_CASE_WITH_THF(ReduceSumMultipleAxisFloat32, ReduceSumMultipleAxisTest) + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt index 1b20e5bf2d..1f4298be5d 100644 --- a/src/backends/reference/workloads/CMakeLists.txt +++ b/src/backends/reference/workloads/CMakeLists.txt @@ -44,8 +44,6 @@ list(APPEND armnnRefBackendWorkloads_sources LstmUtils.hpp LstmUtils.cpp Maximum.hpp - Mean.cpp - Mean.hpp Concatenate.hpp Concatenate.cpp Minimum.hpp @@ -55,6 +53,8 @@ list(APPEND armnnRefBackendWorkloads_sources Pooling2d.hpp PreluImpl.cpp PreluImpl.hpp + Reduce.cpp + Reduce.hpp RefActivationWorkload.cpp RefActivationWorkload.hpp RefArgMinMaxWorkload.cpp @@ -132,6 +132,8 @@ list(APPEND armnnRefBackendWorkloads_sources RefQLstmWorkload.cpp RefQLstmWorkload.hpp RefRankWorkload.hpp + RefReduceWorkload.cpp + RefReduceWorkload.hpp RefReshapeWorkload.cpp RefReshapeWorkload.hpp RefResizeBilinearWorkload.cpp diff --git a/src/backends/reference/workloads/Mean.cpp b/src/backends/reference/workloads/Mean.cpp deleted file mode 100644 index fe34efe0c7..0000000000 --- a/src/backends/reference/workloads/Mean.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "Mean.hpp" -#include - -#include - -#include -#include -#include -#include - -namespace armnn -{ -bool NextIndex(const unsigned int numDims, const armnn::TensorShape& dims, std::vector& current) -{ - unsigned int carry = 1; - - for (unsigned int idx = numDims; idx-- > 0; ) - { - unsigned int current_val = current[idx] + carry; - if (dims[idx] == current_val) - { - current[idx] = 0; - } - else - { - current[idx] = current_val; - carry = 0; - break; - } - } - return (carry == 0); -} - -unsigned int ReducedOutputOffset(const unsigned int numDims, - const armnn::TensorShape& dims, - std::vector& index, - const unsigned int numAxis, - const std::vector& axis) -{ - unsigned int offset = 0; - for (unsigned int idx = 0; idx < numDims; ++idx) - { - bool isAxis = false; - if (!axis.empty()) - { - for (unsigned int axisIdx = 0; axisIdx < numAxis; ++axisIdx) - { - if (idx == axis[axisIdx]) - { - isAxis = true; - break; - } - } - } - if (!isAxis) - { - offset = offset * dims[idx] + index[idx]; - } - } - return offset; -} -} // namespace - -namespace armnn -{ -void Mean(const armnn::TensorInfo& inputInfo, - const armnn::TensorInfo& outputInfo, - const std::vector& axis, - Decoder& input, - Encoder& output) -{ - - unsigned int inputNumDims = inputInfo.GetNumDimensions(); - unsigned int outputNumDims = outputInfo.GetNumDimensions(); - - armnn::TensorShape outputDims = outputInfo.GetShape(); - armnn::TensorShape inputDims = inputInfo.GetShape(); - - // Initialise output data. - unsigned int numOutputs = 1; - for (unsigned int idx = 0; idx < outputNumDims; ++idx) - { - numOutputs *= outputDims[idx]; - } - - std::vector tempSum(numOutputs); - for (unsigned int idx = 0; idx < numOutputs; ++idx) - { - output[idx]; - output.Set(0.0f); - tempSum[idx] = 0.0f; - } - - // Initialise temp index. - std::vector tempIndex(inputNumDims); - for (unsigned int idx = 0; idx < inputNumDims; ++idx) - { - tempIndex[idx] = 0; - } - - std::vector resolvedAxis = axis; - if (resolvedAxis.empty()) - { - for (unsigned int idx = 0; idx < inputNumDims; ++idx) - { - resolvedAxis.push_back(idx); - } - } - auto numResolvedAxis = armnn::numeric_cast(resolvedAxis.size()); - - // Iterates through input_data and sum up the reduced axis. - for (bool hasNext = true; hasNext; hasNext = NextIndex(inputNumDims, inputDims, tempIndex)) - { - unsigned int inputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex, 0, {}); - unsigned int outputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex, - numResolvedAxis, resolvedAxis); - input[inputOffset]; - tempSum[outputOffset] += input.Get(); - } - - // Takes average by num of elements added to get mean. - size_t numElementsInAxis = 1; - for (unsigned int idx = 0; idx < numResolvedAxis; ++idx) - { - unsigned int current = inputDims[resolvedAxis[idx]]; - ARMNN_ASSERT(armnn::numeric_cast(current) < - (std::numeric_limits::max() / armnn::numeric_cast(numElementsInAxis))); - numElementsInAxis *= current; - } - if (numElementsInAxis > 0) { - for (unsigned int idx = 0; idx < numOutputs; ++idx) - { - output[idx]; - output.Set(tempSum[idx] / armnn::numeric_cast(numElementsInAxis)); - } - } -} -} //namespace armnn diff --git a/src/backends/reference/workloads/Mean.hpp b/src/backends/reference/workloads/Mean.hpp deleted file mode 100644 index dfb0302bf9..0000000000 --- a/src/backends/reference/workloads/Mean.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#pragma once - -#include "armnn/DescriptorsFwd.hpp" -#include "armnn/Tensor.hpp" -#include "BaseIterator.hpp" - -#include - -namespace armnn -{ -void Mean(const TensorInfo& inputInfo, - const TensorInfo& outputInfo, - const std::vector& axis, - Decoder& input, - Encoder& output); -} //namespace armnn - diff --git a/src/backends/reference/workloads/Reduce.cpp b/src/backends/reference/workloads/Reduce.cpp new file mode 100644 index 0000000000..5375c7163a --- /dev/null +++ b/src/backends/reference/workloads/Reduce.cpp @@ -0,0 +1,151 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "Reduce.hpp" + +#include + +#include + +#include +#include +#include +#include + +namespace armnn +{ + +bool NextIndex(const unsigned int numDims, const armnn::TensorShape& dims, std::vector& current) +{ + unsigned int carry = 1; + + for (unsigned int idx = numDims; idx-- > 0; ) + { + unsigned int current_val = current[idx] + carry; + if (dims[idx] == current_val) + { + current[idx] = 0; + } + else + { + current[idx] = current_val; + carry = 0; + break; + } + } + return (carry == 0); +} + +unsigned int ReducedOutputOffset(const unsigned int numDims, + const armnn::TensorShape& dims, + std::vector& index, + const unsigned int numAxis, + const std::vector& axis) +{ + unsigned int offset = 0; + for (unsigned int idx = 0; idx < numDims; ++idx) + { + bool isAxis = false; + if (!axis.empty()) + { + for (unsigned int axisIdx = 0; axisIdx < numAxis; ++axisIdx) + { + if (idx == axis[axisIdx]) + { + isAxis = true; + break; + } + } + } + if (!isAxis) + { + offset = offset * dims[idx] + index[idx]; + } + } + return offset; +} + + +void Reduce(const TensorInfo& inputInfo, + const TensorInfo& outputInfo, + Decoder& input, + Encoder& output, + const std::vector axis, + const ReduceOperation reduceOperation) +{ + unsigned int inputNumDims = inputInfo.GetNumDimensions(); + unsigned int outputNumDims = outputInfo.GetNumDimensions(); + + armnn::TensorShape outputDims = outputInfo.GetShape(); + armnn::TensorShape inputDims = inputInfo.GetShape(); + + // Initialise output data. + unsigned int numOutputs = 1; + for (unsigned int idx = 0; idx < outputNumDims; ++idx) + { + numOutputs *= outputDims[idx]; + } + + std::vector tempSum(numOutputs); + for (unsigned int idx = 0; idx < numOutputs; ++idx) + { + output[idx]; + output.Set(0.0f); + tempSum[idx] = 0.0f; + } + + // Initialise temp index. + std::vector tempIndex(inputNumDims); + for (unsigned int idx = 0; idx < inputNumDims; ++idx) + { + tempIndex[idx] = 0; + } + + std::vector resolvedAxis = axis; + if (resolvedAxis.empty()) + { + for (unsigned int idx = 0; idx < inputNumDims; ++idx) + { + resolvedAxis.push_back(idx); + } + } + auto numResolvedAxis = armnn::numeric_cast(resolvedAxis.size()); + + // Iterates through input_data and sum up the reduced axis. + for (bool hasNext = true; hasNext; hasNext = NextIndex(inputNumDims, inputDims, tempIndex)) + { + unsigned int inputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex, 0, {}); + unsigned int outputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex, + numResolvedAxis, resolvedAxis); + input[inputOffset]; + tempSum[outputOffset] += input.Get(); + } + + // Takes average by num of elements added to get mean. + size_t numElementsInAxis = 1; + for (unsigned int idx = 0; idx < numResolvedAxis; ++idx) + { + unsigned int current = inputDims[resolvedAxis[idx]]; + ARMNN_ASSERT(armnn::numeric_cast(current) < + (std::numeric_limits::max() / armnn::numeric_cast(numElementsInAxis))); + numElementsInAxis *= current; + } + if (numElementsInAxis > 0) { + for (unsigned int idx = 0; idx < numOutputs; ++idx) + { + output[idx]; + if (reduceOperation == ReduceOperation::Sum) + { + output.Set(tempSum[idx]); + } + else if (reduceOperation == ReduceOperation::Mean) + { + output.Set(tempSum[idx] / armnn::numeric_cast(numElementsInAxis)); + } + } + } +} + +} //namespace armnn \ No newline at end of file diff --git a/src/backends/reference/workloads/Reduce.hpp b/src/backends/reference/workloads/Reduce.hpp new file mode 100644 index 0000000000..ad777adcf5 --- /dev/null +++ b/src/backends/reference/workloads/Reduce.hpp @@ -0,0 +1,24 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "BaseIterator.hpp" +#include "Decoders.hpp" +#include "Encoders.hpp" + +#include + +namespace armnn +{ + +void Reduce(const TensorInfo& inputInfo, + const TensorInfo& outputInfo, + Decoder& input, + Encoder& output, + const std::vector axis, + const ReduceOperation reduceOperation); + +} //namespace armnn diff --git a/src/backends/reference/workloads/RefMeanWorkload.cpp b/src/backends/reference/workloads/RefMeanWorkload.cpp index 375ab395be..00e59bca4c 100644 --- a/src/backends/reference/workloads/RefMeanWorkload.cpp +++ b/src/backends/reference/workloads/RefMeanWorkload.cpp @@ -5,7 +5,7 @@ #include "RefMeanWorkload.hpp" -#include "Mean.hpp" +#include "Reduce.hpp" #include "RefWorkloadUtils.hpp" #include "Profiling.hpp" @@ -28,7 +28,12 @@ void RefMeanWorkload::Execute() const auto inputDecoder = MakeDecoder(inputInfo, m_Data.m_Inputs[0]->Map()); auto outputEncoder = MakeEncoder(outputInfo, m_Data.m_Outputs[0]->Map()); - Mean(inputInfo, outputInfo, m_Data.m_Parameters.m_Axis, *inputDecoder, *outputEncoder); + Reduce(inputInfo, + outputInfo, + *inputDecoder, + *outputEncoder, + m_Data.m_Parameters.m_Axis, + armnn::ReduceOperation::Mean); } } //namespace armnn diff --git a/src/backends/reference/workloads/RefReduceWorkload.cpp b/src/backends/reference/workloads/RefReduceWorkload.cpp new file mode 100644 index 0000000000..7a46ff9ffc --- /dev/null +++ b/src/backends/reference/workloads/RefReduceWorkload.cpp @@ -0,0 +1,42 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "RefReduceWorkload.hpp" + +#include "Reduce.hpp" +#include "RefWorkloadUtils.hpp" +#include "BaseIterator.hpp" +#include "Profiling.hpp" + +namespace armnn +{ + +RefReduceWorkload::RefReduceWorkload( + const ReduceQueueDescriptor& descriptor, + const WorkloadInfo& info) + : BaseWorkload(descriptor, info) {} + +void RefReduceWorkload::Execute() const +{ + ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefReduceWorkload_Execute"); + + const TensorInfo& inputInfo = GetTensorInfo(m_Data.m_Inputs[0]); + const TensorInfo& outputInfo = GetTensorInfo(m_Data.m_Outputs[0]); + + std::unique_ptr> decoderPtr = MakeDecoder(inputInfo, m_Data.m_Inputs[0]->Map()); + Decoder& decoder = *decoderPtr; + + std::unique_ptr> encoderPtr = MakeEncoder(outputInfo, m_Data.m_Outputs[0]->Map()); + Encoder& encoder = *encoderPtr; + + Reduce(inputInfo, + outputInfo, + decoder, + encoder, + m_Data.m_Parameters.m_vAxis, + m_Data.m_Parameters.m_ReduceOperation); +} + +} //namespace armnn diff --git a/src/backends/reference/workloads/RefReduceWorkload.hpp b/src/backends/reference/workloads/RefReduceWorkload.hpp new file mode 100644 index 0000000000..1d551acb4a --- /dev/null +++ b/src/backends/reference/workloads/RefReduceWorkload.hpp @@ -0,0 +1,23 @@ +// +// Copyright © 2020 Samsung Electronics Co Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +namespace armnn +{ + +class RefReduceWorkload : public BaseWorkload +{ +public: + explicit RefReduceWorkload(const ReduceQueueDescriptor& descriptor, + const WorkloadInfo& info); + + virtual void Execute() const override; +}; + +} //namespace armnn diff --git a/src/backends/reference/workloads/RefWorkloads.hpp b/src/backends/reference/workloads/RefWorkloads.hpp index 390b2a8d55..989644f633 100644 --- a/src/backends/reference/workloads/RefWorkloads.hpp +++ b/src/backends/reference/workloads/RefWorkloads.hpp @@ -54,6 +54,7 @@ #include "RefQLstmWorkload.hpp" #include "RefQuantizeWorkload.hpp" #include "RefRankWorkload.hpp" +#include "RefReduceWorkload.hpp" #include "RefReshapeWorkload.hpp" #include "RefResizeBilinearWorkload.hpp" #include "RefResizeWorkload.hpp" -- cgit v1.2.1