Files
tensorflow--tensorflow/tensorflow/lite/kernels/elementwise.cc
T
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

613 lines
23 KiB
C++

/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <stdint.h>
#include <stdlib.h>
#include <algorithm>
#include <cmath>
#include <functional>
#include <limits>
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/quantization_util.h"
#include "tensorflow/lite/kernels/internal/reference/integer_ops/lut.h"
#include "tensorflow/lite/kernels/internal/reference/reference_ops.h"
#include "tensorflow/lite/kernels/internal/tensor.h"
#include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/kernels/op_macros.h"
#include "tensorflow/lite/types/half.h"
namespace tflite {
namespace ops {
namespace builtin {
namespace elementwise {
namespace {
const char kAbsName[] = "Abs";
const char kLogName[] = "Log";
const char kRsqrtName[] = "Rsqrt";
struct OpData {
int32_t multiplier;
int32_t shift;
int input_offset;
int output_offset;
bool needs_rescale;
union {
int8_t* lut_int8;
int16_t* lut_int16;
};
};
bool IsNumericSupportedType(const TfLiteType type) {
return type == kTfLiteFloat32;
}
bool IsSinCosSupportedType(const TfLiteType type) {
return type == kTfLiteFloat32 || type == kTfLiteFloat16;
}
bool IsLogicalSupportedType(const TfLiteType type) {
return type == kTfLiteBool;
}
bool IsAbsSupportedType(const TfLiteType type) {
return type == kTfLiteFloat32 || type == kTfLiteInt8 ||
type == kTfLiteInt16 || type == kTfLiteInt32;
}
bool IsRsqrtSupportedType(const TfLiteType type) {
return type == kTfLiteFloat32 || type == kTfLiteInt8 || type == kTfLiteInt16;
}
bool IsSqrtSupportedType(const TfLiteType type) {
return type == kTfLiteFloat32 || type == kTfLiteInt8 || type == kTfLiteInt16;
}
bool IsLogSupportedType(const TfLiteType type) {
return type == kTfLiteFloat32 || type == kTfLiteInt8 || type == kTfLiteInt16;
}
inline void SetAbsOutputMultiplier(const float input_scale,
const float output_scale,
int32_t* multiplier, int32_t* shift) {
QuantizeMultiplier(input_scale / output_scale, multiplier, shift);
}
inline void SetRsqrtOutputMultiplier(const float input_scale,
const float output_scale,
int32_t* multiplier, int32_t* shift) {
const double scale = 1. / (std::sqrt(input_scale) * output_scale);
QuantizeMultiplier(scale, multiplier, shift);
}
void LogLUTPrepare(TfLiteType type, OpData* op_data, float input_scale,
int32_t input_zero_point, float output_scale,
int32_t output_zero_point) {
const float output_min =
(((type == kTfLiteInt8) ? std::numeric_limits<int8>::min()
: std::numeric_limits<int16>::min()) -
output_zero_point) *
output_scale;
const void* lut_func_params = static_cast<const void*>(&output_min);
const auto lut_func = [](float value, const void* lut_func_params) {
if (value <= 0.0f) {
const float output_min = *static_cast<const float*>(lut_func_params);
return output_min;
}
return std::log(value);
};
if (type == kTfLiteInt8) {
if (!op_data->lut_int8) {
op_data->lut_int8 = new int8_t[LUTSize<int8_t>()];
}
LUTPopulate<int8_t>(input_scale, input_zero_point, output_scale,
output_zero_point, lut_func, lut_func_params,
op_data->lut_int8);
} else {
if (!op_data->lut_int16) {
op_data->lut_int16 = new int16_t[LUTSize<int16_t>()];
}
LUTPopulate<int16_t>(input_scale, input_zero_point, output_scale,
output_zero_point, lut_func, lut_func_params,
op_data->lut_int16);
}
}
typedef bool (*IsSupportedType)(TfLiteType);
TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node,
IsSupportedType is_supported_type,
const char* op_name) {
TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
if (!is_supported_type(input->type)) {
TF_LITE_UNSUPPORTED_TYPE(context, input->type, op_name);
}
// For int16 type input, we support both quantized and non-quantized
// evaluation.
if (input->type == kTfLiteInt8 ||
(input->type == kTfLiteInt16 &&
input->quantization.type != kTfLiteNoQuantization)) {
TfLiteTensor* output = GetOutput(context, node, 0);
auto* op_data = static_cast<OpData*>(node->user_data);
TF_LITE_ENSURE_EQ(context, input->quantization.type,
kTfLiteAffineQuantization);
TF_LITE_ENSURE_EQ(context, output->quantization.type,
kTfLiteAffineQuantization);
const auto* input_params =
reinterpret_cast<TfLiteAffineQuantization*>(input->quantization.params);
const auto* output_params = reinterpret_cast<TfLiteAffineQuantization*>(
output->quantization.params);
TF_LITE_ENSURE(context, input_params != nullptr);
TF_LITE_ENSURE(context, input_params->scale != nullptr);
TF_LITE_ENSURE(context, input_params->scale->size > 0);
TF_LITE_ENSURE(context, input_params->zero_point->size > 0);
TF_LITE_ENSURE(context, output_params != nullptr);
TF_LITE_ENSURE(context, output_params->scale != nullptr);
TF_LITE_ENSURE(context, output_params->scale->size > 0);
TF_LITE_ENSURE(context, output_params->zero_point->size > 0);
op_data->input_offset = input_params->zero_point->data[0];
op_data->output_offset = output_params->zero_point->data[0];
if (input->type == kTfLiteInt16) {
TF_LITE_ENSURE_EQ(context, op_data->input_offset, 0);
TF_LITE_ENSURE_EQ(context, op_data->output_offset, 0);
}
const float input_scale = input_params->scale->data[0];
const float output_scale = output_params->scale->data[0];
op_data->needs_rescale = input_scale != output_scale;
if (op_name == kAbsName && op_data->needs_rescale) {
SetAbsOutputMultiplier(input_scale, output_scale, &op_data->multiplier,
&op_data->shift);
} else if (op_name == kRsqrtName) {
if (input->type == kTfLiteInt16) {
const void* lut_func_params = static_cast<const void*>(&output_scale);
const auto lut_func = [](float value, const void* lut_func_params) {
if (value <= 0.0f) {
const float output_scale =
*static_cast<const float*>(lut_func_params);
return std::numeric_limits<int16>::max() * output_scale;
}
return 1.0f / std::sqrt(value);
};
if (!op_data->lut_int16) {
op_data->lut_int16 = new int16_t[LUTSize<int16_t>()];
}
LUTPopulate<int16_t>(input_scale, input_params->zero_point->data[0],
output_scale, output_params->zero_point->data[0],
lut_func, lut_func_params, op_data->lut_int16);
} else {
SetRsqrtOutputMultiplier(input_scale, output_scale,
&op_data->multiplier, &op_data->shift);
}
} else if (op_name == kLogName) {
LogLUTPrepare(input->type, op_data, input_scale, op_data->input_offset,
output_scale, op_data->output_offset);
}
}
return context->ResizeTensor(context, output,
TfLiteIntArrayCopy(input->dims));
}
template <typename T>
inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
std::function<T(T)> func,
std::function<TfLiteStatus(T)> validate_input_func,
TfLiteType expected_type) {
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
const int64_t num_elements = NumElements(input);
const T* in_data = GetTensorData<T>(input);
T* out_data = GetTensorData<T>(output);
for (int64_t i = 0; i < num_elements; ++i) {
if (validate_input_func) {
TF_LITE_ENSURE_OK(context, validate_input_func(in_data[i]));
}
out_data[i] = func(in_data[i]);
}
return kTfLiteOk;
}
// Non-quantized evaluation of Abs op when input is int16.
inline TfLiteStatus AbsInt16EvalImpl(TfLiteContext* context, TfLiteNode* node,
TfLiteType expected_type) {
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
const int64_t num_elements = NumElements(input);
const int16_t* in_data = GetTensorData<int16_t>(input);
int16_t* out_data = GetTensorData<int16_t>(output);
for (int64_t i = 0; i < num_elements; ++i) {
out_data[i] = static_cast<int16_t>(
std::abs<int32_t>(static_cast<int32_t>(in_data[i])));
}
return kTfLiteOk;
}
template <typename T>
inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
std::function<T(T)> func,
TfLiteType expected_type) {
return EvalImpl<T>(context, node, func, /*validate_input_func=*/nullptr,
expected_type);
}
inline TfLiteStatus EvalNumeric(TfLiteContext* context, TfLiteNode* node,
float float_func(float)) {
return EvalImpl<float>(context, node, float_func, kTfLiteFloat32);
}
inline TfLiteStatus EvalLogical(TfLiteContext* context, TfLiteNode* node,
bool bool_func(bool)) {
return EvalImpl<bool>(context, node, bool_func, kTfLiteBool);
}
void* ElementWiseQuantizedInit(TfLiteContext* context, const char* buffer,
size_t length) {
return new OpData();
}
void ElementWiseQuantizedFree(TfLiteContext* context, void* buffer) {
OpData* data = static_cast<OpData*>(buffer);
if (data && data->lut_int8) {
delete[] data->lut_int8;
}
delete data;
}
template <typename T>
TfLiteStatus AbsEvalQuantized(TfLiteContext* context, TfLiteNode* node,
TfLiteType type) {
const auto* op_data = static_cast<const OpData*>(node->user_data);
const int kMin = std::numeric_limits<T>::min();
const int kMax = std::numeric_limits<T>::max();
std::function<T(T)> func = [&](T i) {
const int32_t value = std::abs(i - op_data->input_offset);
if (!op_data->needs_rescale) {
return static_cast<T>(
std::min(std::max(value + op_data->output_offset, kMin), kMax));
}
const int32_t output = MultiplyByQuantizedMultiplier(
value, op_data->multiplier, op_data->shift) +
op_data->output_offset;
return static_cast<T>(std::min(std::max(output, kMin), kMax));
};
return EvalImpl<T>(context, node, func, type);
}
TfLiteStatus AbsEval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input = GetInput(context, node, 0);
const TfLiteType type = input->type;
switch (type) {
case kTfLiteFloat32:
return EvalImpl<float>(
context, node, [](float f) { return std::abs(f); }, type);
case kTfLiteInt8:
return AbsEvalQuantized<int8_t>(context, node, type);
case kTfLiteInt16:
return input->quantization.type == kTfLiteNoQuantization
? AbsInt16EvalImpl(context, node, type)
: AbsEvalQuantized<int16_t>(context, node, type);
case kTfLiteInt32:
return EvalImpl<int32_t>(
context, node, [](int32_t i) { return std::abs(i); }, type);
default:
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
TfLiteTypeGetName(type));
return kTfLiteError;
}
}
TfLiteStatus SinEval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input = GetInput(context, node, 0);
if (input->type == kTfLiteFloat16) {
return EvalImpl<half>(
context, node,
[](half h) {
return static_cast<half>(std::sin(static_cast<float>(h)));
},
kTfLiteFloat16);
}
return EvalNumeric(context, node, std::sin);
}
TfLiteStatus CosEval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input = GetInput(context, node, 0);
if (input->type == kTfLiteFloat16) {
return EvalImpl<half>(
context, node,
[](half h) {
return static_cast<half>(std::cos(static_cast<float>(h)));
},
kTfLiteFloat16);
}
return EvalNumeric(context, node, std::cos);
}
TfLiteStatus LogEval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
auto op_data = reinterpret_cast<OpData*>(node->user_data);
switch (input->type) {
case kTfLiteFloat32:
return EvalNumeric(context, node, std::log);
case kTfLiteInt8:
reference_integer_ops::LookupTable(
GetTensorData<int8_t>(input),
MatchingFlatSize(GetTensorShape(input), GetTensorShape(output)),
op_data->lut_int8, GetTensorData<int8_t>(output));
return kTfLiteOk;
case kTfLiteInt16:
reference_integer_ops::LookupTable(
GetTensorData<int16_t>(input),
MatchingFlatSize(GetTensorShape(input), GetTensorShape(output)),
op_data->lut_int16, GetTensorData<int16_t>(output));
return kTfLiteOk;
default:
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
TfLiteTypeGetName(input->type));
return kTfLiteError;
}
}
template <typename T>
TfLiteStatus SqrtEvalQuantized(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
const auto* input_params =
reinterpret_cast<TfLiteAffineQuantization*>(input->quantization.params);
const auto* output_params =
reinterpret_cast<TfLiteAffineQuantization*>(output->quantization.params);
const float input_scale = input_params->scale->data[0];
const int input_zp = input_params->zero_point->data[0];
const float output_scale = output_params->scale->data[0];
const int output_zp = output_params->zero_point->data[0];
const int64_t num_elements = NumElements(input);
const T* in_data = GetTensorData<T>(input);
T* out_data = GetTensorData<T>(output);
const int kMin = std::numeric_limits<T>::min();
const int kMax = std::numeric_limits<T>::max();
for (int64_t i = 0; i < num_elements; ++i) {
const float dequantized_input =
input_scale * (static_cast<int>(in_data[i]) - input_zp);
TF_LITE_ENSURE_MSG(context, dequantized_input >= 0.0f,
"Sqrt is only defined for non-negative values");
const float float_output = std::sqrt(dequantized_input);
const int quantized_output =
static_cast<int>(float_output / output_scale) + output_zp;
out_data[i] =
static_cast<T>(std::min(std::max(quantized_output, kMin), kMax));
}
return kTfLiteOk;
}
TfLiteStatus SqrtEval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
const TfLiteType type = input->type;
switch (type) {
case kTfLiteFloat32:
return EvalNumeric(context, node, std::sqrt);
case kTfLiteInt8:
return SqrtEvalQuantized<int8_t>(context, node);
case kTfLiteInt16:
return SqrtEvalQuantized<int16_t>(context, node);
default:
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
TfLiteTypeGetName(type));
return kTfLiteError;
}
}
TfLiteStatus RsqrtEvalQuantizedInt8(TfLiteContext* context, TfLiteNode* node,
TfLiteType type) {
const auto* op_data = static_cast<const OpData*>(node->user_data);
const int kMin = std::numeric_limits<int8_t>::min();
const int kMax = std::numeric_limits<int8_t>::max();
std::function<TfLiteStatus(int8_t)> validate_input_func = [&](int8_t i) {
TF_LITE_ENSURE_MSG(context, i >= op_data->input_offset,
"Rsqrt is only defined for positive values");
return kTfLiteOk;
};
std::function<int8_t(int8_t)> func = [&](int8_t i) {
const int32_t value = (i - op_data->input_offset);
const int32_t kShift = 20; // Shift to keep value integer.
if (value == 0) {
// Assume that any value close to 0 represents the max output value.
return static_cast<int8_t>(kMax);
}
int32_t inv_sqrt_multiplier;
int inv_sqrt_shift;
GetInvSqrtQuantizedMultiplierExp(value, kReverseShift, &inv_sqrt_multiplier,
&inv_sqrt_shift);
const int32_t data = MultiplyByQuantizedMultiplier(1, inv_sqrt_multiplier,
inv_sqrt_shift + kShift);
const int32_t output =
MultiplyByQuantizedMultiplier(data, op_data->multiplier,
op_data->shift - kShift) +
op_data->output_offset;
return static_cast<int8_t>(std::min(std::max(output, kMin), kMax));
};
return EvalImpl<int8_t>(context, node, func, validate_input_func, type);
}
TfLiteStatus RsqrtEvalQuantizedInt16(TfLiteContext* context, TfLiteNode* node,
TfLiteType type) {
auto op_data = reinterpret_cast<OpData*>(node->user_data);
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, 0, &input));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context, GetOutputSafe(context, node, 0, &output));
const int64_t num_elements = NumElements(input);
const int16_t* in_data = GetTensorData<int16_t>(input);
for (int64_t i = 0; i < num_elements; ++i) {
TF_LITE_ENSURE_MSG(context, in_data[i] >= op_data->input_offset,
"Rsqrt is only defined for positive values");
}
reference_integer_ops::LookupTable(
GetTensorData<int16_t>(input),
MatchingFlatSize(GetTensorShape(input), GetTensorShape(output)),
op_data->lut_int16, GetTensorData<int16_t>(output));
return kTfLiteOk;
}
TfLiteStatus RsqrtEval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteType type = GetInput(context, node, 0)->type;
switch (type) {
case kTfLiteFloat32:
return EvalImpl<float>(
context, node, [](float f) { return 1.f / std::sqrt(f); }, type);
case kTfLiteInt8:
return RsqrtEvalQuantizedInt8(context, node, type);
case kTfLiteInt16:
return RsqrtEvalQuantizedInt16(context, node, type);
default:
TF_LITE_KERNEL_LOG(context, "Current data type %s is not supported.",
TfLiteTypeGetName(type));
return kTfLiteError;
}
}
TfLiteStatus SquareEval(TfLiteContext* context, TfLiteNode* node) {
return EvalNumeric(context, node, [](float f) { return f * f; });
}
TfLiteStatus LogicalNotEval(TfLiteContext* context, TfLiteNode* node) {
return EvalLogical(context, node, [](bool v) { return !v; });
}
} // namespace
} // namespace elementwise
// Given a function...
// template<int T>
// int Foo(int b)
//
// typedef int(*Bar)(int);
//
// MSVC2015 will not see Foo<10> as the same type as Bar.
//
// This works around the issue by instantiating wrapper methods around
// elementwise::GenericPrepare() rather than using a templated
// elementwise::GenericPrepare method.
#define GENERIC_PREPARE(function_name, is_supported_type_function, type_name) \
static TfLiteStatus function_name(TfLiteContext* context, \
TfLiteNode* node) { \
return elementwise::GenericPrepare(context, node, \
is_supported_type_function, type_name); \
}
GENERIC_PREPARE(PrepareAbs, elementwise::IsAbsSupportedType,
elementwise::kAbsName)
TfLiteRegistration* Register_ABS() {
static TfLiteRegistration r = {elementwise::ElementWiseQuantizedInit,
elementwise::ElementWiseQuantizedFree,
PrepareAbs, elementwise::AbsEval};
return &r;
}
GENERIC_PREPARE(PrepareSin, elementwise::IsSinCosSupportedType, "Sin")
TfLiteRegistration* Register_SIN() {
static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, PrepareSin,
elementwise::SinEval};
return &r;
}
GENERIC_PREPARE(PrepareCos, elementwise::IsSinCosSupportedType, "Cos")
TfLiteRegistration* Register_COS() {
static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, PrepareCos,
elementwise::CosEval};
return &r;
}
GENERIC_PREPARE(PrepareLog, elementwise::IsLogSupportedType,
elementwise::kLogName)
TfLiteRegistration* Register_LOG() {
static TfLiteRegistration r = {elementwise::ElementWiseQuantizedInit,
elementwise::ElementWiseQuantizedFree,
PrepareLog, elementwise::LogEval};
return &r;
}
GENERIC_PREPARE(PrepareSqrt, elementwise::IsSqrtSupportedType, "Sqrt")
TfLiteRegistration* Register_SQRT() {
static TfLiteRegistration r = {elementwise::ElementWiseQuantizedInit,
elementwise::ElementWiseQuantizedFree,
PrepareSqrt, elementwise::SqrtEval};
return &r;
}
GENERIC_PREPARE(PrepareRsqrt, elementwise::IsRsqrtSupportedType,
elementwise::kRsqrtName)
TfLiteRegistration* Register_RSQRT() {
static TfLiteRegistration r = {elementwise::ElementWiseQuantizedInit,
elementwise::ElementWiseQuantizedFree,
PrepareRsqrt, elementwise::RsqrtEval};
return &r;
}
GENERIC_PREPARE(PrepareSquare, elementwise::IsNumericSupportedType, "Square")
TfLiteRegistration* Register_SQUARE() {
static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr,
PrepareSquare, elementwise::SquareEval};
return &r;
}
GENERIC_PREPARE(PrepareNot, elementwise::IsLogicalSupportedType, "Not")
TfLiteRegistration* Register_LOGICAL_NOT() {
static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr, PrepareNot,
elementwise::LogicalNotEval};
return &r;
}
} // namespace builtin
} // namespace ops
} // namespace tflite