Imported Upstream version 1.9.0
[platform/core/ml/nnfw.git] / runtime / onert / core / src / interp / operations / ElementwiseActivations.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 <cmath>
18
19 #include "OperationUtil.h"
20
21 #include "interp/Registration.h"
22
23 #include "ir/operation/ElementwiseActivation.h"
24
25 #include <misc/polymorphic_downcast.h>
26 #include <cker/operation/Logistic.h>
27 #include <cker/operation/Tanh.h>
28
29 namespace onert
30 {
31 namespace interp
32 {
33 namespace
34 {
35
36 enum class ActivationType
37 {
38   Logistic,
39   ReLU,
40   Tanh
41 };
42
43 void prepare(ExecEnv *env, const ir::Operation &node)
44 {
45   const auto input_index = node.getInputs().at(0);
46   const auto output_index = node.getOutputs().at(0);
47
48   const auto input_tensor = env->tensorAt(input_index);
49
50   const auto output_info = env->graph().operands().at(output_index).info();
51   if (output_info.total_size() == 0)
52   {
53     // Output's shape and type is same with input
54     auto input_info = input_tensor->tensorInfo();
55     // We can handle already allocated (ex. model output)
56     env->allocateIfNeeded(output_index, input_info);
57   }
58   else
59   {
60     env->allocateIfNeeded(output_index, output_info);
61   }
62
63   const auto output_tensor = env->tensorAt(output_index);
64   // Check shape and type lhs is same with output
65   // TODO Util function to compare TensorInfo
66   if (input_tensor->data_type() != output_tensor->data_type())
67   {
68     throw std::runtime_error{"Interp(ElementwiseActivation): Invalid output type"};
69   }
70 }
71
72 template <ActivationType act_type>
73 void evalFloat(const float *input_ptr, float *output_ptr, uint64_t num_elements, float alpha,
74                float beta)
75 {
76   std::function<float(const float &)> fn = [](const float &) { return std::nanf(""); };
77   switch (act_type)
78   {
79     case ActivationType::ReLU:
80       fn = [alpha, beta](const float &in) { return std::min(std::max(beta, in), alpha); };
81       break;
82     case ActivationType::Tanh:
83       fn = [](const float &in) { return std::tanh(in); };
84       break;
85     default:
86       throw std::runtime_error{"Interp(ElementwiseActivation): NYI - Unsupported activation"};
87       break;
88   }
89
90   const float *input_end = input_ptr + num_elements;
91   for (; input_ptr < input_end; input_ptr++, output_ptr++)
92   {
93     *output_ptr = fn(*input_ptr);
94   }
95 }
96
97 template <ActivationType act_type> void invoke(const ExecEnv *env, const ir::Operation &node)
98 {
99   const auto input_index = node.getInputs().at(0);
100   const auto output_index = node.getOutputs().at(0);
101
102   // Check lhs shape is same with rhs (with broadcast)
103   const auto input_tensor = env->tensorAt(input_index);
104   const auto output_tensor = env->tensorAt(output_index);
105
106   const auto data_type = input_tensor->data_type();
107   if (data_type == ir::DataType::FLOAT32)
108   {
109     uint64_t elements = input_tensor->num_elements();
110     const float *input_start = reinterpret_cast<const float *>(input_tensor->bufferRO());
111     float *out = reinterpret_cast<float *>(output_tensor->buffer());
112     if (act_type == ActivationType::Logistic)
113     {
114       const auto cker_input_shape = convertShape(input_tensor->tensorInfo().shape());
115       const auto cker_output_shape = convertShape(output_tensor->tensorInfo().shape());
116       nnfw::cker::Logistic(cker_input_shape, input_start, cker_output_shape, out);
117     }
118     else
119     {
120       const auto &act_node =
121           nnfw::misc::polymorphic_downcast<const ir::operation::ElementwiseActivation &>(node);
122       evalFloat<act_type>(input_start, out, elements, act_node.param().alpha,
123                           act_node.param().beta);
124     }
125   }
126   else
127   {
128     throw std::runtime_error{"Interp(" + node.name() + "): NYI - Support float only"};
129   }
130 }
131
132 void invokeElementwiseActivation(const ExecEnv *env, const ir::Operation &node)
133 {
134   const auto &act_node =
135       nnfw::misc::polymorphic_downcast<const ir::operation::ElementwiseActivation &>(node);
136   switch (act_node.param().op_type)
137   {
138     case ir::operation::ElementwiseActivation::Type::LOGISTIC:
139       invoke<ActivationType::Logistic>(env, node);
140       break;
141     case ir::operation::ElementwiseActivation::Type::RELU:
142       invoke<ActivationType::ReLU>(env, node);
143       break;
144     case ir::operation::ElementwiseActivation::Type::TANH:
145       invoke<ActivationType::Tanh>(env, node);
146       break;
147     default:
148       throw std::runtime_error("Interp(" + node.name() + "): NYI - Unsupported activation");
149   }
150 }
151
152 } // namespace
153
154 OpKernel *getElementwiseActivation()
155 {
156   static OpKernel kernel = {prepare, invokeElementwiseActivation};
157   return &kernel;
158 }
159
160 } // namespace interp
161 } // namespace onert