Files
tensorflow--tensorflow/tensorflow/lite/kernels/slice.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

337 lines
13 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 "tensorflow/lite/kernels/internal/reference/slice.h"
#include <stdint.h>
#include <algorithm>
#include <string>
#include <vector>
#include "Eigen/Core"
#include "tensorflow/lite/context_util.h"
#include "tensorflow/lite/core/c/common.h"
#include "tensorflow/lite/kernels/internal/compatibility.h"
#include "tensorflow/lite/kernels/internal/optimized/optimized_ops.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/internal/types.h"
#include "tensorflow/lite/kernels/kernel_util.h"
#include "tensorflow/lite/string_type.h"
namespace tflite {
namespace ops {
namespace builtin {
namespace slice {
enum KernelType {
kReference,
kGenericOptimized,
};
constexpr int kInputTensor = 0;
constexpr int kBeginTensor = 1;
constexpr int kSizeTensor = 2;
constexpr int kOutputTensor = 0;
// The optimized helper supports up to 5D. Higher-rank tensors use the
// runtime-rank reference path.
const int kMaxDim = 5;
template <typename T>
TfLiteStatus CalculateOutputShapeVector(TfLiteContext* context,
const TfLiteTensor* input,
const TfLiteTensor* begin,
const TfLiteTensor* size,
std::vector<int>* output_shape_vector) {
for (int idx = 0; idx < NumDimensions(input); ++idx) {
T size_value = GetTensorData<T>(size)[idx];
if (size_value < 0) {
if (size_value != -1) {
TF_LITE_KERNEL_LOG(context, "Invalid size.");
return kTfLiteError;
}
size_value = SizeOfDimension(input, idx) - GetTensorData<T>(begin)[idx];
} else {
if (SizeOfDimension(input, idx) <
GetTensorData<T>(begin)[idx] + size_value) {
TF_LITE_KERNEL_LOG(context, "Invalid begin and size.");
return kTfLiteError;
}
}
output_shape_vector->push_back(static_cast<int>(size_value));
}
return kTfLiteOk;
}
template <typename T>
void GetBeginAndSizeVectors(int dimensions, const TfLiteTensor* begin,
const TfLiteTensor* size, std::vector<int>* begins,
std::vector<int>* sizes) {
for (int idx = 0; idx < dimensions; ++idx) {
begins->push_back(GetTensorData<T>(begin)[idx]);
sizes->push_back(GetTensorData<T>(size)[idx]);
}
}
TfLiteStatus ResizeOutputShape(TfLiteContext* context,
const TfLiteTensor* input,
const TfLiteTensor* begin,
const TfLiteTensor* size, TfLiteTensor* output) {
std::vector<int> output_shape_vector;
if (begin->type == kTfLiteInt32) {
TF_LITE_ENSURE_STATUS(CalculateOutputShapeVector<int32_t>(
context, input, begin, size, &output_shape_vector));
} else if (begin->type == kTfLiteInt64) {
TF_LITE_ENSURE_STATUS(CalculateOutputShapeVector<int64_t>(
context, input, begin, size, &output_shape_vector));
} else {
TF_LITE_KERNEL_LOG(context, "Type %d is currently not supported by Slice.",
begin->type);
return kTfLiteError;
}
TfLiteIntArray* output_shape =
TfLiteIntArrayCreate(output_shape_vector.size());
std::copy(output_shape_vector.begin(), output_shape_vector.end(),
output_shape->data);
return context->ResizeTensor(context, output, output_shape);
}
bool ShapeHasRank(const TfLiteIntArray* shape) {
// Note that we consider scalar as false here because there is
// no differentiation between scalar and dynamic properly supported.
if (shape == nullptr || shape->size == 0) return false;
return true;
}
TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input));
const TfLiteTensor* begin;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kBeginTensor, &begin));
const TfLiteTensor* size;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kSizeTensor, &size));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context,
GetOutputSafe(context, node, kOutputTensor, &output));
// Ensure validity of input tensor and its dimension.
TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
TF_LITE_ENSURE(context,
begin->type == kTfLiteInt32 || begin->type == kTfLiteInt64);
TF_LITE_ENSURE(context,
size->type == kTfLiteInt32 || size->type == kTfLiteInt64);
TF_LITE_ENSURE_EQ(context, NumDimensions(begin), 1);
TF_LITE_ENSURE_EQ(context, NumDimensions(size), 1);
TF_LITE_ENSURE_EQ(context, NumElements(begin), NumElements(size));
// If the shape of output is fully specified then resize even if
// the input shape is not staticly defined.
if (!HasUnspecifiedDimension(output) && ShapeHasRank(output->dims)) {
return kTfLiteOk;
}
// Postpone allocation of output if any of the indexing tensors is not
// constant, or the input tensor has dynamic dimension.
if (!(IsConstantOrPersistentTensor(begin) &&
IsConstantOrPersistentTensor(size)) ||
HasUnspecifiedDimension(input)) {
SetTensorToDynamic(output);
return kTfLiteOk;
}
return ResizeOutputShape(context, input, begin, size, output);
}
template <KernelType kernel_type>
TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
const TfLiteTensor* input;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kInputTensor, &input));
const TfLiteTensor* begin;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kBeginTensor, &begin));
const TfLiteTensor* size;
TF_LITE_ENSURE_OK(context, GetInputSafe(context, node, kSizeTensor, &size));
TfLiteTensor* output;
TF_LITE_ENSURE_OK(context,
GetOutputSafe(context, node, kOutputTensor, &output));
if (IsDynamicTensor(output)) {
TF_LITE_ENSURE_OK(context,
ResizeOutputShape(context, input, begin, size, output));
}
const int input_dims = NumDimensions(input);
std::vector<int> begins;
begins.reserve(input_dims);
std::vector<int> sizes;
sizes.reserve(input_dims);
if (begin->type == kTfLiteInt32) {
GetBeginAndSizeVectors<int32_t>(NumDimensions(input), begin, size, &begins,
&sizes);
} else if (begin->type == kTfLiteInt64) {
GetBeginAndSizeVectors<int64_t>(NumDimensions(input), begin, size, &begins,
&sizes);
} else {
TF_LITE_KERNEL_LOG(context, "Type %d is currently not supported by Slice.",
begin->type);
return kTfLiteError;
}
if (input_dims > kMaxDim) {
if (input->type == kTfLiteString) {
reference_ops::Slice<string>(begins, GetTensorShape(input), input,
GetTensorShape(output), output);
return kTfLiteOk;
} else if (input->type == kTfLiteInt4) {
reference_ops::SliceInt4(begins, GetTensorShape(input), input,
GetTensorShape(output), output);
return kTfLiteOk;
}
#define TF_LITE_SLICE_DYNAMIC(data_type) \
{ \
reference_ops::Slice<data_type>(begins, GetTensorShape(input), input, \
GetTensorShape(output), output); \
}
switch (TfLiteTypeGetSizeBits(output->type)) {
case 8:
TF_LITE_SLICE_DYNAMIC(int8_t);
break;
case 16:
TF_LITE_SLICE_DYNAMIC(int16_t);
break;
case 32:
TF_LITE_SLICE_DYNAMIC(int32_t);
break;
case 64:
TF_LITE_SLICE_DYNAMIC(int64_t);
break;
default:
TF_LITE_KERNEL_LOG(context,
"Type %d is currently not supported by Slice.",
input->type);
return kTfLiteError;
}
#undef TF_LITE_SLICE_DYNAMIC
return kTfLiteOk;
}
std::vector<int> padded_begins;
padded_begins.reserve(kMaxDim);
std::vector<int> padded_sizes;
padded_sizes.reserve(kMaxDim);
for (int i = input_dims; i < kMaxDim; ++i) {
padded_begins.push_back(0);
padded_sizes.push_back(1);
}
padded_begins.insert(padded_begins.end(), begins.begin(), begins.end());
padded_sizes.insert(padded_sizes.end(), sizes.begin(), sizes.end());
#define TF_LITE_SLICE_INT4() \
{ \
TF_LITE_ENSURE_EQ(context, padded_begins.size(), kMaxDim); \
TF_LITE_ENSURE_EQ(context, padded_sizes.size(), kMaxDim); \
tflite::SliceParams op_params; \
op_params.begin_count = kMaxDim; \
op_params.size_count = kMaxDim; \
for (int i = 0; i < kMaxDim; ++i) { \
op_params.begin[i] = padded_begins[i]; \
op_params.size[i] = padded_sizes[i]; \
} \
\
if (kernel_type == kGenericOptimized) { \
optimized_ops::SliceInt4(op_params, GetTensorShape(input), input, \
GetTensorShape(output), output); \
} else { \
reference_ops::SliceInt4(op_params, GetTensorShape(input), input, \
GetTensorShape(output), output); \
} \
}
#define TF_LITE_SLICE(data_type) \
{ \
TF_LITE_ENSURE_EQ(context, padded_begins.size(), kMaxDim); \
TF_LITE_ENSURE_EQ(context, padded_sizes.size(), kMaxDim); \
tflite::SliceParams op_params; \
op_params.begin_count = kMaxDim; \
op_params.size_count = kMaxDim; \
for (int i = 0; i < kMaxDim; ++i) { \
op_params.begin[i] = padded_begins[i]; \
op_params.size[i] = padded_sizes[i]; \
} \
\
if (kernel_type == kGenericOptimized) { \
optimized_ops::Slice<data_type>(op_params, GetTensorShape(input), input, \
GetTensorShape(output), output); \
} else { \
reference_ops::Slice<data_type>(op_params, GetTensorShape(input), input, \
GetTensorShape(output), output); \
} \
}
if (input->type == kTfLiteString) {
TF_LITE_SLICE(string);
return kTfLiteOk;
} else if (input->type == kTfLiteInt4) {
TF_LITE_SLICE_INT4();
return kTfLiteOk;
}
switch (TfLiteTypeGetSizeBits(output->type)) {
case 8:
TF_LITE_SLICE(int8_t);
break;
case 16:
TF_LITE_SLICE(int16_t);
break;
case 32:
TF_LITE_SLICE(int32_t);
break;
case 64:
TF_LITE_SLICE(int64_t);
break;
default:
TF_LITE_KERNEL_LOG(
context, "Type %d is currently not supported by Slice.", input->type);
return kTfLiteError;
}
#undef TF_LITE_SLICE
return kTfLiteOk;
}
} // namespace slice
TfLiteRegistration* Register_SLICE_REF() {
static TfLiteRegistration r = {nullptr, nullptr, slice::Prepare,
slice::Eval<slice::kReference>};
return &r;
}
TfLiteRegistration* Register_SLICE() {
static TfLiteRegistration r = {nullptr, nullptr, slice::Prepare,
slice::Eval<slice::kGenericOptimized>};
return &r;
}
} // namespace builtin
} // namespace ops
} // namespace tflite