blob: da023aeb9647499be58c75fcf93f91e716a49a41 [file] [log] [blame]
giuros01ba368252019-02-19 13:53:10 +00001/*
Michele Di Giorgiod9eaf612020-07-08 11:12:57 +01002 * Copyright (c) 2019-2020 Arm Limited.
giuros01ba368252019-02-19 13:53:10 +00003 *
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 */
Michalis Spyrouebcebf12020-10-21 00:04:14 +010024#include "src/core/NEON/kernels/NESpaceToBatchLayerKernel.h"
giuros01ba368252019-02-19 13:53:10 +000025
26#include "arm_compute/core/Helpers.h"
27#include "arm_compute/core/ITensor.h"
giuros01ba368252019-02-19 13:53:10 +000028#include "arm_compute/core/Types.h"
giuros01ba368252019-02-19 13:53:10 +000029#include "arm_compute/core/utils/misc/ShapeCalculator.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010030#include "arm_compute/core/Validate.h"
31
Sang-Hoon Park68dd25f2020-10-19 16:00:11 +010032#include "src/core/helpers/AutoConfiguration.h"
33#include "src/core/helpers/WindowHelpers.h"
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010034#include "src/core/NEON/wrapper/wrapper.h"
Sang-Hoon Park68dd25f2020-10-19 16:00:11 +010035
giuros01ba368252019-02-19 13:53:10 +000036#include <arm_neon.h>
37#include <cstdint>
38
39using namespace arm_compute::misc::shape_calculator;
40
41namespace arm_compute
42{
43namespace
44{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010045Status validate_arguments(const ITensorInfo *input,
46 const ITensorInfo *block_info,
47 const ITensorInfo *paddings,
48 const ITensorInfo *output)
giuros01ba368252019-02-19 13:53:10 +000049{
SiCong Li18bdfae2020-11-08 21:58:01 +000050 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, block_info, paddings, output);
Georgios Pinitas33843562019-12-10 13:33:18 +000051 ARM_COMPUTE_RETURN_ERROR_ON(input->data_type() == DataType::UNKNOWN);
giuros01ba368252019-02-19 13:53:10 +000052 ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(block_info, 1, DataType::S32);
53 ARM_COMPUTE_RETURN_ERROR_ON(input->num_dimensions() > 4);
54 ARM_COMPUTE_RETURN_ERROR_ON(block_info->num_dimensions() > 1);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010055 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(block_info->tensor_shape(), TensorShape{2});
SiCong Li18bdfae2020-11-08 21:58:01 +000056 ARM_COMPUTE_RETURN_ERROR_ON(paddings->num_dimensions() > 2);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010057 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(paddings->tensor_shape(), TensorShape{2, 2});
giuros01ba368252019-02-19 13:53:10 +000058
59 // Validate output if initialized
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010060 if (output->total_size() != 0)
giuros01ba368252019-02-19 13:53:10 +000061 {
62 const DataLayout data_layout = input->data_layout();
63 const int idx_channel = get_data_layout_dimension_index(data_layout, DataLayoutDimension::CHANNEL);
64 ARM_COMPUTE_RETURN_ERROR_ON(input->tensor_shape()[idx_channel] != output->tensor_shape()[idx_channel]);
65 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
SiCong Li18bdfae2020-11-08 21:58:01 +000066 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_QUANTIZATION_INFO(input, output);
giuros01ba368252019-02-19 13:53:10 +000067 }
68
69 return Status{};
70}
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010071Status validate_arguments_static(const ITensorInfo *input,
72 const int block_shape_x,
73 const int block_shape_y,
74 const Size2D &padding_left,
75 const Size2D &padding_right,
giuros01ba368252019-02-19 13:53:10 +000076 const ITensorInfo *output)
77{
78 ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output);
Georgios Pinitas33843562019-12-10 13:33:18 +000079 ARM_COMPUTE_RETURN_ERROR_ON(input->data_type() == DataType::UNKNOWN);
giuros01ba368252019-02-19 13:53:10 +000080 ARM_COMPUTE_RETURN_ERROR_ON(input->num_dimensions() > 4);
SiCong Li18bdfae2020-11-08 21:58:01 +000081 ARM_COMPUTE_RETURN_ERROR_ON(block_shape_x < 1 || block_shape_y < 1);
giuros01ba368252019-02-19 13:53:10 +000082
83 // Validate output if initialized
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010084 if (output->total_size() != 0)
giuros01ba368252019-02-19 13:53:10 +000085 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010086 TensorShape expected_output_shape = misc::shape_calculator::compute_space_to_batch_shape(
87 input, block_shape_x, block_shape_y, padding_left, padding_right);
SiCong Li18bdfae2020-11-08 21:58:01 +000088 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DIMENSIONS(output->tensor_shape(), expected_output_shape);
giuros01ba368252019-02-19 13:53:10 +000089 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
Michele Di Giorgio93c70b82019-08-08 11:59:14 +010090 ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_QUANTIZATION_INFO(input, output);
giuros01ba368252019-02-19 13:53:10 +000091 }
92
93 return Status{};
94}
95} // namespace
96
97NESpaceToBatchLayerKernel::NESpaceToBatchLayerKernel()
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +010098 : _input(nullptr),
99 _block_shape(nullptr),
100 _paddings(nullptr),
101 _output(nullptr),
102 _data_layout(DataLayout::UNKNOWN),
103 _padding_left(),
104 _block_shape_x(),
105 _block_shape_y()
giuros01ba368252019-02-19 13:53:10 +0000106{
107}
108
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100109void NESpaceToBatchLayerKernel::configure(const ITensor *input,
110 const ITensor *block_shape,
111 const ITensor *paddings,
112 ITensor *output)
giuros01ba368252019-02-19 13:53:10 +0000113{
SiCong Li18bdfae2020-11-08 21:58:01 +0000114 ARM_COMPUTE_ERROR_ON_NULLPTR(input, block_shape, paddings, output);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100115 ARM_COMPUTE_ERROR_THROW_ON(
116 validate_arguments(input->info(), block_shape->info(), paddings->info(), output->info()));
giuros01ba368252019-02-19 13:53:10 +0000117
118 _input = input;
119 _block_shape = block_shape;
120 _paddings = paddings;
121 _output = output;
Sadik Armagan29658042020-05-11 10:35:08 +0100122 _data_layout = input->info()->data_layout();
giuros01ba368252019-02-19 13:53:10 +0000123
124 // Configure kernel window
125 Window win = calculate_max_window(*output->info(), Steps());
126 ICPPKernel::configure(win);
127}
128
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100129void NESpaceToBatchLayerKernel::configure(const ITensor *input,
130 const int block_shape_x,
131 const int block_shape_y,
132 const Size2D &padding_left,
133 const Size2D &padding_right,
134 ITensor *output)
giuros01ba368252019-02-19 13:53:10 +0000135{
136 ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
137
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100138 TensorShape output_shape = misc::shape_calculator::compute_space_to_batch_shape(
139 input->info(), block_shape_x, block_shape_y, padding_left, padding_right);
140 auto_init_if_empty(*output->info(), output_shape, 1, input->info()->data_type(),
141 input->info()->quantization_info());
giuros01ba368252019-02-19 13:53:10 +0000142
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100143 ARM_COMPUTE_ERROR_THROW_ON(validate_arguments_static(input->info(), block_shape_x, block_shape_y, padding_left,
144 padding_right, output->info()));
giuros01ba368252019-02-19 13:53:10 +0000145
146 _input = input;
147 _output = output;
148 _block_shape_x = block_shape_x;
149 _block_shape_y = block_shape_y;
150 _padding_left = padding_left;
Sadik Armagan29658042020-05-11 10:35:08 +0100151 _data_layout = input->info()->data_layout();
giuros01ba368252019-02-19 13:53:10 +0000152
153 // Configure kernel window
154 Window win = calculate_max_window(*output->info(), Steps());
155 INEKernel::configure(win);
156}
157
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100158Status NESpaceToBatchLayerKernel::validate(const ITensorInfo *input,
159 const ITensorInfo *block_shape,
160 const ITensorInfo *paddings,
161 const ITensorInfo *output)
giuros01ba368252019-02-19 13:53:10 +0000162{
163 ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, block_shape, paddings, output));
164 return Status{};
165}
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100166Status NESpaceToBatchLayerKernel::validate(const ITensorInfo *input,
167 const int block_shape_x,
168 const int block_shape_y,
169 const Size2D &padding_left,
170 const Size2D &padding_right,
giuros01ba368252019-02-19 13:53:10 +0000171 const ITensorInfo *output)
172{
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100173 ARM_COMPUTE_RETURN_ON_ERROR(
174 validate_arguments_static(input, block_shape_x, block_shape_y, padding_left, padding_right, output));
giuros01ba368252019-02-19 13:53:10 +0000175 return Status{};
176}
177
178void NESpaceToBatchLayerKernel::run(const Window &window, const ThreadInfo &info)
179{
180 ARM_COMPUTE_UNUSED(info);
181 ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
182 ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICPPKernel::window(), window);
183
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100184 if (_block_shape != nullptr)
giuros01ba368252019-02-19 13:53:10 +0000185 {
186 // Retrieve the block shapes dynamically
187 _block_shape_x = *(reinterpret_cast<const int *>(_block_shape->ptr_to_element(0)));
188 _block_shape_y = *(reinterpret_cast<const int *>(_block_shape->ptr_to_element(1)));
189 }
190
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100191 if (_paddings != nullptr)
giuros01ba368252019-02-19 13:53:10 +0000192 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100193 const size_t pad_left_x = *reinterpret_cast<const size_t *>(_paddings->ptr_to_element({0, 0}));
194 const size_t pad_left_y = *reinterpret_cast<const size_t *>(_paddings->ptr_to_element({1, 0}));
giuros01ba368252019-02-19 13:53:10 +0000195 _padding_left = Size2D(pad_left_x, pad_left_y);
196 }
Sadik Armagan29658042020-05-11 10:35:08 +0100197 const int height_idx = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::HEIGHT);
198 const int width_idx = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::WIDTH);
199 const int batch_idx = get_data_layout_dimension_index(_data_layout, DataLayoutDimension::BATCHES);
200 const int element_size = _input->info()->element_size();
giuros01ba368252019-02-19 13:53:10 +0000201
202 const size_t height = _input->info()->dimension(height_idx);
203 const size_t width = _input->info()->dimension(width_idx);
Sadik Armagan29658042020-05-11 10:35:08 +0100204 const size_t batch_size = _input->info()->dimension(batch_idx);
giuros01ba368252019-02-19 13:53:10 +0000205
206 Window slice_out = window.first_slice_window_3D();
giuros01ba368252019-02-19 13:53:10 +0000207
208 int batch_id = 0;
209
210 // Main loop for NCHW and NHWC
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100211 if (_data_layout == DataLayout::NCHW)
giuros01ba368252019-02-19 13:53:10 +0000212 {
213 do
214 {
215 Iterator out(_output, slice_out);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100216 execute_window_loop(
217 slice_out,
218 [&](const Coordinates &id)
giuros01ba368252019-02-19 13:53:10 +0000219 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100220 const size_t out_x = id.x();
221 const size_t out_y = id.y();
222 const size_t z = id.z();
223 const size_t pos_x = out_x * _block_shape_x + (batch_id / batch_size) % _block_shape_x;
224 const size_t pos_y = out_y * _block_shape_y + (batch_id / batch_size) / _block_shape_x;
225 if (pos_y >= _padding_left.y() && pos_y < _padding_left.y() + height &&
226 pos_x >= _padding_left.x() && pos_x < _padding_left.x() + width)
227 {
228 const int w = batch_id % batch_size;
229 const int in_x = pos_x - _padding_left.x();
230 const int in_y = pos_y - _padding_left.y();
231 Coordinates input_coords{in_x, in_y, z, w};
232 memcpy(out.ptr(), _input->ptr_to_element(input_coords), element_size);
233 }
234 },
235 out);
giuros01ba368252019-02-19 13:53:10 +0000236 ++batch_id;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100237 } while (window.slide_window_slice_3D(slice_out));
giuros01ba368252019-02-19 13:53:10 +0000238 }
239 else
240 {
241 do
242 {
243 Iterator out(_output, slice_out);
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100244 execute_window_loop(
245 slice_out,
246 [&](const Coordinates &id)
giuros01ba368252019-02-19 13:53:10 +0000247 {
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100248 const size_t out_x = id.y();
249 const size_t out_y = id.z();
250 const size_t z = id.x();
251 const size_t pos_x = out_x * _block_shape_x + (batch_id / batch_size) % _block_shape_x;
252 const size_t pos_y = out_y * _block_shape_y + (batch_id / batch_size) / _block_shape_x;
253 if (pos_y >= _padding_left.y() && pos_y < _padding_left.y() + height &&
254 pos_x >= _padding_left.x() && pos_x < _padding_left.x() + width)
255 {
256 const int w = batch_id % batch_size;
257 const int in_x = pos_x - _padding_left.x();
258 const int in_y = pos_y - _padding_left.y();
259 Coordinates input_coords{z, in_x, in_y, w};
260 memcpy(out.ptr(), _input->ptr_to_element(input_coords), element_size);
261 }
262 },
263 out);
giuros01ba368252019-02-19 13:53:10 +0000264 ++batch_id;
Felix Thomasmathibalanafd38f02023-09-27 17:46:17 +0100265 } while (window.slide_window_slice_3D(slice_out));
giuros01ba368252019-02-19 13:53:10 +0000266 }
267}
268} // namespace arm_compute