ONE - On-device Neural Engine
Loading...
Searching...
No Matches
ReduceMax.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
3 * Copyright 2019 The TensorFlow Authors. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "kernels/ReduceMax.h"
19
20#include "kernels/Utils.h"
21
22#include <tensorflow/lite/kernels/internal/reference/reduce.h>
23
24#include <stdexcept>
25#include <limits>
26
27namespace luci_interpreter
28{
29namespace kernels
30{
31
32// Returns the number of axes that will be reduced. Removes duplicates.
33static int getAxisReductionCount(const int32_t *axes_data, int num_axes, int input_num_dims)
34{
35 int reduction_count = num_axes;
36 for (int i = 0; i < num_axes; ++i)
37 {
38 int current = axes_data[i] >= 0 ? axes_data[i] : axes_data[i] + input_num_dims;
39 assert(current >= 0 && current < input_num_dims);
40 for (int j = 0; j < i; j++)
41 {
42 int previous = axes_data[j] >= 0 ? axes_data[j] : axes_data[j] + input_num_dims;
43 // This checks for duplicate axis
44 if (current == previous)
45 {
46 --reduction_count;
47 break;
48 }
49 }
50 }
51 return reduction_count;
52}
53
54static Shape getOutputShape(const Shape &input_shape, const int32_t *axes_data, int num_axes,
55 bool keep_dims)
56{
57 int input_num_dims = input_shape.num_dims();
58 if (input_num_dims == 0)
59 {
60 return Shape(0);
61 }
62
63 if (keep_dims)
64 {
65 Shape output_shape(input_num_dims);
66 for (int idx = 0; idx < input_num_dims; ++idx)
67 {
68 bool is_axis = false;
69 for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx)
70 {
71 if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx)
72 {
73 is_axis = true;
74 break;
75 }
76 }
77 if (is_axis)
78 {
79 output_shape.dim(idx) = 1;
80 }
81 else
82 {
83 output_shape.dim(idx) = input_shape.dim(idx);
84 }
85 }
86 return output_shape;
87 }
88 else
89 {
90 int num_reduce_axes = getAxisReductionCount(axes_data, num_axes, input_num_dims);
91 Shape output_shape(input_num_dims - num_reduce_axes);
92 int num_skip_axes = 0;
93 for (int idx = 0; idx < input_num_dims; ++idx)
94 {
95 bool is_axis = false;
96 for (int axis_idx = 0; axis_idx < num_axes; ++axis_idx)
97 {
98 if (axes_data[axis_idx] == idx || axes_data[axis_idx] + input_num_dims == idx)
99 {
100 ++num_skip_axes;
101 is_axis = true;
102 break;
103 }
104 }
105 if (!is_axis)
106 {
107 output_shape.dim(idx - num_skip_axes) = input_shape.dim(idx);
108 }
109 }
110 return output_shape;
111 }
112}
113
114ReduceMax::ReduceMax(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index,
115 Tensor *resolved_axes, const ReducerParams &params)
116 : KernelWithParams<ReducerParams>({input, axes}, {output, temp_index, resolved_axes}, params)
117{
118}
119
121{
122 LUCI_INTERPRETER_CHECK(input()->element_type() == output()->element_type());
123 LUCI_INTERPRETER_CHECK(axes()->element_type() == DataType::S32);
124
125 const Shape &input_shape = input()->shape();
126 int input_num_dims = input_shape.num_dims();
127
128 const auto *axes_data = getTensorData<int32_t>(axes());
129 int num_axes = axes()->shape().num_elements();
130 LUCI_INTERPRETER_CHECK(num_axes <= 4);
131
132 // We compute shapes of outputs in configure, assuming that outputs have
133 // static shape
134 // TODO Support dynamic shape
135 Shape output_shape = getOutputShape(input_shape, axes_data, num_axes, _params.keep_dims);
137
138 auto temp_index = getOutputTensors()[1];
139 auto resolved_axes = getOutputTensors()[2];
140
141 temp_index->resize(Shape(input_num_dims));
142 resolved_axes->resize(Shape(num_axes));
143}
144
146{
147 switch (input()->element_type())
148 {
149 case DataType::FLOAT32:
150 evalFloat();
151 break;
152 case DataType::BOOL:
153 evalBool();
154 break;
155 // TODO Support quantized kernels
156 default:
157 throw std::runtime_error("luci-intp ReduceMax Unsupported type.");
158 }
159}
160
161void ReduceMax::evalFloat() const
162{
163 const auto *axes_data = getTensorData<int32_t>(axes());
164 int num_axes = axes()->shape().num_elements();
165
166 auto temp_index = getOutputTensors()[1];
167 auto resolved_axes = getOutputTensors()[2];
168
169 int num_resolved_axis = 0;
171 tflite::reference_ops::ResolveAxis(input()->shape().num_dims(), axes_data, num_axes,
172 getTensorData<int>(resolved_axes), &num_resolved_axis));
173
174 float init_value = std::numeric_limits<float>::lowest();
175 tflite::reference_ops::ReduceGeneric<float>(
176 getTensorData<float>(input()), getTensorShape(input()).DimsData(), input()->shape().num_dims(),
177 getTensorData<float>(output()), getTensorShape(output()).DimsData(),
178 output()->shape().num_dims(), axes_data, num_axes, _params.keep_dims,
179 getTensorData<int>(temp_index), getTensorData<int>(resolved_axes), init_value,
180 [](const float current, const float in) -> float { return (in > current) ? in : current; });
181}
182
183void ReduceMax::evalBool() const
184{
185 const auto *axes_data = getTensorData<int32_t>(axes());
186 int num_axes = axes()->shape().num_elements();
187
188 auto temp_index = getOutputTensors()[1];
189 auto resolved_axes = getOutputTensors()[2];
190
191 int num_resolved_axis = 0;
193 tflite::reference_ops::ResolveAxis(input()->shape().num_dims(), axes_data, num_axes,
194 getTensorData<int>(resolved_axes), &num_resolved_axis));
195
196 bool init_value = std::numeric_limits<bool>::lowest();
197 tflite::reference_ops::ReduceGeneric<bool>(
198 getTensorData<bool>(input()), getTensorShape(input()).DimsData(), input()->shape().num_dims(),
199 getTensorData<bool>(output()), getTensorShape(output()).DimsData(),
200 output()->shape().num_dims(), axes_data, num_axes, _params.keep_dims,
201 getTensorData<int>(temp_index), getTensorData<int>(resolved_axes), init_value,
202 [](const bool current, const bool in) -> bool { return (in > current) ? in : current; });
203}
204
205} // namespace kernels
206} // namespace luci_interpreter
const std::vector< Tensor * > & getOutputTensors() const
Definition Kernel.h:40
int32_t num_elements() const
Definition Tensor.h:53
int num_dims() const
Definition Tensor.h:39
void resize(const Shape &new_shape)
Definition Tensor.cpp:56
const Shape & shape() const
Definition Tensor.h:107
const Tensor * input() const
Definition ReduceMax.h:36
ReduceMax(const Tensor *input, const Tensor *axes, Tensor *output, Tensor *temp_index, Tensor *resolved_axes, const ReducerParams &params)
const Tensor * axes() const
Definition ReduceMax.h:37
#define LUCI_INTERPRETER_CHECK(cond)
Definition Utils.h:36
const luci_interpreter::RuntimeShape output_shape
tflite::RuntimeShape getTensorShape(const Tensor *tensor)
Definition Utils.h:194
Definition Shape.h:28