Files
tensorflow--tensorflow/tensorflow/lite/kernels/internal/reference/select.h
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

254 lines
9.6 KiB
C++

/* Copyright 2022 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.
==============================================================================*/
#ifndef TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
#define TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_
#include <algorithm>
#include <cmath>
#include <cstring>
#include "ruy/profiler/instrumentation.h" // from @ruy
#include "tensorflow/lite/kernels/internal/common.h"
#include "tensorflow/lite/kernels/internal/reference/broadcast_loop.h"
#include "tensorflow/lite/kernels/internal/types.h"
namespace tflite {
namespace reference_ops {
template <typename D, typename T>
void Select(const RuntimeShape& input_condition_shape,
const D* input_condition_data, const RuntimeShape& input_x_shape,
const T* input_x_data, const RuntimeShape& input_y_shape,
const T* input_y_data, const RuntimeShape& output_shape,
T* output_data) {
ruy::profiler::ScopeLabel label("Select");
int64_t flatsize;
// Allow select operator executions on mixed scalar tensors and one element
// tensors.
if (input_condition_shape.FlatSize() == 1 && input_x_shape.FlatSize() == 1 &&
input_y_shape.FlatSize() == 1 && output_shape.FlatSize() == 1) {
flatsize = 1;
} else {
flatsize = MatchingFlatSize(input_condition_shape, input_x_shape,
input_y_shape, output_shape);
}
for (int64_t i = 0; i < flatsize; ++i) {
output_data[i] =
input_condition_data[i] ? input_x_data[i] : input_y_data[i];
}
}
template <typename D, typename T>
void RankOneSelect(const RuntimeShape& input_condition_shape,
const D* input_condition_data,
const RuntimeShape& input_x_shape, const T* input_x_data,
const RuntimeShape& input_y_shape, const T* input_y_data,
const RuntimeShape& output_shape, T* output_data) {
ruy::profiler::ScopeLabel label("Select/RankOneSelect");
const int64_t outer_size = input_condition_shape.FlatSize();
int64_t inner_size;
if (input_condition_shape.DimensionsCount() == 0) {
inner_size = MatchingFlatSize(input_x_shape, input_y_shape, output_shape);
} else {
TFLITE_DCHECK_EQ(
MatchingDim(input_x_shape, 0, input_y_shape, 0, output_shape, 0),
outer_size);
inner_size =
MatchingFlatSizeSkipDim(input_x_shape, 0, input_y_shape, output_shape);
}
int64_t offset = 0;
for (int64_t i = 0; i < outer_size; i++) {
const T* input_data = input_condition_data[i] ? input_x_data : input_y_data;
memcpy(output_data + offset, input_data + offset, inner_size * sizeof(T));
offset += inner_size;
}
}
template <typename D, typename T>
void RunSelectOp(const D* cond, const T* x, const T* y, T* output,
const size_t* cond_stride, const size_t* x_stride,
const size_t* y_stride, const size_t* output_stride,
const size_t* output_shape, int dim) {
TFLITE_DCHECK_GE(dim, 0);
size_t output_shape_0 = output_shape[dim];
size_t output_stride_0 = output_stride[dim];
size_t cond_stride_0 = cond_stride[dim];
size_t x_stride_0 = x_stride[dim];
size_t y_stride_0 = y_stride[dim];
if (dim == 0) {
TFLITE_DCHECK_EQ(output_stride_0, 1);
if (cond_stride_0 == 0) {
if (*cond) {
if (x_stride_0 == 0) {
std::fill_n(output, output_shape_0, *x);
} else {
TFLITE_DCHECK_EQ(x_stride_0, 1);
std::memcpy(output, x, output_shape_0 * sizeof(T));
}
} else {
if (y_stride_0 == 0) {
std::fill_n(output, output_shape_0, *y);
} else {
TFLITE_DCHECK_EQ(y_stride_0, 1);
std::memcpy(output, y, output_shape_0 * sizeof(T));
}
}
} else {
TFLITE_DCHECK_EQ(cond_stride_0, 1);
if (x_stride_0 == 0 && y_stride_0 == 0) {
for (size_t i = 0; i < output_shape_0; ++i) {
output[i] = cond[i] ? *x : *y;
}
} else if (x_stride_0 == 0) {
TFLITE_DCHECK_EQ(y_stride_0, 1);
for (size_t i = 0; i < output_shape_0; ++i) {
output[i] = cond[i] ? *x : y[i];
}
} else if (y_stride_0 == 0) {
TFLITE_DCHECK_EQ(x_stride_0, 1);
for (size_t i = 0; i < output_shape_0; ++i) {
output[i] = cond[i] ? x[i] : *y;
}
} else {
TFLITE_DCHECK_EQ(x_stride_0, 1);
TFLITE_DCHECK_EQ(y_stride_0, 1);
for (size_t i = 0; i < output_shape_0; ++i) {
output[i] = cond[i] ? x[i] : y[i];
}
}
}
} else {
dim -= 1;
for (size_t i = 0; i < output_shape_0; ++i) {
RunSelectOp(cond, x, y, output, cond_stride, x_stride, y_stride,
output_stride, output_shape, dim);
cond += cond_stride_0;
x += x_stride_0;
y += y_stride_0;
output += output_stride_0;
}
}
}
template <typename D, typename T>
inline void BroadcastSelectSimple(const RuntimeShape& cond_shape,
const D* cond_data,
const RuntimeShape& x_shape, const T* x_data,
const RuntimeShape& y_shape, const T* y_data,
const RuntimeShape& output_shape,
T* output_data) {
constexpr int kMaxRank = 8;
const int dims_count = std::max(
output_shape.DimensionsCount(),
std::max(cond_shape.DimensionsCount(),
std::max(x_shape.DimensionsCount(), y_shape.DimensionsCount())));
if (dims_count <= 0) {
*output_data = *cond_data ? *x_data : *y_data;
return;
}
TFLITE_DCHECK_LE(dims_count, kMaxRank);
const RuntimeShape extended_output_shape =
RuntimeShape::ExtendedShape(dims_count, output_shape);
const RuntimeShape extended_cond_shape =
RuntimeShape::ExtendedShape(dims_count, cond_shape);
const RuntimeShape extended_x_shape =
RuntimeShape::ExtendedShape(dims_count, x_shape);
const RuntimeShape extended_y_shape =
RuntimeShape::ExtendedShape(dims_count, y_shape);
size_t cond_strides[kMaxRank];
size_t x_strides[kMaxRank];
size_t y_strides[kMaxRank];
size_t o_strides[kMaxRank];
size_t o_shape[kMaxRank];
size_t cond_accum_stride = 1;
size_t x_accum_stride = 1;
size_t y_accum_stride = 1;
size_t o_accum_stride = 1;
int next_dim_idx = -1;
for (int i = dims_count - 1; i >= 0; --i) {
const int cond_dim = extended_cond_shape.Dims(i);
const int x_dim = extended_x_shape.Dims(i);
const int y_dim = extended_y_shape.Dims(i);
const int output_dim = extended_output_shape.Dims(i);
if (cond_dim <= 0 || x_dim <= 0 || y_dim <= 0 || output_dim <= 0) {
// Empty operation.
return;
}
size_t cond_stride =
(cond_dim == 1 && output_dim != 1) ? 0 : cond_accum_stride;
size_t x_stride = (x_dim == 1 && output_dim != 1) ? 0 : x_accum_stride;
size_t y_stride = (y_dim == 1 && output_dim != 1) ? 0 : y_accum_stride;
size_t o_stride = o_accum_stride;
if (next_dim_idx >= 0 &&
CanFuseLoops(output_dim, cond_dim, cond_stride, cond_accum_stride,
cond_strides[next_dim_idx]) &&
CanFuseLoops(output_dim, x_dim, x_stride, x_accum_stride,
x_strides[next_dim_idx]) &&
CanFuseLoops(output_dim, y_dim, y_stride, y_accum_stride,
y_strides[next_dim_idx]) &&
CanFuseLoops(output_dim, output_dim, o_stride, o_accum_stride,
o_strides[next_dim_idx])) {
// This dimension can be fused into one loop with the previous
// dimension.
o_shape[next_dim_idx] *= output_dim;
} else {
++next_dim_idx;
cond_strides[next_dim_idx] = cond_stride;
x_strides[next_dim_idx] = x_stride;
y_strides[next_dim_idx] = y_stride;
o_strides[next_dim_idx] = o_stride;
o_shape[next_dim_idx] = output_dim;
}
cond_accum_stride *= cond_dim;
x_accum_stride *= x_dim;
y_accum_stride *= y_dim;
o_accum_stride *= output_dim;
}
RunSelectOp(cond_data, x_data, y_data, output_data, cond_strides, x_strides,
y_strides, o_strides, o_shape, next_dim_idx);
}
template <typename D, typename T>
void BroadcastSelect5DSlow(const RuntimeShape& input_condition_shape,
const D* input_condition_data,
const RuntimeShape& input_x_shape,
const T* input_x_data,
const RuntimeShape& input_y_shape,
const T* input_y_data,
const RuntimeShape& output_shape, T* output_data) {
ruy::profiler::ScopeLabel label("Select/BroadcastSelectSlow");
TFLITE_DCHECK_LE(input_condition_shape.DimensionsCount(), 5);
TFLITE_DCHECK_LE(input_x_shape.DimensionsCount(), 5);
TFLITE_DCHECK_LE(input_y_shape.DimensionsCount(), 5);
TFLITE_DCHECK_LE(output_shape.DimensionsCount(), 5);
BroadcastSelectSimple(input_condition_shape, input_condition_data,
input_x_shape, input_x_data, input_y_shape,
input_y_data, output_shape, output_data);
}
} // namespace reference_ops
} // namespace tflite
#endif // TENSORFLOW_LITE_KERNELS_INTERNAL_REFERENCE_SELECT_H_