Compute Library
 21.02
ReductionOperation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017-2020 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 "ReductionOperation.h"
25 
27 
28 #include <algorithm>
29 #include <cmath>
30 
31 namespace arm_compute
32 {
33 namespace test
34 {
35 namespace validation
36 {
37 namespace reference
38 {
39 namespace
40 {
41 template <typename T, typename OT>
42 OT reduce_operation(const T *ptr, int reduce_elements, ReductionOperation op, int stride)
43 {
44  using type = typename std::remove_cv<OT>::type;
45  T res;
46  switch(op)
47  {
49  {
50  res = type(1);
51  }
52  break;
55  {
56  res = *ptr;
57  }
58  break;
59  default:
60  {
61  res = type(0);
62  }
63  }
64 
65  if(std::is_integral<type>::value)
66  {
67  auto int_res = static_cast<int32_t>(res);
68  for(int i = 0; i < reduce_elements; ++i)
69  {
70  auto elem = *(ptr + stride * i);
71 
72  switch(op)
73  {
75  if(static_cast<T>(int_res) > elem)
76  {
77  int_res = elem;
78  }
79  break;
81  if(static_cast<T>(int_res) < elem)
82  {
83  int_res = elem;
84  }
85  break;
87  int_res += elem * elem;
88  break;
91  int_res += elem;
92  break;
94  int_res *= elem;
95  break;
96  default:
97  ARM_COMPUTE_ERROR("Operation not supported");
98  }
99  }
100  if(op == ReductionOperation::MEAN_SUM && reduce_elements > 0)
101  {
102  int_res /= reduce_elements;
103  }
104  res = static_cast<type>(int_res);
105  }
106  else
107  {
108  for(int i = 0; i < reduce_elements; ++i)
109  {
110  auto elem = *(ptr + stride * i);
111  switch(op)
112  {
114  if(res > elem)
115  {
116  res = elem;
117  }
118  break;
120  if(res < elem)
121  {
122  res = elem;
123  }
124  break;
126  res += elem * elem;
127  break;
130  res += elem;
131  break;
133  res *= elem;
134  break;
135  default:
136  ARM_COMPUTE_ERROR("Operation not supported");
137  }
138  }
139  if(op == ReductionOperation::MEAN_SUM && reduce_elements > 0)
140  {
141  res /= reduce_elements;
142  }
143  }
144  return res;
145 }
146 
147 template <typename T, typename OT>
148 OT reduce_operation_arg_min_max(const T *ptr, int reduce_elements, ReductionOperation op, int stride)
149 {
150  uint32_t res = 0;
151  for(int i = 0; i < reduce_elements; ++i)
152  {
153  auto elem = *(ptr + stride * i);
154  switch(op)
155  {
157  if(*(ptr + stride * res) > elem)
158  {
159  res = static_cast<uint32_t>(i);
160  }
161  break;
163  if(*(ptr + stride * res) < elem)
164  {
165  res = static_cast<uint32_t>(i);
166  }
167  break;
168  default:
169  ARM_COMPUTE_ERROR("Operation not supported");
170  }
171  }
172  return static_cast<OT>(res);
173 }
174 
175 } // namespace
176 
177 template <typename T, typename OT>
179 {
180  // Create reference
181  const bool is_arg_min_max = (op == ReductionOperation::ARG_IDX_MIN || op == ReductionOperation::ARG_IDX_MAX);
182  DataType output_data_type = is_arg_min_max ? DataType::S32 : src.data_type();
183  SimpleTensor<OT> dst{ dst_shape, output_data_type, 1, src.quantization_info() };
184  const unsigned int src_width = src.shape().x();
185  const unsigned int src_height = src.shape().y();
186  const unsigned int src_depth = src.shape().z();
187  const unsigned int src_batch = src.shape()[3];
188  const int reduce_elems = src.shape()[axis];
189 
190  switch(axis)
191  {
192  case 0:
193  {
194  const unsigned int upper_dims = src.shape().total_size_upper(1);
195  for(unsigned int du = 0; du < upper_dims; ++du)
196  {
197  const T *src_row_ptr = src.data() + du * reduce_elems;
198  dst[du] = is_arg_min_max ?
199  reduce_operation_arg_min_max<T, OT>(src_row_ptr, reduce_elems, op, 1) :
200  reduce_operation<T, OT>(src_row_ptr, reduce_elems, op, 1);
201  }
202  }
203  break;
204  case 1:
205  {
206  const unsigned int upper_dims = src.shape().total_size_upper(2);
207  for(unsigned int du = 0; du < upper_dims; ++du)
208  {
209  for(unsigned int x = 0; x < src_width; ++x)
210  {
211  const int in_offset = du * src_height * src_width + x;
212  const int out_offset = du * src_width + x;
213  const T *src_row_ptr = src.data() + in_offset;
214  dst[out_offset] = is_arg_min_max ?
215  reduce_operation_arg_min_max<T, OT>(src_row_ptr, reduce_elems, op, src_width) :
216  reduce_operation<T, OT>(src_row_ptr, reduce_elems, op, src_width);
217  }
218  }
219  }
220  break;
221  case 2:
222  {
223  const unsigned int upper_dims = src.shape().total_size_upper(3);
224  for(unsigned int du = 0; du < upper_dims; ++du)
225  {
226  for(unsigned int x = 0; x < src_width; ++x)
227  {
228  for(unsigned int y = 0; y < src_height; ++y)
229  {
230  const int in_offset = du * src_depth * src_height * src_width + y * src_width + x;
231  const int out_offset = du * src_width * src_height + y * src_width + x;
232  const T *src_row_ptr = src.data() + in_offset;
233  dst[out_offset] = is_arg_min_max ?
234  reduce_operation_arg_min_max<T, OT>(src_row_ptr, reduce_elems, op, src_width * src_height) :
235  reduce_operation<T, OT>(src_row_ptr, reduce_elems, op, src_width * src_height);
236  }
237  }
238  }
239  }
240  break;
241  case 3:
242  {
243  const unsigned int upper_dims = src.shape().total_size_upper(4);
244  for(unsigned int du = 0; du < upper_dims; ++du)
245  {
246  for(unsigned int z = 0; z < src_depth; ++z)
247  {
248  for(unsigned int y = 0; y < src_height; ++y)
249  {
250  for(unsigned int x = 0; x < src_width; ++x)
251  {
252  const int in_offset = du * src_batch * src_depth * src_height * src_width + z * src_width * src_height + y * src_width + x;
253  const int out_offset = du * src_depth * src_height * src_width + z * src_width * src_height + y * src_width + x;
254  const T *src_row_ptr = src.data() + in_offset;
255  dst[out_offset] = is_arg_min_max ?
256  reduce_operation_arg_min_max<T, OT>(src_row_ptr, reduce_elems, op, src_width * src_height * src_depth) :
257  reduce_operation<T, OT>(src_row_ptr, reduce_elems, op, src_width * src_height * src_depth);
258  }
259  }
260  }
261  }
262  }
263  break;
264  default:
265  ARM_COMPUTE_ERROR("Unsupported reduction axis");
266  }
267 
268  return dst;
269 }
270 
271 template <typename T, typename OT>
272 SimpleTensor<OT> reduction_operation(const SimpleTensor<T> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op, QuantizationInfo quantization_info_output)
273 {
274  ARM_COMPUTE_UNUSED(quantization_info_output);
275  return compute_reduction_operation<T, OT>(src, dst_shape, axis, op);
276 }
277 
278 template <>
280 {
281  if(src.data_type() == DataType::QASYMM8)
282  {
283  // If the operation is MEAN_SUM, we can directly use the uint8 implementation without taking into account scale and offset
284  if(op == ReductionOperation::MEAN_SUM && src.quantization_info() == quantization_info_output)
285  {
286  return compute_reduction_operation<uint8_t, uint8_t>(src, dst_shape, axis, op);
287  }
288  else
289  {
291  SimpleTensor<float> dst_f = reference::reduction_operation<float, float>(src_f, dst_shape, axis, op);
292  return convert_to_asymmetric<uint8_t>(dst_f, quantization_info_output);
293  }
294  }
295  else
296  {
297  return compute_reduction_operation<uint8_t, uint8_t>(src, dst_shape, axis, op);
298  }
299 }
300 
301 template <>
303 {
305  {
306  // If the operation is MEAN_SUM, we can directly use the int8 implementation without taking into account scale and offset
307  if(op == ReductionOperation::MEAN_SUM && src.quantization_info() == quantization_info_output)
308  {
309  return compute_reduction_operation<int8_t, int8_t>(src, dst_shape, axis, op);
310  }
311  else
312  {
314  SimpleTensor<float> dst_f = reference::reduction_operation<float, float>(src_f, dst_shape, axis, op);
315  return convert_to_asymmetric<int8_t>(dst_f, quantization_info_output);
316  }
317  }
318  else
319  {
320  return compute_reduction_operation<int8_t, int8_t>(src, dst_shape, axis, op);
321  }
322 }
323 
325  QuantizationInfo quantization_info_output = QuantizationInfo());
326 template SimpleTensor<half> reduction_operation(const SimpleTensor<half> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op,
327  QuantizationInfo quantization_info_output = QuantizationInfo());
328 
329 template SimpleTensor<int32_t> reduction_operation(const SimpleTensor<float> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op,
330  QuantizationInfo quantization_info_output = QuantizationInfo());
331 template SimpleTensor<int32_t> reduction_operation(const SimpleTensor<int32_t> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op,
332  QuantizationInfo quantization_info_output = QuantizationInfo());
333 template SimpleTensor<int32_t> reduction_operation(const SimpleTensor<half> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op,
334  QuantizationInfo quantization_info_output = QuantizationInfo());
335 template SimpleTensor<int32_t> reduction_operation(const SimpleTensor<uint8_t> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op,
336  QuantizationInfo quantization_info_output = QuantizationInfo());
337 template SimpleTensor<int32_t> reduction_operation(const SimpleTensor<int8_t> &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op,
338  QuantizationInfo quantization_info_output = QuantizationInfo());
339 
340 } // namespace reference
341 } // namespace validation
342 } // namespace test
343 } // namespace arm_compute
Shape of a tensor.
Definition: TensorShape.h:39
ReductionOperation
Available reduction operations.
Definition: Types.h:521
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
Definition: Error.h:352
SimpleTensor< OT > compute_reduction_operation(const SimpleTensor< T > &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op)
DataType data_type() const override
Data type of the tensor.
Definition: SimpleTensor.h:357
SimpleTensor< float > convert_from_asymmetric(const SimpleTensor< uint8_t > &src)
Definition: Helpers.cpp:112
TensorShape shape() const override
Shape of the tensor.
Definition: SimpleTensor.h:320
decltype(strategy::transforms) typedef type
SimpleTensor< float > src
Definition: DFT.cpp:155
Copyright (c) 2017-2021 Arm Limited.
1 channel, 1 S32 per channel
Quantization information.
#define ARM_COMPUTE_UNUSED(...)
To avoid unused variables warnings.
Definition: Error.h:152
quantized, asymmetric fixed-point 8-bit number unsigned
SimpleTensor< OT > reduction_operation(const SimpleTensor< T > &src, const TensorShape &dst_shape, unsigned int axis, ReductionOperation op, QuantizationInfo quantization_info_output)
Simple tensor object that stores elements in a consecutive chunk of memory.
Definition: SimpleTensor.h:58
quantized, asymmetric fixed-point 8-bit number signed
QuantizationInfo quantization_info() const override
Quantization info in case of asymmetric quantized type.
Definition: SimpleTensor.h:332
DataType
Available data types.
Definition: Types.h:77
const T * data() const
Constant pointer to the underlying buffer.
Definition: SimpleTensor.h:418