Skip to content

Commit b892593

Browse files
manuelcandalesfacebook-github-bot
authored andcommitted
Fix reduction over dim list for empty input
Summary: Allow empty input in `MapReduceOverDimListPlan` Avoid integer division by zero in `parallel_for_each_reduce_over_dim_list_output_index` Add empty input tests for ops: any, mean, sum & var Differential Revision: D81383049
1 parent ebdd12d commit b892593

File tree

5 files changed

+115
-5
lines changed

5 files changed

+115
-5
lines changed

kernels/portable/cpu/util/reduce_util.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,6 @@ class MapReduceOverDimListPlan {
531531
const executorch::aten::Tensor& in,
532532
const std::optional<executorch::aten::ArrayRef<int64_t>>& dim_list)
533533
: plan_(in, dim_list, 1, -1) {
534-
ET_CHECK_MSG(in.numel() > 0, "Input tensor must be nonempty");
535534
}
536535

537536
template <
@@ -834,10 +833,12 @@ template <typename Func>
834833
const Func& func) {
835834
#ifdef ET_USE_THREADPOOL
836835
const ssize_t reduction_size = get_reduced_dim_product(in, dim_list);
837-
const auto grain_size = std::max(
838-
static_cast<ssize_t>(1),
839-
static_cast<ssize_t>(executorch::extension::internal::GRAIN_SIZE) /
840-
reduction_size);
836+
const auto grain_size = reduction_size == 0
837+
? 1
838+
: std::max(
839+
static_cast<ssize_t>(1),
840+
static_cast<ssize_t>(executorch::extension::internal::GRAIN_SIZE) /
841+
reduction_size);
841842
#else // ET_USE_THREADPOOL
842843
const auto grain_size = 1;
843844
#endif // ET_USE_THREADPOOL

kernels/test/op_any_test.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,31 @@ TEST_F(OpAnyOutTest, SmokeTest) {
148148
op_any_out(self, dim, keepdim, out);
149149
EXPECT_TENSOR_CLOSE(out, out_expected);
150150
}
151+
152+
TEST_F(OpAnyOutTest, EmptyInput) {
153+
TensorFactory<ScalarType::Float> tf;
154+
TensorFactory<ScalarType::Bool> tfBool;
155+
156+
Tensor x = tf.make({2, 0, 3}, {});
157+
optional<ArrayRef<int64_t>> dim_list = ArrayRef<int64_t>{};
158+
Tensor out = tfBool.make({2, 0, 3}, {});
159+
160+
op_any_dims_out(x, dim_list, /*keepdim=*/true, out);
161+
EXPECT_TENSOR_CLOSE(out, tfBool.zeros({2, 0, 3}));
162+
163+
out = tfBool.ones({2, 0, 3});
164+
op_any_dims_out(x, dim_list, /*keepdim=*/false, out);
165+
EXPECT_TENSOR_CLOSE(out, tfBool.zeros({2, 0, 3}));
166+
167+
int64_t dims1[1] = {1};
168+
dim_list = ArrayRef<int64_t>{dims1, 1};
169+
out = tfBool.ones({2, 3});
170+
op_any_dims_out(x, dim_list, /*keepdim=*/false, out);
171+
EXPECT_TENSOR_CLOSE(out, tfBool.zeros({2, 3}));
172+
173+
int64_t dims2[1] = {2};
174+
dim_list = ArrayRef<int64_t>{dims2, 1};
175+
out = tfBool.make({2, 0, 1}, {});
176+
op_any_dims_out(x, dim_list, /*keepdim=*/true, out);
177+
EXPECT_TENSOR_CLOSE(out, tfBool.make({2, 0, 1}, {}));
178+
}

kernels/test/op_mean_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,3 +551,30 @@ TEST_F(OpMeanOutTest, DTypeOutFloatNAN) {
551551
Tensor ret = op_mean_dtype_out(x, ScalarType::Float, out);
552552
EXPECT_TENSOR_CLOSE(out, expected_result);
553553
}
554+
555+
TEST_F(OpMeanOutTest, EmptyInput) {
556+
TensorFactory<ScalarType::Float> tf;
557+
558+
Tensor x = tf.make({2, 0, 3}, {});
559+
optional<ScalarType> dtype = ScalarType::Float;
560+
optional<ArrayRef<int64_t>> dim_list = ArrayRef<int64_t>{};
561+
Tensor out = tf.zeros({1, 1, 1});
562+
op_mean_out(x, dim_list, /*keepdim=*/true, dtype, out);
563+
EXPECT_TENSOR_CLOSE(out, tf.make({1, 1, 1}, {NAN}));
564+
565+
out = tf.zeros({});
566+
op_mean_out(x, dim_list, /*keepdim=*/false, dtype, out);
567+
EXPECT_TENSOR_CLOSE(out, tf.make({}, {NAN}));
568+
569+
int64_t dims1[1] = {1};
570+
dim_list = ArrayRef<int64_t>{dims1, 1};
571+
out = tf.zeros({2, 3});
572+
op_mean_out(x, dim_list, /*keepdim=*/false, dtype, out);
573+
EXPECT_TENSOR_CLOSE(out, tf.make({2, 3}, {NAN, NAN, NAN, NAN, NAN, NAN}));
574+
575+
int64_t dims2[1] = {2};
576+
dim_list = ArrayRef<int64_t>{dims2, 1};
577+
out = tf.make({2, 0, 1}, {});
578+
op_mean_out(x, dim_list, /*keepdim=*/true, dtype, out);
579+
EXPECT_TENSOR_CLOSE(out, tf.make({2, 0, 1}, {}));
580+
}

kernels/test/op_sum_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,3 +490,30 @@ TEST_F(OpSumOutTest, InfinityAndNANTest) {
490490
}));
491491
// clang-format on
492492
}
493+
494+
TEST_F(OpSumOutTest, EmptyInput) {
495+
TensorFactory<ScalarType::Float> tf;
496+
497+
Tensor x = tf.make({2, 0, 3}, {});
498+
optional<ScalarType> dtype = ScalarType::Float;
499+
optional<ArrayRef<int64_t>> dim_list = ArrayRef<int64_t>{};
500+
Tensor out = tf.ones({1, 1, 1});
501+
op_sum_intlist_out(x, dim_list, /*keepdim=*/true, dtype, out);
502+
EXPECT_TENSOR_CLOSE(out, tf.zeros({1, 1, 1}));
503+
504+
out = tf.ones({});
505+
op_sum_intlist_out(x, dim_list, /*keepdim=*/false, dtype, out);
506+
EXPECT_TENSOR_CLOSE(out, tf.zeros({}));
507+
508+
int64_t dims1[1] = {1};
509+
dim_list = ArrayRef<int64_t>{dims1, 1};
510+
out = tf.ones({2, 3});
511+
op_sum_intlist_out(x, dim_list, /*keepdim=*/false, dtype, out);
512+
EXPECT_TENSOR_CLOSE(out, tf.zeros({2, 3}));
513+
514+
int64_t dims2[1] = {2};
515+
dim_list = ArrayRef<int64_t>{dims2, 1};
516+
out = tf.make({2, 0, 1}, {});
517+
op_sum_intlist_out(x, dim_list, /*keepdim=*/true, dtype, out);
518+
EXPECT_TENSOR_CLOSE(out, tf.make({2, 0, 1}, {}));
519+
}

kernels/test/op_var_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,30 @@ TEST_F(OpVarCorrectionOutTest, SmokeTest) {
468468
ET_FORALL_FLOATHBF16_TYPES(TEST_ENTRY);
469469
#undef TEST_ENTRY
470470
}
471+
472+
TEST_F(OpVarOutTest, EmptyInput) {
473+
TensorFactory<ScalarType::Float> tf;
474+
475+
Tensor x = tf.make({2, 0, 3}, {});
476+
bool unbiased = true;
477+
optional<ArrayRef<int64_t>> dim_list = ArrayRef<int64_t>{};
478+
Tensor out = tf.zeros({1, 1, 1});
479+
op_var_out(x, dim_list, unbiased, /*keepdim=*/true, out);
480+
EXPECT_TENSOR_CLOSE(out, tf.make({1, 1, 1}, {NAN}));
481+
482+
out = tf.zeros({});
483+
op_var_out(x, dim_list, unbiased, /*keepdim=*/false, out);
484+
EXPECT_TENSOR_CLOSE(out, tf.make({}, {NAN}));
485+
486+
int64_t dims1[1] = {1};
487+
dim_list = ArrayRef<int64_t>{dims1, 1};
488+
out = tf.zeros({2, 3});
489+
op_var_out(x, dim_list, unbiased, /*keepdim=*/false, out);
490+
EXPECT_TENSOR_CLOSE(out, tf.make({2, 3}, {NAN, NAN, NAN, NAN, NAN, NAN}));
491+
492+
int64_t dims2[1] = {2};
493+
dim_list = ArrayRef<int64_t>{dims2, 1};
494+
out = tf.make({2, 0, 1}, {});
495+
op_var_out(x, dim_list, unbiased, /*keepdim=*/true, out);
496+
EXPECT_TENSOR_CLOSE(out, tf.make({2, 0, 1}, {}));
497+
}

0 commit comments

Comments
 (0)