// // Copyright © 2021 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #include "Reduce.hpp" #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) { armnn::TensorShape inputDims = inputInfo.GetShape(); unsigned int inputNumDims = inputInfo.GetNumDimensions(); unsigned int numOutputs = outputInfo.GetNumElements(); // Initialise temp output std::vector tempOut(numOutputs); switch(reduceOperation) { case ReduceOperation::Mean: case ReduceOperation::Sum: std::fill(tempOut.begin(), tempOut.end(), 0.0f); break; case ReduceOperation::Prod: std::fill(tempOut.begin(), tempOut.end(), 1.0f); break; case ReduceOperation::Max: std::fill(tempOut.begin(), tempOut.end(), -1 * std::numeric_limits::max()); break; case ReduceOperation::Min: std::fill(tempOut.begin(), tempOut.end(), std::numeric_limits::max()); break; default: throw armnn::InvalidArgumentException("Unknown reduce method: " + std::to_string(static_cast(reduceOperation))); } // Initialise temp index std::vector tempIndex(inputNumDims, 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 operates over 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]; auto inputValue = input.Get(); switch(reduceOperation) { case ReduceOperation::Mean: case ReduceOperation::Sum: tempOut[outputOffset] += inputValue; break; case ReduceOperation::Prod: tempOut[outputOffset] *= inputValue; break; case ReduceOperation::Max: if (inputValue > tempOut[outputOffset]) { tempOut[outputOffset] = inputValue; } break; case ReduceOperation::Min: if (inputValue < tempOut[outputOffset]) { tempOut[outputOffset] = inputValue; } break; default: throw armnn::InvalidArgumentException("Unknown reduce method: " + std::to_string(static_cast(reduceOperation))); } } // 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; } for (unsigned int idx = 0; idx < numOutputs; ++idx) { output[idx]; if (reduceOperation == ReduceOperation::Mean) { if (numElementsInAxis > 0) { output.Set(tempOut[idx] / armnn::numeric_cast(numElementsInAxis)); } } else { output.Set(tempOut[idx]); } } } } //namespace armnn