c1d63172b2b3d7a91446a20a7652445609d89bc2
[platform/core/ml/nnfw.git] / runtime / onert / backend / cpu / ops / ElementwiseActivationLayer.cc
1 /*
2  * Copyright (c) 2020 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 #include "ElementwiseActivationLayer.h"
18
19 #include "OperationUtils.h"
20
21 #include <cker/operation/Logistic.h>
22 #include <cker/operation/ReLU.h>
23 #include <cker/operation/ReLU6.h>
24 #include <cker/operation/Tanh.h>
25
26 namespace onert
27 {
28 namespace backend
29 {
30 namespace cpu
31 {
32 namespace ops
33 {
34
35 ElementwiseActivationLayer::ElementwiseActivationLayer()
36     : _input(nullptr), _output(nullptr), _kernel()
37 {
38   // DO NOTHING
39 }
40
41 void ElementwiseActivationLayer::PopulateLookupTable(const ElementwiseActivationType op_type)
42 {
43   const auto input_scale = static_cast<double>(_input->data_scale());
44   const auto input_zero_point = static_cast<int32_t>(_input->data_offset());
45   const auto output_scale = static_cast<double>(_output->data_scale());
46   const auto output_zero_point = static_cast<int32_t>(_output->data_offset());
47   const float inverse_scale = 1 / output_scale;
48   int32_t maxval = std::numeric_limits<uint8_t>::max();
49   int32_t minval = std::numeric_limits<uint8_t>::min();
50   for (int32_t val = minval; val <= maxval; ++val)
51   {
52     const float dequantized = input_scale * (val - input_zero_point);
53     float transformed = 0.f;
54     if (op_type == ElementwiseActivationType::kTanh)
55     {
56       transformed = std::tanh(dequantized);
57     }
58     else if (op_type == ElementwiseActivationType::kLogistic)
59     {
60       transformed = 1.0f / (1.0f + std::exp(-dequantized));
61     }
62     else
63     {
64       throw std::runtime_error("ElementwiseActivationLayer : unsupported activation type");
65     }
66     const float rescaled = std::round(transformed * inverse_scale);
67     const int32_t quantized = static_cast<int32_t>(rescaled + output_zero_point);
68     _table[val] = static_cast<uint8_t>(std::max(std::min(maxval, quantized), minval));
69   }
70 }
71
72 void ElementwiseActivationLayer::EvalUsingLookupTable(const IPortableTensor *input,
73                                                       IPortableTensor *output)
74 {
75   const int size = MatchingFlatSize(getTensorShape(input), getTensorShape(output));
76   const uint8_t *input_data = reinterpret_cast<const uint8_t *>(input->buffer());
77   uint8_t *output_data = reinterpret_cast<uint8_t *>(output->buffer());
78
79   for (int i = 0; i < size; ++i)
80   {
81     output_data[i] = _table[input_data[i]];
82   }
83 }
84
85 void ElementwiseActivationLayer::configure(const IPortableTensor *input, IPortableTensor *output,
86                                            float alpha, float beta,
87                                            ElementwiseActivationType op_type)
88 {
89   _input = input;
90   _output = output;
91
92   switch (op_type)
93   {
94     case ElementwiseActivationType::kLogistic:
95       if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM)
96       {
97         PopulateLookupTable(op_type);
98         _kernel = std::bind(&ElementwiseActivationLayer::EvalUsingLookupTable, this,
99                             std::placeholders::_1, std::placeholders::_2);
100       }
101       else if (_input->data_type() == OperandType::FLOAT32)
102       {
103         _kernel = [](const IPortableTensor *input, IPortableTensor *output) {
104           nnfw::cker::Logistic(getTensorShape(input),
105                                reinterpret_cast<const float *>(input->buffer()),
106                                getTensorShape(output), reinterpret_cast<float *>(output->buffer()));
107         };
108       }
109       else
110       {
111         throw std::runtime_error{"ElementwiseActivationLayer(Logistic): unsupported data type"};
112       }
113       break;
114     case ElementwiseActivationType::kReLU:
115       if (_input->data_type() == OperandType::FLOAT32)
116       {
117         if (alpha == std::numeric_limits<float>::infinity() && beta == 0.f)
118         {
119           _kernel = [](const IPortableTensor *input, IPortableTensor *output) {
120             nnfw::cker::ReLU(getTensorShape(input),
121                              reinterpret_cast<const float *>(input->buffer()),
122                              getTensorShape(output), reinterpret_cast<float *>(output->buffer()));
123           };
124         }
125         else if (alpha == 6.f && beta == 0.f)
126         {
127           _kernel = [](const IPortableTensor *input, IPortableTensor *output) {
128             nnfw::cker::ReLU6(getTensorShape(input),
129                               reinterpret_cast<const float *>(input->buffer()),
130                               reinterpret_cast<float *>(output->buffer()));
131           };
132         }
133         else
134         {
135           throw std::runtime_error(
136               "ElementwiseActivationLayer : This layer suppports only ReLU(0-inf) and ReLU6(0-6)");
137         }
138       }
139       else
140       {
141         throw std::runtime_error{"ElementwiseActivationLayer(ReLU): unsupported data type"};
142       }
143       break;
144     case ElementwiseActivationType::kTanh:
145       if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM)
146       {
147         PopulateLookupTable(op_type);
148         _kernel = std::bind(&ElementwiseActivationLayer::EvalUsingLookupTable, this,
149                             std::placeholders::_1, std::placeholders::_2);
150       }
151       else if (_input->data_type() == OperandType::FLOAT32)
152       {
153         _kernel = [](const IPortableTensor *input, IPortableTensor *output) {
154           nnfw::cker::Tanh(getTensorShape(input), reinterpret_cast<const float *>(input->buffer()),
155                            getTensorShape(output), reinterpret_cast<float *>(output->buffer()));
156         };
157       }
158       else
159       {
160         throw std::runtime_error{"ElementwiseActivationLayer(Logistic): unsupported data type"};
161       }
162       break;
163     default:
164       throw std::runtime_error("ElementwiseActivationLayer: unsupported op type");
165   }
166 }
167
168 void ElementwiseActivationLayer::run() { _kernel(_input, _output); }
169
170 } // namespace ops
171 } // namespace cpu
172 } // namespace backend
173 } // namespace onert