Clean up tensor_testutil.

Only report the 10 first mismatches.

PiperOrigin-RevId: 319429240
Change-Id: I187c65f6016d8020402f0eee6b05ccbcc50ae900
This commit is contained in:
Christian Sigg 2020-07-02 11:17:26 -07:00 committed by TensorFlower Gardener
parent e25d3e084b
commit 2ae9c259aa
5 changed files with 404 additions and 407 deletions

View File

@ -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<Tensor> outputs;
Status s = graph_runner.Run(root.graph(), nullptr, {}, {c.name()}, &outputs);
TF_ASSERT_OK(s);
ExpectEqual(42.0f, outputs[0].scalar<float>()());
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<float>()());
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<float>()());
ExpectEqual(3.14f, outputs[1].scalar<float>()());
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<float>()());
test::ExpectEqual(test::AsScalar(3.0f), outputs[0]);
}
} // namespace

View File

@ -115,25 +115,39 @@ TEST(TensorTest, DataType_Traits) {
EXPECT_TRUE(std::is_trivial<MyComplex128>::value);
}
template <typename T>
void ExpectEqual(const Tensor& x, const Tensor& y) {
test::ExpectEqual(x, y);
}
// test::ExpectEqual does not support ResourceHandle or Variant.
template <>
void ExpectEqual<ResourceHandle>(const Tensor& x, const Tensor& y) {
EXPECT_EQ(x, y);
}
template <>
void ExpectEqual<Variant>(const Tensor& x, const Tensor& y) {
EXPECT_EQ(x, y);
}
template <typename T>
void TestCopies(const Tensor& t) {
{
LOG(INFO) << "CopyFrom()";
Tensor t2(t.dtype());
EXPECT_TRUE(t2.CopyFrom(t, t.shape()));
test::ExpectTensorEqual<T>(t, t2);
ExpectEqual<T>(t, t2);
}
{
LOG(INFO) << "operator=()";
Tensor t2(t.dtype());
t2 = t;
test::ExpectTensorEqual<T>(t, t2);
ExpectEqual<T>(t, t2);
}
{
LOG(INFO) << "deep copy";
Tensor t2(t.dtype(), t.shape());
t2.flat<T>() = t.flat<T>();
test::ExpectTensorEqual<T>(t, t2);
ExpectEqual<T>(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>(t, t2);
ExpectEqual<T>(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>(t, t2);
ExpectEqual<T>(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>(t, t2);
ExpectEqual<T>(t, t2);
}
{
LOG(INFO) << "AsTensor";
gtl::ArraySlice<T> values(t.flat<T>().data(), t.NumElements());
Tensor t2 = test::AsTensor(values, t.shape());
test::ExpectTensorEqual<T>(t, t2);
ExpectEqual<T>(t, t2);
}
{
LOG(INFO) << "Move constructor";
Tensor t2 = t;
Tensor t3(std::move(t2));
test::ExpectTensorEqual<T>(t, t3);
ExpectEqual<T>(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>(t, t3);
ExpectEqual<T>(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<Variant>(t, t2);
ExpectEqual<Variant>(t, t2);
}
{
LOG(INFO) << "operator=()";
Tensor t2(t.dtype());
t2 = t;
test::ExpectTensorEqual<Variant>(t, t2);
ExpectEqual<Variant>(t, t2);
}
{
LOG(INFO) << "deep copy";
Tensor t2(t.dtype(), t.shape());
t2.flat<Variant>() = t.flat<Variant>();
test::ExpectTensorEqual<Variant>(t, t2);
ExpectEqual<Variant>(t, t2);
}
{
LOG(INFO) << "AsTensor";
gtl::ArraySlice<Variant> values(t.flat<Variant>().data(), t.NumElements());
Tensor t2 = test::AsTensor(values, t.shape());
test::ExpectTensorEqual<Variant>(t, t2);
ExpectEqual<Variant>(t, t2);
}
{
LOG(INFO) << "Move constructor";
Tensor t2 = t;
Tensor t3(std::move(t2));
test::ExpectTensorEqual<Variant>(t, t3);
ExpectEqual<Variant>(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<Variant>(t, t3);
ExpectEqual<Variant>(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<float>() = t1.flat<float>() * 2.0f;
Tensor t3 = test::AsTensor<float>({0, 2, 4, 6, 8, 10}, t1.shape());
test::ExpectTensorEqual<float>(t2, t3);
ExpectEqual<float>(t2, t3);
}
TEST(Tensor_Int32, SimpleWithHelper) {
@ -941,7 +955,7 @@ TEST(Tensor_Int32, SimpleWithHelper) {
Tensor t2(t1.dtype(), t1.shape());
t2.flat<int32>() = t1.flat<int32>() * 2;
Tensor t3 = test::AsTensor<int32>({0, 2, 4, 6, 8, 10}, t1.shape());
test::ExpectTensorEqual<int32>(t2, t3);
ExpectEqual<int32>(t2, t3);
}
TEST(Tensor_UInt16, SimpleWithHelper) {
@ -949,7 +963,7 @@ TEST(Tensor_UInt16, SimpleWithHelper) {
Tensor t2(t1.dtype(), t1.shape());
t2.flat<uint16>() = t1.flat<uint16>() * uint16(2);
Tensor t3 = test::AsTensor<uint16>({0, 2, 4, 6, 8, 10}, t1.shape());
test::ExpectTensorEqual<uint16>(t2, t3);
ExpectEqual<uint16>(t2, t3);
}
TEST(Tensor_QInt8, SimpleWithHelper) {
@ -957,7 +971,7 @@ TEST(Tensor_QInt8, SimpleWithHelper) {
Tensor t2(t1.dtype(), t1.shape());
t2.flat<qint8>() = t1.flat<qint8>() + qint8(-2);
Tensor t3 = test::AsTensor<qint8>({-2, -1, 0, 1, 2, 3}, {2, 3});
test::ExpectTensorEqual<qint8>(t2, t3);
ExpectEqual<qint8>(t2, t3);
}
TEST(Tensor_QUInt8, SimpleWithHelper) {
@ -965,7 +979,7 @@ TEST(Tensor_QUInt8, SimpleWithHelper) {
Tensor t2(t1.dtype(), t1.shape());
t2.flat<quint8>() = t1.flat<quint8>() + quint8(2);
Tensor t3 = test::AsTensor<quint8>({2, 3, 4, 5, 6, 7}, {2, 3});
test::ExpectTensorEqual<quint8>(t2, t3);
ExpectEqual<quint8>(t2, t3);
}
TEST(Tensor_Int64, SimpleWithHelper) {
@ -977,7 +991,7 @@ TEST(Tensor_Int64, SimpleWithHelper) {
Tensor t3 = test::AsTensor<int64>(
{0LL << 48, 2LL << 48, 4LL << 48, 6LL << 48, 8LL << 48, 10LL << 48},
{2, 3});
test::ExpectTensorEqual<int64>(t2, t3);
ExpectEqual<int64>(t2, t3);
}
TEST(Tensor_String, SimpleWithHelper) {
@ -990,7 +1004,7 @@ TEST(Tensor_String, SimpleWithHelper) {
}
// Test with helper.
test::ExpectTensorEqual<tstring>(t1, t2);
ExpectEqual<tstring>(t1, t2);
}
TEST(Tensor_Bool, SimpleWithHelper) {
@ -1005,7 +1019,7 @@ TEST(Tensor_Bool, SimpleWithHelper) {
}
// Test with helper.
test::ExpectTensorEqual<bool>(t1, t2);
ExpectEqual<bool>(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<complex64>(t2, t3);
ExpectEqual<complex64>(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<complex128>(t2, t3);
ExpectEqual<complex128>(t2, t3);
}
// Does some numeric operations for complex128 numbers.
@ -1223,7 +1237,7 @@ TEST(Tensor, Slice_Basic) {
// A simple slice equivalent to identity.
TestCopies<float>(y);
y = x.Slice(0, 10);
test::ExpectTensorEqual<float>(x, y);
ExpectEqual<float>(x, y);
EXPECT_EQ(x.flat<float>().data(), y.flat<float>().data());
// A slice of a slice.

View File

@ -14,68 +14,236 @@ limitations under the License.
==============================================================================*/
#include "tensorflow/core/framework/tensor_testutil.h"
#include <cmath>
#include "tensorflow/core/framework/tensor.h"
namespace tensorflow {
namespace test {
template <typename T>
void ExpectClose(const Tensor& x, const Tensor& y, double atol, double rtol) {
const T* Tx = x.flat<T>().data();
const T* Ty = y.flat<T>().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<float>, then RealType = float.
// Did not use std::numeric_limits<T> because
// 1) It returns 0 for Eigen::half.
// 2) It doesn't support T=std::complex<RealType>.
// (Would have to write a templated struct to handle this.)
typedef decltype(Eigen::NumTraits<T>::epsilon()) RealType;
const RealType kSlackFactor = static_cast<RealType>(5.0);
const RealType kDefaultTol = kSlackFactor * Eigen::NumTraits<T>::epsilon();
const RealType typed_atol =
(atol < 0) ? kDefaultTol : static_cast<RealType>(atol);
const RealType typed_rtol =
(rtol < 0) ? kDefaultTol : static_cast<RealType>(rtol);
ASSERT_GE(typed_atol, static_cast<RealType>(0.0))
<< "typed_atol is negative: " << typed_atol;
ASSERT_GE(typed_rtol, static_cast<RealType>(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 <typename T>
static ::testing::AssertionResult EqualFailure(const T& x, const T& y) {
return ::testing::AssertionFailure()
<< std::setprecision(std::numeric_limits<T>::digits10 + 2) << x
<< " not equal to " << y;
}
static ::testing::AssertionResult IsEqual(float x, float y) {
if (::testing::internal::CmpHelperFloatingPointEQ<float>("", "", x, y))
return ::testing::AssertionSuccess();
return EqualFailure(x, y);
}
static ::testing::AssertionResult IsEqual(double x, double y) {
if (::testing::internal::CmpHelperFloatingPointEQ<double>("", "", 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<Eigen::half>, 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 <typename T>
static ::testing::AssertionResult IsEqual(const T& x, const T& y) {
if (::testing::internal::CmpHelperEQ<T>("", "", x, y))
return ::testing::AssertionSuccess();
return EqualFailure(x, y);
}
template <typename T>
static ::testing::AssertionResult IsEqual(const std::complex<T>& x,
const std::complex<T>& y) {
if (IsEqual(x.real(), y.real()) && IsEqual(x.imag(), y.imag()))
return ::testing::AssertionSuccess();
return EqualFailure(x, y);
}
template <typename T>
static void ExpectEqual(const Tensor& x, const Tensor& y) {
const T* Tx = x.unaligned_flat<T>().data();
const T* Ty = y.unaligned_flat<T>().data();
auto size = x.NumElements();
int max_failures = 10;
int num_failures = 0;
for (int i = 0; i < size; ++i) {
EXPECT_TRUE(
internal::Helper<T>::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 <typename T>
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 <typename T>
static ::testing::AssertionResult IsClose(const std::complex<T>& x,
const std::complex<T>& 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<float>.
template <typename T>
static auto GetTolerance(double tolerance) {
using Real = typename Eigen::NumTraits<T>::Real;
auto default_tol = static_cast<Real>(5.0) * Eigen::NumTraits<T>::epsilon();
auto result = tolerance < 0.0 ? default_tol : static_cast<Real>(tolerance);
EXPECT_GE(result, static_cast<Real>(0));
return result;
}
template <typename T>
static void ExpectClose(const Tensor& x, const Tensor& y, double atol,
double rtol) {
auto typed_atol = GetTolerance<T>(atol);
auto typed_rtol = GetTolerance<T>(rtol);
const T* Tx = x.unaligned_flat<T>().data();
const T* Ty = y.unaligned_flat<T>().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<float>(x, y);
case DT_DOUBLE:
return ExpectEqual<double>(x, y);
case DT_INT32:
return ExpectEqual<int32>(x, y);
case DT_UINT32:
return ExpectEqual<uint32>(x, y);
case DT_UINT16:
return ExpectEqual<uint16>(x, y);
case DT_UINT8:
return ExpectEqual<uint8>(x, y);
case DT_INT16:
return ExpectEqual<int16>(x, y);
case DT_INT8:
return ExpectEqual<int8>(x, y);
case DT_STRING:
return ExpectEqual<tstring>(x, y);
case DT_COMPLEX64:
return ExpectEqual<complex64>(x, y);
case DT_COMPLEX128:
return ExpectEqual<complex128>(x, y);
case DT_INT64:
return ExpectEqual<int64>(x, y);
case DT_UINT64:
return ExpectEqual<uint64>(x, y);
case DT_BOOL:
return ExpectEqual<bool>(x, y);
case DT_QINT8:
return ExpectEqual<qint8>(x, y);
case DT_QUINT8:
return ExpectEqual<quint8>(x, y);
case DT_QINT16:
return ExpectEqual<qint16>(x, y);
case DT_QUINT16:
return ExpectEqual<quint16>(x, y);
case DT_QINT32:
return ExpectEqual<qint32>(x, y);
case DT_BFLOAT16:
return ExpectEqual<bfloat16>(x, y);
case DT_HALF:
return ExpectEqual<Eigen::half>(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<Eigen::half>(x, y, atol, rtol);
break;
return ExpectClose<Eigen::half>(x, y, atol, rtol);
case DT_FLOAT:
ExpectClose<float>(x, y, atol, rtol);
break;
return ExpectClose<float>(x, y, atol, rtol);
case DT_DOUBLE:
ExpectClose<double>(x, y, atol, rtol);
break;
return ExpectClose<double>(x, y, atol, rtol);
case DT_COMPLEX64:
ExpectClose<complex64>(x, y, atol, rtol);
break;
return ExpectClose<complex64>(x, y, atol, rtol);
case DT_COMPLEX128:
ExpectClose<complex128>(x, y, atol, rtol);
break;
return ExpectClose<complex128>(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<Eigen::half>(atol),
GetTolerance<Eigen::half>(rtol));
}
::testing::AssertionResult internal_test::IsClose(float x, float y, double atol,
double rtol) {
return test::IsClose(x, y, GetTolerance<float>(atol),
GetTolerance<float>(rtol));
}
::testing::AssertionResult internal_test::IsClose(double x, double y,
double atol, double rtol) {
return test::IsClose(x, y, GetTolerance<double>(atol),
GetTolerance<double>(rtol));
}
} // end namespace test
} // end namespace tensorflow

View File

@ -94,190 +94,42 @@ void FillFn(Tensor* tensor, std::function<T(int)> fn) {
}
// Expects "x" and "y" are tensors of the same type, same shape, and
// identical values.
template <typename T>
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 <typename T>
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 <typename T>
struct is_floating_point_type {
static constexpr bool value = std::is_same<T, Eigen::half>::value ||
std::is_same<T, float>::value ||
std::is_same<T, double>::value ||
std::is_same<T, std::complex<float>>::value ||
std::is_same<T, std::complex<double>>::value;
};
template <typename T>
inline void ExpectEqual(const T& a, const T& b) {
EXPECT_EQ(a, b);
}
template <>
inline void ExpectEqual<float>(const float& a, const float& b) {
EXPECT_FLOAT_EQ(a, b);
}
template <>
inline void ExpectEqual<double>(const double& a, const double& b) {
EXPECT_DOUBLE_EQ(a, b);
}
template <>
inline void ExpectEqual<complex64>(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<complex128>(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 <typename T>
inline void ExpectEqual(const T& a, const T& b, int index) {
EXPECT_EQ(a, b) << " at index " << index;
}
template <>
inline void ExpectEqual<float>(const float& a, const float& b, int index) {
EXPECT_FLOAT_EQ(a, b) << " at index " << index;
}
template <>
inline void ExpectEqual<double>(const double& a, const double& b, int index) {
EXPECT_DOUBLE_EQ(a, b) << " at index " << index;
}
template <>
inline void ExpectEqual<complex64>(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<complex128>(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 <typename T, bool is_fp = is_floating_point_type<T>::value>
struct Expector;
template <typename T>
struct Expector<T, false> {
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<T>::v());
AssertSameTypeDims(x, y);
const auto size = x.NumElements();
const T* a = x.unaligned_flat<T>().data();
const T* b = y.unaligned_flat<T>().data();
for (int i = 0; i < size; ++i) {
ExpectEqual(a[i], b[i]);
}
}
};
// Partial specialization for float and double.
template <typename T>
struct Expector<T, true> {
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<T>::v());
AssertSameTypeDims(x, y);
const auto size = x.NumElements();
const T* a = x.unaligned_flat<T>().data();
const T* b = y.unaligned_flat<T>().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<double>(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<T>::v());
AssertSameTypeDims(x, y);
const auto size = x.NumElements();
const T* a = x.unaligned_flat<T>().data();
const T* b = y.unaligned_flat<T>().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 <typename T>
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 <typename T>
struct Helper<std::complex<T>> {
static bool IsClose(const std::complex<T>& x, const std::complex<T>& y,
const T& atol, const T& rtol) {
return Helper<T>::IsClose(x.real(), y.real(), atol, rtol) &&
Helper<T>::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 <typename T>
void ExpectTensorEqual(const Tensor& x, const Tensor& y) {
internal::Expector<T>::Equal(x, y);
EXPECT_EQ(x.dtype(), DataTypeToEnum<T>::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 <typename T>
void ExpectTensorNear(const Tensor& x, const Tensor& y, const double abs_err) {
static_assert(internal::is_floating_point_type<T>::value,
"T is not a floating point types.");
ASSERT_GE(abs_err, 0.0) << "abs_error is negative" << abs_err;
internal::Expector<T>::Near(x, y, abs_err);
void ExpectTensorNear(const Tensor& x, const Tensor& y, double atol) {
EXPECT_EQ(x.dtype(), DataTypeToEnum<T>::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

View File

@ -22,24 +22,23 @@ namespace tensorflow {
namespace test {
namespace {
using internal::Expector;
using internal::Helper;
using internal_test::IsClose;
template <typename T>
static void TestEdgeCasesNear() {
EXPECT_TRUE(Expector<T>::Near(Eigen::NumTraits<T>::infinity(),
Eigen::NumTraits<T>::infinity(), 0.0));
EXPECT_TRUE(Expector<T>::Near(Eigen::NumTraits<T>::lowest(),
Eigen::NumTraits<T>::highest(),
Eigen::NumTraits<double>::infinity()));
EXPECT_FALSE(Expector<T>::Near(Eigen::NumTraits<T>::lowest(),
Eigen::NumTraits<T>::highest(),
Eigen::NumTraits<double>::highest()));
EXPECT_FALSE(Expector<T>::Near(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(), 0.0));
EXPECT_FALSE(Expector<T>::Near(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<double>::infinity()));
void TestEdgeCasesNear() {
EXPECT_TRUE(IsClose(Eigen::NumTraits<T>::infinity(),
Eigen::NumTraits<T>::infinity(), 0.0, 0.0));
EXPECT_TRUE(IsClose(Eigen::NumTraits<T>::lowest(),
Eigen::NumTraits<T>::highest(),
Eigen::NumTraits<double>::infinity(), 0.0));
EXPECT_FALSE(
IsClose(Eigen::NumTraits<T>::lowest(), Eigen::NumTraits<T>::highest(),
static_cast<double>(Eigen::NumTraits<T>::highest()), 0.0));
EXPECT_FALSE(IsClose(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(), 0.0, 0.0));
EXPECT_FALSE(IsClose(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<double>::infinity(), 0.0));
}
// For debug printing. Example usage:
@ -48,7 +47,7 @@ static void TestEdgeCasesNear() {
// dumpFloatingPointStorage<float, uint32>(-2.718281f);
// dumpFloatingPointStorage <double, uint64>(-2.71828182846);
template <typename T, typename U>
static void dumpFloatingPointStorage(T value) {
void dumpFloatingPointStorage(T value) {
U* integral = reinterpret_cast<U*>(&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<T>(x)
// Trivial cases: equalities.
EXPECT_TRUE(Expector<T>::Near(HALF(1.0f), HALF(1.0f), 0.0));
EXPECT_TRUE(Expector<T>::Near(HALF(0.0f), HALF(-0.0f), 0.0));
EXPECT_TRUE(Expector<T>::Near(HALF(3.141592f), HALF(3.141592f), 0.0));
EXPECT_TRUE(IsClose(static_cast<T>(1.0f), static_cast<T>(1.0f), 0.0, 0.0));
EXPECT_TRUE(IsClose(static_cast<T>(0.0f), static_cast<T>(-0.0f), 0.0, 0.0));
EXPECT_TRUE(
IsClose(static_cast<T>(3.141592f), static_cast<T>(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<T>::Near(HALF(8.9875f), HALF(8.99f), 0.0078125));
EXPECT_FALSE(Expector<T>::Near(HALF(8.9875f), HALF(8.99f), 0.007));
EXPECT_TRUE(
IsClose(static_cast<T>(8.9875f), static_cast<T>(8.99f), 0.0078125, 0.0));
EXPECT_FALSE(
IsClose(static_cast<T>(8.9875f), static_cast<T>(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<T>::Near(HALF(720.2f), HALF(720.3f), 0.5));
EXPECT_FALSE(Expector<T>::Near(HALF(720.2f), HALF(720.3f), 0.4));
EXPECT_TRUE(
IsClose(static_cast<T>(720.2f), static_cast<T>(720.3f), 0.5, 0.0));
EXPECT_FALSE(
IsClose(static_cast<T>(720.2f), static_cast<T>(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<T>::Near(HALF(1234.f), HALF(1235.f), 1.0));
EXPECT_FALSE(Expector<T>::Near(HALF(1234.5f), HALF(1235.f), 0.5));
EXPECT_TRUE(Expector<T>::Near(HALF(1234.5f), HALF(1235.f), 1.0));
EXPECT_TRUE(
IsClose(static_cast<T>(1234.f), static_cast<T>(1235.f), 1.0, 0.0));
EXPECT_FALSE(
IsClose(static_cast<T>(1234.5f), static_cast<T>(1235.f), 0.5, 0.0));
EXPECT_TRUE(
IsClose(static_cast<T>(1234.5f), static_cast<T>(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<T>::Near(HALF(-2.71f), HALF(-2.72f), 0.01));
EXPECT_TRUE(
IsClose(static_cast<T>(-2.71f), static_cast<T>(-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<T>();
TestEdgeCasesNear<T>();
}
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<T>::Near(1.0f, 1.0f, 0.0));
EXPECT_TRUE(Expector<T>::Near(0.0f, -0.0f, 0.0));
EXPECT_TRUE(Expector<T>::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<T>::Near(8.9875f, 8.9876f, 0.0001002));
EXPECT_FALSE(Expector<T>::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<T>::Near(720.2017f, 720.2018f, 0.0001));
EXPECT_FALSE(Expector<T>::Near(720.20175f, 720.20185f, 0.0001));
EXPECT_TRUE(Expector<T>::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<T>::Near(123456788.f, 123456789.f, 4.0));
EXPECT_TRUE(Expector<T>::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<T>::Near(-2.718281f, -2.718282f, 0.1));
EXPECT_TRUE(IsClose(-2.718281f, -2.718282f, 0.1f, 0.0f));
TestEdgeCasesNear<T>();
}
@ -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<T>::Near(1.0, 1.0, 0.0));
EXPECT_TRUE(Expector<T>::Near(0.0, -0.0, 0.0));
EXPECT_TRUE(Expector<T>::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<T>::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<T>::Near(100720.2018, 100720.2019, 0.0001));
EXPECT_TRUE(Expector<T>::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<T>::Near(12345678901234567., 12345678901234566., 1.0));
EXPECT_TRUE(Expector<T>::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<T>::Near(-2.71828182846, -2.71828182847, 1.0e-11));
EXPECT_TRUE(
Expector<T>::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<T>();
}
@ -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<float>(&x, [](int i) -> float { return 1.0; });
test::FillFn<float>(&x, [](int i) { return 1.0f; });
test::ExpectTensorNear<float>(
x.SubSlice(3), test::AsTensor<float>({1.0, 1.0, 1.0}, TensorShape({3})),
1e-10);
}
static const double kSlackFactor = 5.0;
template <typename T>
static void TestEdgeCasesClose() {
T kZero = static_cast<T>(0.0);
EXPECT_TRUE(Helper<T>::IsClose(Eigen::NumTraits<T>::infinity(),
Eigen::NumTraits<T>::infinity(), kZero,
kZero));
EXPECT_TRUE(Helper<T>::IsClose(
Eigen::NumTraits<T>::lowest(), Eigen::NumTraits<T>::highest(),
Eigen::NumTraits<T>::infinity(), Eigen::NumTraits<T>::infinity()));
EXPECT_TRUE(Helper<T>::IsClose(
Eigen::NumTraits<T>::lowest(), Eigen::NumTraits<T>::highest(),
Eigen::NumTraits<T>::highest(), Eigen::NumTraits<T>::highest()));
EXPECT_FALSE(Helper<T>::IsClose(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(), kZero,
kZero));
EXPECT_FALSE(Helper<T>::IsClose(
Eigen::NumTraits<T>::quiet_NaN(), Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::infinity(), Eigen::NumTraits<T>::infinity()));
void TestEdgeCasesClose() {
EXPECT_TRUE(IsClose(Eigen::NumTraits<T>::infinity(),
Eigen::NumTraits<T>::infinity(), 0.0, 0.0));
EXPECT_TRUE(IsClose(Eigen::NumTraits<T>::lowest(),
Eigen::NumTraits<T>::highest(),
Eigen::NumTraits<double>::infinity(),
Eigen::NumTraits<double>::infinity()));
EXPECT_TRUE(IsClose(Eigen::NumTraits<T>::lowest(),
Eigen::NumTraits<T>::highest(),
static_cast<double>(Eigen::NumTraits<T>::highest()),
static_cast<double>(Eigen::NumTraits<T>::highest())));
EXPECT_FALSE(IsClose(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(), 0.0, 0.0));
EXPECT_FALSE(IsClose(Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<T>::quiet_NaN(),
Eigen::NumTraits<double>::infinity(),
Eigen::NumTraits<double>::infinity()));
}
TEST(TensorTestUtilTest, ExpectTensorCloseHalf) {
typedef Eigen::half T;
#define HALF(x) static_cast<T>(x)
EXPECT_TRUE(
Helper<T>::IsClose(HALF(1.0f), HALF(1.1f), HALF(0.1f), HALF(0.1f)));
EXPECT_TRUE(
Helper<T>::IsClose(HALF(1.0f), HALF(1.0f), HALF(0.0f), HALF(0.0f)));
EXPECT_FALSE(
Helper<T>::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<T>(kSlackFactor) * Eigen::NumTraits<T>::epsilon();
EXPECT_TRUE(IsClose(static_cast<T>(1.0f), static_cast<T>(1.1f), 0.1, 0.1));
EXPECT_TRUE(IsClose(static_cast<T>(1.0f), static_cast<T>(1.0f), 0.0, 0.0));
EXPECT_FALSE(IsClose(static_cast<T>(1.0f), static_cast<T>(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<T>::IsClose(HALF(1.234f), HALF(1.234f), kDefaultTol, kDefaultTol));
EXPECT_TRUE(
Helper<T>::IsClose(HALF(1.234f), HALF(1.233f), kDefaultTol, kDefaultTol));
EXPECT_TRUE(
Helper<T>::IsClose(HALF(1.234f), HALF(1.235f), kDefaultTol, kDefaultTol));
EXPECT_TRUE(IsClose(static_cast<T>(1.234f), static_cast<T>(1.234f)));
EXPECT_TRUE(IsClose(static_cast<T>(1.234f), static_cast<T>(1.233f)));
EXPECT_TRUE(IsClose(static_cast<T>(1.234f), static_cast<T>(1.235f)));
// Diff = 0.001953125
EXPECT_FALSE(
Helper<T>::IsClose(HALF(1.234f), HALF(1.232f), kDefaultTol, kDefaultTol));
EXPECT_FALSE(
Helper<T>::IsClose(HALF(1.234f), HALF(1.236f), kDefaultTol, kDefaultTol));
EXPECT_FALSE(IsClose(static_cast<T>(1.234f), static_cast<T>(1.232f)));
EXPECT_FALSE(IsClose(static_cast<T>(1.234f), static_cast<T>(1.236f)));
EXPECT_TRUE(
Helper<T>::IsClose(HALF(1.234f), HALF(1.232f), HALF(8e-4f), HALF(1e-3f)));
EXPECT_TRUE(Helper<T>::IsClose(HALF(1.234f), HALF(1.236f), HALF(1.4e-3f),
HALF(5e-4f)));
IsClose(static_cast<T>(1.234f), static_cast<T>(1.232f), 8e-4f, 1e-3f));
EXPECT_TRUE(
IsClose(static_cast<T>(1.234f), static_cast<T>(1.236f), 1.4e-3f, 5e-4f));
// Too fine-grained: won't detect the difference
EXPECT_TRUE(Helper<T>::IsClose(HALF(3.141592f), HALF(3.141593f), HALF(0.0),
HALF(0.0)));
EXPECT_TRUE(
IsClose(static_cast<T>(3.141592f), static_cast<T>(3.141593f), 0.0, 0.0));
// Trivial case.
EXPECT_FALSE(
Helper<T>::IsClose(HALF(1e4f), HALF(1e-4f), kDefaultTol, kDefaultTol));
#undef HALF
EXPECT_FALSE(IsClose(static_cast<T>(1e4f), static_cast<T>(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<T>();
TestEdgeCasesClose<T>();
}
TEST(TensorTestUtilTest, ExpectTensorCloseFloat) {
typedef float T;
EXPECT_TRUE(Helper<T>::IsClose(1.0f, 1.1f, 0.1f, 0.1f));
EXPECT_TRUE(Helper<T>::IsClose(1.0f, 1.0f, 0.0f, 0.0f));
EXPECT_FALSE(Helper<T>::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<T>(kSlackFactor) * Eigen::NumTraits<T>::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<T>::IsClose(1.234567f, 1.234567f, kDefaultTol, kDefaultTol));
EXPECT_TRUE(
Helper<T>::IsClose(1.234567f, 1.234568f, kDefaultTol, kDefaultTol));
EXPECT_TRUE(
Helper<T>::IsClose(1.234567f, 1.234566f, kDefaultTol, kDefaultTol));
EXPECT_FALSE(
Helper<T>::IsClose(1.234567f, 1.234569f, kDefaultTol, kDefaultTol));
EXPECT_FALSE(
Helper<T>::IsClose(1.234567f, 1.234565f, kDefaultTol, kDefaultTol));
EXPECT_TRUE(Helper<T>::IsClose(1.234567f, 1.234569f, 8e-7f, 1e-6f));
EXPECT_TRUE(Helper<T>::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<T>::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<T>::IsClose(1e8f, 1e-8f, kDefaultTol, kDefaultTol));
EXPECT_FALSE(Helper<T>::IsClose(1e15f, 1e-15f, kDefaultTol, kDefaultTol));
EXPECT_FALSE(IsClose(1e8f, 1e-8f));
EXPECT_FALSE(IsClose(1e15f, 1e-15f));
TestEdgeCasesClose<T>();
}
@ -314,14 +291,12 @@ TEST(TensorTestUtilTest, ExpectTensorCloseFloat) {
TEST(TensorTestUtilTest, ExpectTensorCloseDouble) {
typedef double T;
EXPECT_TRUE(Helper<T>::IsClose(1.0, 1.1, 0.1, 0.1));
EXPECT_TRUE(Helper<T>::IsClose(1.0, 1.0, 0.0, 0.0));
EXPECT_FALSE(Helper<T>::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<T>(kSlackFactor) * Eigen::NumTraits<T>::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<T>::IsClose(1.234567890123456, 1.234567890123456,
kDefaultTol, kDefaultTol));
EXPECT_TRUE(Helper<T>::IsClose(1.234567890123456, 1.234567890123457,
kDefaultTol, kDefaultTol));
EXPECT_TRUE(Helper<T>::IsClose(1.234567890123456, 1.234567890123455,
kDefaultTol, kDefaultTol));
EXPECT_TRUE(Helper<T>::IsClose(1.234567890123456, 1.234567890123458,
kDefaultTol, kDefaultTol));
EXPECT_TRUE(Helper<T>::IsClose(1.234567890123456, 1.234567890123454,
kDefaultTol, kDefaultTol));
EXPECT_FALSE(Helper<T>::IsClose(1.234567890123456, 1.234567890123459,
kDefaultTol, kDefaultTol));
EXPECT_FALSE(Helper<T>::IsClose(1.234567890123456, 1.234567890123453,
kDefaultTol, kDefaultTol));
EXPECT_TRUE(Helper<T>::IsClose(1.234567890123456, 1.234567890123459, 9.5e-16,
1.6e-15));
EXPECT_TRUE(
Helper<T>::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<T>::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<T>::IsClose(1e15, 1e-15, kDefaultTol, kDefaultTol));
EXPECT_FALSE(Helper<T>::IsClose(1e30, 1e-30, kDefaultTol, kDefaultTol));
EXPECT_FALSE(IsClose(1e15, 1e-15));
EXPECT_FALSE(IsClose(1e30, 1e-30));
TestEdgeCasesClose<T>();
}