blob: 7bacf93c740bbdbfcf817f034a4825faab795e73 [file] [log] [blame]
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <executorch/kernels/test/FunctionHeaderWrapper.h> // Declares the operator
#include <executorch/kernels/test/TestUtil.h>
#include <executorch/kernels/test/supported_features.h>
#include <executorch/runtime/core/exec_aten/exec_aten.h>
#include <executorch/runtime/core/exec_aten/testing_util/tensor_factory.h>
#include <executorch/runtime/core/exec_aten/testing_util/tensor_util.h>
#include <executorch/runtime/core/exec_aten/util/scalar_type_util.h>
#include <executorch/runtime/platform/runtime.h>
#include <gtest/gtest.h>
#include <cstdint>
#include <limits>
using namespace ::testing;
using exec_aten::ArrayRef;
using exec_aten::Scalar;
using exec_aten::ScalarType;
using exec_aten::Tensor;
using torch::executor::testing::TensorFactory;
class OpArangeOutTest : public OperatorTest {
protected:
Tensor& op_arange_out(const Scalar& end, Tensor& out) {
return torch::executor::aten::arange_outf(context_, end, out);
}
template <class CTYPE, exec_aten::ScalarType DTYPE>
void test_arange_dtype() {
TensorFactory<DTYPE> tf;
Scalar end = Scalar(static_cast<CTYPE>(10));
Tensor out = tf.zeros({10});
Tensor ret = op_arange_out(end, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, filled with 0, 1, ..., 9
Tensor expected = tf.make({10}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
EXPECT_TENSOR_EQ(out, expected);
}
};
class OpArangeStartOutTest : public OperatorTest {
protected:
Tensor& op_arange_start_out(
const Scalar& start,
const Scalar& end,
const Scalar& step,
Tensor& out) {
return torch::executor::aten::arange_outf(context_, start, end, step, out);
}
template <class CTYPE, exec_aten::ScalarType DTYPE>
void test_arange_start_dtype() {
TensorFactory<DTYPE> tf;
Scalar start = Scalar(static_cast<CTYPE>(0));
Scalar end = Scalar(static_cast<CTYPE>(10));
Scalar step = Scalar(static_cast<CTYPE>(1));
Tensor out = tf.zeros({10});
Tensor ret = op_arange_start_out(start, end, step, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, filled with 0, 1, ..., 9
Tensor expected = tf.make({10}, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
EXPECT_TENSOR_EQ(out, expected);
}
};
/// A generic smoke test that works for any dtype that supports zeros().
TEST_F(OpArangeOutTest, AllRealDtypesSupported) {
#define TEST_ENTRY(ctype, dtype) test_arange_dtype<ctype, ScalarType::dtype>();
ET_FORALL_REAL_TYPES(TEST_ENTRY);
#undef TEST_ENTRY
}
TEST_F(OpArangeOutTest, FloatNumberNotEqualIntSupport) {
TensorFactory<ScalarType::Float> tf;
// end = any floating point number between [a, a+1) where a is an arbitrary
// integer should have same result as end = a. So here arage(end = 5.5) ==
// arange(5)
Scalar end = Scalar(5.5);
Tensor out = tf.zeros({6});
Tensor ret = op_arange_out(end, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, equal
Tensor expected = tf.make({6}, {0.0, 1.0, 2.0, 3.0, 4.0, 5.0});
EXPECT_TENSOR_EQ(out, expected);
}
TEST_F(OpArangeOutTest, OutDimUnsupportedDie) {
if (torch::executor::testing::SupportedFeatures::get()->is_aten) {
GTEST_SKIP() << "ATen kernel can handle mismatched out dim";
}
TensorFactory<ScalarType::Float> tf;
Scalar end = Scalar(5);
Tensor out = tf.zeros({5, 1});
// out.dim() should be 1, not 2
ET_EXPECT_KERNEL_FAILURE(context_, op_arange_out(end, out));
}
TEST_F(OpArangeOutTest, DynamicShapeUpperBoundSameAsExpected) {
TensorFactory<ScalarType::Float> tf;
Tensor expected_result = tf.make({5}, {0, 1, 2, 3, 4});
Tensor out =
tf.zeros({5}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
Tensor ret = op_arange_out(Scalar(5), out);
EXPECT_TENSOR_CLOSE(out, expected_result);
}
TEST_F(OpArangeOutTest, DynamicShapeUpperBoundLargerThanExpected) {
TensorFactory<ScalarType::Float> tf;
Tensor expected_result = tf.make({5}, {0, 1, 2, 3, 4});
Tensor out =
tf.zeros({10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
Tensor ret = op_arange_out(Scalar(5), out);
EXPECT_TENSOR_CLOSE(out, expected_result);
}
TEST_F(OpArangeOutTest, DynamicShapeUnbound) {
if (!torch::executor::testing::SupportedFeatures::get()->is_aten) {
GTEST_SKIP() << "Dynamic Unbound not supported";
}
TensorFactory<ScalarType::Float> tf;
Tensor expected_result = tf.make({5}, {0, 1, 2, 3, 4});
Tensor out =
tf.zeros({1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
Tensor ret = op_arange_out(Scalar(5), out);
EXPECT_TENSOR_CLOSE(out, expected_result);
}
/// A generic smoke test that works for any dtype that supports zeros().
TEST_F(OpArangeStartOutTest, AllRealDtypesSupported) {
#define TEST_ENTRY(ctype, dtype) \
test_arange_start_dtype<ctype, ScalarType::dtype>();
ET_FORALL_REAL_TYPES(TEST_ENTRY);
#undef TEST_ENTRY
}
TEST_F(OpArangeStartOutTest, FloatNumberNotEqualIntSupport) {
TensorFactory<ScalarType::Float> tf;
// Tested in bento:
// import torch
// torch.arange(5.5)
// >> tensor([0., 1., 2., 3., 4., 5.])
Scalar start = Scalar(0);
Scalar end = Scalar(5.5);
Scalar step = Scalar(1);
Tensor out = tf.zeros({6});
Tensor ret = op_arange_start_out(start, end, step, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, equal
Tensor expected = tf.make({6}, {0.0, 1.0, 2.0, 3.0, 4.0, 5.0});
EXPECT_TENSOR_EQ(out, expected);
}
TEST_F(OpArangeStartOutTest, OutDimUnsupportedDie) {
if (torch::executor::testing::SupportedFeatures::get()->is_aten) {
GTEST_SKIP() << "ATen kernel can handle mismatched out dim";
}
TensorFactory<ScalarType::Float> tf;
Scalar start = Scalar(0);
Scalar end = Scalar(5);
Scalar step = Scalar(1);
Tensor out = tf.zeros({5, 1});
// out.dim() should be 1, not 2
ET_EXPECT_KERNEL_FAILURE(
context_, op_arange_start_out(start, end, step, out));
}
TEST_F(OpArangeStartOutTest, DynamicShapeUpperBoundSameAsExpected) {
TensorFactory<ScalarType::Float> tf;
Tensor expected_result = tf.make({5}, {0, 1, 2, 3, 4});
Tensor out =
tf.zeros({5}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
Tensor ret = op_arange_start_out(Scalar(0), Scalar(5), Scalar(1), out);
EXPECT_TENSOR_CLOSE(out, expected_result);
}
TEST_F(OpArangeStartOutTest, DynamicShapeUpperBoundLargerThanExpected) {
TensorFactory<ScalarType::Float> tf;
Tensor expected_result = tf.make({5}, {0, 1, 2, 3, 4});
Tensor out =
tf.zeros({10}, torch::executor::TensorShapeDynamism::DYNAMIC_BOUND);
Tensor ret = op_arange_start_out(Scalar(0), Scalar(5), Scalar(1), out);
EXPECT_TENSOR_CLOSE(out, expected_result);
}
TEST_F(OpArangeStartOutTest, DynamicShapeUnbound) {
if (!torch::executor::testing::SupportedFeatures::get()->is_aten) {
GTEST_SKIP() << "Dynamic Unbound not supported";
}
TensorFactory<ScalarType::Float> tf;
Tensor expected_result = tf.make({5}, {0, 1, 2, 3, 4});
Tensor out =
tf.zeros({1}, torch::executor::TensorShapeDynamism::DYNAMIC_UNBOUND);
Tensor ret = op_arange_start_out(Scalar(0), Scalar(5), Scalar(1), out);
EXPECT_TENSOR_CLOSE(out, expected_result);
}
TEST_F(OpArangeStartOutTest, StartOut) {
TensorFactory<ScalarType::Float> tf;
Scalar start = Scalar(1.1);
Scalar end = Scalar(5.5);
Scalar step = Scalar(1.1);
Tensor out = tf.zeros({4});
Tensor ret = op_arange_start_out(start, end, step, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, equal
Tensor expected = tf.make({4}, {1.1, 2.2, 3.3, 4.4});
EXPECT_TENSOR_EQ(out, expected);
end = Scalar(5.51);
out = tf.zeros({5});
ret = op_arange_start_out(start, end, step, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, equal
expected = tf.make({5}, {1.1, 2.2, 3.3, 4.4, 5.5});
EXPECT_TENSOR_EQ(out, expected);
}
TEST_F(OpArangeStartOutTest, StartOutNegativeStep) {
TensorFactory<ScalarType::Float> tf;
Scalar start = Scalar(5.5);
Scalar end = Scalar(1.1);
Scalar step = Scalar(-1.1);
Tensor out = tf.zeros({4});
Tensor ret = op_arange_start_out(start, end, step, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, equal
Tensor expected = tf.make({4}, {5.5, 4.4, 3.3, 2.2});
EXPECT_TENSOR_EQ(out, expected);
end = Scalar(1.09);
out = tf.zeros({5});
ret = op_arange_start_out(start, end, step, out);
// Should always return the provided out Tensor.
EXPECT_TENSOR_EQ(ret, out);
// Expected tensor, equal
expected = tf.make({5}, {5.5, 4.4, 3.3, 2.2, 1.1});
EXPECT_TENSOR_EQ(out, expected);
}