2 * Copyright (c) 2020 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "kernels/Utils.h"
19 #include "SISOKernel.h"
23 namespace luci_interpreter
30 void calculateArithmeticData(const circle::Tensor *input, const circle::Tensor *output,
31 int32_t &input_zero_point, int32_t &input_range_radius,
32 int32_t &input_multiplier, int &input_left_shift)
34 const auto input_dtype = Tensor::element_type(input);
41 static constexpr int input_integer_bits = 4;
42 const double input_real_multiplier = static_cast<double>(Tensor::scale(input)) *
43 static_cast<double>(1 << (31 - input_integer_bits));
45 const double q = std::frexp(input_real_multiplier, &input_left_shift);
46 input_multiplier = static_cast<int32_t>(std::round(q * (1ll << 31)));
47 input_range_radius = kernels::calculateInputRadius(input_integer_bits, input_left_shift, 31);
53 static constexpr int input_integer_bits = 3;
54 static constexpr int output_fractional_bits = 15;
56 // These operators are implemented in fixed-point arithmetic,
57 // which intrinsically wants symmetric ranges (zero_point==0)
58 // and power-of-two scales (power-of-two is abbreviated below as POT).
59 // While more general support would be possible by means of rescaling,
60 // that would add some overhead and some loss of accuracy and wouldn't
61 // be used at the moment as current quantized LSTM applications are
62 // happy with symmetric, power-of-two-scales quantization. So we just
63 // implement that narrow case only for now.
65 int input_scale_log2_rounded;
66 bool param_scale_pot = kernels::checkedLog2(Tensor::scale(input), &input_scale_log2_rounded);
68 input_left_shift = (15 - input_integer_bits) + input_scale_log2_rounded;
69 param_scale_pot &= (input_left_shift == 0 || input_left_shift == 1);
77 // Calculate multiplier to change input scale to 1/(3*4096)
78 // as required by the table lookup.
79 // The number 3.0 in the multiplier comes from here,
80 // because the interval is [-10.7, 10.7] instead of [-8, 8].
81 // So, in this scaling +/-2^17 represents +/-10.7.
83 double multiplier = static_cast<double>(Tensor::scale(input)) * 4096.0 * 3.0;
86 while (multiplier <= 32767.0 / 2.0 && input_left_shift <= 30)
89 multiplier = multiplier * 2.0;
92 input_multiplier = static_cast<int32_t>(multiplier);
95 int output_scale_log2_rounded;
96 kernels::checkedLog2(Tensor::scale(output), &output_scale_log2_rounded);
97 assert(output_scale_log2_rounded == -output_fractional_bits);
101 assert(false && "Unsupported type");
107 void evalInteger(const circle::Tensor *input, const circle::Tensor *output,
108 BaseRuntimeGraph *runtime_graph)
110 int32_t input_zero_point = 0;
111 int32_t input_range_radius = 0;
112 int32_t input_multiplier = 0;
113 int input_left_shift = 0;
115 calculateArithmeticData(input, output, input_zero_point, input_range_radius, input_multiplier,
118 const auto *input_data = runtime_graph->getDataByTensor(input);
121 auto *output_data = runtime_graph->getDataByTensor(output);
124 const int flat_size = kernels::getTensorRuntimeShape(input, runtime_graph).flatSize();
126 const auto input_dtype = Tensor::element_type(input);
132 luci_interpreter_pal::Tanh(
133 input_zero_point, input_range_radius, input_multiplier, input_left_shift,
134 flat_size, kernels::getTensorData<int8_t>(input_data), kernels::getTensorData<int8_t>(output_data));
138 luci_interpreter_pal::Tanh(input_multiplier, input_left_shift, flat_size,
139 kernels::getTensorData<int16_t>(input_data),
140 kernels::getTensorData<int16_t>(output_data));
143 assert(false && "Not support yet");
148 void configure_kernel_CircleTanh(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph)
150 kernels::SISOKernel kernel(cur_op, runtime_graph);
152 LUCI_INTERPRETER_CHECK(Tensor::element_type(kernel.input()) ==
153 Tensor::element_type(kernel.output()));
156 void execute_kernel_CircleTanh(const circle::Operator *cur_op, BaseRuntimeGraph *runtime_graph)
158 kernels::SISOKernel kernel(cur_op, runtime_graph);
160 const auto *input_data = runtime_graph->getDataByTensor(kernel.input());
163 auto *output_data = runtime_graph->getDataByTensor(kernel.output());
165 bool is_inplace = runtime_graph->is_inplace_op(cur_op);
167 switch (Tensor::element_type(kernel.input()))
170 case DataType::FLOAT32:
172 const float *input_data_float = kernels::getTensorData<float>(input_data);
173 float *output_data_float = kernels::getTensorData<float>(output_data);
176 output_data_float = const_cast<float *>(input_data_float);
179 assert(output_data_float);
181 const int flat_size =
182 kernels::getTensorRuntimeShape(kernel.input(), runtime_graph).flatSize();
184 luci_interpreter_pal::Tanh(flat_size, input_data_float, output_data_float);
194 evalInteger(kernel.input(), kernel.output(), runtime_graph);
198 assert(false && "Unsupported type");
202 runtime_graph->makeInplaceOperation(kernel.input(), kernel.output());
204 } // namespace luci_interpreter