Compute Library
 20.08
NEReductionOperation.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  */
25 
29 
30 namespace arm_compute
31 {
32 namespace
33 {
34 /** Define dimension to split the window
35  *
36  * @param[in] axis Reduction axis
37  *
38  * @return The dimension to split the window
39  */
40 size_t reduction_window_split_dimension(unsigned int axis)
41 {
42  switch(axis)
43  {
44  case 0:
45  return Window::DimY;
46  case 1:
47  case 2:
48  case 3:
49  return Window::DimX;
50  default:
51  ARM_COMPUTE_ERROR("Unsupported reduction axis");
52  }
53 }
54 } // namespace
55 
56 NEReductionOperation::NEReductionOperation(std::shared_ptr<IMemoryManager> memory_manager)
57  : _memory_group(memory_manager), _reduction_kernel(), _fill_border_kernel(), _reshape(), _output_internal(), _window_split(0), _reduction_axis(), _is_reshape_required(false)
58 {
59 }
60 
61 Status NEReductionOperation::validate(const ITensorInfo *input, const ITensorInfo *output, unsigned int axis, ReductionOperation op, bool keep_dims)
62 {
63  ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis >= TensorShape::num_max_dimensions, "Reduction axis greater than max number of dimensions");
64  ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis > 3, "Unsupported reduction axis");
65 
66  const auto is_reshape_required = !keep_dims;
67 
68  auto *output_internal = output;
69 
70  TensorInfo info_before_reshape;
71 
72  if(is_reshape_required)
73  {
74  const TensorInfo expected_output_shape = output->clone()->set_tensor_shape(arm_compute::misc::shape_calculator::compute_reduced_shape(input->tensor_shape(), axis, keep_dims));
75  ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(&expected_output_shape, output);
76 
77  auto shape_before_reshape = input->tensor_shape();
78  shape_before_reshape.set(axis, 1);
79 
80  const auto input_num_channles = input->num_channels();
81  const auto input_qinfo = input->quantization_info();
82  const auto is_arg_min_max = (op == ReductionOperation::ARG_IDX_MAX) || (op == ReductionOperation::ARG_IDX_MIN);
83  const auto output_data_type = is_arg_min_max ? DataType::S32 : output->data_type();
84 
85  info_before_reshape.set_data_type(output_data_type).set_tensor_shape(shape_before_reshape).set_num_channels(input_num_channles).set_quantization_info(input_qinfo);
86 
87  output_internal = &info_before_reshape;
88  }
89 
91 
92  if(is_reshape_required)
93  {
94  ARM_COMPUTE_RETURN_ON_ERROR(NEReshapeLayer::validate(output_internal, output));
95  }
96 
97  return Status{};
98 }
99 
100 void NEReductionOperation::configure(ITensor *input, ITensor *output, unsigned int axis, ReductionOperation op, bool keep_dims)
101 {
103 
104  _is_reshape_required = !keep_dims;
105 
106  auto *output_internal = output;
107  const auto is_arg_min_max = (op == ReductionOperation::ARG_IDX_MAX) || (op == ReductionOperation::ARG_IDX_MIN);
108 
109  if(_is_reshape_required)
110  {
111  const auto output_internal_shape = arm_compute::misc::shape_calculator::compute_reduced_shape(input->info()->tensor_shape(), axis);
112  const auto output_external_shape = arm_compute::misc::shape_calculator::compute_reduced_shape(input->info()->tensor_shape(), axis, false);
113  const auto output_data_type = is_arg_min_max ? DataType::S32 : input->info()->data_type();
114  const auto num_channels = input->info()->num_channels();
115  const auto qinfo = input->info()->quantization_info();
116 
117  _output_internal.allocator()->init(input->info()->clone()->set_data_type(output_data_type).set_tensor_shape(output_internal_shape).reset_padding().set_is_resizable(true).set_num_channels(
118  num_channels).set_quantization_info(qinfo));
119  _memory_group.manage(&_output_internal);
120  output_internal = &_output_internal;
121  auto_init_if_empty(*output->info(), input->info()->clone()->set_data_type(output_data_type).set_tensor_shape(output_external_shape).reset_padding().set_is_resizable(true));
122  }
123 
124  ARM_COMPUTE_ERROR_THROW_ON(NEReductionOperation::validate(input->info(), output->info(), axis, op, keep_dims));
125 
126  // Configure reduction kernel
127  _reduction_kernel.configure(input, output_internal, axis, op);
128  _window_split = reduction_window_split_dimension(axis);
129  _reduction_axis = axis;
130 
131  if(axis == 0)
132  {
133  // Configure fill border kernel
134  const BorderSize fill_border_size = _reduction_kernel.border_size();
135  PixelValue pixelValue;
136  switch(op)
137  {
139  {
140  pixelValue = PixelValue(1, input->info()->data_type(), input->info()->quantization_info());
141  break;
142  }
144  {
145  pixelValue = std::get<1>(get_min_max(input->info()->data_type()));
146  break;
147  }
149  {
150  pixelValue = std::get<0>(get_min_max(input->info()->data_type()));
151  break;
152  }
155  {
156  pixelValue = PixelValue(0, input->info()->data_type(), input->info()->quantization_info());
157  break;
158  }
162  {
163  pixelValue = PixelValue(static_cast<uint32_t>(0));
164  break;
165  }
166  default:
167  ARM_COMPUTE_ERROR("Reduction Operation unsupported");
168  }
169  _fill_border_kernel.configure(input, fill_border_size, (is_arg_min_max ? BorderMode::REPLICATE : BorderMode::CONSTANT), pixelValue);
170  }
171 
172  if(_is_reshape_required)
173  {
174  _reshape.configure(output_internal, output);
175  _output_internal.allocator()->allocate();
176  }
177 }
178 
180 {
181  if(_reduction_axis == 0)
182  {
183  NEScheduler::get().schedule(&_fill_border_kernel, Window::DimY);
184  }
185  NEScheduler::get().schedule(&_reduction_kernel, _window_split);
186  if(_is_reshape_required)
187  {
188  _reshape.run();
189  }
190 }
191 } // namespace arm_compute
virtual ITensorInfo & set_num_channels(int num_channels)=0
Set the number of channels to the specified value.
Class describing the value of a pixel for any image format.
Definition: PixelValue.h:34
void run() override
Run the kernels contained in the function.
void init(const TensorAllocator &allocator, const Coordinates &coords, TensorInfo &sub_info)
Shares the same backing memory with another tensor allocator, while the tensor info might be differen...
ReductionOperation
Available reduction operations.
Definition: Types.h:498
Container for 2D border size.
Definition: Types.h:272
static Status validate(const ITensorInfo *input, const ITensorInfo *output)
Static function to check if given info will lead to a valid configuration of NEReshapeLayer.
#define ARM_COMPUTE_ERROR(msg)
Print the given message then throw an std::runtime_error.
Definition: Error.h:352
virtual ITensorInfo & set_tensor_shape(const TensorShape &shape)=0
Set the shape of an already initialized tensor.
#define ARM_COMPUTE_RETURN_ON_ERROR(status)
Checks if a status contains an error and returns it.
Definition: Error.h:204
virtual DataType data_type() const =0
Data type used for each element of the tensor.
ITensorInfo & set_data_type(DataType data_type) override
Set the data type to the specified value.
Definition: TensorInfo.cpp:319
Store the tensor's metadata.
Definition: ITensorInfo.h:40
#define ARM_COMPUTE_ERROR_THROW_ON(status)
Definition: Error.h:455
void configure(ITensor *tensor, BorderSize border_size, BorderMode border_mode, const PixelValue &constant_border_value=PixelValue())
Initialise the function.
Status class.
Definition: Error.h:52
Interface for NEON tensor.
Definition: ITensor.h:36
Copyright (c) 2017-2020 Arm Limited.
bool auto_init_if_empty(ITensorInfo &info, const TensorShape &shape, int num_channels, DataType data_type, QuantizationInfo quantization_info=QuantizationInfo())
Auto initialize the tensor info (shape, number of channels and data type) if the current assignment i...
Definition: Helpers.inl:207
TensorAllocator * allocator()
Return a pointer to the tensor's allocator.
Definition: Tensor.cpp:48
1 channel, 1 S32 per channel
void manage(IMemoryManageable *obj) override
Sets a object to be managed by the given memory group.
Definition: MemoryGroup.h:79
static Status validate(const ITensorInfo *input, const ITensorInfo *output, unsigned int axis, ReductionOperation op)
Static function to check if given info will lead to a valid configuration of NEReductionOperationKern...
static constexpr size_t DimX
Alias for dimension 0 also known as X dimension.
Definition: Window.h:43
#define ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_SHAPES(...)
Definition: Validate.h:443
static Status validate(const ITensorInfo *input, const ITensorInfo *output, unsigned int axis, ReductionOperation op, bool keep_dims=true)
Static function to check if given info will lead to a valid configuration of NEReductionOperation.
BorderSize border_size() const override
The size of the border for that kernel.
void allocate() override
Allocate size specified by TensorInfo of CPU memory.
TensorShape compute_reduced_shape(const TensorShape &input, unsigned int axis, bool keep_dims=true)
Calculate the reduced shape of a tensor given an axis.
virtual std::unique_ptr< T > clone() const =0
Provide a clone of the current object of class T.
virtual ITensorInfo * info() const =0
Interface to be implemented by the child class to return the tensor's metadata.
virtual ITensorInfo & set_quantization_info(const QuantizationInfo &quantization_info)=0
Set the quantization settings (scale and offset) of the tensor.
void configure(const ITensor *input, ITensor *output, unsigned int axis, ReductionOperation op)
Set the source, destination of the kernel.
static constexpr size_t DimY
Alias for dimension 1 also known as Y dimension.
Definition: Window.h:45
#define ARM_COMPUTE_ERROR_ON_NULLPTR(...)
Definition: Validate.h:161
virtual void schedule(ICPPKernel *kernel, const Hints &hints)=0
Runs the kernel in the same thread as the caller synchronously.
Pixels outside the image are assumed to have the same value as the closest image pixel.
const QuantizationInfo qinfo
Definition: Im2Col.cpp:150
void configure(const ITensor *input, ITensor *output)
Initialise the kernel's inputs and outputs.
#define ARM_COMPUTE_RETURN_ERROR_ON_MSG(cond, msg)
If the condition is true, an error is returned.
Definition: Error.h:244
Store the tensor's metadata.
Definition: TensorInfo.h:45
static constexpr size_t num_max_dimensions
Number of dimensions the tensor has.
Definition: Dimensions.h:45
std::tuple< PixelValue, PixelValue > get_min_max(DataType dt)
Compute the mininum and maximum values a data type can take.
Definition: Utils.h:560
NEReductionOperation(std::shared_ptr< IMemoryManager > memory_manager=nullptr)
Default constructor.
void run() override
Run the kernels contained in the function.
void configure(ITensor *input, ITensor *output, unsigned int axis, ReductionOperation op, bool keep_dims=true)
Set the input and output tensors.
static IScheduler & get()
Access the scheduler singleton.
Definition: Scheduler.cpp:95