| 275 | |
| 276 | template <KernelType kernel_type> |
| 277 | TfLiteStatus TanhPrepare(TfLiteContext* context, TfLiteNode* node) { |
| 278 | OpData* data = reinterpret_cast<OpData*>(node->user_data); |
| 279 | |
| 280 | TF_LITE_ENSURE_EQ(context, NumInputs(node), 1); |
| 281 | TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1); |
| 282 | const TfLiteTensor* input = GetInput(context, node, 0); |
| 283 | TfLiteTensor* output = GetOutput(context, node, 0); |
| 284 | TF_LITE_ENSURE_EQ(context, input->type, output->type); |
| 285 | |
| 286 | if (kernel_type == kFixedPointOptimized) { |
| 287 | if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) { |
| 288 | static constexpr int kInputIntegerBits = 4; |
| 289 | |
| 290 | const double input_real_multiplier = |
| 291 | input->params.scale * |
| 292 | static_cast<double>(1 << (15 - kInputIntegerBits)); |
| 293 | |
| 294 | const double q = |
| 295 | std::frexp(input_real_multiplier, &data->input_left_shift); |
| 296 | auto q_fixed = static_cast<int32_t>(TfLiteRound(q * (1ll << 15))); |
| 297 | data->input_multiplier = static_cast<int16_t>(q_fixed); |
| 298 | |
| 299 | int16_t input_range_radius = |
| 300 | CalculateInputRadius(kInputIntegerBits, data->input_left_shift, 15); |
| 301 | data->input_range_radius = input_range_radius; |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | if (kernel_type == kGenericOptimized || kernel_type == kReference) { |
| 306 | if (input->type == kTfLiteUInt8) { |
| 307 | PopulateLookupTable<uint8_t>( |
| 308 | data, input, output, [](float value) { return std::tanh(value); }); |
| 309 | } else if (input->type == kTfLiteInt8) { |
| 310 | PopulateLookupTable<int8_t>(data, input, output, |
| 311 | [](float value) { return std::tanh(value); }); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | if (input->type == kTfLiteInt16) { |
| 316 | static constexpr int kInputIntegerBits = 3; |
| 317 | static constexpr int kOutputFractionalBits = 15; |
| 318 | |
| 319 | // These operators are implemented in fixed-point arithmetic, |
| 320 | // which intrinsically wants symmetric ranges (zero_point==0) |
| 321 | // and power-of-two scales (power-of-two is abbreviated below as POT). |
| 322 | // While more general support would be possible by means of rescaling, |
| 323 | // that would add some overhead and some loss of accuracy and wouldn't |
| 324 | // be used at the moment as current quantized LSTM applications are |
| 325 | // happy with symmetric, power-of-two-scales quantization. So we just |
| 326 | // implement that narrow case only for now. |
| 327 | |
| 328 | TF_LITE_ENSURE_EQ(context, input->params.zero_point, 0); |
| 329 | TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0); |
| 330 | |
| 331 | int input_scale_log2_rounded; |
| 332 | TF_LITE_ENSURE(context, |
| 333 | CheckedLog2(input->params.scale, &input_scale_log2_rounded)); |
| 334 |
nothing calls this directly
no test coverage detected