arm_compute v17.04
[platform/upstream/armcl.git] / src / core / NEON / kernels / NEPoolingLayerKernel.cpp
1 /*
2  * Copyright (c) 2017 ARM Limited.
3  *
4  * SPDX-License-Identifier: MIT
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 #include "arm_compute/core/NEON/kernels/NEPoolingLayerKernel.h"
25
26 #include "arm_compute/core/AccessWindowStatic.h"
27 #include "arm_compute/core/Error.h"
28 #include "arm_compute/core/Helpers.h"
29 #include "arm_compute/core/ITensor.h"
30 #include "arm_compute/core/TensorInfo.h"
31 #include "arm_compute/core/Utils.h"
32 #include "arm_compute/core/Validate.h"
33 #include "arm_compute/core/Window.h"
34
35 #include <algorithm>
36 #include <arm_neon.h>
37 #include <limits>
38 #include <string>
39 #include <tuple>
40
41 using namespace arm_compute;
42
43 namespace
44 {
45 inline float calculate_avg_scale(const Coordinates &id, const int pool_size, const int upper_bound_w, const int upper_bound_h,
46                                  const int pad_x, const int pad_y, const int stride_x, const int stride_y)
47 {
48     int start_x = id.x() * stride_x - pad_x;
49     int start_y = id.y() * stride_y - pad_y;
50     int end_x   = std::min(start_x + pool_size, upper_bound_w);
51     int end_y   = std::min(start_y + pool_size, upper_bound_h);
52     return 1.f / ((end_y - start_y) * (end_x - start_x));
53 }
54 } // namespace
55
56 NEPoolingLayerKernel::NEPoolingLayerKernel()
57     : _func(nullptr), _input(nullptr), _output(nullptr), _pool_info(), _border_size(0)
58 {
59 }
60
61 BorderSize NEPoolingLayerKernel::border_size() const
62 {
63     return _border_size;
64 }
65
66 void NEPoolingLayerKernel::configure(const ITensor *input, ITensor *output, const PoolingLayerInfo &pool_info)
67 {
68     int                   pool_pad_x      = 0;
69     int                   pool_pad_y      = 0;
70     int                   pool_stride_x   = 0;
71     int                   pool_stride_y   = 0;
72     unsigned int          pooled_w        = 0;
73     unsigned int          pooled_h        = 0;
74     PoolingType           pool_type       = pool_info.pool_type();
75     int                   pool_size       = pool_info.pool_size();
76     const PadStrideInfo   pad_stride_info = pool_info.pad_stride_info();
77     DimensionRoundingType pool_round      = pad_stride_info.round();
78     std::tie(pool_pad_x, pool_pad_y)       = pad_stride_info.pad();
79     std::tie(pool_stride_x, pool_stride_y) = pad_stride_info.stride();
80
81     ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32);
82     ARM_COMPUTE_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(output, 1, DataType::F32);
83     ARM_COMPUTE_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
84     ARM_COMPUTE_ERROR_ON(2 != pool_size && 3 != pool_size);
85     ARM_COMPUTE_ERROR_ON(pool_pad_x >= pool_size || pool_pad_y >= pool_size);
86
87     // Check output dimensions
88     std::tie(pooled_w, pooled_h) = scaled_dimensions(input->info()->dimension(0), input->info()->dimension(1),
89                                                      pool_size, pool_stride_x, pool_stride_y,
90                                                      pool_pad_x, pool_pad_y, pool_round);
91     ARM_COMPUTE_UNUSED(pooled_w);
92     ARM_COMPUTE_UNUSED(pooled_h);
93     ARM_COMPUTE_ERROR_ON((output->info()->dimension(0) != pooled_w) || (output->info()->dimension(1) != pooled_h));
94
95     const int num_elems_read_per_iteration = (pool_size == 2) ? 2 : 4; // We use vload4 for pooling3
96     const int input_width                  = input->info()->dimension(0);
97     const int input_height                 = input->info()->dimension(1);
98     const int upper_bound_w                = ((pooled_w - 1) * pool_stride_x - pool_pad_x + num_elems_read_per_iteration) - input_width;
99     const int upper_bound_h                = ((pooled_h - 1) * pool_stride_y - pool_pad_y + pool_size) - input_height;
100
101     // Set instance variables
102     _input              = input;
103     _output             = output;
104     _pool_info          = pool_info;
105     _border_size        = BorderSize(pool_pad_y, pool_pad_x);
106     _border_size.right  = std::max(upper_bound_w, pool_pad_x);
107     _border_size.bottom = std::max(upper_bound_h, pool_pad_y);
108
109     // Select appropriate function
110     switch(pool_size)
111     {
112         case 2:
113             _func = (PoolingType::AVG == pool_type) ? &NEPoolingLayerKernel::pooling2<PoolingType::AVG> : &NEPoolingLayerKernel::pooling2<PoolingType::MAX>;
114             break;
115         case 3:
116             _func = (PoolingType::AVG == pool_type) ? &NEPoolingLayerKernel::pooling3<PoolingType::AVG> : &NEPoolingLayerKernel::pooling3<PoolingType::MAX>;
117             break;
118         default:
119             ARM_COMPUTE_ERROR("Unsupported pooling size");
120             break;
121     }
122
123     // Configure kernel window
124     constexpr unsigned int num_elems_processed_per_iteration = 1;
125
126     Window                 win = calculate_max_window(*output->info(), Steps(num_elems_processed_per_iteration));
127     AccessWindowStatic     input_access(input->info(), -pool_pad_x, -pool_pad_y, input_width + _border_size.right, input_height + _border_size.bottom);
128     AccessWindowHorizontal output_access(output->info(), 0, num_elems_processed_per_iteration);
129     update_window_and_padding(win, input_access, output_access);
130     output_access.set_valid_region(win, ValidRegion(Coordinates(), output->info()->tensor_shape()));
131     INEKernel::configure(win);
132 }
133
134 template <PoolingType pooling_type>
135 void NEPoolingLayerKernel::pooling2(const Window &window_input, const Window &window)
136 {
137     Iterator input(_input, window_input);
138     Iterator output(_output, window);
139
140     constexpr int pool_size = 2;
141     int           pool_pad_x, pool_pad_y, pool_stride_x, pool_stride_y = 0;
142     std::tie(pool_pad_x, pool_pad_y)       = _pool_info.pad_stride_info().pad();
143     std::tie(pool_stride_x, pool_stride_y) = _pool_info.pad_stride_info().stride();
144     const int upper_bound_w = _input->info()->dimension(0) + pool_pad_x;
145     const int upper_bound_h = _input->info()->dimension(1) + pool_pad_y;
146
147     const unsigned char *const input_top_ptr    = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_x), -static_cast<int>(pool_pad_y)));
148     const unsigned char *const input_bottom_ptr = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_x), -static_cast<int>(pool_pad_y) + 1));
149
150     execute_window_loop(window, [&](const Coordinates & id)
151     {
152         const float32x2_t top_data    = vld1_f32(reinterpret_cast<const float *>(input_top_ptr + input.offset()));
153         const float32x2_t bottom_data = vld1_f32(reinterpret_cast<const float *>(input_bottom_ptr + input.offset()));
154         float32x2_t       res         = {};
155         if(pooling_type == PoolingType::AVG)
156         {
157             // Calculate scale
158             float             scale   = calculate_avg_scale(id, pool_size, upper_bound_w, upper_bound_h, pool_pad_x, pool_pad_y, pool_stride_x, pool_stride_y);
159             const float32x2_t scale_v = vdup_n_f32(scale);
160
161             // Perform pooling
162             const float32x2_t sum_data = vadd_f32(top_data, bottom_data);
163             res                        = vmul_f32(vpadd_f32(sum_data, sum_data), scale_v);
164         }
165         else
166         {
167             const float32x2_t max_data = vmax_f32(top_data, bottom_data);
168             res                        = vpmax_f32(max_data, max_data);
169         }
170         *(reinterpret_cast<float *>(output.ptr())) = vget_lane_f32(res, 0);
171     },
172     input, output);
173 }
174
175 template <PoolingType pooling_type>
176 void NEPoolingLayerKernel::pooling3(const Window &window_input, const Window &window)
177 {
178     Iterator input(_input, window_input);
179     Iterator output(_output, window);
180
181     constexpr const int pool_size = 3;
182     int                 pool_pad_x, pool_pad_y, pool_stride_x, pool_stride_y = 0;
183     std::tie(pool_pad_x, pool_pad_y)       = _pool_info.pad_stride_info().pad();
184     std::tie(pool_stride_x, pool_stride_y) = _pool_info.pad_stride_info().stride();
185     const int upper_bound_w = _input->info()->dimension(0) + pool_pad_x;
186     const int upper_bound_h = _input->info()->dimension(1) + pool_pad_y;
187
188     const unsigned char *const input_top_ptr    = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_x), -static_cast<int>(pool_pad_y)));
189     const unsigned char *const input_middle_ptr = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_x), -static_cast<int>(pool_pad_y) + 1));
190     const unsigned char *const input_bottom_ptr = _input->ptr_to_element(Coordinates(-static_cast<int>(pool_pad_x), -static_cast<int>(pool_pad_y) + 2));
191
192     execute_window_loop(window, [&](const Coordinates & id)
193     {
194         const float32x4_t top_data    = vld1q_f32(reinterpret_cast<const float *>(input_top_ptr + input.offset()));
195         const float32x4_t middle_data = vld1q_f32(reinterpret_cast<const float *>(input_middle_ptr + input.offset()));
196         const float32x4_t bottom_data = vld1q_f32(reinterpret_cast<const float *>(input_bottom_ptr + input.offset()));
197         float32x2_t       res         = {};
198         if(pooling_type == PoolingType::AVG)
199         {
200             // Calculate scale
201             float             scale   = calculate_avg_scale(id, pool_size, upper_bound_w, upper_bound_h, pool_pad_x, pool_pad_y, pool_stride_x, pool_stride_y);
202             const float32x2_t scale_v = vdup_n_f32(scale);
203
204             // Perform pooling
205             const float32x4_t sum_data = vaddq_f32(vaddq_f32(top_data, bottom_data), middle_data);
206             res                        = vpadd_f32(vget_high_f32(vsetq_lane_f32(0.f, sum_data, 3)), vget_low_f32(sum_data));
207             res                        = vmul_f32(vpadd_f32(res, res), scale_v);
208         }
209         else
210         {
211             const float32x4_t max_data = vmaxq_f32(vmaxq_f32(top_data, bottom_data), middle_data);
212             res                        = vpmax_f32(vget_high_f32(vsetq_lane_f32(-std::numeric_limits<float>::max(), max_data, 3)), vget_low_f32(max_data));
213             res                        = vpmax_f32(res, res);
214         }
215         *(reinterpret_cast<float *>(output.ptr())) = vget_lane_f32(res, 0);
216     },
217     input, output);
218 }
219
220 void NEPoolingLayerKernel::run(const Window &window)
221 {
222     ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
223     ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window);
224     ARM_COMPUTE_ERROR_ON(_func == nullptr);
225
226     unsigned int pool_stride_x, pool_stride_y = 0;
227     std::tie(pool_stride_x, pool_stride_y)    = _pool_info.pad_stride_info().stride();
228
229     // Set step for input in x and y direction for the input
230     Window window_input(window);
231     window_input.set(Window::DimX, Window::Dimension(window.x().start() * pool_stride_x, window.x().end() * pool_stride_x, pool_stride_x));
232     window_input.set(Window::DimY, Window::Dimension(window.y().start() * pool_stride_y, window.y().end() * pool_stride_y, pool_stride_y));
233
234     // Run function
235     (this->*_func)(window_input, window);
236 }