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 --- src/backends/reference/workloads/Reduce.cpp | 151 ++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 src/backends/reference/workloads/Reduce.cpp (limited to 'src/backends/reference/workloads/Reduce.cpp') 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 -- cgit v1.2.1