Files
tensorflow--tensorflow/tensorflow/lite/delegates/ynnpack/utils.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

779 lines
28 KiB
C++

/* Copyright 2026 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 "tensorflow/lite/delegates/ynnpack/utils.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <limits>
#include <numeric>
#include <vector>
#include "ynnpack/include/ynnpack.h" // from @XNNPACK
#include "tensorflow/lite/builtin_ops.h"
#include "tensorflow/lite/core/c/builtin_op_data.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/logger.h"
#include "tensorflow/lite/minimal_logging.h"
#include "tensorflow/lite/types/half.h"
namespace tflite {
namespace ynnpack {
namespace {
uint16_t FloatToBfloat16(float f) {
uint32_t val;
std::memcpy(&val, &f, sizeof(float));
// Round to nearest even.
uint32_t rounding_bias = 0x7fff + ((val >> 16) & 1);
val += rounding_bias;
return static_cast<uint16_t>(val >> 16);
}
template <typename T>
T QuantizeValue(float value, float scale, int32_t zero_point) {
int32_t quantized = zero_point + std::round(value / scale);
return static_cast<T>(std::max<int32_t>(
std::numeric_limits<T>::min(),
std::min<int32_t>(std::numeric_limits<T>::max(), quantized)));
}
} // namespace
TfLiteStatus GetTfLiteTensorValueAsDouble(TfLiteContext* context,
const TfLiteTensor& tensor, int index,
double* value) {
TF_LITE_ENSURE(context, tensor.data.raw != nullptr);
int num_elements = tflite::NumElements(&tensor);
TF_LITE_ENSURE_MSG(context, index >= 0 && index < num_elements,
"Index %d out of bounds for tensor with %d elements",
index, num_elements);
switch (tensor.type) {
case kTfLiteFloat32:
*value = reinterpret_cast<const float*>(tensor.data.raw)[index];
break;
case kTfLiteInt32:
*value = reinterpret_cast<const int32_t*>(tensor.data.raw)[index];
break;
case kTfLiteFloat16:
*value = static_cast<float>(
reinterpret_cast<const tflite::half*>(tensor.data.raw)[index]);
break;
case kTfLiteInt8:
*value = reinterpret_cast<const int8_t*>(tensor.data.raw)[index];
break;
case kTfLiteUInt8:
*value = reinterpret_cast<const uint8_t*>(tensor.data.raw)[index];
break;
default:
TF_LITE_ENSURE_MSG(context, false, "Unsupported type %d", tensor.type);
}
return kTfLiteOk;
}
ynn_type GetYnnType(TfLiteType type) {
switch (type) {
case kTfLiteFloat32:
return ynn_type_fp32;
case kTfLiteInt32:
return ynn_type_int32;
case kTfLiteFloat16:
return ynn_type_fp16;
case kTfLiteFloat64:
return ynn_type_fp64;
case kTfLiteBFloat16:
return ynn_type_bf16;
case kTfLiteInt8:
return ynn_type_int8;
case kTfLiteUInt8:
return ynn_type_uint8;
case kTfLiteInt4:
return ynn_type_int4;
case kTfLiteUInt4:
return ynn_type_uint4;
case kTfLiteInt2:
return ynn_type_int2;
default:
return ynn_type_invalid;
}
}
ynn_unary_operator GetYnnUnaryOperator(int builtin_code) {
switch (builtin_code) {
case kTfLiteBuiltinAbs:
case kTfLiteBuiltinStablehloAbs:
return ynn_unary_abs;
case kTfLiteBuiltinCeil:
return ynn_unary_ceil;
case kTfLiteBuiltinCos:
case kTfLiteBuiltinStablehloCosine:
return ynn_unary_cosine;
case kTfLiteBuiltinExp:
case kTfLiteBuiltinStablehloExponential:
return ynn_unary_exp;
case kTfLiteBuiltinFloor:
case kTfLiteBuiltinStablehloFloor:
return ynn_unary_floor;
case kTfLiteBuiltinLog:
case kTfLiteBuiltinStablehloLog:
return ynn_unary_log;
case kTfLiteBuiltinNeg:
case kTfLiteBuiltinStablehloNegate:
return ynn_unary_negate;
case kTfLiteBuiltinRsqrt:
case kTfLiteBuiltinStablehloRsqrt:
return ynn_unary_reciprocal_square_root;
case kTfLiteBuiltinRound:
return ynn_unary_round;
case kTfLiteBuiltinLogistic:
case kTfLiteBuiltinStablehloLogistic:
return ynn_unary_sigmoid;
case kTfLiteBuiltinSin:
return ynn_unary_sine;
case kTfLiteBuiltinSquare:
return ynn_unary_square;
case kTfLiteBuiltinSqrt:
return ynn_unary_square_root;
case kTfLiteBuiltinTanh:
case kTfLiteBuiltinStablehloTanh:
return ynn_unary_tanh;
case kTfLiteBuiltinSign:
return ynn_unary_sign;
case kTfLiteBuiltinCast:
case kTfLiteBuiltinStablehloConvert:
return ynn_unary_convert;
default:
return ynn_unary_invalid;
}
}
ynn_binary_operator GetYnnBinaryOperator(int builtin_code) {
switch (builtin_code) {
case kTfLiteBuiltinAdd:
case kTfLiteBuiltinStablehloAdd:
return ynn_binary_add;
case kTfLiteBuiltinDiv:
case kTfLiteBuiltinStablehloDivide:
return ynn_binary_divide;
case kTfLiteBuiltinMaximum:
case kTfLiteBuiltinStablehloMaximum:
return ynn_binary_max;
case kTfLiteBuiltinMinimum:
case kTfLiteBuiltinStablehloMinimum:
return ynn_binary_min;
case kTfLiteBuiltinMul:
case kTfLiteBuiltinStablehloMultiply:
return ynn_binary_multiply;
case kTfLiteBuiltinPow:
case kTfLiteBuiltinStablehloPower:
return ynn_binary_pow;
case kTfLiteBuiltinSub:
case kTfLiteBuiltinStablehloSubtract:
return ynn_binary_subtract;
case kTfLiteBuiltinSquaredDifference:
return ynn_binary_squared_difference;
case kTfLiteBuiltinPrelu:
return ynn_binary_leaky_relu;
default:
return ynn_binary_invalid;
}
}
ynn_reduce_operator GetYnnReduceOperator(int builtin_code) {
switch (builtin_code) {
case kTfLiteBuiltinSum:
case kTfLiteBuiltinMean:
return ynn_reduce_sum;
case kTfLiteBuiltinReduceMin:
return ynn_reduce_min;
case kTfLiteBuiltinReduceMax:
return ynn_reduce_max;
default:
return ynn_reduce_invalid;
}
}
bool IsUnaryOp(int builtin_code) {
return GetYnnUnaryOperator(builtin_code) != ynn_unary_invalid ||
builtin_code == kTfLiteBuiltinGelu ||
builtin_code == kTfLiteBuiltinElu ||
builtin_code == kTfLiteBuiltinLeakyRelu ||
builtin_code == kTfLiteBuiltinHardSwish ||
builtin_code == kTfLiteBuiltinRelu ||
builtin_code == kTfLiteBuiltinRelu6 ||
builtin_code == kTfLiteBuiltinReluN1To1 ||
builtin_code == kTfLiteBuiltinRelu0To1;
}
bool IsBinaryOp(int builtin_code) {
return GetYnnBinaryOperator(builtin_code) != ynn_binary_invalid;
}
bool IsStablehloOp(int builtin_code) {
switch (builtin_code) {
case kTfLiteBuiltinStablehloLogistic:
case kTfLiteBuiltinStablehloAdd:
case kTfLiteBuiltinStablehloDivide:
case kTfLiteBuiltinStablehloMultiply:
case kTfLiteBuiltinStablehloMaximum:
case kTfLiteBuiltinStablehloAbs:
case kTfLiteBuiltinStablehloCosine:
case kTfLiteBuiltinStablehloExponential:
case kTfLiteBuiltinStablehloFloor:
case kTfLiteBuiltinStablehloLog:
case kTfLiteBuiltinStablehloMinimum:
case kTfLiteBuiltinStablehloNegate:
case kTfLiteBuiltinStablehloPower:
case kTfLiteBuiltinStablehloRsqrt:
case kTfLiteBuiltinStablehloSubtract:
case kTfLiteBuiltinStablehloTanh:
case kTfLiteBuiltinStablehloConvert:
case kTfLiteBuiltinStablehloClamp:
return true;
default:
return false;
}
}
bool IsQuantized(const TfLiteTensor& tensor) {
if (tensor.type == kTfLiteFloat32 || tensor.type == kTfLiteFloat16 ||
tensor.type == kTfLiteBFloat16 || tensor.type == kTfLiteFloat64 ||
tensor.type == kTfLiteFloat8E4M3FN || tensor.type == kTfLiteFloat8E5M2) {
return false;
}
return tensor.quantization.type != kTfLiteNoQuantization ||
tensor.params.scale != 0.0f;
}
bool IsSupportedQuantization(const TfLiteTensor& tensor,
bool allow_per_channel) {
if (!IsQuantized(tensor)) return true;
if (tensor.quantization.type != kTfLiteAffineQuantization) return false;
const auto* params =
static_cast<const TfLiteAffineQuantization*>(tensor.quantization.params);
if (!params) return false;
if (!params->scale) return false;
if (params->scale->size != 1 && !allow_per_channel) return false;
if (tensor.type == kTfLiteUInt8) {
if (!params->zero_point) return false;
if (params->zero_point->size != params->scale->size &&
params->zero_point->size != 1) {
return false;
}
}
return true;
}
size_t YnnTypeElementCount(ynn_type type) {
switch (type) {
case ynn_type_int2:
case ynn_type_uint2:
return 4;
case ynn_type_int4:
case ynn_type_uint4:
return 2;
default:
return 1;
}
}
bool IsTensorSupported(const TfLiteTensor& tensor, bool allow_per_channel) {
ynn_type type = GetYnnType(tensor.type);
if (type == ynn_type_invalid) return false;
size_t element_count = YnnTypeElementCount(type);
if (element_count > 1) {
if (tensor.dims == nullptr) return false;
size_t dense_dim =
tensor.dims->size == 0 ? 1 : tensor.dims->data[tensor.dims->size - 1];
if (dense_dim % element_count != 0) return false;
}
return IsSupportedQuantization(tensor, allow_per_channel);
}
bool QuantizationParamsEqual(const TfLiteTensor& tensor1,
const TfLiteTensor& tensor2) {
if (IsQuantized(tensor1) != IsQuantized(tensor2)) return false;
if (!IsQuantized(tensor1)) return true;
return tensor1.params.scale == tensor2.params.scale &&
tensor1.params.zero_point == tensor2.params.zero_point;
}
bool IsActivationSupported(TfLiteFusedActivation activation,
TfLiteType output_type) {
if (activation == kTfLiteActNone) return true;
return (activation == kTfLiteActRelu || activation == kTfLiteActRelu6 ||
activation == kTfLiteActReluN1To1 || activation == kTfLiteActTanh ||
activation == kTfLiteActSigmoid);
}
TfLiteFusedActivation GetFusedActivation(const TfLiteRegistration* registration,
const TfLiteNode* node) {
TfLiteFusedActivation activation = kTfLiteActNone;
switch (registration->builtin_code) {
case kTfLiteBuiltinAdd: {
const auto* params =
reinterpret_cast<const TfLiteAddParams*>(node->builtin_data);
if (params) activation = params->activation;
break;
}
case kTfLiteBuiltinSub: {
const auto* params =
reinterpret_cast<const TfLiteSubParams*>(node->builtin_data);
if (params) activation = params->activation;
break;
}
case kTfLiteBuiltinMul: {
const auto* params =
reinterpret_cast<const TfLiteMulParams*>(node->builtin_data);
if (params) activation = params->activation;
break;
}
case kTfLiteBuiltinDiv: {
const auto* params =
reinterpret_cast<const TfLiteDivParams*>(node->builtin_data);
if (params) activation = params->activation;
break;
}
case kTfLiteBuiltinMaxPool2d:
case kTfLiteBuiltinAveragePool2d: {
const auto* params =
reinterpret_cast<const TfLitePoolParams*>(node->builtin_data);
if (params) activation = params->activation;
break;
}
default:
break;
}
return activation;
}
TfLiteStatus DefineScalarConstant(TfLiteContext* context,
ynn_subgraph_t subgraph, ynn_type type,
double value, uint32_t* id_out) {
TF_LITE_ENSURE(context, type != ynn_type_invalid);
switch (type) {
case ynn_type_fp32: {
float f_val = static_cast<float>(value);
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &f_val,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
case ynn_type_int32: {
int32_t i_val = static_cast<int32_t>(value);
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &i_val,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
case ynn_type_int8: {
int8_t i8_val = static_cast<int8_t>(value);
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &i8_val,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
case ynn_type_uint8: {
uint8_t u8_val = static_cast<uint8_t>(value);
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &u8_val,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
case ynn_type_fp64: {
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &value,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
case ynn_type_fp16: {
tflite::half f16_val(static_cast<float>(value));
uint16_t bits = f16_val.to_bits();
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &bits,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
case ynn_type_bf16: {
uint16_t bits = FloatToBfloat16(static_cast<float>(value));
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, type, 0, nullptr, &bits,
YNN_VALUE_FLAG_COPY_DATA, id_out),
ynn_status_success);
return kTfLiteOk;
}
default:
TF_LITE_ENSURE(context, false);
}
}
TfLiteStatus DefineQuantizationParams(TfLiteContext* context,
ynn_subgraph_t subgraph,
const TfLiteTensor& tensor,
uint32_t* scale_id, uint32_t* zp_id,
int32_t zp_offset) {
if (!IsQuantized(tensor)) {
*scale_id = YNN_INVALID_VALUE_ID;
*zp_id = YNN_INVALID_VALUE_ID;
return kTfLiteOk;
}
const auto* params =
static_cast<const TfLiteAffineQuantization*>(tensor.quantization.params);
if (params && params->scale && params->scale->size > 1) {
TF_LITE_ENSURE(context, params->zero_point != nullptr);
TF_LITE_ENSURE(context, params->zero_point->size == params->scale->size ||
params->zero_point->size == 1);
size_t size = params->scale->size;
size_t dims[] = {size};
TF_LITE_ENSURE_EQ(
context,
ynn_define_tensor(subgraph, ynn_type_fp32, 1, dims, params->scale->data,
YNN_VALUE_FLAG_COPY_DATA, scale_id),
ynn_status_success);
std::vector<int32_t> adjusted_zp(size);
for (size_t i = 0; i < size; ++i) {
int32_t zp = params->zero_point->size == 1 ? params->zero_point->data[0]
: params->zero_point->data[i];
adjusted_zp[i] = zp + zp_offset;
}
bool all_zero = std::all_of(adjusted_zp.begin(), adjusted_zp.end(),
[](int32_t zp) { return zp == 0; });
if (all_zero) {
*zp_id = YNN_INVALID_VALUE_ID;
} else {
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, ynn_type_int32, 1, dims,
adjusted_zp.data(),
YNN_VALUE_FLAG_COPY_DATA, zp_id),
ynn_status_success);
}
} else {
float scale = 0.0f;
int32_t zp = 0;
if (params && params->scale) {
scale = params->scale->data[0];
zp = params->zero_point ? params->zero_point->data[0] : 0;
} else {
TFLITE_LOG_PROD(
TFLITE_LOG_WARNING,
"DefineQuantizationParams: missing or invalid affine quantization "
"params for tensor, using legacy params. scale: %f, zp: %d",
tensor.params.scale, tensor.params.zero_point);
scale = tensor.params.scale;
zp = tensor.params.zero_point;
TF_LITE_ENSURE_MSG(context, scale != 0.0f,
"DefineQuantizationParams: scale is 0.0");
}
zp += zp_offset;
TF_LITE_ENSURE_EQ(
context,
ynn_define_tensor(subgraph, ynn_type_fp32, 0, nullptr, &scale,
YNN_VALUE_FLAG_COPY_DATA, scale_id),
ynn_status_success);
if (zp == 0) {
*zp_id = YNN_INVALID_VALUE_ID;
} else {
TF_LITE_ENSURE_EQ(context,
ynn_define_tensor(subgraph, ynn_type_int32, 0, nullptr,
&zp, YNN_VALUE_FLAG_COPY_DATA, zp_id),
ynn_status_success);
}
}
return kTfLiteOk;
}
TfLiteStatus ApplyClamp(TfLiteContext* context, ynn_subgraph_t subgraph,
double min_val, double max_val, uint32_t input_id,
uint32_t& output_id, int output_tensor_index,
ynn_type internal_type) {
const TfLiteTensor& output_tensor = context->tensors[output_tensor_index];
const bool is_quantized =
IsQuantized(output_tensor) &&
(internal_type == ynn_type_int8 || internal_type == ynn_type_uint8);
auto quantize_val = [&](double val) -> double {
if (!is_quantized) return val;
const float scale = output_tensor.params.scale;
const int32_t zero_point = output_tensor.params.zero_point;
if (internal_type == ynn_type_int8) {
return QuantizeValue<int8_t>(val, scale, zero_point);
} else {
return QuantizeValue<uint8_t>(val, scale, zero_point);
}
};
uint32_t current_input_id = input_id;
uint32_t min_const_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_STATUS(DefineScalarConstant(
context, subgraph, internal_type, quantize_val(min_val), &min_const_id));
uint32_t max_output_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_EQ(
context,
ynn_define_binary(subgraph, ynn_binary_max, current_input_id,
min_const_id, &max_output_id, 0),
ynn_status_success);
current_input_id = max_output_id;
uint32_t max_const_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_STATUS(DefineScalarConstant(
context, subgraph, internal_type, quantize_val(max_val), &max_const_id));
TF_LITE_ENSURE_EQ(
context,
ynn_define_binary(subgraph, ynn_binary_min, current_input_id,
max_const_id, &output_id, 0),
ynn_status_success);
return kTfLiteOk;
}
TfLiteStatus ApplyActivation(TfLiteContext* context, ynn_subgraph_t subgraph,
TfLiteFusedActivation activation,
uint32_t input_id, uint32_t& output_id,
int output_tensor_index, ynn_type internal_type) {
if (activation == kTfLiteActNone) {
return kTfLiteOk;
}
const TfLiteTensor& output_tensor = context->tensors[output_tensor_index];
bool is_quantized =
IsQuantized(output_tensor) &&
(internal_type == ynn_type_int8 || internal_type == ynn_type_uint8);
auto quantize_val = [&](double val) -> double {
if (!is_quantized) return val;
float scale = output_tensor.params.scale;
int32_t zero_point = output_tensor.params.zero_point;
if (internal_type == ynn_type_int8) {
return QuantizeValue<int8_t>(val, scale, zero_point);
} else {
return QuantizeValue<uint8_t>(val, scale, zero_point);
}
};
uint32_t current_input_id = input_id;
if (activation == kTfLiteActRelu) {
uint32_t min_const_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_STATUS(DefineScalarConstant(
context, subgraph, internal_type, quantize_val(0.0), &min_const_id));
TF_LITE_ENSURE_EQ(context,
ynn_define_binary(subgraph, ynn_binary_max, input_id,
min_const_id, &output_id, 0),
ynn_status_success);
} else if (activation == kTfLiteActRelu6 ||
activation == kTfLiteActReluN1To1) {
double min_val = (activation == kTfLiteActReluN1To1) ? -1.0 : 0.0;
double max_val = (activation == kTfLiteActRelu6) ? 6.0 : 1.0;
return ApplyClamp(context, subgraph, min_val, max_val, input_id, output_id,
output_tensor_index, internal_type);
} else if (activation == kTfLiteActTanh) {
TF_LITE_ENSURE_EQ(context,
ynn_define_unary(subgraph, ynn_unary_tanh,
current_input_id, &output_id, 0),
ynn_status_success);
} else if (activation == kTfLiteActSigmoid) {
TF_LITE_ENSURE_EQ(context,
ynn_define_unary(subgraph, ynn_unary_sigmoid,
current_input_id, &output_id, 0),
ynn_status_success);
} else {
TF_LITE_ENSURE(context, false);
}
return kTfLiteOk;
}
TfLiteStatus DequantizeIfNeeded(TfLiteContext* context, ynn_subgraph_t subgraph,
TensorToValueIdMap& tensor_to_value_id,
int tensor_index, uint32_t val_id,
uint32_t* float_val_id) {
const TfLiteTensor& tensor = context->tensors[tensor_index];
if (IsQuantized(tensor)) {
uint32_t scale_id = YNN_INVALID_VALUE_ID;
uint32_t zp_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_STATUS(
DefineQuantizationParams(context, subgraph, tensor, &scale_id, &zp_id));
TF_LITE_ENSURE_YNN_STATUS(ynn_define_dequantize(
subgraph, val_id, zp_id, scale_id, ynn_type_fp32, float_val_id, 0));
} else {
*float_val_id = val_id;
}
return kTfLiteOk;
}
TfLiteStatus Quantize(TfLiteContext* context, ynn_subgraph_t subgraph,
TensorToValueIdMap& tensor_to_value_id, int tensor_index,
uint32_t float_val_id, uint32_t quant_val_id) {
const TfLiteTensor& tensor = context->tensors[tensor_index];
uint32_t scale_id = YNN_INVALID_VALUE_ID;
uint32_t zp_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_STATUS(
DefineQuantizationParams(context, subgraph, tensor, &scale_id, &zp_id));
ynn_type ynn_type = GetYnnType(tensor.type);
TF_LITE_ENSURE_YNN_STATUS(ynn_define_quantize(
subgraph, float_val_id, ynn_type, zp_id, scale_id, &quant_val_id, 0));
return kTfLiteOk;
}
uint32_t GetOrCreateValueId(TfLiteContext* context, ynn_subgraph_t subgraph,
TensorToValueIdMap& mapping, int tensor_index) {
if (tensor_index < 0 || tensor_index >= context->tensors_size) {
return YNN_INVALID_VALUE_ID;
}
auto it = mapping.find(tensor_index);
if (it != mapping.end()) return it->second;
const TfLiteTensor& tensor = context->tensors[tensor_index];
uint32_t val_id = YNN_INVALID_VALUE_ID;
ynn_type ynn_type = GetYnnType(tensor.type);
if (ynn_type == ynn_type_invalid) {
return YNN_INVALID_VALUE_ID;
}
if (tensor.dims->size > YNN_MAX_TENSOR_RANK) {
return YNN_INVALID_VALUE_ID;
}
const void* data = nullptr;
uint32_t flags = 0;
size_t dims_data[YNN_MAX_TENSOR_RANK];
const size_t* dims = nullptr;
if (tensor.allocation_type == kTfLiteMmapRo) {
data = tensor.data.raw;
std::copy_n(tensor.dims->data, tensor.dims->size, dims_data);
dims = dims_data;
}
ynn_status status = ynn_define_tensor(subgraph, ynn_type, tensor.dims->size,
dims, data, flags, &val_id);
if (status != ynn_status_success) return YNN_INVALID_VALUE_ID;
mapping[tensor_index] = val_id;
return val_id;
}
TfLiteStatus ImplementMutualBroadcasting(TfLiteContext* context,
ynn_subgraph_t subgraph, int rank_a,
int rank_b, int exclude_a,
int exclude_b, uint32_t& current_a_id,
uint32_t& current_b_id) {
int max_rank = std::max(rank_a, rank_b);
uint32_t input_a_id = current_a_id;
uint32_t input_b_id = current_b_id;
// 1. Expand A if needed.
uint32_t expanded_a_id = input_a_id;
if (max_rank > rank_a) {
int diff = max_rank - rank_a;
int32_t axes[YNN_MAX_TENSOR_RANK];
std::iota(axes, axes + diff, 0);
expanded_a_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_YNN_STATUS(ynn_define_static_expand_dims(
subgraph, diff, axes, input_a_id, &expanded_a_id, 0));
}
// 2. Expand B if needed.
uint32_t expanded_b_id = input_b_id;
if (max_rank > rank_b) {
int diff = max_rank - rank_b;
int32_t axes[YNN_MAX_TENSOR_RANK];
std::iota(axes, axes + diff, 0);
expanded_b_id = YNN_INVALID_VALUE_ID;
TF_LITE_ENSURE_YNN_STATUS(ynn_define_static_expand_dims(
subgraph, diff, axes, input_b_id, &expanded_b_id, 0));
}
uint32_t broadcasted_a_id = expanded_a_id;
if (max_rank > exclude_a) {
broadcasted_a_id = YNN_INVALID_VALUE_ID;
int num_axes = max_rank - exclude_a;
int32_t axes[YNN_MAX_TENSOR_RANK];
std::iota(axes, axes + num_axes, 0);
TF_LITE_ENSURE_YNN_STATUS(
ynn_define_broadcast_like(subgraph, num_axes, axes, expanded_a_id,
expanded_b_id, &broadcasted_a_id, 0));
}
uint32_t broadcasted_b_id = expanded_b_id;
if (max_rank > exclude_b) {
broadcasted_b_id = YNN_INVALID_VALUE_ID;
int num_axes = max_rank - exclude_b;
int32_t axes[YNN_MAX_TENSOR_RANK];
std::iota(axes, axes + num_axes, 0);
TF_LITE_ENSURE_YNN_STATUS(
ynn_define_broadcast_like(subgraph, num_axes, axes, expanded_b_id,
expanded_a_id, &broadcasted_b_id, 0));
}
current_a_id = broadcasted_a_id;
current_b_id = broadcasted_b_id;
return kTfLiteOk;
}
TfLiteStatus DefineYnnStencil(TfLiteContext* context, ynn_subgraph_t subgraph,
const TfLiteTensor& input_tensor,
uint32_t input_id, size_t filter_height,
size_t filter_width, size_t stride_height,
size_t stride_width, size_t dilation_height,
size_t dilation_width, TfLitePadding padding,
float padding_value, uint32_t* stencil_id) {
uint32_t padding_id = YNN_INVALID_VALUE_ID;
if (padding == kTfLitePaddingSame) {
ynn_type padding_type = GetYnnType(input_tensor.type);
TF_LITE_ENSURE(context, padding_type != ynn_type_invalid);
TF_LITE_ENSURE_STATUS(DefineScalarConstant(context, subgraph, padding_type,
padding_value, &padding_id));
}
const int32_t stencil_axes[] = {1, 2};
const int32_t new_axes[] = {-3, -2};
const size_t stencil_dims[] = {filter_height, filter_width};
const size_t stencil_strides[] = {stride_height, stride_width};
const size_t stencil_dilations[] = {dilation_height, dilation_width};
TF_LITE_ENSURE_YNN_STATUS(ynn_define_stencil_copy(
subgraph, /*num_stencils=*/2, stencil_axes, new_axes, stencil_dims,
stencil_strides, stencil_dilations, input_id, padding_id, stencil_id,
/*flags=*/0));
return kTfLiteOk;
}
} // namespace ynnpack
} // namespace tflite