ArmNN
 21.02
Reduce.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include "Reduce.hpp"
7 
9 
11 
12 #include <cmath>
13 #include <cstddef>
14 #include <functional>
15 #include <limits>
16 
17 namespace armnn
18 {
19 
20 bool NextIndex(const unsigned int numDims, const armnn::TensorShape& dims, std::vector<unsigned int>& current)
21 {
22  unsigned int carry = 1;
23 
24  for (unsigned int idx = numDims; idx-- > 0; )
25  {
26  unsigned int current_val = current[idx] + carry;
27  if (dims[idx] == current_val)
28  {
29  current[idx] = 0;
30  }
31  else
32  {
33  current[idx] = current_val;
34  carry = 0;
35  break;
36  }
37  }
38  return (carry == 0);
39 }
40 
41 unsigned int ReducedOutputOffset(const unsigned int numDims,
42  const armnn::TensorShape& dims,
43  std::vector<unsigned int>& index,
44  const unsigned int numAxis,
45  const std::vector<unsigned int>& axis)
46 {
47  unsigned int offset = 0;
48  for (unsigned int idx = 0; idx < numDims; ++idx)
49  {
50  bool isAxis = false;
51  if (!axis.empty())
52  {
53  for (unsigned int axisIdx = 0; axisIdx < numAxis; ++axisIdx)
54  {
55  if (idx == axis[axisIdx])
56  {
57  isAxis = true;
58  break;
59  }
60  }
61  }
62  if (!isAxis)
63  {
64  offset = offset * dims[idx] + index[idx];
65  }
66  }
67  return offset;
68 }
69 
70 
71 void Reduce(const TensorInfo& inputInfo,
72  const TensorInfo& outputInfo,
73  Decoder<float>& input,
74  Encoder<float>& output,
75  const std::vector<uint32_t> axis,
76  const ReduceOperation reduceOperation)
77 {
78  armnn::TensorShape inputDims = inputInfo.GetShape();
79  unsigned int inputNumDims = inputInfo.GetNumDimensions();
80  unsigned int numOutputs = outputInfo.GetNumElements();
81 
82  // Initialise temp output
83  std::vector<float> tempOut(numOutputs);
84  switch(reduceOperation)
85  {
88  std::fill(tempOut.begin(), tempOut.end(), 0.0);
89  break;
91  std::fill(tempOut.begin(), tempOut.end(), -1 * std::numeric_limits<float>::max());
92  break;
94  std::fill(tempOut.begin(), tempOut.end(), std::numeric_limits<float>::max());
95  break;
96  default:
97  throw armnn::InvalidArgumentException("Unknown reduce method: " +
98  std::to_string(static_cast<int>(reduceOperation)));
99  }
100 
101  // Initialise temp index
102  std::vector<unsigned int> tempIndex(inputNumDims, 0);
103 
104  std::vector<unsigned int> resolvedAxis = axis;
105  if (resolvedAxis.empty())
106  {
107  for (unsigned int idx = 0; idx < inputNumDims; ++idx)
108  {
109  resolvedAxis.push_back(idx);
110  }
111  }
112  auto numResolvedAxis = armnn::numeric_cast<unsigned int>(resolvedAxis.size());
113 
114  // Iterates through input_data and operates over the reduced axis
115  for (bool hasNext = true; hasNext; hasNext = NextIndex(inputNumDims, inputDims, tempIndex))
116  {
117  unsigned int inputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex, 0, {});
118  unsigned int outputOffset = ReducedOutputOffset(inputNumDims, inputDims, tempIndex,
119  numResolvedAxis, resolvedAxis);
120  input[inputOffset];
121  auto inputValue = input.Get();
122  if (reduceOperation == ReduceOperation::Max)
123  {
124  if (inputValue > tempOut[outputOffset])
125  {
126  tempOut[outputOffset] = inputValue;
127  }
128  }
129  else if (reduceOperation == ReduceOperation::Min)
130  {
131  if (inputValue < tempOut[outputOffset])
132  {
133  tempOut[outputOffset] = inputValue;
134  }
135  }
136  else
137  {
138  tempOut[outputOffset] += inputValue;
139  }
140  }
141 
142  // Takes average by num of elements added to get MEAN
143  size_t numElementsInAxis = 1;
144  for (unsigned int idx = 0; idx < numResolvedAxis; ++idx)
145  {
146  unsigned int current = inputDims[resolvedAxis[idx]];
147  ARMNN_ASSERT(armnn::numeric_cast<float>(current) <
148  (std::numeric_limits<float>::max() / armnn::numeric_cast<float>(numElementsInAxis)));
149  numElementsInAxis *= current;
150  }
151 
152  for (unsigned int idx = 0; idx < numOutputs; ++idx)
153  {
154  output[idx];
155  if (reduceOperation == ReduceOperation::Mean)
156  {
157  if (numElementsInAxis > 0)
158  {
159  output.Set(tempOut[idx] / armnn::numeric_cast<float>(numElementsInAxis));
160  }
161  }
162  else
163  {
164  output.Set(tempOut[idx]);
165  }
166  }
167 }
168 
169 } //namespace armnn
bool NextIndex(const unsigned int numDims, const armnn::TensorShape &dims, std::vector< unsigned int > &current)
Definition: Reduce.cpp:20
const TensorShape & GetShape() const
Definition: Tensor.hpp:187
unsigned int ReducedOutputOffset(const unsigned int numDims, const armnn::TensorShape &dims, std::vector< unsigned int > &index, const unsigned int numAxis, const std::vector< unsigned int > &axis)
Definition: Reduce.cpp:41
void Reduce(const TensorInfo &inputInfo, const TensorInfo &outputInfo, Decoder< float > &input, Encoder< float > &output, const std::vector< uint32_t > axis, const ReduceOperation reduceOperation)
Definition: Reduce.cpp:71
virtual void Set(IType right)=0
Copyright (c) 2021 ARM Limited and Contributors.
virtual IType Get() const =0
ReduceOperation
Definition: Types.hpp:111
#define ARMNN_ASSERT(COND)
Definition: Assert.hpp:14
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
unsigned int GetNumDimensions() const
Definition: Tensor.hpp:191
unsigned int GetNumElements() const
Definition: Tensor.hpp:192