Release 18.02
[platform/upstream/armnn.git] / src / armnn / backends / RefWorkloads / Pooling2d.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
5
6 #include "Pooling2d.hpp"
7
8 #include <armnn/Exceptions.hpp>
9 #include <armnn/Types.hpp>
10
11 #include <boost/numeric/conversion/cast.hpp>
12
13 #include <limits>
14 #include <algorithm>
15 #include <functional>
16
17 namespace
18 {
19     using PoolingAlgorithm = armnn::PoolingAlgorithm;
20
21     float DefaultInitializer(PoolingAlgorithm algorithm)
22     {
23         switch (algorithm)
24         {
25             case PoolingAlgorithm::Max:
26             {
27                 return std::numeric_limits<float>::lowest();
28             }
29             case PoolingAlgorithm::Average:
30             case PoolingAlgorithm::L2:
31             {
32                 return 0.0f;
33             }
34             default:
35             {
36                 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
37             }
38         }
39     }
40
41     using Accumulator = std::function<void(float & accu, float value)>;
42
43     Accumulator GetAccumulator(PoolingAlgorithm algorithm)
44     {
45         switch (algorithm)
46         {
47             case PoolingAlgorithm::Max:
48             {
49                 return [](float & accu, float value) {
50                     if (value > accu) {
51                         accu = value;
52                     }
53                 };
54             }
55
56             case PoolingAlgorithm::Average:
57             {
58                 return [](float & accu, float value) {
59                     accu += value;
60                 };
61             }
62
63             case PoolingAlgorithm::L2:
64             {
65                 return [](float & accu, float value) {
66                     accu += (value*value);
67                 };
68             }
69
70             default:
71             {
72                 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
73             }
74         }
75     }
76
77     using Executor = std::function<void(float & accumulated, float kernelSize)>;
78
79     Executor GetExecutor(PoolingAlgorithm algorithm)
80     {
81         switch (algorithm)
82         {
83             case PoolingAlgorithm::Max:
84             {
85                 return [](float & accumulated, float kernelSize) {};
86             }
87
88             case PoolingAlgorithm::Average:
89             {
90                 return [](float & accumulated, float kernelSize) {
91                     accumulated /= kernelSize;
92                 };
93             }
94
95             case PoolingAlgorithm::L2:
96             {
97                 return [](float & accumulated, float kernelSize) {
98                     accumulated = sqrtf(accumulated / kernelSize);
99                 };
100             }
101
102             default:
103             {
104                 throw armnn::InvalidArgumentException("Unsupported pooling algorithm");
105             }
106         }
107     }
108
109     bool OnPaddingOnly(int start, int end, int maxRange, int padding)
110     {
111         if (end <= 0 || start > (maxRange - padding))
112         {
113             return true;
114         }
115         else
116         {
117             return false;
118         }
119     }
120
121
122     bool ClampRange(int & start, int & end, int maxRange)
123     {
124         if (start < 0 || end > maxRange)
125         {
126             start = std::min(std::max(start, 0), maxRange);
127             end   = std::min(std::max(end, 0), maxRange);
128             return true;
129         }
130         else
131         {
132             return false;
133         }
134     }
135 }
136
137 namespace armnn
138 {
139
140 void Pooling2d(const float* in,
141                float* out,
142                const TensorInfo& inputInfo,
143                const TensorInfo& outputInfo,
144                const Pooling2dDescriptor& params)
145 {
146     const int batchSize    = boost::numeric_cast<int>(outputInfo.GetShape()[0]);
147     const int channels     = boost::numeric_cast<int>(outputInfo.GetShape()[1]);
148     const int heightOutput = boost::numeric_cast<int>(outputInfo.GetShape()[2]);
149     const int widthOutput  = boost::numeric_cast<int>(outputInfo.GetShape()[3]);
150     const int heightInput  = boost::numeric_cast<int>(inputInfo.GetShape()[2]);
151     const int widthInput   = boost::numeric_cast<int>(inputInfo.GetShape()[3]);
152     const int padLeft      = boost::numeric_cast<int>(params.m_PadLeft);
153     const int padRight     = boost::numeric_cast<int>(params.m_PadRight);
154     const int padTop       = boost::numeric_cast<int>(params.m_PadTop);
155     const int padBottom    = boost::numeric_cast<int>(params.m_PadBottom);
156     const int strideX      = boost::numeric_cast<int>(params.m_StrideX);
157     const int strideY      = boost::numeric_cast<int>(params.m_StrideY);
158     const int poolHeight   = boost::numeric_cast<int>(params.m_PoolHeight);
159     const int poolWidth    = boost::numeric_cast<int>(params.m_PoolWidth);
160
161     float defaultInitializer = DefaultInitializer(params.m_PoolType);
162
163     Accumulator accumulate = GetAccumulator(params.m_PoolType);
164     Executor execute       = GetExecutor(params.m_PoolType);
165
166     // Check supported padding methods outside the loop to simplify
167     // the inner loop
168     if (params.m_PaddingMethod != PaddingMethod::Exclude &&
169         params.m_PaddingMethod != PaddingMethod::IgnoreValue)
170     {
171         throw armnn::InvalidArgumentException("Unsupported padding type");
172     }
173
174     for (int n = 0; n < batchSize; n++)
175     {
176         for (int c = 0; c < channels; c++)
177         {
178             for (int yOutput = 0; yOutput < heightOutput; yOutput++)
179             {
180                 for (int xOutput = 0; xOutput < widthOutput; xOutput++)
181                 {
182                     int hstart = (yOutput * strideY) - padTop;
183                     int wstart = (xOutput * strideX) - padLeft;
184                     int hend = hstart + poolHeight;
185                     int wend = wstart + poolWidth;
186
187                     // Clamp the pooling region inside the valid input area (which includes the padding).
188                     // This is necessary because the final pooling in a row may overlap beyond the padding.
189                     hend = std::min(hend, heightInput + padRight);
190                     wend = std::min(wend, widthInput + padBottom);
191
192                     float result = defaultInitializer;
193                     float poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
194
195                     // special case: when the pooling kernel is over a padding region and the padding
196                     //               size is larger or equal to the kernel and the kernel only covers
197                     //               padding and no real values, then we initialize the result as zero
198                     //               by convention. This is because we need to choose a value here and
199                     //               all values we have are padding, which we ignore.
200                     if (OnPaddingOnly(hstart, hend, heightInput, padBottom) ||
201                         OnPaddingOnly(wstart, wend, widthInput, padRight))
202                     {
203                         result = 0.0f;
204                     }
205
206                     bool clamped = ClampRange(wstart, wend, widthInput);
207                     clamped |= ClampRange(hstart, hend, heightInput);
208
209                     if (clamped && params.m_PaddingMethod == PaddingMethod::Exclude)
210                     {
211                         // when we exclude the padding, it means we calculate with a smaller
212                         // kernel size, so I change the divisor here
213                         poolAreaSize = boost::numeric_cast<float>((hend - hstart) * (wend - wstart));
214                     }
215
216                     for (auto yInput = hstart; yInput < hend; yInput++)
217                     {
218                         for (auto xInput = wstart; xInput < wend; xInput++)
219                         {
220                             float inval = in[n * widthInput * heightInput * channels +
221                                              c * widthInput * heightInput +
222                                              yInput * widthInput +
223                                              xInput];
224
225                             accumulate(result, inval);
226                         }
227                     }
228
229                     execute(result, poolAreaSize);
230
231                     out[n * widthOutput * heightOutput * channels +
232                         c * widthOutput * heightOutput +
233                         yOutput * widthOutput +
234                         xOutput] = result;
235                 }
236             }
237         }
238     }
239 }
240
241 } //namespace armnn