diff --git a/tensorflow/core/common_runtime/graph_runner_test.cc b/tensorflow/core/common_runtime/graph_runner_test.cc index e969ee8df77..b559ccc9374 100644 --- a/tensorflow/core/common_runtime/graph_runner_test.cc +++ b/tensorflow/core/common_runtime/graph_runner_test.cc @@ -41,8 +41,6 @@ limitations under the License. namespace tensorflow { namespace { -using test::internal::ExpectEqual; - TEST(GraphRunnerTest, SingleConst) { Scope root = Scope::NewRootScope(); auto c = ops::Const(root, 42.0f); @@ -50,7 +48,7 @@ TEST(GraphRunnerTest, SingleConst) { std::vector outputs; Status s = graph_runner.Run(root.graph(), nullptr, {}, {c.name()}, &outputs); TF_ASSERT_OK(s); - ExpectEqual(42.0f, outputs[0].scalar()()); + test::ExpectEqual(test::AsScalar(42.0f), outputs[0]); } // If not using DeepCopy, and the allocator is deleted with the cpu-device, @@ -77,7 +75,7 @@ TEST(GraphRunnerTest, DeepCopy) { graph_runner.Run(root.graph(), nullptr, inputs, {"add:0"}, &outputs); TF_ASSERT_OK(s); } - ExpectEqual(3.0f, outputs[0].scalar()()); + test::ExpectEqual(test::AsScalar(3.0f), outputs[0]); } TEST(GraphRunnerTest, MultiFetchConst) { @@ -89,8 +87,8 @@ TEST(GraphRunnerTest, MultiFetchConst) { Status s = graph_runner.Run(root.graph(), nullptr, {}, {c.name(), pi.name()}, &outputs); TF_ASSERT_OK(s); - ExpectEqual(42.0f, outputs[0].scalar()()); - ExpectEqual(3.14f, outputs[1].scalar()()); + test::ExpectEqual(test::AsScalar(42.0f), outputs[0]); + test::ExpectEqual(test::AsScalar(3.14f), outputs[1]); } TEST(GraphRunnerTest, FeedAndFetch) { @@ -111,7 +109,7 @@ TEST(GraphRunnerTest, FeedAndFetch) { Status s = graph_runner.Run(root.graph(), nullptr, inputs, {"add:0"}, &outputs); TF_ASSERT_OK(s); - ExpectEqual(3.0f, outputs[0].scalar()()); + test::ExpectEqual(test::AsScalar(3.0f), outputs[0]); } } // namespace diff --git a/tensorflow/core/framework/tensor_test.cc b/tensorflow/core/framework/tensor_test.cc index a994360f250..566dbbf033a 100644 --- a/tensorflow/core/framework/tensor_test.cc +++ b/tensorflow/core/framework/tensor_test.cc @@ -115,25 +115,39 @@ TEST(TensorTest, DataType_Traits) { EXPECT_TRUE(std::is_trivial::value); } +template +void ExpectEqual(const Tensor& x, const Tensor& y) { + test::ExpectEqual(x, y); +} +// test::ExpectEqual does not support ResourceHandle or Variant. +template <> +void ExpectEqual(const Tensor& x, const Tensor& y) { + EXPECT_EQ(x, y); +} +template <> +void ExpectEqual(const Tensor& x, const Tensor& y) { + EXPECT_EQ(x, y); +} + template void TestCopies(const Tensor& t) { { LOG(INFO) << "CopyFrom()"; Tensor t2(t.dtype()); EXPECT_TRUE(t2.CopyFrom(t, t.shape())); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "operator=()"; Tensor t2(t.dtype()); t2 = t; - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "deep copy"; Tensor t2(t.dtype(), t.shape()); t2.flat() = t.flat(); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "AsProtoField()"; @@ -141,7 +155,7 @@ void TestCopies(const Tensor& t) { t.AsProtoField(&proto); Tensor t2(t.dtype()); EXPECT_TRUE(t2.FromProto(proto)); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "AsProtoTensorContent()"; @@ -149,24 +163,24 @@ void TestCopies(const Tensor& t) { t.AsProtoTensorContent(&proto); Tensor t2(t.dtype()); EXPECT_TRUE(t2.FromProto(proto)); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); // Make another copy via tensor_content field. *proto.mutable_tensor_content() = proto.tensor_content(); Tensor t3(t.dtype()); EXPECT_TRUE(t3.FromProto(proto)); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "AsTensor"; gtl::ArraySlice values(t.flat().data(), t.NumElements()); Tensor t2 = test::AsTensor(values, t.shape()); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "Move constructor"; Tensor t2 = t; Tensor t3(std::move(t2)); - test::ExpectTensorEqual(t, t3); + ExpectEqual(t, t3); EXPECT_TRUE(t3.IsInitialized()); EXPECT_FALSE(t2.IsInitialized()); } @@ -176,7 +190,7 @@ void TestCopies(const Tensor& t) { Tensor t3 = std::move(t2); Tensor* t4 = &t3; *t4 = std::move(t3); - test::ExpectTensorEqual(t, t3); + ExpectEqual(t, t3); EXPECT_TRUE(t3.IsInitialized()); EXPECT_FALSE(t2.IsInitialized()); } @@ -236,31 +250,31 @@ TEST(Tensor_Variant, Simple) { LOG(INFO) << "CopyFrom()"; Tensor t2(t.dtype()); EXPECT_TRUE(t2.CopyFrom(t, t.shape())); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "operator=()"; Tensor t2(t.dtype()); t2 = t; - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "deep copy"; Tensor t2(t.dtype(), t.shape()); t2.flat() = t.flat(); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "AsTensor"; gtl::ArraySlice values(t.flat().data(), t.NumElements()); Tensor t2 = test::AsTensor(values, t.shape()); - test::ExpectTensorEqual(t, t2); + ExpectEqual(t, t2); } { LOG(INFO) << "Move constructor"; Tensor t2 = t; Tensor t3(std::move(t2)); - test::ExpectTensorEqual(t, t3); + ExpectEqual(t, t3); EXPECT_TRUE(t3.IsInitialized()); EXPECT_FALSE(t2.IsInitialized()); } @@ -270,7 +284,7 @@ TEST(Tensor_Variant, Simple) { Tensor t3 = std::move(t2); Tensor* t4 = &t3; *t4 = std::move(t3); - test::ExpectTensorEqual(t, t3); + ExpectEqual(t, t3); EXPECT_TRUE(t3.IsInitialized()); EXPECT_FALSE(t2.IsInitialized()); } @@ -933,7 +947,7 @@ TEST(Tensor_Float, SimpleWithHelper) { Tensor t2(t1.dtype(), t1.shape()); t2.flat() = t1.flat() * 2.0f; Tensor t3 = test::AsTensor({0, 2, 4, 6, 8, 10}, t1.shape()); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } TEST(Tensor_Int32, SimpleWithHelper) { @@ -941,7 +955,7 @@ TEST(Tensor_Int32, SimpleWithHelper) { Tensor t2(t1.dtype(), t1.shape()); t2.flat() = t1.flat() * 2; Tensor t3 = test::AsTensor({0, 2, 4, 6, 8, 10}, t1.shape()); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } TEST(Tensor_UInt16, SimpleWithHelper) { @@ -949,7 +963,7 @@ TEST(Tensor_UInt16, SimpleWithHelper) { Tensor t2(t1.dtype(), t1.shape()); t2.flat() = t1.flat() * uint16(2); Tensor t3 = test::AsTensor({0, 2, 4, 6, 8, 10}, t1.shape()); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } TEST(Tensor_QInt8, SimpleWithHelper) { @@ -957,7 +971,7 @@ TEST(Tensor_QInt8, SimpleWithHelper) { Tensor t2(t1.dtype(), t1.shape()); t2.flat() = t1.flat() + qint8(-2); Tensor t3 = test::AsTensor({-2, -1, 0, 1, 2, 3}, {2, 3}); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } TEST(Tensor_QUInt8, SimpleWithHelper) { @@ -965,7 +979,7 @@ TEST(Tensor_QUInt8, SimpleWithHelper) { Tensor t2(t1.dtype(), t1.shape()); t2.flat() = t1.flat() + quint8(2); Tensor t3 = test::AsTensor({2, 3, 4, 5, 6, 7}, {2, 3}); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } TEST(Tensor_Int64, SimpleWithHelper) { @@ -977,7 +991,7 @@ TEST(Tensor_Int64, SimpleWithHelper) { Tensor t3 = test::AsTensor( {0LL << 48, 2LL << 48, 4LL << 48, 6LL << 48, 8LL << 48, 10LL << 48}, {2, 3}); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } TEST(Tensor_String, SimpleWithHelper) { @@ -990,7 +1004,7 @@ TEST(Tensor_String, SimpleWithHelper) { } // Test with helper. - test::ExpectTensorEqual(t1, t2); + ExpectEqual(t1, t2); } TEST(Tensor_Bool, SimpleWithHelper) { @@ -1005,7 +1019,7 @@ TEST(Tensor_Bool, SimpleWithHelper) { } // Test with helper. - test::ExpectTensorEqual(t1, t2); + ExpectEqual(t1, t2); } TEST(Tensor_Complex, Simple64) { @@ -1035,7 +1049,7 @@ TEST(Tensor_Complex, SimpleWithHelper64) { {0, {-2, 2}, {0, 4}, {-6, 6}, {-8, 0}, {-10, 4}}, // shape {2, 3}); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } // Does some numeric operations for complex64 numbers. @@ -1084,7 +1098,7 @@ TEST(Tensor_Complex, SimpleWithHelper128) { {0, {-2, 2}, {0, 4}, {-6, 6}, {-8, 0}, {-10, 4}}, // shape {2, 3}); - test::ExpectTensorEqual(t2, t3); + ExpectEqual(t2, t3); } // Does some numeric operations for complex128 numbers. @@ -1223,7 +1237,7 @@ TEST(Tensor, Slice_Basic) { // A simple slice equivalent to identity. TestCopies(y); y = x.Slice(0, 10); - test::ExpectTensorEqual(x, y); + ExpectEqual(x, y); EXPECT_EQ(x.flat().data(), y.flat().data()); // A slice of a slice. diff --git a/tensorflow/core/framework/tensor_testutil.cc b/tensorflow/core/framework/tensor_testutil.cc index 313451d6b83..89a20b9a039 100644 --- a/tensorflow/core/framework/tensor_testutil.cc +++ b/tensorflow/core/framework/tensor_testutil.cc @@ -14,68 +14,236 @@ limitations under the License. ==============================================================================*/ #include "tensorflow/core/framework/tensor_testutil.h" + #include +#include "tensorflow/core/framework/tensor.h" + namespace tensorflow { namespace test { -template -void ExpectClose(const Tensor& x, const Tensor& y, double atol, double rtol) { - const T* Tx = x.flat().data(); - const T* Ty = y.flat().data(); - const auto size = x.NumElements(); +static ::testing::AssertionResult IsSameType(const Tensor& x, const Tensor& y) { + if (x.dtype() != y.dtype()) { + return ::testing::AssertionFailure() + << "Tensors have different dtypes (" << x.dtype() << " vs " + << y.dtype() << ")"; + } + return ::testing::AssertionSuccess(); +} - // Tolerance's type (RealType) can be different from T. - // For example, if T = std::complex, then RealType = float. - // Did not use std::numeric_limits because - // 1) It returns 0 for Eigen::half. - // 2) It doesn't support T=std::complex. - // (Would have to write a templated struct to handle this.) - typedef decltype(Eigen::NumTraits::epsilon()) RealType; - const RealType kSlackFactor = static_cast(5.0); - const RealType kDefaultTol = kSlackFactor * Eigen::NumTraits::epsilon(); - const RealType typed_atol = - (atol < 0) ? kDefaultTol : static_cast(atol); - const RealType typed_rtol = - (rtol < 0) ? kDefaultTol : static_cast(rtol); - ASSERT_GE(typed_atol, static_cast(0.0)) - << "typed_atol is negative: " << typed_atol; - ASSERT_GE(typed_rtol, static_cast(0.0)) - << "typed_rtol is negative: " << typed_rtol; - const int max_failures = 10; +static ::testing::AssertionResult IsSameShape(const Tensor& x, + const Tensor& y) { + if (!x.IsSameSize(y)) { + return ::testing::AssertionFailure() + << "Tensors have different shapes (" << x.shape().DebugString() + << " vs " << y.shape().DebugString() << ")"; + } + return ::testing::AssertionSuccess(); +} + +template +static ::testing::AssertionResult EqualFailure(const T& x, const T& y) { + return ::testing::AssertionFailure() + << std::setprecision(std::numeric_limits::digits10 + 2) << x + << " not equal to " << y; +} +static ::testing::AssertionResult IsEqual(float x, float y) { + if (::testing::internal::CmpHelperFloatingPointEQ("", "", x, y)) + return ::testing::AssertionSuccess(); + return EqualFailure(x, y); +} +static ::testing::AssertionResult IsEqual(double x, double y) { + if (::testing::internal::CmpHelperFloatingPointEQ("", "", x, y)) + return ::testing::AssertionSuccess(); + return EqualFailure(x, y); +} +static ::testing::AssertionResult IsEqual(Eigen::half x, Eigen::half y) { + // Below is a reimplementation of CmpHelperFloatingPointEQ, which + // we cannot use because Eigen::half is not default-constructible. + + if (isnan(x) || isnan(y)) return EqualFailure(x, y); + + auto sign_and_magnitude_to_biased = [](uint16_t sam) { + const uint16_t kSignBitMask = 0x8000; + if (kSignBitMask & sam) return ~sam + 1; // negative number. + return kSignBitMask | sam; // positive number. + }; + + auto xb = sign_and_magnitude_to_biased(x.x); + auto yb = sign_and_magnitude_to_biased(y.x); + auto distance = xb >= yb ? xb - yb : yb - xb; + const uint16_t kMaxUlps = 4; + + if (distance <= kMaxUlps) return ::testing::AssertionSuccess(); + return EqualFailure(x, y); +} +template +static ::testing::AssertionResult IsEqual(const T& x, const T& y) { + if (::testing::internal::CmpHelperEQ("", "", x, y)) + return ::testing::AssertionSuccess(); + return EqualFailure(x, y); +} +template +static ::testing::AssertionResult IsEqual(const std::complex& x, + const std::complex& y) { + if (IsEqual(x.real(), y.real()) && IsEqual(x.imag(), y.imag())) + return ::testing::AssertionSuccess(); + return EqualFailure(x, y); +} + +template +static void ExpectEqual(const Tensor& x, const Tensor& y) { + const T* Tx = x.unaligned_flat().data(); + const T* Ty = y.unaligned_flat().data(); + auto size = x.NumElements(); + int max_failures = 10; int num_failures = 0; - for (int i = 0; i < size; ++i) { - EXPECT_TRUE( - internal::Helper::IsClose(Tx[i], Ty[i], typed_atol, typed_rtol)) - << "index = " << (++num_failures, i) << " x = " << Tx[i] - << " y = " << Ty[i] << " typed_atol = " << typed_atol - << " typed_rtol = " << typed_rtol; + for (decltype(size) i = 0; i < size; ++i) { + EXPECT_TRUE(IsEqual(Tx[i], Ty[i])) << "i = " << (++num_failures, i); ASSERT_LT(num_failures, max_failures) << "Too many mismatches, giving up."; } } +template +static ::testing::AssertionResult IsClose(const T& x, const T& y, const T& atol, + const T& rtol) { + if (x == y) return ::testing::AssertionSuccess(); // Handle infinity. + auto tolerance = atol + rtol * Eigen::numext::abs(x); + if (Eigen::numext::abs(x - y) <= tolerance) + return ::testing::AssertionSuccess(); + return ::testing::AssertionFailure() << x << " not close to " << y; +} + +template +static ::testing::AssertionResult IsClose(const std::complex& x, + const std::complex& y, + const T& atol, const T& rtol) { + if (IsClose(x.real(), y.real(), atol, rtol) && + IsClose(x.imag(), y.imag(), atol, rtol)) + return ::testing::AssertionSuccess(); + return ::testing::AssertionFailure() << x << " not close to " << y; +} + +// Return type can be different from T, e.g. float for T=std::complex. +template +static auto GetTolerance(double tolerance) { + using Real = typename Eigen::NumTraits::Real; + auto default_tol = static_cast(5.0) * Eigen::NumTraits::epsilon(); + auto result = tolerance < 0.0 ? default_tol : static_cast(tolerance); + EXPECT_GE(result, static_cast(0)); + return result; +} + +template +static void ExpectClose(const Tensor& x, const Tensor& y, double atol, + double rtol) { + auto typed_atol = GetTolerance(atol); + auto typed_rtol = GetTolerance(rtol); + + const T* Tx = x.unaligned_flat().data(); + const T* Ty = y.unaligned_flat().data(); + auto size = x.NumElements(); + int max_failures = 10; + int num_failures = 0; + for (decltype(size) i = 0; i < size; ++i) { + EXPECT_TRUE(IsClose(Tx[i], Ty[i], typed_atol, typed_rtol)) + << "i = " << (++num_failures, i) << " Tx[i] = " << Tx[i] + << " Ty[i] = " << Ty[i]; + ASSERT_LT(num_failures, max_failures) + << "Too many mismatches (atol = " << atol << " rtol = " << rtol + << "), giving up."; + } + EXPECT_EQ(num_failures, 0) + << "Mismatches detected (atol = " << atol << " rtol = " << rtol << ")."; +} + +void ExpectEqual(const Tensor& x, const Tensor& y) { + ASSERT_TRUE(IsSameType(x, y)); + ASSERT_TRUE(IsSameShape(x, y)); + + switch (x.dtype()) { + case DT_FLOAT: + return ExpectEqual(x, y); + case DT_DOUBLE: + return ExpectEqual(x, y); + case DT_INT32: + return ExpectEqual(x, y); + case DT_UINT32: + return ExpectEqual(x, y); + case DT_UINT16: + return ExpectEqual(x, y); + case DT_UINT8: + return ExpectEqual(x, y); + case DT_INT16: + return ExpectEqual(x, y); + case DT_INT8: + return ExpectEqual(x, y); + case DT_STRING: + return ExpectEqual(x, y); + case DT_COMPLEX64: + return ExpectEqual(x, y); + case DT_COMPLEX128: + return ExpectEqual(x, y); + case DT_INT64: + return ExpectEqual(x, y); + case DT_UINT64: + return ExpectEqual(x, y); + case DT_BOOL: + return ExpectEqual(x, y); + case DT_QINT8: + return ExpectEqual(x, y); + case DT_QUINT8: + return ExpectEqual(x, y); + case DT_QINT16: + return ExpectEqual(x, y); + case DT_QUINT16: + return ExpectEqual(x, y); + case DT_QINT32: + return ExpectEqual(x, y); + case DT_BFLOAT16: + return ExpectEqual(x, y); + case DT_HALF: + return ExpectEqual(x, y); + default: + EXPECT_TRUE(false) << "Unsupported type : " << DataTypeString(x.dtype()); + } +} + void ExpectClose(const Tensor& x, const Tensor& y, double atol, double rtol) { - internal::AssertSameTypeDims(x, y); + ASSERT_TRUE(IsSameType(x, y)); + ASSERT_TRUE(IsSameShape(x, y)); + switch (x.dtype()) { case DT_HALF: - ExpectClose(x, y, atol, rtol); - break; + return ExpectClose(x, y, atol, rtol); case DT_FLOAT: - ExpectClose(x, y, atol, rtol); - break; + return ExpectClose(x, y, atol, rtol); case DT_DOUBLE: - ExpectClose(x, y, atol, rtol); - break; + return ExpectClose(x, y, atol, rtol); case DT_COMPLEX64: - ExpectClose(x, y, atol, rtol); - break; + return ExpectClose(x, y, atol, rtol); case DT_COMPLEX128: - ExpectClose(x, y, atol, rtol); - break; + return ExpectClose(x, y, atol, rtol); default: - LOG(FATAL) << "Unexpected type : " << DataTypeString(x.dtype()); + EXPECT_TRUE(false) << "Unsupported type : " << DataTypeString(x.dtype()); } } +::testing::AssertionResult internal_test::IsClose(Eigen::half x, Eigen::half y, + double atol, double rtol) { + return test::IsClose(x, y, GetTolerance(atol), + GetTolerance(rtol)); +} +::testing::AssertionResult internal_test::IsClose(float x, float y, double atol, + double rtol) { + return test::IsClose(x, y, GetTolerance(atol), + GetTolerance(rtol)); +} +::testing::AssertionResult internal_test::IsClose(double x, double y, + double atol, double rtol) { + return test::IsClose(x, y, GetTolerance(atol), + GetTolerance(rtol)); +} + } // end namespace test } // end namespace tensorflow diff --git a/tensorflow/core/framework/tensor_testutil.h b/tensorflow/core/framework/tensor_testutil.h index 80dddfba801..5ca768d9742 100644 --- a/tensorflow/core/framework/tensor_testutil.h +++ b/tensorflow/core/framework/tensor_testutil.h @@ -94,190 +94,42 @@ void FillFn(Tensor* tensor, std::function fn) { } // Expects "x" and "y" are tensors of the same type, same shape, and -// identical values. -template -void ExpectTensorEqual(const Tensor& x, const Tensor& y); +// identical values (within 4 ULPs for floating point types). +void ExpectEqual(const Tensor& x, const Tensor& y); -// Expects "x" and "y" are tensors of the same type, same shape, and -// approximate equal values, each within "abs_err". -template -void ExpectTensorNear(const Tensor& x, const Tensor& y, const T& abs_err); - -// Expects "x" and "y" are tensors of the same type (float or double), +// Expects "x" and "y" are tensors of the same (floating point) type, // same shape and element-wise difference between x and y is no more -// than atol + rtol * abs(x). If atol or rtol is negative, it is replaced -// with a default tolerance value = data type's epsilon * kSlackFactor. +// than atol + rtol * abs(x). If atol or rtol is negative, the data type's +// epsilon * kSlackFactor is used. void ExpectClose(const Tensor& x, const Tensor& y, double atol = -1.0, double rtol = -1.0); -// Implementation details. - -namespace internal { - -template -struct is_floating_point_type { - static constexpr bool value = std::is_same::value || - std::is_same::value || - std::is_same::value || - std::is_same>::value || - std::is_same>::value; -}; - -template -inline void ExpectEqual(const T& a, const T& b) { - EXPECT_EQ(a, b); -} - -template <> -inline void ExpectEqual(const float& a, const float& b) { - EXPECT_FLOAT_EQ(a, b); -} - -template <> -inline void ExpectEqual(const double& a, const double& b) { - EXPECT_DOUBLE_EQ(a, b); -} - -template <> -inline void ExpectEqual(const complex64& a, const complex64& b) { - EXPECT_FLOAT_EQ(a.real(), b.real()) << a << " vs. " << b; - EXPECT_FLOAT_EQ(a.imag(), b.imag()) << a << " vs. " << b; -} - -template <> -inline void ExpectEqual(const complex128& a, const complex128& b) { - EXPECT_DOUBLE_EQ(a.real(), b.real()) << a << " vs. " << b; - EXPECT_DOUBLE_EQ(a.imag(), b.imag()) << a << " vs. " << b; -} - -template -inline void ExpectEqual(const T& a, const T& b, int index) { - EXPECT_EQ(a, b) << " at index " << index; -} - -template <> -inline void ExpectEqual(const float& a, const float& b, int index) { - EXPECT_FLOAT_EQ(a, b) << " at index " << index; -} - -template <> -inline void ExpectEqual(const double& a, const double& b, int index) { - EXPECT_DOUBLE_EQ(a, b) << " at index " << index; -} - -template <> -inline void ExpectEqual(const complex64& a, const complex64& b, - int index) { - EXPECT_FLOAT_EQ(a.real(), b.real()) - << a << " vs. " << b << " at index " << index; - EXPECT_FLOAT_EQ(a.imag(), b.imag()) - << a << " vs. " << b << " at index " << index; -} - -template <> -inline void ExpectEqual(const complex128& a, const complex128& b, - int index) { - EXPECT_DOUBLE_EQ(a.real(), b.real()) - << a << " vs. " << b << " at index " << index; - EXPECT_DOUBLE_EQ(a.imag(), b.imag()) - << a << " vs. " << b << " at index " << index; -} - -inline void AssertSameTypeDims(const Tensor& x, const Tensor& y) { - ASSERT_EQ(x.dtype(), y.dtype()); - ASSERT_TRUE(x.IsSameSize(y)) - << "x.shape [" << x.shape().DebugString() << "] vs " - << "y.shape [ " << y.shape().DebugString() << "]"; -} - -template ::value> -struct Expector; - -template -struct Expector { - static void Equal(const T& a, const T& b) { ExpectEqual(a, b); } - - static void Equal(const Tensor& x, const Tensor& y) { - ASSERT_EQ(x.dtype(), DataTypeToEnum::v()); - AssertSameTypeDims(x, y); - const auto size = x.NumElements(); - const T* a = x.unaligned_flat().data(); - const T* b = y.unaligned_flat().data(); - for (int i = 0; i < size; ++i) { - ExpectEqual(a[i], b[i]); - } - } -}; - -// Partial specialization for float and double. -template -struct Expector { - static void Equal(const T& a, const T& b) { ExpectEqual(a, b); } - - static void Equal(const Tensor& x, const Tensor& y) { - ASSERT_EQ(x.dtype(), DataTypeToEnum::v()); - AssertSameTypeDims(x, y); - const auto size = x.NumElements(); - const T* a = x.unaligned_flat().data(); - const T* b = y.unaligned_flat().data(); - for (int i = 0; i < size; ++i) { - ExpectEqual(a[i], b[i]); - } - } - - static bool Near(const T& a, const T& b, const double abs_err) { - // Need a == b so that infinities are close to themselves. - return (a == b) || - (static_cast(Eigen::numext::abs(a - b)) <= abs_err); - } - - static void Near(const Tensor& x, const Tensor& y, const double abs_err) { - ASSERT_EQ(x.dtype(), DataTypeToEnum::v()); - AssertSameTypeDims(x, y); - const auto size = x.NumElements(); - const T* a = x.unaligned_flat().data(); - const T* b = y.unaligned_flat().data(); - for (int i = 0; i < size; ++i) { - EXPECT_TRUE(Near(a[i], b[i], abs_err)) - << "a = " << a[i] << " b = " << b[i] << " index = " << i; - } - } -}; - -template -struct Helper { - // Assumes atol and rtol are nonnegative. - static bool IsClose(const T& x, const T& y, const T& atol, const T& rtol) { - // Need x == y so that infinities are close to themselves. - return (x == y) || - (Eigen::numext::abs(x - y) <= atol + rtol * Eigen::numext::abs(x)); - } -}; - -template -struct Helper> { - static bool IsClose(const std::complex& x, const std::complex& y, - const T& atol, const T& rtol) { - return Helper::IsClose(x.real(), y.real(), atol, rtol) && - Helper::IsClose(x.imag(), y.imag(), atol, rtol); - } -}; - -} // namespace internal - +// Expects "x" and "y" are tensors of the same type T, same shape, and +// equal equal values. Consider using ExpectEqual above instead. template void ExpectTensorEqual(const Tensor& x, const Tensor& y) { - internal::Expector::Equal(x, y); + EXPECT_EQ(x.dtype(), DataTypeToEnum::value); + ExpectEqual(x, y); } +// Expects "x" and "y" are tensors of the same type T, same shape, and +// approximate equal values. Consider using ExpectClose above instead. template -void ExpectTensorNear(const Tensor& x, const Tensor& y, const double abs_err) { - static_assert(internal::is_floating_point_type::value, - "T is not a floating point types."); - ASSERT_GE(abs_err, 0.0) << "abs_error is negative" << abs_err; - internal::Expector::Near(x, y, abs_err); +void ExpectTensorNear(const Tensor& x, const Tensor& y, double atol) { + EXPECT_EQ(x.dtype(), DataTypeToEnum::value); + ExpectClose(x, y, atol, /*rtol=*/0.0); } +// For tensor_testutil_test only. +namespace internal_test { +::testing::AssertionResult IsClose(Eigen::half x, Eigen::half y, + double atol = -1.0, double rtol = -1.0); +::testing::AssertionResult IsClose(float x, float y, double atol = -1.0, + double rtol = -1.0); +::testing::AssertionResult IsClose(double x, double y, double atol = -1.0, + double rtol = -1.0); +} // namespace internal_test + } // namespace test } // namespace tensorflow diff --git a/tensorflow/core/framework/tensor_testutil_test.cc b/tensorflow/core/framework/tensor_testutil_test.cc index 879f8d2b491..8c02f18d77f 100644 --- a/tensorflow/core/framework/tensor_testutil_test.cc +++ b/tensorflow/core/framework/tensor_testutil_test.cc @@ -22,24 +22,23 @@ namespace tensorflow { namespace test { namespace { -using internal::Expector; -using internal::Helper; +using internal_test::IsClose; template -static void TestEdgeCasesNear() { - EXPECT_TRUE(Expector::Near(Eigen::NumTraits::infinity(), - Eigen::NumTraits::infinity(), 0.0)); - EXPECT_TRUE(Expector::Near(Eigen::NumTraits::lowest(), - Eigen::NumTraits::highest(), - Eigen::NumTraits::infinity())); - EXPECT_FALSE(Expector::Near(Eigen::NumTraits::lowest(), - Eigen::NumTraits::highest(), - Eigen::NumTraits::highest())); - EXPECT_FALSE(Expector::Near(Eigen::NumTraits::quiet_NaN(), - Eigen::NumTraits::quiet_NaN(), 0.0)); - EXPECT_FALSE(Expector::Near(Eigen::NumTraits::quiet_NaN(), - Eigen::NumTraits::quiet_NaN(), - Eigen::NumTraits::infinity())); +void TestEdgeCasesNear() { + EXPECT_TRUE(IsClose(Eigen::NumTraits::infinity(), + Eigen::NumTraits::infinity(), 0.0, 0.0)); + EXPECT_TRUE(IsClose(Eigen::NumTraits::lowest(), + Eigen::NumTraits::highest(), + Eigen::NumTraits::infinity(), 0.0)); + EXPECT_FALSE( + IsClose(Eigen::NumTraits::lowest(), Eigen::NumTraits::highest(), + static_cast(Eigen::NumTraits::highest()), 0.0)); + EXPECT_FALSE(IsClose(Eigen::NumTraits::quiet_NaN(), + Eigen::NumTraits::quiet_NaN(), 0.0, 0.0)); + EXPECT_FALSE(IsClose(Eigen::NumTraits::quiet_NaN(), + Eigen::NumTraits::quiet_NaN(), + Eigen::NumTraits::infinity(), 0.0)); } // For debug printing. Example usage: @@ -48,7 +47,7 @@ static void TestEdgeCasesNear() { // dumpFloatingPointStorage(-2.718281f); // dumpFloatingPointStorage (-2.71828182846); template -static void dumpFloatingPointStorage(T value) { +void dumpFloatingPointStorage(T value) { U* integral = reinterpret_cast(&value); int shift_amount = (sizeof(U) << 3) - 1; int exponent_bits = 2 + (log2(sizeof(U)) * 3); @@ -67,40 +66,43 @@ TEST(TensorTestUtilTest, ExpectTensorNearHalf) { // The exponent is offset at 15. // https://en.wikipedia.org/wiki/Half-precision_floating-point_format typedef Eigen::half T; -#define HALF(x) static_cast(x) // Trivial cases: equalities. - EXPECT_TRUE(Expector::Near(HALF(1.0f), HALF(1.0f), 0.0)); - EXPECT_TRUE(Expector::Near(HALF(0.0f), HALF(-0.0f), 0.0)); - EXPECT_TRUE(Expector::Near(HALF(3.141592f), HALF(3.141592f), 0.0)); + EXPECT_TRUE(IsClose(static_cast(1.0f), static_cast(1.0f), 0.0, 0.0)); + EXPECT_TRUE(IsClose(static_cast(0.0f), static_cast(-0.0f), 0.0, 0.0)); + EXPECT_TRUE( + IsClose(static_cast(3.141592f), static_cast(3.141592f), 0.0, 0.0)); // 0 10010 0001111110 -> 1150/128 = 8.984375 vs // 0 10010 0001111111 -> 1151/128 = 8.9921875 (diff = 0.0078125) - EXPECT_TRUE(Expector::Near(HALF(8.9875f), HALF(8.99f), 0.0078125)); - EXPECT_FALSE(Expector::Near(HALF(8.9875f), HALF(8.99f), 0.007)); + EXPECT_TRUE( + IsClose(static_cast(8.9875f), static_cast(8.99f), 0.0078125, 0.0)); + EXPECT_FALSE( + IsClose(static_cast(8.9875f), static_cast(8.99f), 0.007, 0.0)); // 0 11000 0110100000 -> 1440/2 = 720 vs // 0 11000 0110100001 -> 1441/2 = 720.5 (diff = 0.5) - EXPECT_TRUE(Expector::Near(HALF(720.2f), HALF(720.3f), 0.5)); - EXPECT_FALSE(Expector::Near(HALF(720.2f), HALF(720.3f), 0.4)); + EXPECT_TRUE( + IsClose(static_cast(720.2f), static_cast(720.3f), 0.5, 0.0)); + EXPECT_FALSE( + IsClose(static_cast(720.2f), static_cast(720.3f), 0.4, 0.0)); // 0 11001 0011010010 -> 1234 vs // 0 11001 0011010011 -> 1235 (diff = 1) // Rounds to even (1234.5 -> 1234). - EXPECT_TRUE(Expector::Near(HALF(1234.f), HALF(1235.f), 1.0)); - EXPECT_FALSE(Expector::Near(HALF(1234.5f), HALF(1235.f), 0.5)); - EXPECT_TRUE(Expector::Near(HALF(1234.5f), HALF(1235.f), 1.0)); + EXPECT_TRUE( + IsClose(static_cast(1234.f), static_cast(1235.f), 1.0, 0.0)); + EXPECT_FALSE( + IsClose(static_cast(1234.5f), static_cast(1235.f), 0.5, 0.0)); + EXPECT_TRUE( + IsClose(static_cast(1234.5f), static_cast(1235.f), 1.0, 0.0)); // 1 10000 0101101100 -> -1388/512 = -2.7109375 vs // 1 10000 0101110001 -> -1393/512 = -2.720703125 (diff = 0.009765625) - EXPECT_TRUE(Expector::Near(HALF(-2.71f), HALF(-2.72f), 0.01)); + EXPECT_TRUE( + IsClose(static_cast(-2.71f), static_cast(-2.72f), 0.01, 0.0)); -#undef HALF - - // Some of the cases failed because Eigen::half doesn't behave as expected. - // For example, (inf == inf) should have been true, but it returns false. - // TODO(penporn): uncomment this test once we fix Eigen::half - // TestEdgeCasesNear(); + TestEdgeCasesNear(); } TEST(TensorTestUtilTest, ExpectTensorNearFloat) { @@ -109,32 +111,32 @@ TEST(TensorTestUtilTest, ExpectTensorNearFloat) { // https://en.wikipedia.org/wiki/Single-precision_floating-point_format typedef float T; // Trivial cases: equalities. - EXPECT_TRUE(Expector::Near(1.0f, 1.0f, 0.0)); - EXPECT_TRUE(Expector::Near(0.0f, -0.0f, 0.0)); - EXPECT_TRUE(Expector::Near(3.14159265359f, 3.14159265359f, 0.0)); + EXPECT_TRUE(IsClose(1.0f, 1.0f, 0.0f, 0.0f)); + EXPECT_TRUE(IsClose(0.0f, -0.0f, 0.0f, 0.0f)); + EXPECT_TRUE(IsClose(3.14159265359f, 3.14159265359f, 0.0f, 0.0f)); // 0 10000010 00011111100110011001101 -> 9,424,077/2^20 vs // 0 10000010 00011111100110100110110 -> 9,424,182/2^20 // diff = 105/2^20 = 0.000100135803223 - EXPECT_TRUE(Expector::Near(8.9875f, 8.9876f, 0.0001002)); - EXPECT_FALSE(Expector::Near(8.9875f, 8.9876f, 0.0001)); + EXPECT_TRUE(IsClose(8.9875f, 8.9876f, 0.0001002f, 0.0f)); + EXPECT_FALSE(IsClose(8.9875f, 8.9876f, 0.0001f, 0.0f)); // 0 10001000 01101000000110011101001 -> 11,799,785/2^14 vs // 0 10001000 01101000000110011101010 -> 11,799,786/2^14 // diff = 1/2^14 = 0.00006103515625 - EXPECT_TRUE(Expector::Near(720.2017f, 720.2018f, 0.0001)); - EXPECT_FALSE(Expector::Near(720.20175f, 720.20185f, 0.0001)); - EXPECT_TRUE(Expector::Near(720.20175f, 720.20185f, 0.00013)); + EXPECT_TRUE(IsClose(720.2017f, 720.2018f, 0.0001f, 0.0f)); + EXPECT_FALSE(IsClose(720.20175f, 720.20185f, 0.0001f, 0.0f)); + EXPECT_TRUE(IsClose(720.20175f, 720.20185f, 0.00013f, 0.0f)); // 0 10011001 11010110111100110100010 -> 15,432,098*2^3 vs // 0 10011001 11010110111100110100011 -> 15,432,099*2^3 (diff = 2^3 = 8) - EXPECT_FALSE(Expector::Near(123456788.f, 123456789.f, 4.0)); - EXPECT_TRUE(Expector::Near(123456788.f, 123456789.f, 8.0)); + EXPECT_FALSE(IsClose(123456788.f, 123456789.f, 4.0f, 0.0f)); + EXPECT_TRUE(IsClose(123456788.f, 123456789.f, 8.0f, 0.0f)); // 1 10000000 01011011111100001010001 -> 11,401,297/2^22 vs // 1 10000000 01011011111100001010101 -> 11,401,301/2^22 // diff = 4/2^22 = 0.000000953674316 - EXPECT_TRUE(Expector::Near(-2.718281f, -2.718282f, 0.1)); + EXPECT_TRUE(IsClose(-2.718281f, -2.718282f, 0.1f, 0.0f)); TestEdgeCasesNear(); } @@ -145,41 +147,40 @@ TEST(TensorTestUtilTest, ExpectTensorNearDouble) { // https://en.wikipedia.org/wiki/Double-precision_floating-point_format typedef double T; // Trivial cases: equalities. - EXPECT_TRUE(Expector::Near(1.0, 1.0, 0.0)); - EXPECT_TRUE(Expector::Near(0.0, -0.0, 0.0)); - EXPECT_TRUE(Expector::Near(3.14159265359, 3.14159265359, 0.0)); + EXPECT_TRUE(IsClose(1.0, 1.0, 0.0, 0.0)); + EXPECT_TRUE(IsClose(0.0, -0.0, 0.0, 0.0)); + EXPECT_TRUE(IsClose(3.14159265359, 3.14159265359, 0.0, 0.0)); // 0 10000000010 0001111110011001100110011001100110011001100110011010 // -> 5,059,512,706,374,042/2^49 vs // 0 10000000010 0001111110011010011010110101000010110000111100101000 // -> 5,059,569,001,369,384/2^49 // diff = 56,294,995,342/2^49 = 9.999999999976694198267E-5 - EXPECT_TRUE(Expector::Near(8.9875, 8.9876, 0.0001)); + EXPECT_TRUE(IsClose(8.9875, 8.9876, 0.0001, 0.0)); // 0 10000001111 1000100101110000001100111010100100101010001100000101 // -> 6,921,439,564,440,325/2^36 // 0 10000001111 1000100101110000001100111010111110110111111010010001 // -> 6,921,439,571,312,273/2^36 // diff = 6,871,948/2^36 = 1.000000047497451305389E-4 - EXPECT_FALSE(Expector::Near(100720.2018, 100720.2019, 0.0001)); - EXPECT_TRUE(Expector::Near(100720.2018, 100720.2019, 1.00000005e-4)); + EXPECT_FALSE(IsClose(100720.2018, 100720.2019, 0.0001, 0.0)); + EXPECT_TRUE(IsClose(100720.2018, 100720.2019, 1.00000005e-4, 0.0)); // 0 10000110100 0101111011100010101000101110101101011010010111000100 // -> 6,172,839,450,617,284 * 2 // 0 10000110100 0101111011100010101000101110101101011010010111000011 // -> 6,172,839,450,617,283 * 2 // diff = 1 * 2 = 2 - EXPECT_FALSE(Expector::Near(12345678901234567., 12345678901234566., 1.0)); - EXPECT_TRUE(Expector::Near(12345678901234567., 12345678901234566., 2.0)); + EXPECT_FALSE(IsClose(12345678901234567., 12345678901234566., 1.0, 0.0)); + EXPECT_TRUE(IsClose(12345678901234567., 12345678901234566., 2.0, 0.0)); // 1 10000000000 0101101111110000101010001011000101000101111111001111 // -> -6,121,026,514,870,223/2^51 // 1 10000000000 0101101111110000101010001011000101001011011111000101 // -> -6,121,026,514,892,741/2^51 // diff = 22,518/2^51 = 1.00000008274037099909E-11 - EXPECT_FALSE(Expector::Near(-2.71828182846, -2.71828182847, 1.0e-11)); - EXPECT_TRUE( - Expector::Near(-2.71828182846, -2.71828182847, 1.00000009e-11)); + EXPECT_FALSE(IsClose(-2.71828182846, -2.71828182847, 1.0e-11, 0.0)); + EXPECT_TRUE(IsClose(-2.71828182846, -2.71828182847, 1.00000009e-11, 0.0)); TestEdgeCasesNear(); } @@ -187,49 +188,42 @@ TEST(TensorTestUtilTest, ExpectTensorNearDouble) { // Tensor::Slice() and Tensor::SubSlice() may return unaligned Tensor. TEST(TensorTestUtilTest, ExpectTensorNearSlice) { Tensor x(DT_FLOAT, TensorShape({7, 3})); - test::FillFn(&x, [](int i) -> float { return 1.0; }); + test::FillFn(&x, [](int i) { return 1.0f; }); test::ExpectTensorNear( x.SubSlice(3), test::AsTensor({1.0, 1.0, 1.0}, TensorShape({3})), 1e-10); } -static const double kSlackFactor = 5.0; - template -static void TestEdgeCasesClose() { - T kZero = static_cast(0.0); - EXPECT_TRUE(Helper::IsClose(Eigen::NumTraits::infinity(), - Eigen::NumTraits::infinity(), kZero, - kZero)); - EXPECT_TRUE(Helper::IsClose( - Eigen::NumTraits::lowest(), Eigen::NumTraits::highest(), - Eigen::NumTraits::infinity(), Eigen::NumTraits::infinity())); - EXPECT_TRUE(Helper::IsClose( - Eigen::NumTraits::lowest(), Eigen::NumTraits::highest(), - Eigen::NumTraits::highest(), Eigen::NumTraits::highest())); - EXPECT_FALSE(Helper::IsClose(Eigen::NumTraits::quiet_NaN(), - Eigen::NumTraits::quiet_NaN(), kZero, - kZero)); - EXPECT_FALSE(Helper::IsClose( - Eigen::NumTraits::quiet_NaN(), Eigen::NumTraits::quiet_NaN(), - Eigen::NumTraits::infinity(), Eigen::NumTraits::infinity())); +void TestEdgeCasesClose() { + EXPECT_TRUE(IsClose(Eigen::NumTraits::infinity(), + Eigen::NumTraits::infinity(), 0.0, 0.0)); + EXPECT_TRUE(IsClose(Eigen::NumTraits::lowest(), + Eigen::NumTraits::highest(), + Eigen::NumTraits::infinity(), + Eigen::NumTraits::infinity())); + EXPECT_TRUE(IsClose(Eigen::NumTraits::lowest(), + Eigen::NumTraits::highest(), + static_cast(Eigen::NumTraits::highest()), + static_cast(Eigen::NumTraits::highest()))); + EXPECT_FALSE(IsClose(Eigen::NumTraits::quiet_NaN(), + Eigen::NumTraits::quiet_NaN(), 0.0, 0.0)); + EXPECT_FALSE(IsClose(Eigen::NumTraits::quiet_NaN(), + Eigen::NumTraits::quiet_NaN(), + Eigen::NumTraits::infinity(), + Eigen::NumTraits::infinity())); } TEST(TensorTestUtilTest, ExpectTensorCloseHalf) { typedef Eigen::half T; -#define HALF(x) static_cast(x) - EXPECT_TRUE( - Helper::IsClose(HALF(1.0f), HALF(1.1f), HALF(0.1f), HALF(0.1f))); - EXPECT_TRUE( - Helper::IsClose(HALF(1.0f), HALF(1.0f), HALF(0.0f), HALF(0.0f))); - EXPECT_FALSE( - Helper::IsClose(HALF(1.0f), HALF(1.1f), HALF(0.0f), HALF(0.0f))); - // Epsilon: 0 00010 0000000000 -> 2^-13 = 0.0001220703125 - // kDefaultTol: 0 00100 0100000000 -> 5/2^13 = 0.0006103515625 - const T kDefaultTol = - static_cast(kSlackFactor) * Eigen::NumTraits::epsilon(); + EXPECT_TRUE(IsClose(static_cast(1.0f), static_cast(1.1f), 0.1, 0.1)); + EXPECT_TRUE(IsClose(static_cast(1.0f), static_cast(1.0f), 0.0, 0.0)); + EXPECT_FALSE(IsClose(static_cast(1.0f), static_cast(1.1f), 0.0, 0.0)); + + // Epsilon: 0 00010 0000000000 -> 2^-13 = 0.0001220703125 + // Default Tolerance: 0 00100 0100000000 -> 5/2^13 = 0.0006103515625 // 1.234 -> 0 01111 0011110000 -> 1264/2^10 = 1.234375 // 1.233 -> 0 01111 0011101111 -> 1263/2^10 = 1.2333984375 @@ -238,49 +232,37 @@ TEST(TensorTestUtilTest, ExpectTensorCloseHalf) { // 1.236 -> 0 01111 0011110010 -> 1266/2^10 = 1.236328125 // 1/2^10 = 0.0009765625E // Threshold = 0.0013637542724609375 - EXPECT_TRUE( - Helper::IsClose(HALF(1.234f), HALF(1.234f), kDefaultTol, kDefaultTol)); - EXPECT_TRUE( - Helper::IsClose(HALF(1.234f), HALF(1.233f), kDefaultTol, kDefaultTol)); - EXPECT_TRUE( - Helper::IsClose(HALF(1.234f), HALF(1.235f), kDefaultTol, kDefaultTol)); + EXPECT_TRUE(IsClose(static_cast(1.234f), static_cast(1.234f))); + EXPECT_TRUE(IsClose(static_cast(1.234f), static_cast(1.233f))); + EXPECT_TRUE(IsClose(static_cast(1.234f), static_cast(1.235f))); // Diff = 0.001953125 - EXPECT_FALSE( - Helper::IsClose(HALF(1.234f), HALF(1.232f), kDefaultTol, kDefaultTol)); - EXPECT_FALSE( - Helper::IsClose(HALF(1.234f), HALF(1.236f), kDefaultTol, kDefaultTol)); + EXPECT_FALSE(IsClose(static_cast(1.234f), static_cast(1.232f))); + EXPECT_FALSE(IsClose(static_cast(1.234f), static_cast(1.236f))); EXPECT_TRUE( - Helper::IsClose(HALF(1.234f), HALF(1.232f), HALF(8e-4f), HALF(1e-3f))); - EXPECT_TRUE(Helper::IsClose(HALF(1.234f), HALF(1.236f), HALF(1.4e-3f), - HALF(5e-4f))); + IsClose(static_cast(1.234f), static_cast(1.232f), 8e-4f, 1e-3f)); + EXPECT_TRUE( + IsClose(static_cast(1.234f), static_cast(1.236f), 1.4e-3f, 5e-4f)); // Too fine-grained: won't detect the difference - EXPECT_TRUE(Helper::IsClose(HALF(3.141592f), HALF(3.141593f), HALF(0.0), - HALF(0.0))); + EXPECT_TRUE( + IsClose(static_cast(3.141592f), static_cast(3.141593f), 0.0, 0.0)); // Trivial case. - EXPECT_FALSE( - Helper::IsClose(HALF(1e4f), HALF(1e-4f), kDefaultTol, kDefaultTol)); -#undef HALF + EXPECT_FALSE(IsClose(static_cast(1e4f), static_cast(1e-4f))); - // Some of the cases failed because Eigen::half doesn't behave as expected. - // For example, (inf == inf) should have been true, but it returns false. - // TODO(penporn): uncomment this test once we fix Eigen::half - // TestEdgeCasesClose(); + TestEdgeCasesClose(); } TEST(TensorTestUtilTest, ExpectTensorCloseFloat) { typedef float T; - EXPECT_TRUE(Helper::IsClose(1.0f, 1.1f, 0.1f, 0.1f)); - EXPECT_TRUE(Helper::IsClose(1.0f, 1.0f, 0.0f, 0.0f)); - EXPECT_FALSE(Helper::IsClose(1.0f, 1.1f, 0.0f, 0.0f)); + EXPECT_TRUE(IsClose(1.0f, 1.1f, 0.1f, 0.1f)); + EXPECT_TRUE(IsClose(1.0f, 1.0f, 0.0f, 0.0f)); + EXPECT_FALSE(IsClose(1.0f, 1.1f, 0.0f, 0.0f)); - // Epsilon: 2^-23 ~ 0.00000011920928955078 - // kDefaultTol: 5/2^23 ~ 0.00000059604644775391 - const T kDefaultTol = - static_cast(kSlackFactor) * Eigen::NumTraits::epsilon(); + // Epsilon: 2^-23 ~ 0.00000011920928955078 + // Default Tolerance: 5/2^23 ~ 0.00000059604644775391 // 1.234567f -> 10,356,299/2^23 ~ 1.234567046165466308594 // 1.234568f -> 10,356,307/2^23 ~ 1.234567999839782714844 @@ -288,25 +270,20 @@ TEST(TensorTestUtilTest, ExpectTensorCloseFloat) { // 1.234569f -> 10,356,315/2^23 ~ 1.234568953514099121094 // 1.234565f -> 10,356,282/2^23 ~ 1.234565019607543945313 // Threshold ~ 0.00000133190576434572 - EXPECT_TRUE( - Helper::IsClose(1.234567f, 1.234567f, kDefaultTol, kDefaultTol)); - EXPECT_TRUE( - Helper::IsClose(1.234567f, 1.234568f, kDefaultTol, kDefaultTol)); - EXPECT_TRUE( - Helper::IsClose(1.234567f, 1.234566f, kDefaultTol, kDefaultTol)); - EXPECT_FALSE( - Helper::IsClose(1.234567f, 1.234569f, kDefaultTol, kDefaultTol)); - EXPECT_FALSE( - Helper::IsClose(1.234567f, 1.234565f, kDefaultTol, kDefaultTol)); - EXPECT_TRUE(Helper::IsClose(1.234567f, 1.234569f, 8e-7f, 1e-6f)); - EXPECT_TRUE(Helper::IsClose(1.234567f, 1.234565f, 3e-7f, 1.5e-6f)); + EXPECT_TRUE(IsClose(1.234567f, 1.234567f)); + EXPECT_TRUE(IsClose(1.234567f, 1.234568f)); + EXPECT_TRUE(IsClose(1.234567f, 1.234566f)); + EXPECT_FALSE(IsClose(1.234567f, 1.234569f)); + EXPECT_FALSE(IsClose(1.234567f, 1.234565f)); + EXPECT_TRUE(IsClose(1.234567f, 1.234569f, 8e-7f, 1e-6f)); + EXPECT_TRUE(IsClose(1.234567f, 1.234565f, 3e-7f, 1.5e-6f)); // Too fine-grained: won't detect the difference - EXPECT_TRUE(Helper::IsClose(3.14159265f, 3.14159266f, 0.0f, 0.0f)); + EXPECT_TRUE(IsClose(3.14159265f, 3.14159266f, 0.0f, 0.0f)); // Trivial cases - EXPECT_FALSE(Helper::IsClose(1e8f, 1e-8f, kDefaultTol, kDefaultTol)); - EXPECT_FALSE(Helper::IsClose(1e15f, 1e-15f, kDefaultTol, kDefaultTol)); + EXPECT_FALSE(IsClose(1e8f, 1e-8f)); + EXPECT_FALSE(IsClose(1e15f, 1e-15f)); TestEdgeCasesClose(); } @@ -314,14 +291,12 @@ TEST(TensorTestUtilTest, ExpectTensorCloseFloat) { TEST(TensorTestUtilTest, ExpectTensorCloseDouble) { typedef double T; - EXPECT_TRUE(Helper::IsClose(1.0, 1.1, 0.1, 0.1)); - EXPECT_TRUE(Helper::IsClose(1.0, 1.0, 0.0, 0.0)); - EXPECT_FALSE(Helper::IsClose(1.0, 1.1, 0.0, 0.0)); + EXPECT_TRUE(IsClose(1.0, 1.1, 0.1, 0.1)); + EXPECT_TRUE(IsClose(1.0, 1.0, 0.0, 0.0)); + EXPECT_FALSE(IsClose(1.0, 1.1, 0.0, 0.0)); - // Epsilon: 2^-52 ~ 2.220446049250313080847E-16 - // kDefaultTol: 5/2^52 ~ 1.110223024625156540424E-15 - const T kDefaultTol = - static_cast(kSlackFactor) * Eigen::NumTraits::epsilon(); + // Epsilon: 2^-52 ~ 2.220446049250313080847E-16 + // Default Tolerance: 5/2^52 ~ 1.110223024625156540424E-15 // 1.234567890123456 -> 5,559,999,489,923,576/2^52 ~ 1.234567890123456024298 // 1.234567890123457 -> 5,559,999,489,923,580/2^52 ~ 1.234567890123456912477 @@ -331,32 +306,22 @@ TEST(TensorTestUtilTest, ExpectTensorCloseDouble) { // 1.234567890123459 -> 5,559,999,489,923,589/2^52 ~ 1.234567890123458910878 // 1.234567890123453 -> 5,559,999,489,923,562/2^52 ~ 1.234567890123452915674 // Threshold ~ 2.480868721703117812159E-15 - EXPECT_TRUE(Helper::IsClose(1.234567890123456, 1.234567890123456, - kDefaultTol, kDefaultTol)); - EXPECT_TRUE(Helper::IsClose(1.234567890123456, 1.234567890123457, - kDefaultTol, kDefaultTol)); - EXPECT_TRUE(Helper::IsClose(1.234567890123456, 1.234567890123455, - kDefaultTol, kDefaultTol)); - EXPECT_TRUE(Helper::IsClose(1.234567890123456, 1.234567890123458, - kDefaultTol, kDefaultTol)); - EXPECT_TRUE(Helper::IsClose(1.234567890123456, 1.234567890123454, - kDefaultTol, kDefaultTol)); - EXPECT_FALSE(Helper::IsClose(1.234567890123456, 1.234567890123459, - kDefaultTol, kDefaultTol)); - EXPECT_FALSE(Helper::IsClose(1.234567890123456, 1.234567890123453, - kDefaultTol, kDefaultTol)); - EXPECT_TRUE(Helper::IsClose(1.234567890123456, 1.234567890123459, 9.5e-16, - 1.6e-15)); - EXPECT_TRUE( - Helper::IsClose(1.234567890123456, 1.234567890123453, 7e-16, 2e-15)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123456)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123457)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123455)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123458)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123454)); + EXPECT_FALSE(IsClose(1.234567890123456, 1.234567890123459)); + EXPECT_FALSE(IsClose(1.234567890123456, 1.234567890123453)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123459, 9.5e-16, 1.6e-15)); + EXPECT_TRUE(IsClose(1.234567890123456, 1.234567890123453, 7e-16, 2e-15)); // Too fine-grained: won't detect the difference - EXPECT_TRUE( - Helper::IsClose(3.141592653589793238, 3.141592653589793239, 0.0, 0.0)); + EXPECT_TRUE(IsClose(3.141592653589793238, 3.141592653589793239, 0.0, 0.0)); // Trivial cases - EXPECT_FALSE(Helper::IsClose(1e15, 1e-15, kDefaultTol, kDefaultTol)); - EXPECT_FALSE(Helper::IsClose(1e30, 1e-30, kDefaultTol, kDefaultTol)); + EXPECT_FALSE(IsClose(1e15, 1e-15)); + EXPECT_FALSE(IsClose(1e30, 1e-30)); TestEdgeCasesClose(); }