From fcfa6187b929ac9f498781f5a08c2d9c0862d9a3 Mon Sep 17 00:00:00 2001 From: pytorchbot Date: Mon, 27 Jan 2025 12:13:58 -0600 Subject: [PATCH] Support Half/BFloat16 in op_allclose Pull Request resolved: https://github.com/pytorch/executorch/pull/7766 We incorrectly required these types to be bitwise-identical rather than close. (I had to develop this internally because the op_allclose_test doesn't run in OSS.) Differential Revision: [D68366831](https://our.internmc.facebook.com/intern/diff/D68366831/) ghstack-source-id: 262600586 Co-authored-by: Scott Wolchok --- kernels/portable/cpu/op_allclose.cpp | 14 + kernels/portable/test/op_allclose_test.cpp | 294 ++++++--------------- 2 files changed, 88 insertions(+), 220 deletions(-) diff --git a/kernels/portable/cpu/op_allclose.cpp b/kernels/portable/cpu/op_allclose.cpp index a896d9a852..3784fbffc5 100644 --- a/kernels/portable/cpu/op_allclose.cpp +++ b/kernels/portable/cpu/op_allclose.cpp @@ -84,6 +84,20 @@ bool tensors_are_close( a.numel(), rtol, atol); + } else if (a.scalar_type() == ScalarType::Half) { + return data_is_close( + a.const_data_ptr(), + b.const_data_ptr(), + a.numel(), + rtol, + atol); + } else if (a.scalar_type() == ScalarType::BFloat16) { + return data_is_close( + a.const_data_ptr(), + b.const_data_ptr(), + a.numel(), + rtol, + atol); } else { // Non-floating-point types can be compared bitwise. return memcmp(a.mutable_data_ptr(), b.mutable_data_ptr(), a.nbytes()) == 0; diff --git a/kernels/portable/test/op_allclose_test.cpp b/kernels/portable/test/op_allclose_test.cpp index 25dbebce2a..3d1e96264d 100644 --- a/kernels/portable/test/op_allclose_test.cpp +++ b/kernels/portable/test/op_allclose_test.cpp @@ -20,13 +20,47 @@ using namespace ::testing; using exec_aten::ScalarType; using exec_aten::Tensor; using torch::executor::native::allclose_out; -using torch::executor::testing::IsCloseTo; using torch::executor::testing::TensorFactory; const double default_atol{1e-08}; const double default_rtol{1e-05}; -TEST(OpAllCloseTest, IdenticalFloatTensors) { +class OpAllCloseTest : public OperatorTest { + protected: + template + void test_tensors_vary_tolerance( + double rtol, + double rdiff, + double atol, + double adiff, + bool should_match) { + TensorFactory tf; + Tensor a = tf.ones(/*sizes=*/{2, 2}); + Tensor b = tf.ones(/*sizes=*/{2, 2}); + + auto a_data = a.data_ptr(); + auto b_data = b.data_ptr(); + b_data[0] = a_data[0] + adiff + a_data[0] * rdiff; + + TensorFactory tf_bool; + Tensor out = tf_bool.zeros(/*sizes=*/{1}); + + allclose_out( + a, + b, + rtol, + atol, + /*equal_nan=*/false, + /*dummy_param=*/false, + out); + + auto out_data = out.data_ptr(); + EXPECT_EQ(out_data[0], should_match) + << a_data[0] << " doesn't match " << b_data[0] << "; dtype " << DTYPE; + } +}; + +TEST_F(OpAllCloseTest, IdenticalFloatTensors) { TensorFactory tf_float; Tensor a = tf_float.ones(/*sizes=*/{2, 2}); Tensor b = tf_float.ones(/*sizes=*/{2, 2}); @@ -46,7 +80,7 @@ TEST(OpAllCloseTest, IdenticalFloatTensors) { EXPECT_EQ(out_data[0], true); } -TEST(OpAllCloseTest, IdenticalDoubleTensors) { +TEST_F(OpAllCloseTest, IdenticalDoubleTensors) { TensorFactory tf_double; Tensor a = tf_double.ones(/*sizes=*/{2, 2}); Tensor b = tf_double.ones(/*sizes=*/{2, 2}); @@ -66,7 +100,7 @@ TEST(OpAllCloseTest, IdenticalDoubleTensors) { EXPECT_EQ(out_data[0], true); } -TEST(OpAllCloseTest, NonEqualFloatTensors) { +TEST_F(OpAllCloseTest, NonEqualFloatTensors) { TensorFactory tf_float; Tensor a = tf_float.make(/*sizes=*/{2, 2}, /*data=*/{1., 2., 3., 4.}); Tensor b = tf_float.make(/*sizes=*/{2, 2}, /*data=*/{5., 6., 7., 8.}); @@ -86,7 +120,7 @@ TEST(OpAllCloseTest, NonEqualFloatTensors) { EXPECT_EQ(out_data[0], false); } -TEST(OpAllCloseTest, NonEqualDoubleTensors) { +TEST_F(OpAllCloseTest, NonEqualDoubleTensors) { TensorFactory tf_double; Tensor a = tf_double.make(/*sizes=*/{2, 2}, /*data=*/{1., 2., 3., 4.}); Tensor b = tf_double.make(/*sizes=*/{2, 2}, /*data=*/{5., 6., 7., 8.}); @@ -106,7 +140,7 @@ TEST(OpAllCloseTest, NonEqualDoubleTensors) { EXPECT_EQ(out_data[0], false); } -TEST(OpAllCloseTest, IdenticalIntTensors) { +TEST_F(OpAllCloseTest, IdenticalIntTensors) { TensorFactory tf_int; Tensor a = tf_int.ones(/*sizes=*/{2, 2}); Tensor b = tf_int.ones(/*sizes=*/{2, 2}); @@ -125,7 +159,7 @@ TEST(OpAllCloseTest, IdenticalIntTensors) { EXPECT_EQ(out_data[0], true); } -TEST(OpAllCloseTest, NonEqualIntTensors) { +TEST_F(OpAllCloseTest, NonEqualIntTensors) { TensorFactory tf_int; Tensor a = tf_int.make(/*sizes=*/{2, 2}, /*data=*/{1, 2, 3, 4}); Tensor b = tf_int.make(/*sizes=*/{2, 2}, /*data=*/{5, 6, 7, 8}); @@ -144,7 +178,7 @@ TEST(OpAllCloseTest, NonEqualIntTensors) { EXPECT_EQ(out_data[0], false); } -TEST(OpAllCloseTest, IdenticalBoolTensors) { +TEST_F(OpAllCloseTest, IdenticalBoolTensors) { TensorFactory tf_bool; Tensor a = tf_bool.ones(/*sizes=*/{2, 2}); Tensor b = tf_bool.ones(/*sizes=*/{2, 2}); @@ -162,7 +196,7 @@ TEST(OpAllCloseTest, IdenticalBoolTensors) { EXPECT_EQ(out_data[0], true); } -TEST(OpAllCloseTest, NonEqualBoolTensors) { +TEST_F(OpAllCloseTest, NonEqualBoolTensors) { TensorFactory tf_bool; Tensor a = tf_bool.ones(/*sizes=*/{2, 2}); Tensor b = tf_bool.ones(/*sizes=*/{2, 2}); @@ -182,7 +216,7 @@ TEST(OpAllCloseTest, NonEqualBoolTensors) { EXPECT_EQ(out_data[0], false); } -TEST(OpAllCloseTest, MismatchedInputShapesDeath) { +TEST_F(OpAllCloseTest, MismatchedInputShapesDeath) { TensorFactory tf_int; Tensor a = tf_int.ones(/*sizes=*/{2, 1}); Tensor b = tf_int.ones(/*sizes=*/{2, 2}); @@ -201,7 +235,7 @@ TEST(OpAllCloseTest, MismatchedInputShapesDeath) { ""); } -TEST(OpAllCloseTest, MismatchedInputDtypesDeath) { +TEST_F(OpAllCloseTest, MismatchedInputDtypesDeath) { TensorFactory tf_float; Tensor a = tf_float.ones(/*sizes=*/{2, 2}); @@ -223,7 +257,7 @@ TEST(OpAllCloseTest, MismatchedInputDtypesDeath) { ""); } -TEST(OpAllCloseTest, IncorrectOutputDtypeDeath) { +TEST_F(OpAllCloseTest, IncorrectOutputDtypeDeath) { TensorFactory tf_float; Tensor a = tf_float.ones(/*sizes=*/{2, 2}); Tensor b = tf_float.ones(/*sizes=*/{2, 2}); @@ -241,7 +275,7 @@ TEST(OpAllCloseTest, IncorrectOutputDtypeDeath) { ""); } -TEST(OpAllCloseTest, IncorrectOutputShapeDeath) { +TEST_F(OpAllCloseTest, IncorrectOutputShapeDeath) { TensorFactory tf_float; Tensor a = tf_float.ones(/*sizes=*/{2, 2}); Tensor b = tf_float.ones(/*sizes=*/{2, 2}); @@ -260,219 +294,39 @@ TEST(OpAllCloseTest, IncorrectOutputShapeDeath) { ""); } -TEST(OpAllCloseTest, FloatTensorsVaryWithinRelativeTolerance) { - const double rtol = 1e-05; - const double rdiff = 1e-06; - - TensorFactory tf_float; - Tensor a = tf_float.ones(/*sizes=*/{2, 2}); - Tensor b = tf_float.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] * (1. + rdiff); - - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, rtol, /*atol=*/0., /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], true); -} - -TEST(OpAllCloseTest, DoubleTensorsVaryWithinRelativeTolerance) { - const double rtol = 1e-05; - const double rdiff = 1e-06; - - TensorFactory tf_double; - Tensor a = tf_double.ones(/*sizes=*/{2, 2}); - Tensor b = tf_double.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] * (1. + rdiff); - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, rtol, /*atol=*/0., /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], true); -} - -TEST(OpAllCloseTest, FloatTensorsVaryOutsideRelativeTolerance) { - const double rtol = 1e-05; - const double rdiff = 1e-04; - - TensorFactory tf_float; - Tensor a = tf_float.ones(/*sizes=*/{2, 2}); - Tensor b = tf_float.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] * (1. + rdiff); - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, rtol, /*atol=*/0., /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], false); -} - -TEST(OpAllCloseTest, DoubleTensorsVaryOutsideRelativeTolerance) { - const double rtol = 1e-05; - const double rdiff = 1e-04; - - TensorFactory tf_double; - Tensor a = tf_double.ones(/*sizes=*/{2, 2}); - Tensor b = tf_double.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] * (1. + rdiff); - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, rtol, /*atol=*/0., /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], false); -} - -TEST(OpAllCloseTest, FloatTensorsVaryWithinAbsoluteTolerance) { - const double atol = 1e-08; - const double adiff = 1e-09; - - TensorFactory tf_float; - Tensor a = tf_float.ones(/*sizes=*/{2, 2}); - Tensor b = tf_float.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] + adiff; - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, /*rtol=*/0., atol, /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], true); +TEST_F(OpAllCloseTest, TensorsVaryWithinRelativeTolerance) { +#define TEST_ENTRY(ctype, dtype) \ + test_tensors_vary_tolerance( \ + 1e-01, 1e-02, 0, 0, true); + ET_FORALL_FLOATHBF16_TYPES(TEST_ENTRY); +#undef TEST_ENTRY } -TEST(OpAllCloseTest, DoubleTensorsVaryWithinAbsoluteTolerance) { - const double atol = 1e-08; - const double adiff = 1e-09; - - TensorFactory tf_double; - Tensor a = tf_double.ones(/*sizes=*/{2, 2}); - Tensor b = tf_double.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] + adiff; - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, /*rtol=*/0., atol, /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], true); +TEST_F(OpAllCloseTest, TensorsVaryOutsideRelativeTolerance) { +#define TEST_ENTRY(ctype, dtype) \ + test_tensors_vary_tolerance(1e-01, 1, 0, 0, false); + ET_FORALL_FLOATHBF16_TYPES(TEST_ENTRY); +#undef TEST_ENTRY } -TEST(OpAllCloseTest, FloatTensorsVaryOutsideAbsoluteTolerance) { - const double atol = 1e-08; - const double adiff = 1e-07; - - TensorFactory tf_float; - Tensor a = tf_float.ones(/*sizes=*/{2, 2}); - Tensor b = tf_float.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] + adiff; - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, /*rtol=*/0., atol, /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], false); +TEST_F(OpAllCloseTest, TensorsVaryWithinAbsoluteTolerance) { +#define TEST_ENTRY(ctype, dtype) \ + test_tensors_vary_tolerance( \ + 0, 0, 1e-01, 1e-02, true); + ET_FORALL_FLOATHBF16_TYPES(TEST_ENTRY); +#undef TEST_ENTRY } -TEST(OpAllCloseTest, DoubleTensorsVaryOutsideAbsoluteTolerance) { - const double atol = 1e-08; - const double adiff = 1e-07; - - TensorFactory tf_double; - Tensor a = tf_double.ones(/*sizes=*/{2, 2}); - Tensor b = tf_double.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] + adiff; - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, b, /*rtol=*/0., atol, /*equal_nan=*/false, /*dummy_param=*/false, out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], false); +TEST_F(OpAllCloseTest, TensorsVaryOutsideAbsoluteTolerance) { +#define TEST_ENTRY(ctype, dtype) \ + test_tensors_vary_tolerance(0, 0, 1e-01, 1, false); + ET_FORALL_FLOATHBF16_TYPES(TEST_ENTRY); +#undef TEST_ENTRY } -TEST(OpAllCloseTest, FloatTensorsVaryWithZeroTolerance) { - TensorFactory tf_float; - Tensor a = tf_float.ones(/*sizes=*/{2, 2}); - Tensor b = tf_float.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] + 1e-07; - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, - b, - /*rtol=*/0., - /*atol=*/0, - /*equal_nan=*/false, - /*dummy_param=*/false, - out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], false); -} - -TEST(OpAllCloseTest, DoubleTensorsVaryWithZeroTolerance) { - TensorFactory tf_double; - Tensor a = tf_double.ones(/*sizes=*/{2, 2}); - Tensor b = tf_double.ones(/*sizes=*/{2, 2}); - - auto a_data = a.data_ptr(); - auto b_data = b.data_ptr(); - b_data[0] = a_data[0] + 1e-09; - TensorFactory tf_bool; - Tensor out = tf_bool.zeros(/*sizes=*/{1}); - - allclose_out( - a, - b, - /*rtol=*/0., - /*atol=*/0., - /*equal_nan=*/false, - /*dummy_param=*/false, - out); - - auto out_data = out.data_ptr(); - EXPECT_EQ(out_data[0], false); +TEST_F(OpAllCloseTest, TensorsVaryWithZeroTolerance) { +#define TEST_ENTRY(ctype, dtype) \ + test_tensors_vary_tolerance(0, 0, 0, 1e-01, false); + ET_FORALL_FLOATHBF16_TYPES(TEST_ENTRY); +#undef TEST_ENTRY }