02ee4ad8aca4ecd3e79cecc51a0f580ccd0fea34
[platform/core/ml/nnfw.git] / compute / ARMComputeEx / src / runtime / CL / functions / CLReduceOperation.cpp
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * Copyright (c) 2017-2018 ARM Limited.
19  *
20  * SPDX-License-Identifier: MIT
21  *
22  * Permission is hereby granted, free of charge, to any person obtaining a copy
23  * of this software and associated documentation files (the "Software"), to
24  * deal in the Software without restriction, including without limitation the
25  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
26  * sell copies of the Software, and to permit persons to whom the Software is
27  * furnished to do so, subject to the following conditions:
28  *
29  * The above copyright notice and this permission notice shall be included in all
30  * copies or substantial portions of the Software.
31  *
32  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38  * SOFTWARE.
39  */
40
41 #include "arm_compute/runtime/CL/functions/CLReduceOperation.h"
42
43 #include "arm_compute/core/TensorShape.h"
44 #include "arm_compute/runtime/CL/CLScheduler.h"
45
46 using namespace arm_compute;
47
48 CLReduceOperation::CLReduceOperation(std::shared_ptr<IMemoryManager> memory_manager)
49     : _memory_group(std::move(memory_manager)), _input(nullptr), _output(nullptr), _axis(),
50       _keep_dims(false), _interm_tensors(), _reduce_kernels(), _reshape()
51 {
52 }
53
54 Status CLReduceOperation::validate(const ITensorInfo *input, const ITensorInfo *output,
55                                    const std::set<uint32_t> &axis, bool keep_dims,
56                                    const ReductionOperation &op)
57 {
58   const size_t num_of_kernels = axis.size();
59   const size_t num_of_interm_tensors = num_of_kernels - (keep_dims ? 1 : 0);
60
61   ARM_COMPUTE_RETURN_ERROR_ON(num_of_kernels < 1);
62
63   // Create temporary tensor infos
64   auto interm_tensors = support::cpp14::make_unique<TensorInfo[]>(num_of_interm_tensors);
65
66   // Create intermediate tensor info
67   TensorShape shape{input->tensor_shape()};
68
69   auto it = axis.begin();
70   for (size_t i = 0; i < num_of_interm_tensors; ++i, ++it)
71   {
72     shape.set(*it, 1, false);
73     interm_tensors[i].set_data_type(input->data_type());
74     interm_tensors[i].set_tensor_shape(shape);
75     interm_tensors[i].set_num_channels(input->num_channels());
76     interm_tensors[i].set_data_layout(input->data_layout());
77     interm_tensors[i].set_quantization_info(input->quantization_info());
78   }
79
80   // Set a vector that is ordered ITensorInfo sequentially.
81   std::vector<const ITensorInfo *> tensors;
82   tensors.emplace_back(input);
83   for (size_t i = 0; i < num_of_interm_tensors; ++i)
84   {
85     tensors.emplace_back(interm_tensors.get() + i);
86   }
87   tensors.emplace_back(output);
88
89   // Validate ReduceOperation only on all kernels
90   it = axis.begin();
91   for (size_t i = 0; i < num_of_kernels; ++i, ++it)
92   {
93     ARM_COMPUTE_RETURN_ON_ERROR(
94         CLReduceOperationKernel::validate(tensors[i], tensors[i + 1], *it, op));
95   }
96
97   if (!keep_dims)
98   {
99     ARM_COMPUTE_RETURN_ON_ERROR(
100         CLReshapeLayer::validate(&interm_tensors[num_of_interm_tensors - 1], output));
101   }
102
103   return Status{};
104 }
105
106 void CLReduceOperation::configure(ICLTensor *input, ICLTensor *output,
107                                   const std::set<uint32_t> &axis, bool keep_dims,
108                                   ReductionOperation op)
109 {
110   ARM_COMPUTE_ERROR_THROW_ON(validate(input->info(), output->info(), axis, keep_dims, op));
111
112   _axis = axis;
113
114   _input = input;
115   _output = output;
116   _keep_dims = keep_dims;
117
118   // NOTE The axis must have no duplication.
119   const size_t num_of_kernels = axis.size();
120   const size_t num_of_interm_tensors = num_of_kernels - (keep_dims ? 1 : 0);
121
122   if (num_of_kernels < 1)
123   {
124     throw std::runtime_error("CLReduceOperation: there is no axis to reduce");
125   }
126
127   _interm_tensors = support::cpp14::make_unique<CLTensor[]>(num_of_interm_tensors);
128   _reduce_kernels = support::cpp14::make_unique<CLReduceOperationKernel[]>(num_of_kernels);
129
130   // Set a vector that is ordered ICLTensors sequentially.
131   std::vector<ICLTensor *> tensors;
132   tensors.emplace_back(input);
133   for (size_t i = 0; i < num_of_interm_tensors; ++i)
134   {
135     tensors.emplace_back(_interm_tensors.get() + i);
136   }
137   tensors.emplace_back(output);
138
139   // Apply ReductionOperation on all kernels
140   TensorShape shape{input->info()->tensor_shape()};
141   auto it = axis.begin();
142   for (size_t i = 0; i < num_of_kernels; ++i, ++it)
143   {
144     shape.set(*it, 1, false);
145     if (!keep_dims || i != (num_of_kernels - 1))
146     {
147       _interm_tensors[i].allocator()->init(input->info()->clone()->set_tensor_shape(shape));
148       _memory_group.manage(&_interm_tensors[i]);
149     }
150     _reduce_kernels[i].configure(tensors[i], tensors[i + 1], *it, op);
151     if (i != 0)
152     {
153       _interm_tensors[i - 1].allocator()->allocate();
154     }
155   }
156
157   // Configure reshape layer if we want to drop the dimensions
158   if (!keep_dims)
159   {
160     _reshape.configure(&_interm_tensors[num_of_interm_tensors - 1], output);
161     _interm_tensors[num_of_interm_tensors - 1].allocator()->allocate();
162   }
163 }
164
165 void CLReduceOperation::run()
166 {
167   MemoryGroupResourceScope scope_mg(_memory_group);
168
169   const size_t num_of_kernels = _axis.size();
170   for (size_t i = 0; i < num_of_kernels; ++i)
171   {
172     CLScheduler::get().enqueue(_reduce_kernels[i]);
173   }
174
175   if (!_keep_dims)
176   {
177     _reshape.run();
178   }
179 }