aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/core/framework/tensor_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/core/framework/tensor_test.cc')
-rw-r--r--tensorflow/core/framework/tensor_test.cc551
1 files changed, 551 insertions, 0 deletions
diff --git a/tensorflow/core/framework/tensor_test.cc b/tensorflow/core/framework/tensor_test.cc
new file mode 100644
index 0000000000..4963c2c219
--- /dev/null
+++ b/tensorflow/core/framework/tensor_test.cc
@@ -0,0 +1,551 @@
+#include "tensorflow/core/public/tensor.h"
+
+#include "tensorflow/core/framework/tensor_testutil.h"
+#include "tensorflow/core/framework/types.h"
+#include "tensorflow/core/platform/logging.h"
+#include "tensorflow/core/lib/strings/strcat.h"
+#include "tensorflow/core/platform/test_benchmark.h"
+#include <gtest/gtest.h>
+
+namespace tensorflow {
+
+TEST(TensorTest, Default) {
+ Tensor t;
+ EXPECT_EQ(t.dtype(), DT_FLOAT);
+ EXPECT_EQ(t.dims(), 1);
+ EXPECT_EQ(t.NumElements(), 0);
+}
+
+TEST(TensorTest, DataType_Traits) {
+ EXPECT_TRUE(std::is_trivial<float>::value);
+ EXPECT_TRUE(std::is_trivial<double>::value);
+ EXPECT_TRUE(std::is_trivial<int32>::value);
+ EXPECT_TRUE(std::is_trivial<uint8>::value);
+ EXPECT_TRUE(std::is_trivial<int16>::value);
+ EXPECT_TRUE(std::is_trivial<int8>::value);
+ EXPECT_TRUE(std::is_trivial<int64>::value);
+ EXPECT_TRUE(std::is_trivial<bool>::value);
+ EXPECT_FALSE(std::is_trivial<string>::value);
+
+ EXPECT_EQ(sizeof(bool), 1);
+
+ // Unfortunately. std::complex::complex() initializes (0, 0).
+ EXPECT_FALSE(std::is_trivial<complex64>::value);
+ EXPECT_FALSE(std::is_trivial<std::complex<double>>::value);
+ EXPECT_TRUE(std::is_trivial<float[2]>::value);
+ struct MyComplex {
+ float re, im;
+ };
+ EXPECT_TRUE(std::is_trivial<MyComplex>::value);
+}
+
+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);
+ }
+ {
+ LOG(INFO) << "operator=()";
+ Tensor t2(t.dtype());
+ t2 = t;
+ test::ExpectTensorEqual<T>(t, t2);
+ }
+ {
+ LOG(INFO) << "deep copy";
+ Tensor t2(t.dtype(), t.shape());
+ t2.flat<T>() = t.flat<T>();
+ test::ExpectTensorEqual<T>(t, t2);
+ }
+ {
+ LOG(INFO) << "AsProtoField()";
+ TensorProto proto;
+ t.AsProtoField(&proto);
+ Tensor t2(t.dtype());
+ EXPECT_TRUE(t2.FromProto(proto));
+ test::ExpectTensorEqual<T>(t, t2);
+ }
+ {
+ LOG(INFO) << "AsProtoTensorContent()";
+ TensorProto proto;
+ t.AsProtoTensorContent(&proto);
+ Tensor t2(t.dtype());
+ EXPECT_TRUE(t2.FromProto(proto));
+ test::ExpectTensorEqual<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);
+ }
+ {
+ 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);
+ }
+}
+
+TEST(Tensor_Float, Simple) {
+ Tensor t(DT_FLOAT, TensorShape({10, 20}));
+ EXPECT_TRUE(t.shape().IsSameSize(TensorShape({10, 20})));
+ for (int64 a = 0; a < t.shape().dim_size(0); a++) {
+ for (int64 b = 0; b < t.shape().dim_size(1); b++) {
+ t.matrix<float>()(a, b) = static_cast<float>(a * b);
+ }
+ }
+ TestCopies<float>(t);
+}
+
+TEST(Tensor_QInt8, Simple) {
+ Tensor t(DT_QINT8, TensorShape({2, 2}));
+ EXPECT_TRUE(t.shape().IsSameSize(TensorShape({2, 2})));
+ for (int64 a = 0; a < t.shape().dim_size(0); a++) {
+ for (int64 b = 0; b < t.shape().dim_size(1); b++) {
+ t.matrix<qint8>()(a, b) = qint8(a * b);
+ }
+ }
+ TestCopies<qint8>(t);
+}
+
+TEST(Tensor_QUInt8, Simple) {
+ Tensor t(DT_QUINT8, TensorShape({2, 2}));
+ EXPECT_TRUE(t.shape().IsSameSize(TensorShape({2, 2})));
+ for (int64 a = 0; a < t.shape().dim_size(0); a++) {
+ for (int64 b = 0; b < t.shape().dim_size(1); b++) {
+ t.matrix<Eigen::QUInt8>()(a, b) = Eigen::QUInt8(a * b);
+ }
+ }
+ TestCopies<Eigen::QUInt8>(t);
+}
+
+TEST(Tensor_QInt32, Simple) {
+ Tensor t(DT_QINT32, TensorShape({2, 2}));
+ EXPECT_TRUE(t.shape().IsSameSize(TensorShape({2, 2})));
+ for (int64 a = 0; a < t.shape().dim_size(0); a++) {
+ for (int64 b = 0; b < t.shape().dim_size(1); b++) {
+ t.matrix<qint32>()(a, b) = qint32(static_cast<int32>(a * b));
+ }
+ }
+ TestCopies<qint32>(t);
+}
+
+TEST(Tensor_Float, Reshape) {
+ Tensor t(DT_FLOAT, TensorShape({2, 3, 4, 5}));
+ EXPECT_TRUE(t.shape().IsSameSize(TensorShape({2, 3, 4, 5})));
+
+ {
+ auto tensor = t.tensor<float, 4>();
+ EXPECT_EQ(2, tensor.dimension(0));
+ EXPECT_EQ(3, tensor.dimension(1));
+ EXPECT_EQ(4, tensor.dimension(2));
+ EXPECT_EQ(5, tensor.dimension(3));
+
+ // Set first and last elements.
+ tensor(0, 0, 0, 0) = 0.01f;
+ tensor(1, 2, 3, 4) = 0.02f;
+ }
+ {
+ auto shaped = t.shaped<float, 1>({120});
+ EXPECT_EQ(120, shaped.dimension(0));
+ EXPECT_EQ(shaped(0), 0.01f);
+ EXPECT_EQ(shaped(119), 0.02f);
+ }
+ {
+ auto shaped = t.shaped<float, 2>({6, 20});
+ EXPECT_EQ(6, shaped.dimension(0));
+ EXPECT_EQ(20, shaped.dimension(1));
+ EXPECT_EQ(shaped(0, 0), 0.01f);
+ EXPECT_EQ(shaped(5, 19), 0.02f);
+ }
+ {
+ auto shaped = t.shaped<float, 3>({6, 4, 5});
+ EXPECT_EQ(6, shaped.dimension(0));
+ EXPECT_EQ(4, shaped.dimension(1));
+ EXPECT_EQ(5, shaped.dimension(2));
+ EXPECT_EQ(shaped(0, 0, 0), 0.01f);
+ EXPECT_EQ(shaped(5, 3, 4), 0.02f);
+ }
+ {
+ auto shaped = t.shaped<float, 4>({2, 3, 4, 5});
+ EXPECT_EQ(2, shaped.dimension(0));
+ EXPECT_EQ(3, shaped.dimension(1));
+ EXPECT_EQ(4, shaped.dimension(2));
+ EXPECT_EQ(5, shaped.dimension(3));
+
+ EXPECT_EQ(shaped(0, 0, 0, 0), 0.01f);
+ EXPECT_EQ(shaped(1, 2, 3, 4), 0.02f);
+ }
+ {
+ auto flat = t.flat<float>();
+ EXPECT_EQ(flat(0), 0.01f);
+ EXPECT_EQ(120, flat.dimension(0));
+ EXPECT_EQ(flat(0), 0.01f);
+ EXPECT_EQ(flat(119), 0.02f);
+ }
+ {
+ auto flat_inner_dims = t.flat_inner_dims<float>();
+ EXPECT_EQ(24, flat_inner_dims.dimension(0));
+ EXPECT_EQ(5, flat_inner_dims.dimension(1));
+ EXPECT_EQ(flat_inner_dims(0, 0), 0.01f);
+ EXPECT_EQ(flat_inner_dims(23, 4), 0.02f);
+ }
+}
+
+TEST(Tensor_Scalar, Basics) {
+ {
+ Tensor t(DT_FLOAT, TensorShape({}));
+ EXPECT_EQ(1, t.NumElements());
+ auto Tt = t.scalar<float>();
+ EXPECT_EQ(1, Tt.size());
+ EXPECT_EQ(0, Tt.rank());
+ t.scalar<float>()() = 123.45f;
+ EXPECT_FLOAT_EQ(123.45f, Tt());
+ }
+ {
+ Tensor t(DT_FLOAT, TensorShape({1}));
+ EXPECT_EQ(1, t.NumElements());
+ auto Tt = t.vec<float>();
+ EXPECT_EQ(1, Tt.size());
+ t.vec<float>()(0) = 123.45f;
+ EXPECT_FLOAT_EQ(123.45f, Tt(0));
+ }
+ {
+ Tensor t(DT_FLOAT, TensorShape({1, 1, 1}));
+ EXPECT_EQ(1, t.NumElements());
+ auto Tt = t.scalar<float>();
+ EXPECT_EQ(1, Tt.size());
+ EXPECT_EQ(0, Tt.rank());
+ t.flat<float>()(0) = 123.45f;
+ EXPECT_FLOAT_EQ(123.45f, Tt());
+ }
+ {
+ Tensor t(DT_STRING, TensorShape({}));
+ EXPECT_EQ(1, t.NumElements());
+ auto Tt = t.scalar<string>();
+ EXPECT_EQ(1, Tt.size());
+ EXPECT_EQ(0, Tt.rank());
+ t.scalar<string>()() = "foo";
+ EXPECT_EQ("foo", Tt());
+ }
+ {
+ Tensor t(DT_STRING, TensorShape({1}));
+ EXPECT_EQ(1, t.NumElements());
+ auto Tt = t.vec<string>();
+ EXPECT_EQ(1, Tt.size());
+ t.flat<string>()(0) = "foo";
+ EXPECT_EQ("foo", Tt(0));
+ }
+ {
+ Tensor t(DT_STRING, TensorShape({1, 1, 1}));
+ EXPECT_EQ(1, t.NumElements());
+ auto Tt = t.scalar<string>();
+ EXPECT_EQ(1, Tt.size());
+ EXPECT_EQ(0, Tt.rank());
+ t.flat<string>()(0) = "bar";
+ EXPECT_EQ("bar", Tt());
+ }
+ {
+ Tensor t(DT_FLOAT, TensorShape({0, 1}));
+ EXPECT_EQ(0, t.NumElements());
+ auto Tt = t.flat<float>();
+ EXPECT_EQ(0, Tt.size());
+ auto Tm = t.matrix<float>();
+ EXPECT_EQ(0, Tm.size());
+ EXPECT_EQ(0, Tm.dimensions()[0]);
+ EXPECT_EQ(1, Tm.dimensions()[1]);
+ }
+}
+
+TEST(Tensor_Float, Reshape_And_Slice_Assignment) {
+ // A test to experiment with a way to assign to a subset of a tensor
+ Tensor t(DT_FLOAT, TensorShape({10, 4, 3, 2}));
+ EXPECT_TRUE(t.shape().IsSameSize(TensorShape({10, 4, 3, 2})));
+
+ // Get the N dimensional tensor (N==4 here)
+ auto e_t = t.tensor<float, 4>();
+ // Reshape to view it as a two-dimensional tensor
+ auto e_2d = t.shaped<float, 2>({10, 4 * 3 * 2});
+ for (int i = 0; i < 10; i++) {
+ // Assign a 1 x 4*3*2 matrix (really vector) to a slice of size
+ // 1 x 4*3*2 in e_t.
+ Eigen::Tensor<float, 2, Eigen::RowMajor> m(1, 4 * 3 * 2);
+ m.setConstant(i * 2.0);
+
+ Eigen::DSizes<Eigen::DenseIndex, 2> indices(i, 0);
+ Eigen::DSizes<Eigen::DenseIndex, 2> sizes(1, 4 * 3 * 2);
+ e_2d.slice(indices, sizes) = m;
+ }
+ for (int i = 0; i < 10; i++) {
+ for (int j = 0; j < 4; j++) {
+ for (int k = 0; k < 3; k++) {
+ for (int l = 0; l < 2; l++) {
+ EXPECT_EQ(e_t(i, j, k, l), i * 2.0f);
+ LOG(INFO) << i << "," << j << "," << k << "," << l
+ << " &e_t(i, j, k, l): " << &e_t(i, j, k, l) << " = "
+ << e_t(i, j, k, l);
+ }
+ }
+ }
+ }
+}
+
+TEST(Tensor_String, Simple) {
+ Tensor t = test::AsTensor<string>(
+ {"hello", "world", "machine", "learning", "new", "york"},
+ TensorShape({3, 2}));
+ auto s = t.shape();
+ ASSERT_EQ(s.dims(), 2);
+ ASSERT_EQ(s.dim_size(0), 3);
+ ASSERT_EQ(s.dim_size(1), 2);
+ auto m = t.matrix<string>();
+ EXPECT_EQ(t.TotalBytes(), 3 * 2 * sizeof(string) + 5 + 5 + 7 + 8 + 3 + 4);
+
+ EXPECT_EQ(m(0, 0), "hello");
+ EXPECT_EQ(m(0, 1), "world");
+ EXPECT_EQ(m(1, 0), "machine");
+ EXPECT_EQ(m(1, 1), "learning");
+ EXPECT_EQ(m(2, 0), "new");
+ EXPECT_EQ(m(2, 1), "york");
+
+ TestCopies<string>(t);
+}
+
+TEST(Tensor_Float, SimpleWithHelper) {
+ Tensor t1 = test::AsTensor<float>({0, 1, 2, 3, 4, 5}, {2, 3});
+ 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);
+}
+
+TEST(Tensor_Int32, SimpleWithHelper) {
+ Tensor t1 = test::AsTensor<int32>({0, 1, 2, 3, 4, 5}, {2, 3});
+ 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);
+}
+
+TEST(Tensor_QInt8, SimpleWithHelper) {
+ Tensor t1 = test::AsTensor<qint8>({0, 1, 2, 3, 4, 5}, {2, 3});
+ 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);
+}
+
+TEST(Tensor_QUInt8, SimpleWithHelper) {
+ Tensor t1 = test::AsTensor<quint8>({0, 1, 2, 3, 4, 5}, {2, 3});
+ 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);
+}
+
+TEST(Tensor_Int64, SimpleWithHelper) {
+ Tensor t1 = test::AsTensor<int64>(
+ {0LL << 48, 1LL << 48, 2LL << 48, 3LL << 48, 4LL << 48, 5LL << 48},
+ {2, 3});
+ Tensor t2(t1.dtype(), t1.shape());
+ t2.flat<int64>() = t1.flat<int64>() * static_cast<int64>(2);
+ Tensor t3 = test::AsTensor<int64>(
+ {0LL << 48, 2LL << 48, 4LL << 48, 6LL << 48, 8LL << 48, 10LL << 48},
+ {2, 3});
+ test::ExpectTensorEqual<int64>(t2, t3);
+}
+
+TEST(Tensor_String, SimpleWithHelper) {
+ Tensor t1 = test::AsTensor<string>({"0", "1", "2", "3", "4", "5"}, {2, 3});
+ Tensor t2(DT_STRING, {2, 3});
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ t2.matrix<string>()(i, j) = strings::StrCat(i * 3 + j);
+ }
+ }
+
+ // Test with helper.
+ test::ExpectTensorEqual<string>(t1, t2);
+}
+
+TEST(Tensor_Bool, SimpleWithHelper) {
+ Tensor t1 =
+ test::AsTensor<bool>({false, true, false, true, false, true}, {2, 3});
+
+ Tensor t2(DT_BOOL, {2, 3});
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ t2.matrix<bool>()(i, j) = (((i + j) % 2) != 0);
+ }
+ }
+
+ // Test with helper.
+ test::ExpectTensorEqual<bool>(t1, t2);
+}
+
+TEST(Tensor_Complex, Simple) {
+ Tensor t(DT_COMPLEX64, {4, 5, 3, 7});
+ t.flat<complex64>().setRandom();
+ TestCopies<complex64>(t);
+}
+
+TEST(Tensor_Complex, SimpleWithHelper) {
+ {
+ Tensor t1 = test::AsTensor<complex64>({0,
+ {1, 1},
+ complex64(2),
+ complex64(3, 3),
+ complex64(0, 4),
+ complex64(2, 5)},
+ {2, 3});
+ Tensor t2(t1.dtype(), t1.shape());
+ t2.flat<complex64>() = t1.flat<complex64>() * complex64(0, 2);
+ Tensor t3 = test::AsTensor<complex64>(
+ {0, {-2, 2}, {0, 4}, {-6, 6}, {-8, 0}, {-10, 4}},
+ // shape
+ {2, 3});
+ test::ExpectTensorEqual<complex64>(t2, t3);
+ }
+
+ // Does some numeric operations for complex numbers.
+ {
+ const float PI = std::acos(-1);
+ const complex64 rotate_45 = std::polar(1.0f, PI / 4);
+
+ // x contains all the 8-th root of unity.
+ Tensor x(DT_COMPLEX64, TensorShape({8}));
+ for (int i = 0; i < 8; ++i) {
+ x.vec<complex64>()(i) = std::pow(rotate_45, i);
+ }
+
+ // Shift the roots by 45 degree.
+ Tensor y(DT_COMPLEX64, TensorShape({8}));
+ y.vec<complex64>() = x.vec<complex64>() * rotate_45;
+ Tensor y_expected(DT_COMPLEX64, TensorShape({8}));
+ for (int i = 0; i < 8; ++i) {
+ y_expected.vec<complex64>()(i) = std::pow(rotate_45, i + 1);
+ }
+ test::ExpectTensorNear<complex64>(y, y_expected, 1e-5);
+
+ // Raise roots to the power of 8.
+ Tensor z(DT_COMPLEX64, TensorShape({8}));
+ z.vec<complex64>() = x.vec<complex64>().pow(8);
+ Tensor z_expected(DT_COMPLEX64, TensorShape({8}));
+ for (int i = 0; i < 8; ++i) {
+ z_expected.vec<complex64>()(i) = 1;
+ }
+ test::ExpectTensorNear<complex64>(z, z_expected, 1e-5);
+ }
+}
+
+// On the alignment.
+//
+// As of 2015/8, tensorflow::Tensor allocates its buffer with 32-byte
+// alignment. Tensor::tensor/flat/vec/matrix methods requires the the
+// buffer satisfies Eigen::Aligned (e.g., 16-bytes aligned usually,
+// and 32-bytes for AVX). Tensor::Slice requires the caller to ensure
+// its result is aligned if the caller intends to use those methods.
+// In this test case, we simply make sure each slice is 32-byte
+// aligned: sizeof(float) * 4 * 2 = 32.
+TEST(Tensor, Slice_Basic) {
+ Tensor saved;
+ { // General
+ Tensor x(DT_FLOAT, TensorShape({10, 4, 34}));
+ // Fills in known values.
+ for (int i = 0; i < 10; ++i) {
+ x.Slice(i, i + 1).flat<float>().setConstant(i * 1.f);
+ }
+ // A simple slice along dim0.
+ Tensor y = x.Slice(4, 8);
+ EXPECT_TRUE(y.shape().IsSameSize(TensorShape({4, 4, 34})));
+ auto tx = x.tensor<float, 3>();
+ auto ty = y.tensor<float, 3>();
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < 34; ++k) {
+ EXPECT_EQ(ty(i, j, k), 4.0 + i);
+ EXPECT_EQ(&tx(4 + i, j, k), &ty(i, j, k));
+ }
+ }
+ }
+ // A simple slice equivalent to identity.
+ TestCopies<float>(y);
+ y = x.Slice(0, 10);
+ test::ExpectTensorEqual<float>(x, y);
+ EXPECT_EQ(x.flat<float>().data(), y.flat<float>().data());
+
+ // A slice of a slice.
+ auto z = x.Slice(4, 8).Slice(2, 3);
+ auto tz = z.tensor<float, 3>();
+ EXPECT_EQ(1, z.dim_size(0));
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < 34; ++k) {
+ EXPECT_EQ(tz(0, j, k), 6.0);
+ }
+ }
+
+ // x and y will be out of scope. But 'saved' should be alive.
+ saved = z;
+ }
+ {
+ EXPECT_EQ(1, saved.dim_size(0));
+ auto tsaved = saved.tensor<float, 3>();
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < 34; ++k) {
+ EXPECT_EQ(tsaved(0, j, k), 6.0);
+ }
+ }
+ }
+ { // Empty
+ Tensor x(DT_FLOAT, TensorShape({10, 0, 34}));
+ x.flat<float>().setRandom();
+ Tensor y = x.Slice(4, 8);
+ EXPECT_TRUE(y.shape().IsSameSize(TensorShape({4, 0, 34})));
+ }
+
+ {
+ // Test unaligned access via a Slice.
+ Tensor x(DT_FLOAT, TensorShape({30}));
+ x.flat<float>().setConstant(0.0);
+
+ // Take an unaligned slice.
+ Tensor y = x.Slice(1, 13);
+ y.unaligned_flat<float>().setConstant(1.0);
+ for (int64 i = 0; i < y.NumElements(); ++i) {
+ EXPECT_EQ(1.0, y.unaligned_flat<float>()(i));
+ }
+ }
+}
+
+static void BM_CreateAndDestroy(int iters) {
+ TensorShape shape({10, 20});
+ while (--iters) {
+ Tensor t(DT_FLOAT, shape);
+ }
+}
+BENCHMARK(BM_CreateAndDestroy);
+
+static void BM_Assign(int iters) {
+ Tensor a(DT_FLOAT, TensorShape({10, 20}));
+ Tensor b(DT_FLOAT, TensorShape({10, 20}));
+ bool a_to_b = true;
+ while (--iters) {
+ if (a_to_b) {
+ b = a;
+ } else {
+ a = b;
+ }
+ a_to_b = !a_to_b;
+ }
+}
+BENCHMARK(BM_Assign);
+
+// Ensure tensor_data() works on empty tensors
+TEST(Tensor, EmptyTensorData) {
+ Tensor empty;
+ EXPECT_EQ(empty.tensor_data().size(), 0);
+}
+
+} // namespace tensorflow