aboutsummaryrefslogtreecommitdiffhomepage
path: root/unsupported/test
diff options
context:
space:
mode:
authorGravatar Gael Guennebaud <g.gael@free.fr>2015-02-12 21:48:41 +0100
committerGravatar Gael Guennebaud <g.gael@free.fr>2015-02-12 21:48:41 +0100
commit0918c51e600bed36a53448fa276b01387119a3c2 (patch)
tree8183416a03dc22d1cc37b886e0e8f0dd0afe4e85 /unsupported/test
parent409547a0c83604b6dea70b8523674ac19e2af958 (diff)
parent4470c9997559522e9b81810948d9783b58444ae4 (diff)
merge Tensor module within Eigen/unsupported and update gemv BLAS wrapper
Diffstat (limited to 'unsupported/test')
-rw-r--r--unsupported/test/CMakeLists.txt50
-rw-r--r--unsupported/test/cxx11_tensor_assign.cpp370
-rw-r--r--unsupported/test/cxx11_tensor_broadcasting.cpp194
-rw-r--r--unsupported/test/cxx11_tensor_casts.cpp41
-rw-r--r--unsupported/test/cxx11_tensor_chipping.cpp397
-rw-r--r--unsupported/test/cxx11_tensor_comparisons.cpp84
-rw-r--r--unsupported/test/cxx11_tensor_concatenation.cpp116
-rw-r--r--unsupported/test/cxx11_tensor_const.cpp39
-rw-r--r--unsupported/test/cxx11_tensor_contract_cuda.cpp121
-rw-r--r--unsupported/test/cxx11_tensor_contraction.cpp480
-rw-r--r--unsupported/test/cxx11_tensor_convolution.cpp141
-rw-r--r--unsupported/test/cxx11_tensor_cuda.cpp514
-rw-r--r--unsupported/test/cxx11_tensor_device.cpp391
-rw-r--r--unsupported/test/cxx11_tensor_dimension.cpp54
-rw-r--r--unsupported/test/cxx11_tensor_expr.cpp314
-rw-r--r--unsupported/test/cxx11_tensor_fixed_size.cpp198
-rw-r--r--unsupported/test/cxx11_tensor_forced_eval.cpp78
-rw-r--r--unsupported/test/cxx11_tensor_image_patch.cpp476
-rw-r--r--unsupported/test/cxx11_tensor_index_list.cpp268
-rw-r--r--unsupported/test/cxx11_tensor_intdiv.cpp77
-rw-r--r--unsupported/test/cxx11_tensor_io.cpp114
-rw-r--r--unsupported/test/cxx11_tensor_layout_swap.cpp61
-rw-r--r--unsupported/test/cxx11_tensor_lvalue.cpp42
-rw-r--r--unsupported/test/cxx11_tensor_map.cpp147
-rw-r--r--unsupported/test/cxx11_tensor_morphing.cpp342
-rw-r--r--unsupported/test/cxx11_tensor_of_complex.cpp81
-rw-r--r--unsupported/test/cxx11_tensor_of_const_values.cpp105
-rw-r--r--unsupported/test/cxx11_tensor_of_strings.cpp152
-rw-r--r--unsupported/test/cxx11_tensor_padding.cpp93
-rw-r--r--unsupported/test/cxx11_tensor_patch.cpp120
-rw-r--r--unsupported/test/cxx11_tensor_random.cpp78
-rw-r--r--unsupported/test/cxx11_tensor_reduction.cpp420
-rw-r--r--unsupported/test/cxx11_tensor_ref.cpp208
-rw-r--r--unsupported/test/cxx11_tensor_reverse.cpp167
-rw-r--r--unsupported/test/cxx11_tensor_shuffling.cpp187
-rw-r--r--unsupported/test/cxx11_tensor_simple.cpp33
-rw-r--r--unsupported/test/cxx11_tensor_striding.cpp119
-rw-r--r--unsupported/test/cxx11_tensor_thread_pool.cpp270
38 files changed, 7134 insertions, 8 deletions
diff --git a/unsupported/test/CMakeLists.txt b/unsupported/test/CMakeLists.txt
index 94a5cf445..7b6751f00 100644
--- a/unsupported/test/CMakeLists.txt
+++ b/unsupported/test/CMakeLists.txt
@@ -50,7 +50,7 @@ if(MPFR_FOUND)
include_directories(${MPFR_INCLUDES} ./mpreal)
ei_add_property(EIGEN_TESTED_BACKENDS "MPFR C++, ")
set(EIGEN_MPFR_TEST_LIBRARIES ${MPFR_LIBRARIES} ${GMP_LIBRARIES})
- ei_add_test(mpreal_support "" "${EIGEN_MPFR_TEST_LIBRARIES}" )
+# ei_add_test(mpreal_support "" "${EIGEN_MPFR_TEST_LIBRARIES}" )
else()
ei_add_property(EIGEN_MISSING_BACKENDS "MPFR C++, ")
endif()
@@ -95,12 +95,50 @@ ei_add_test(minres)
ei_add_test(levenberg_marquardt)
ei_add_test(kronecker_product)
-option(EIGEN_TEST_CXX11 "Enable testing of C++11 features (e.g. Tensor module)." OFF)
+option(EIGEN_TEST_CXX11 "Enable testing of C++11 features (e.g. Tensor module)." ON)
if(EIGEN_TEST_CXX11)
- # FIXME: add C++11 compiler switch in some portable way
- # (MSVC doesn't need any for example, so this will
- # clash there)
+ # It should be safe to always run these tests as there is some fallback code for
+ # older compiler that don't support cxx11.
ei_add_test(cxx11_meta "-std=c++0x")
ei_add_test(cxx11_tensor_simple "-std=c++0x")
- ei_add_test(cxx11_tensor_symmetry "-std=c++0x")
+# ei_add_test(cxx11_tensor_symmetry "-std=c++0x")
+ ei_add_test(cxx11_tensor_assign "-std=c++0x")
+ ei_add_test(cxx11_tensor_dimension "-std=c++0x")
+ ei_add_test(cxx11_tensor_index_list "-std=c++0x")
+ ei_add_test(cxx11_tensor_comparisons "-std=c++0x")
+ ei_add_test(cxx11_tensor_contraction "-std=c++0x")
+ ei_add_test(cxx11_tensor_convolution "-std=c++0x")
+ ei_add_test(cxx11_tensor_expr "-std=c++0x")
+ ei_add_test(cxx11_tensor_forced_eval "-std=c++0x")
+ ei_add_test(cxx11_tensor_fixed_size "-std=c++0x")
+ ei_add_test(cxx11_tensor_const "-std=c++0x")
+ ei_add_test(cxx11_tensor_of_const_values "-std=c++0x")
+ ei_add_test(cxx11_tensor_of_complex "-std=c++0x")
+ ei_add_test(cxx11_tensor_of_strings "-std=c++0x")
+ ei_add_test(cxx11_tensor_intdiv "-std=c++0x")
+ ei_add_test(cxx11_tensor_lvalue "-std=c++0x")
+ ei_add_test(cxx11_tensor_map "-std=c++0x")
+ ei_add_test(cxx11_tensor_broadcasting "-std=c++0x")
+ ei_add_test(cxx11_tensor_chipping "-std=c++0x")
+ ei_add_test(cxx11_tensor_concatenation "-std=c++0x")
+ ei_add_test(cxx11_tensor_morphing "-std=c++0x")
+ ei_add_test(cxx11_tensor_padding "-std=c++0x")
+ ei_add_test(cxx11_tensor_patch "-std=c++0x")
+ ei_add_test(cxx11_tensor_image_patch "-std=c++0x")
+ ei_add_test(cxx11_tensor_reduction "-std=c++0x")
+ ei_add_test(cxx11_tensor_shuffling "-std=c++0x")
+ ei_add_test(cxx11_tensor_striding "-std=c++0x")
+ ei_add_test(cxx11_tensor_thread_pool "-std=c++0x")
+ ei_add_test(cxx11_tensor_ref "-std=c++0x")
+ ei_add_test(cxx11_tensor_random "-std=c++0x")
+ ei_add_test(cxx11_tensor_casts "-std=c++0x")
+ ei_add_test(cxx11_tensor_reverse "-std=c++0x")
+ ei_add_test(cxx11_tensor_layout_swap "-std=c++0x")
+ ei_add_test(cxx11_tensor_io "-std=c++0x")
+
+ # These tests needs nvcc
+# ei_add_test(cxx11_tensor_device "-std=c++0x")
+# ei_add_test(cxx11_tensor_cuda "-std=c++0x")
+# ei_add_test(cxx11_tensor_contract_cuda "-std=c++0x")
+
endif()
diff --git a/unsupported/test/cxx11_tensor_assign.cpp b/unsupported/test/cxx11_tensor_assign.cpp
new file mode 100644
index 000000000..d16aaf847
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_assign.cpp
@@ -0,0 +1,370 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+static void test_1d()
+{
+ Tensor<int, 1> vec1(6);
+ Tensor<int, 1, RowMajor> vec2(6);
+ vec1(0) = 4; vec2(0) = 0;
+ vec1(1) = 8; vec2(1) = 1;
+ vec1(2) = 15; vec2(2) = 2;
+ vec1(3) = 16; vec2(3) = 3;
+ vec1(4) = 23; vec2(4) = 4;
+ vec1(5) = 42; vec2(5) = 5;
+
+ int col_major[6];
+ int row_major[6];
+ memset(col_major, 0, 6*sizeof(int));
+ memset(row_major, 0, 6*sizeof(int));
+ TensorMap<Tensor<int, 1>> vec3(col_major, 6);
+ TensorMap<Tensor<int, 1, RowMajor>> vec4(row_major, 6);
+
+ vec3 = vec1;
+ vec4 = vec2;
+
+ VERIFY_IS_EQUAL(vec3(0), 4);
+ VERIFY_IS_EQUAL(vec3(1), 8);
+ VERIFY_IS_EQUAL(vec3(2), 15);
+ VERIFY_IS_EQUAL(vec3(3), 16);
+ VERIFY_IS_EQUAL(vec3(4), 23);
+ VERIFY_IS_EQUAL(vec3(5), 42);
+
+ VERIFY_IS_EQUAL(vec4(0), 0);
+ VERIFY_IS_EQUAL(vec4(1), 1);
+ VERIFY_IS_EQUAL(vec4(2), 2);
+ VERIFY_IS_EQUAL(vec4(3), 3);
+ VERIFY_IS_EQUAL(vec4(4), 4);
+ VERIFY_IS_EQUAL(vec4(5), 5);
+
+ vec1.setZero();
+ vec2.setZero();
+ vec1 = vec3;
+ vec2 = vec4;
+
+ VERIFY_IS_EQUAL(vec1(0), 4);
+ VERIFY_IS_EQUAL(vec1(1), 8);
+ VERIFY_IS_EQUAL(vec1(2), 15);
+ VERIFY_IS_EQUAL(vec1(3), 16);
+ VERIFY_IS_EQUAL(vec1(4), 23);
+ VERIFY_IS_EQUAL(vec1(5), 42);
+
+ VERIFY_IS_EQUAL(vec2(0), 0);
+ VERIFY_IS_EQUAL(vec2(1), 1);
+ VERIFY_IS_EQUAL(vec2(2), 2);
+ VERIFY_IS_EQUAL(vec2(3), 3);
+ VERIFY_IS_EQUAL(vec2(4), 4);
+ VERIFY_IS_EQUAL(vec2(5), 5);
+}
+
+static void test_2d()
+{
+ Tensor<int, 2> mat1(2,3);
+ Tensor<int, 2, RowMajor> mat2(2,3);
+
+ mat1(0,0) = 0;
+ mat1(0,1) = 1;
+ mat1(0,2) = 2;
+ mat1(1,0) = 3;
+ mat1(1,1) = 4;
+ mat1(1,2) = 5;
+
+ mat2(0,0) = 0;
+ mat2(0,1) = 1;
+ mat2(0,2) = 2;
+ mat2(1,0) = 3;
+ mat2(1,1) = 4;
+ mat2(1,2) = 5;
+
+ int col_major[6];
+ int row_major[6];
+ memset(col_major, 0, 6*sizeof(int));
+ memset(row_major, 0, 6*sizeof(int));
+ TensorMap<Tensor<int, 2>> mat3(row_major, 2, 3);
+ TensorMap<Tensor<int, 2, RowMajor>> mat4(col_major, 2, 3);
+
+ mat3 = mat1;
+ mat4 = mat2;
+
+ VERIFY_IS_EQUAL(mat3(0,0), 0);
+ VERIFY_IS_EQUAL(mat3(0,1), 1);
+ VERIFY_IS_EQUAL(mat3(0,2), 2);
+ VERIFY_IS_EQUAL(mat3(1,0), 3);
+ VERIFY_IS_EQUAL(mat3(1,1), 4);
+ VERIFY_IS_EQUAL(mat3(1,2), 5);
+
+ VERIFY_IS_EQUAL(mat4(0,0), 0);
+ VERIFY_IS_EQUAL(mat4(0,1), 1);
+ VERIFY_IS_EQUAL(mat4(0,2), 2);
+ VERIFY_IS_EQUAL(mat4(1,0), 3);
+ VERIFY_IS_EQUAL(mat4(1,1), 4);
+ VERIFY_IS_EQUAL(mat4(1,2), 5);
+
+ mat1.setZero();
+ mat2.setZero();
+ mat1 = mat3;
+ mat2 = mat4;
+
+ VERIFY_IS_EQUAL(mat1(0,0), 0);
+ VERIFY_IS_EQUAL(mat1(0,1), 1);
+ VERIFY_IS_EQUAL(mat1(0,2), 2);
+ VERIFY_IS_EQUAL(mat1(1,0), 3);
+ VERIFY_IS_EQUAL(mat1(1,1), 4);
+ VERIFY_IS_EQUAL(mat1(1,2), 5);
+
+ VERIFY_IS_EQUAL(mat2(0,0), 0);
+ VERIFY_IS_EQUAL(mat2(0,1), 1);
+ VERIFY_IS_EQUAL(mat2(0,2), 2);
+ VERIFY_IS_EQUAL(mat2(1,0), 3);
+ VERIFY_IS_EQUAL(mat2(1,1), 4);
+ VERIFY_IS_EQUAL(mat2(1,2), 5);
+}
+
+static void test_3d()
+{
+ Tensor<int, 3> mat1(2,3,7);
+ Tensor<int, 3, RowMajor> mat2(2,3,7);
+
+ int val = 0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ mat2(i,j,k) = val;
+ val++;
+ }
+ }
+ }
+
+ int col_major[2*3*7];
+ int row_major[2*3*7];
+ memset(col_major, 0, 2*3*7*sizeof(int));
+ memset(row_major, 0, 2*3*7*sizeof(int));
+ TensorMap<Tensor<int, 3>> mat3(col_major, 2, 3, 7);
+ TensorMap<Tensor<int, 3, RowMajor>> mat4(row_major, 2, 3, 7);
+
+ mat3 = mat1;
+ mat4 = mat2;
+
+ val = 0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(mat3(i,j,k), val);
+ VERIFY_IS_EQUAL(mat4(i,j,k), val);
+ val++;
+ }
+ }
+ }
+
+ mat1.setZero();
+ mat2.setZero();
+ mat1 = mat3;
+ mat2 = mat4;
+
+ val = 0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(mat1(i,j,k), val);
+ VERIFY_IS_EQUAL(mat2(i,j,k), val);
+ val++;
+ }
+ }
+ }
+}
+
+static void test_same_type()
+{
+ Tensor<int, 1> orig_tensor(5);
+ Tensor<int, 1> dest_tensor(5);
+ orig_tensor.setRandom();
+ dest_tensor.setRandom();
+ int* orig_data = orig_tensor.data();
+ int* dest_data = dest_tensor.data();
+ dest_tensor = orig_tensor;
+ VERIFY_IS_EQUAL(orig_tensor.data(), orig_data);
+ VERIFY_IS_EQUAL(dest_tensor.data(), dest_data);
+ for (int i = 0; i < 5; ++i) {
+ VERIFY_IS_EQUAL(dest_tensor(i), orig_tensor(i));
+ }
+
+ TensorFixedSize<int, Sizes<5> > orig_array;
+ TensorFixedSize<int, Sizes<5> > dest_array;
+ orig_array.setRandom();
+ dest_array.setRandom();
+ orig_data = orig_array.data();
+ dest_data = dest_array.data();
+ dest_array = orig_array;
+ VERIFY_IS_EQUAL(orig_array.data(), orig_data);
+ VERIFY_IS_EQUAL(dest_array.data(), dest_data);
+ for (int i = 0; i < 5; ++i) {
+ VERIFY_IS_EQUAL(dest_array(i), orig_array(i));
+ }
+
+ int orig[5] = {1, 2, 3, 4, 5};
+ int dest[5] = {6, 7, 8, 9, 10};
+ TensorMap<Tensor<int, 1> > orig_map(orig, 5);
+ TensorMap<Tensor<int, 1> > dest_map(dest, 5);
+ orig_data = orig_map.data();
+ dest_data = dest_map.data();
+ dest_map = orig_map;
+ VERIFY_IS_EQUAL(orig_map.data(), orig_data);
+ VERIFY_IS_EQUAL(dest_map.data(), dest_data);
+ for (int i = 0; i < 5; ++i) {
+ VERIFY_IS_EQUAL(dest[i], i+1);
+ }
+}
+
+static void test_auto_resize()
+{
+ Tensor<int, 1> tensor1;
+ Tensor<int, 1> tensor2(3);
+ Tensor<int, 1> tensor3(5);
+ Tensor<int, 1> tensor4(7);
+
+ Tensor<int, 1> new_tensor(5);
+ new_tensor.setRandom();
+
+ tensor1 = tensor2 = tensor3 = tensor4 = new_tensor;
+
+ VERIFY_IS_EQUAL(tensor1.dimension(0), new_tensor.dimension(0));
+ VERIFY_IS_EQUAL(tensor2.dimension(0), new_tensor.dimension(0));
+ VERIFY_IS_EQUAL(tensor3.dimension(0), new_tensor.dimension(0));
+ VERIFY_IS_EQUAL(tensor4.dimension(0), new_tensor.dimension(0));
+ for (int i = 0; i < new_tensor.dimension(0); ++i) {
+ VERIFY_IS_EQUAL(tensor1(i), new_tensor(i));
+ VERIFY_IS_EQUAL(tensor2(i), new_tensor(i));
+ VERIFY_IS_EQUAL(tensor3(i), new_tensor(i));
+ VERIFY_IS_EQUAL(tensor4(i), new_tensor(i));
+ }
+}
+
+
+static void test_compound_assign()
+{
+ Tensor<int, 1> start_tensor(10);
+ Tensor<int, 1> offset_tensor(10);
+ start_tensor.setRandom();
+ offset_tensor.setRandom();
+
+ Tensor<int, 1> tensor = start_tensor;
+ tensor += offset_tensor;
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_EQUAL(tensor(i), start_tensor(i) + offset_tensor(i));
+ }
+
+ tensor = start_tensor;
+ tensor -= offset_tensor;
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_EQUAL(tensor(i), start_tensor(i) - offset_tensor(i));
+ }
+
+ tensor = start_tensor;
+ tensor *= offset_tensor;
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_EQUAL(tensor(i), start_tensor(i) * offset_tensor(i));
+ }
+
+ tensor = start_tensor;
+ tensor /= offset_tensor;
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_EQUAL(tensor(i), start_tensor(i) / offset_tensor(i));
+ }
+}
+
+static void test_std_initializers_tensor() {
+#ifdef EIGEN_HAS_VARIADIC_TEMPLATES
+ Tensor<int, 1> a(3);
+ a.setValues({0, 1, 2});
+ VERIFY_IS_EQUAL(a(0), 0);
+ VERIFY_IS_EQUAL(a(1), 1);
+ VERIFY_IS_EQUAL(a(2), 2);
+
+ // It fills the top-left slice.
+ a.setValues({10, 20});
+ VERIFY_IS_EQUAL(a(0), 10);
+ VERIFY_IS_EQUAL(a(1), 20);
+ VERIFY_IS_EQUAL(a(2), 2);
+
+ // Chaining.
+ Tensor<int, 1> a2(3);
+ a2 = a.setValues({100, 200, 300});
+ VERIFY_IS_EQUAL(a(0), 100);
+ VERIFY_IS_EQUAL(a(1), 200);
+ VERIFY_IS_EQUAL(a(2), 300);
+ VERIFY_IS_EQUAL(a2(0), 100);
+ VERIFY_IS_EQUAL(a2(1), 200);
+ VERIFY_IS_EQUAL(a2(2), 300);
+
+ Tensor<int, 2> b(2, 3);
+ b.setValues({{0, 1, 2}, {3, 4, 5}});
+ VERIFY_IS_EQUAL(b(0, 0), 0);
+ VERIFY_IS_EQUAL(b(0, 1), 1);
+ VERIFY_IS_EQUAL(b(0, 2), 2);
+ VERIFY_IS_EQUAL(b(1, 0), 3);
+ VERIFY_IS_EQUAL(b(1, 1), 4);
+ VERIFY_IS_EQUAL(b(1, 2), 5);
+
+ // It fills the top-left slice.
+ b.setValues({{10, 20}, {30}});
+ VERIFY_IS_EQUAL(b(0, 0), 10);
+ VERIFY_IS_EQUAL(b(0, 1), 20);
+ VERIFY_IS_EQUAL(b(0, 2), 2);
+ VERIFY_IS_EQUAL(b(1, 0), 30);
+ VERIFY_IS_EQUAL(b(1, 1), 4);
+ VERIFY_IS_EQUAL(b(1, 2), 5);
+
+ Eigen::Tensor<int, 3> c(3, 2, 4);
+ c.setValues({{{0, 1, 2, 3}, {4, 5, 6, 7}},
+ {{10, 11, 12, 13}, {14, 15, 16, 17}},
+ {{20, 21, 22, 23}, {24, 25, 26, 27}}});
+ VERIFY_IS_EQUAL(c(0, 0, 0), 0);
+ VERIFY_IS_EQUAL(c(0, 0, 1), 1);
+ VERIFY_IS_EQUAL(c(0, 0, 2), 2);
+ VERIFY_IS_EQUAL(c(0, 0, 3), 3);
+ VERIFY_IS_EQUAL(c(0, 1, 0), 4);
+ VERIFY_IS_EQUAL(c(0, 1, 1), 5);
+ VERIFY_IS_EQUAL(c(0, 1, 2), 6);
+ VERIFY_IS_EQUAL(c(0, 1, 3), 7);
+ VERIFY_IS_EQUAL(c(1, 0, 0), 10);
+ VERIFY_IS_EQUAL(c(1, 0, 1), 11);
+ VERIFY_IS_EQUAL(c(1, 0, 2), 12);
+ VERIFY_IS_EQUAL(c(1, 0, 3), 13);
+ VERIFY_IS_EQUAL(c(1, 1, 0), 14);
+ VERIFY_IS_EQUAL(c(1, 1, 1), 15);
+ VERIFY_IS_EQUAL(c(1, 1, 2), 16);
+ VERIFY_IS_EQUAL(c(1, 1, 3), 17);
+ VERIFY_IS_EQUAL(c(2, 0, 0), 20);
+ VERIFY_IS_EQUAL(c(2, 0, 1), 21);
+ VERIFY_IS_EQUAL(c(2, 0, 2), 22);
+ VERIFY_IS_EQUAL(c(2, 0, 3), 23);
+ VERIFY_IS_EQUAL(c(2, 1, 0), 24);
+ VERIFY_IS_EQUAL(c(2, 1, 1), 25);
+ VERIFY_IS_EQUAL(c(2, 1, 2), 26);
+ VERIFY_IS_EQUAL(c(2, 1, 3), 27);
+#endif // EIGEN_HAS_VARIADIC_TEMPLATES
+}
+
+void test_cxx11_tensor_assign()
+{
+ CALL_SUBTEST(test_1d());
+ CALL_SUBTEST(test_2d());
+ CALL_SUBTEST(test_3d());
+ CALL_SUBTEST(test_same_type());
+ CALL_SUBTEST(test_auto_resize());
+ CALL_SUBTEST(test_compound_assign());
+ CALL_SUBTEST(test_std_initializers_tensor());
+}
diff --git a/unsupported/test/cxx11_tensor_broadcasting.cpp b/unsupported/test/cxx11_tensor_broadcasting.cpp
new file mode 100644
index 000000000..2ddf47234
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_broadcasting.cpp
@@ -0,0 +1,194 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+template <int DataLayout>
+static void test_simple_broadcasting()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+ array<ptrdiff_t, 4> broadcasts;
+ broadcasts[0] = 1;
+ broadcasts[1] = 1;
+ broadcasts[2] = 1;
+ broadcasts[3] = 1;
+
+ Tensor<float, 4, DataLayout> no_broadcast;
+ no_broadcast = tensor.broadcast(broadcasts);
+
+ VERIFY_IS_EQUAL(no_broadcast.dimension(0), 2);
+ VERIFY_IS_EQUAL(no_broadcast.dimension(1), 3);
+ VERIFY_IS_EQUAL(no_broadcast.dimension(2), 5);
+ VERIFY_IS_EQUAL(no_broadcast.dimension(3), 7);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), no_broadcast(i,j,k,l));
+ }
+ }
+ }
+ }
+
+ broadcasts[0] = 2;
+ broadcasts[1] = 3;
+ broadcasts[2] = 1;
+ broadcasts[3] = 4;
+ Tensor<float, 4, DataLayout> broadcast;
+ broadcast = tensor.broadcast(broadcasts);
+
+ VERIFY_IS_EQUAL(broadcast.dimension(0), 4);
+ VERIFY_IS_EQUAL(broadcast.dimension(1), 9);
+ VERIFY_IS_EQUAL(broadcast.dimension(2), 5);
+ VERIFY_IS_EQUAL(broadcast.dimension(3), 28);
+
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 9; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 28; ++l) {
+ VERIFY_IS_EQUAL(tensor(i%2,j%3,k%5,l%7), broadcast(i,j,k,l));
+ }
+ }
+ }
+ }
+}
+
+
+template <int DataLayout>
+static void test_vectorized_broadcasting()
+{
+ Tensor<float, 3, DataLayout> tensor(8,3,5);
+ tensor.setRandom();
+ array<ptrdiff_t, 3> broadcasts;
+ broadcasts[0] = 2;
+ broadcasts[1] = 3;
+ broadcasts[2] = 4;
+
+ Tensor<float, 3, DataLayout> broadcast;
+ broadcast = tensor.broadcast(broadcasts);
+
+ VERIFY_IS_EQUAL(broadcast.dimension(0), 16);
+ VERIFY_IS_EQUAL(broadcast.dimension(1), 9);
+ VERIFY_IS_EQUAL(broadcast.dimension(2), 20);
+
+ for (int i = 0; i < 16; ++i) {
+ for (int j = 0; j < 9; ++j) {
+ for (int k = 0; k < 20; ++k) {
+ VERIFY_IS_EQUAL(tensor(i%8,j%3,k%5), broadcast(i,j,k));
+ }
+ }
+ }
+
+ tensor.resize(11,3,5);
+ tensor.setRandom();
+ broadcast = tensor.broadcast(broadcasts);
+
+ VERIFY_IS_EQUAL(broadcast.dimension(0), 22);
+ VERIFY_IS_EQUAL(broadcast.dimension(1), 9);
+ VERIFY_IS_EQUAL(broadcast.dimension(2), 20);
+
+ for (int i = 0; i < 22; ++i) {
+ for (int j = 0; j < 9; ++j) {
+ for (int k = 0; k < 20; ++k) {
+ VERIFY_IS_EQUAL(tensor(i%11,j%3,k%5), broadcast(i,j,k));
+ }
+ }
+ }
+}
+
+
+template <int DataLayout>
+static void test_static_broadcasting()
+{
+ Tensor<float, 3, DataLayout> tensor(8,3,5);
+ tensor.setRandom();
+
+#ifdef EIGEN_HAS_CONSTEXPR
+ Eigen::IndexList<Eigen::type2index<2>, Eigen::type2index<3>, Eigen::type2index<4>> broadcasts;
+#else
+ Eigen::array<int, 3> broadcasts;
+ broadcasts[0] = 2;
+ broadcasts[1] = 3;
+ broadcasts[2] = 4;
+#endif
+
+ Tensor<float, 3, DataLayout> broadcast;
+ broadcast = tensor.broadcast(broadcasts);
+
+ VERIFY_IS_EQUAL(broadcast.dimension(0), 16);
+ VERIFY_IS_EQUAL(broadcast.dimension(1), 9);
+ VERIFY_IS_EQUAL(broadcast.dimension(2), 20);
+
+ for (int i = 0; i < 16; ++i) {
+ for (int j = 0; j < 9; ++j) {
+ for (int k = 0; k < 20; ++k) {
+ VERIFY_IS_EQUAL(tensor(i%8,j%3,k%5), broadcast(i,j,k));
+ }
+ }
+ }
+
+ tensor.resize(11,3,5);
+ tensor.setRandom();
+ broadcast = tensor.broadcast(broadcasts);
+
+ VERIFY_IS_EQUAL(broadcast.dimension(0), 22);
+ VERIFY_IS_EQUAL(broadcast.dimension(1), 9);
+ VERIFY_IS_EQUAL(broadcast.dimension(2), 20);
+
+ for (int i = 0; i < 22; ++i) {
+ for (int j = 0; j < 9; ++j) {
+ for (int k = 0; k < 20; ++k) {
+ VERIFY_IS_EQUAL(tensor(i%11,j%3,k%5), broadcast(i,j,k));
+ }
+ }
+ }
+}
+
+
+template <int DataLayout>
+static void test_fixed_size_broadcasting()
+{
+ // Need to add a [] operator to the Size class for this to work
+#if 0
+ Tensor<float, 1, DataLayout> t1(10);
+ t1.setRandom();
+ TensorFixedSize<float, Sizes<1>, DataLayout> t2;
+ t2 = t2.constant(20.0f);
+
+ Tensor<float, 1, DataLayout> t3 = t1 + t2.broadcast(Eigen::array<int, 1>{{10}});
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_APPROX(t3(i), t1(i) + t2(0));
+ }
+
+ TensorMap<TensorFixedSize<float, Sizes<1>, DataLayout> > t4(t2.data(), {{1}});
+ Tensor<float, 1, DataLayout> t5 = t1 + t4.broadcast(Eigen::array<int, 1>{{10}});
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_APPROX(t5(i), t1(i) + t2(0));
+ }
+#endif
+}
+
+
+void test_cxx11_tensor_broadcasting()
+{
+ CALL_SUBTEST(test_simple_broadcasting<ColMajor>());
+ CALL_SUBTEST(test_simple_broadcasting<RowMajor>());
+ CALL_SUBTEST(test_vectorized_broadcasting<ColMajor>());
+ CALL_SUBTEST(test_vectorized_broadcasting<RowMajor>());
+ CALL_SUBTEST(test_static_broadcasting<ColMajor>());
+ CALL_SUBTEST(test_static_broadcasting<RowMajor>());
+ CALL_SUBTEST(test_fixed_size_broadcasting<ColMajor>());
+ CALL_SUBTEST(test_fixed_size_broadcasting<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_casts.cpp b/unsupported/test/cxx11_tensor_casts.cpp
new file mode 100644
index 000000000..4f7ff7067
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_casts.cpp
@@ -0,0 +1,41 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::array;
+
+static void test_simple_cast()
+{
+ Tensor<float, 2> ftensor(20,30);
+ ftensor.setRandom();
+ Tensor<char, 2> chartensor(20,30);
+ chartensor.setRandom();
+ Tensor<std::complex<float>, 2> cplextensor(20,30);
+ cplextensor.setRandom();
+
+ chartensor = ftensor.cast<char>();
+ cplextensor = ftensor.cast<std::complex<float>>();
+
+ for (int i = 0; i < 20; ++i) {
+ for (int j = 0; j < 30; ++j) {
+ VERIFY_IS_EQUAL(chartensor(i,j), static_cast<char>(ftensor(i,j)));
+ VERIFY_IS_EQUAL(cplextensor(i,j), static_cast<std::complex<float>>(ftensor(i,j)));
+ }
+ }
+}
+
+
+void test_cxx11_tensor_casts()
+{
+ CALL_SUBTEST(test_simple_cast());
+}
diff --git a/unsupported/test/cxx11_tensor_chipping.cpp b/unsupported/test/cxx11_tensor_chipping.cpp
new file mode 100644
index 000000000..d83417872
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_chipping.cpp
@@ -0,0 +1,397 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+template<int DataLayout>
+static void test_simple_chip()
+{
+ Tensor<float, 5, DataLayout> tensor(2,3,5,7,11);
+ tensor.setRandom();
+
+ Tensor<float, 4, DataLayout> chip1;
+ chip1 = tensor.template chip<0>(1);
+
+ VERIFY_IS_EQUAL(chip1.dimension(0), 3);
+ VERIFY_IS_EQUAL(chip1.dimension(1), 5);
+ VERIFY_IS_EQUAL(chip1.dimension(2), 7);
+ VERIFY_IS_EQUAL(chip1.dimension(3), 11);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ VERIFY_IS_EQUAL(chip1(i,j,k,l), tensor(1,i,j,k,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip2 = tensor.template chip<1>(1);
+ VERIFY_IS_EQUAL(chip2.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip2.dimension(1), 5);
+ VERIFY_IS_EQUAL(chip2.dimension(2), 7);
+ VERIFY_IS_EQUAL(chip2.dimension(3), 11);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ VERIFY_IS_EQUAL(chip2(i,j,k,l), tensor(i,1,j,k,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip3 = tensor.template chip<2>(2);
+ VERIFY_IS_EQUAL(chip3.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip3.dimension(1), 3);
+ VERIFY_IS_EQUAL(chip3.dimension(2), 7);
+ VERIFY_IS_EQUAL(chip3.dimension(3), 11);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ VERIFY_IS_EQUAL(chip3(i,j,k,l), tensor(i,j,2,k,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip4(tensor.template chip<3>(5));
+ VERIFY_IS_EQUAL(chip4.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip4.dimension(1), 3);
+ VERIFY_IS_EQUAL(chip4.dimension(2), 5);
+ VERIFY_IS_EQUAL(chip4.dimension(3), 11);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(chip4(i,j,k,l), tensor(i,j,k,5,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip5(tensor.template chip<4>(7));
+ VERIFY_IS_EQUAL(chip5.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip5.dimension(1), 3);
+ VERIFY_IS_EQUAL(chip5.dimension(2), 5);
+ VERIFY_IS_EQUAL(chip5.dimension(3), 7);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(chip5(i,j,k,l), tensor(i,j,k,l,7));
+ }
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_dynamic_chip()
+{
+ Tensor<float, 5, DataLayout> tensor(2,3,5,7,11);
+ tensor.setRandom();
+
+ Tensor<float, 4, DataLayout> chip1;
+ chip1 = tensor.chip(1, 0);
+ VERIFY_IS_EQUAL(chip1.dimension(0), 3);
+ VERIFY_IS_EQUAL(chip1.dimension(1), 5);
+ VERIFY_IS_EQUAL(chip1.dimension(2), 7);
+ VERIFY_IS_EQUAL(chip1.dimension(3), 11);
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ VERIFY_IS_EQUAL(chip1(i,j,k,l), tensor(1,i,j,k,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip2 = tensor.chip(1, 1);
+ VERIFY_IS_EQUAL(chip2.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip2.dimension(1), 5);
+ VERIFY_IS_EQUAL(chip2.dimension(2), 7);
+ VERIFY_IS_EQUAL(chip2.dimension(3), 11);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ VERIFY_IS_EQUAL(chip2(i,j,k,l), tensor(i,1,j,k,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip3 = tensor.chip(2, 2);
+ VERIFY_IS_EQUAL(chip3.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip3.dimension(1), 3);
+ VERIFY_IS_EQUAL(chip3.dimension(2), 7);
+ VERIFY_IS_EQUAL(chip3.dimension(3), 11);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ VERIFY_IS_EQUAL(chip3(i,j,k,l), tensor(i,j,2,k,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip4(tensor.chip(5, 3));
+ VERIFY_IS_EQUAL(chip4.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip4.dimension(1), 3);
+ VERIFY_IS_EQUAL(chip4.dimension(2), 5);
+ VERIFY_IS_EQUAL(chip4.dimension(3), 11);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(chip4(i,j,k,l), tensor(i,j,k,5,l));
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> chip5(tensor.chip(7, 4));
+ VERIFY_IS_EQUAL(chip5.dimension(0), 2);
+ VERIFY_IS_EQUAL(chip5.dimension(1), 3);
+ VERIFY_IS_EQUAL(chip5.dimension(2), 5);
+ VERIFY_IS_EQUAL(chip5.dimension(3), 7);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(chip5(i,j,k,l), tensor(i,j,k,l,7));
+ }
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_chip_in_expr() {
+ Tensor<float, 5, DataLayout> input1(2,3,5,7,11);
+ input1.setRandom();
+ Tensor<float, 4, DataLayout> input2(3,5,7,11);
+ input2.setRandom();
+
+ Tensor<float, 4, DataLayout> result = input1.template chip<0>(0) + input2;
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 11; ++l) {
+ float expected = input1(0,i,j,k,l) + input2(i,j,k,l);
+ VERIFY_IS_EQUAL(result(i,j,k,l), expected);
+ }
+ }
+ }
+ }
+
+ Tensor<float, 3, DataLayout> input3(3,7,11);
+ input3.setRandom();
+ Tensor<float, 3, DataLayout> result2 = input1.template chip<0>(0).template chip<1>(2) + input3;
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ for (int k = 0; k < 11; ++k) {
+ float expected = input1(0,i,2,j,k) + input3(i,j,k);
+ VERIFY_IS_EQUAL(result2(i,j,k), expected);
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_chip_as_lvalue()
+{
+ Tensor<float, 5, DataLayout> input1(2,3,5,7,11);
+ input1.setRandom();
+
+ Tensor<float, 4, DataLayout> input2(3,5,7,11);
+ input2.setRandom();
+ Tensor<float, 5, DataLayout> tensor = input1;
+ tensor.template chip<0>(1) = input2;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ for (int m = 0; m < 11; ++m) {
+ if (i != 1) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input1(i,j,k,l,m));
+ } else {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input2(j,k,l,m));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> input3(2,5,7,11);
+ input3.setRandom();
+ tensor = input1;
+ tensor.template chip<1>(1) = input3;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ for (int m = 0; m < 11; ++m) {
+ if (j != 1) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input1(i,j,k,l,m));
+ } else {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input3(i,k,l,m));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> input4(2,3,7,11);
+ input4.setRandom();
+ tensor = input1;
+ tensor.template chip<2>(3) = input4;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ for (int m = 0; m < 11; ++m) {
+ if (k != 3) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input1(i,j,k,l,m));
+ } else {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input4(i,j,l,m));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> input5(2,3,5,11);
+ input5.setRandom();
+ tensor = input1;
+ tensor.template chip<3>(4) = input5;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ for (int m = 0; m < 11; ++m) {
+ if (l != 4) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input1(i,j,k,l,m));
+ } else {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input5(i,j,k,m));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4, DataLayout> input6(2,3,5,7);
+ input6.setRandom();
+ tensor = input1;
+ tensor.template chip<4>(5) = input6;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ for (int m = 0; m < 11; ++m) {
+ if (m != 5) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input1(i,j,k,l,m));
+ } else {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input6(i,j,k,l));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 5, DataLayout> input7(2,3,5,7,11);
+ input7.setRandom();
+ tensor = input1;
+ tensor.chip(0, 0) = input7.chip(0, 0);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ for (int m = 0; m < 11; ++m) {
+ if (i != 0) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input1(i,j,k,l,m));
+ } else {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l,m), input7(i,j,k,l,m));
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+template<int DataLayout>
+static void test_chip_raw_data()
+{
+ Tensor<float, 5, DataLayout> tensor(2,3,5,7,11);
+ tensor.setRandom();
+
+ typedef TensorEvaluator<decltype(tensor.template chip<4>(3)), DefaultDevice> Evaluator4;
+ auto chip = Evaluator4(tensor.template chip<4>(3), DefaultDevice());
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ int chip_index;
+ if (DataLayout == ColMajor) {
+ chip_index = i + 2 * (j + 3 * (k + 5 * l));
+ } else {
+ chip_index = 11 * (l + 7 * (k + 5 * (j + 3 * i)));
+ }
+ VERIFY_IS_EQUAL(chip.data()[chip_index], tensor(i,j,k,l,3));
+ }
+ }
+ }
+ }
+
+ typedef TensorEvaluator<decltype(tensor.template chip<0>(0)), DefaultDevice> Evaluator0;
+ auto chip0 = Evaluator0(tensor.template chip<0>(0), DefaultDevice());
+ VERIFY_IS_EQUAL(chip0.data(), static_cast<float*>(0));
+
+ typedef TensorEvaluator<decltype(tensor.template chip<1>(0)), DefaultDevice> Evaluator1;
+ auto chip1 = Evaluator1(tensor.template chip<1>(0), DefaultDevice());
+ VERIFY_IS_EQUAL(chip1.data(), static_cast<float*>(0));
+
+ typedef TensorEvaluator<decltype(tensor.template chip<2>(0)), DefaultDevice> Evaluator2;
+ auto chip2 = Evaluator2(tensor.template chip<2>(0), DefaultDevice());
+ VERIFY_IS_EQUAL(chip2.data(), static_cast<float*>(0));
+
+ typedef TensorEvaluator<decltype(tensor.template chip<3>(0)), DefaultDevice> Evaluator3;
+ auto chip3 = Evaluator3(tensor.template chip<3>(0), DefaultDevice());
+ VERIFY_IS_EQUAL(chip3.data(), static_cast<float*>(0));
+}
+
+void test_cxx11_tensor_chipping()
+{
+ CALL_SUBTEST(test_simple_chip<ColMajor>());
+ CALL_SUBTEST(test_simple_chip<RowMajor>());
+ CALL_SUBTEST(test_dynamic_chip<ColMajor>());
+ CALL_SUBTEST(test_dynamic_chip<RowMajor>());
+ CALL_SUBTEST(test_chip_in_expr<ColMajor>());
+ CALL_SUBTEST(test_chip_in_expr<RowMajor>());
+ CALL_SUBTEST(test_chip_as_lvalue<ColMajor>());
+ CALL_SUBTEST(test_chip_as_lvalue<RowMajor>());
+ CALL_SUBTEST(test_chip_raw_data<ColMajor>());
+ CALL_SUBTEST(test_chip_raw_data<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_comparisons.cpp b/unsupported/test/cxx11_tensor_comparisons.cpp
new file mode 100644
index 000000000..186f56ac3
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_comparisons.cpp
@@ -0,0 +1,84 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+static void test_orderings()
+{
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+ Tensor<bool, 3> lt(2,3,7);
+ Tensor<bool, 3> le(2,3,7);
+ Tensor<bool, 3> gt(2,3,7);
+ Tensor<bool, 3> ge(2,3,7);
+
+ mat1.setRandom();
+ mat2.setRandom();
+
+ lt = mat1 < mat2;
+ le = mat1 <= mat2;
+ gt = mat1 > mat2;
+ ge = mat1 >= mat2;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(lt(i,j,k), mat1(i,j,k) < mat2(i,j,k));
+ VERIFY_IS_EQUAL(le(i,j,k), mat1(i,j,k) <= mat2(i,j,k));
+ VERIFY_IS_EQUAL(gt(i,j,k), mat1(i,j,k) > mat2(i,j,k));
+ VERIFY_IS_EQUAL(ge(i,j,k), mat1(i,j,k) >= mat2(i,j,k));
+ }
+ }
+ }
+}
+
+
+static void test_equality()
+{
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+
+ mat1.setRandom();
+ mat2.setRandom();
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ if (random() < 0.5) {
+ mat2(i,j,k) = mat1(i,j,k);
+ }
+ }
+ }
+ }
+
+ Tensor<bool, 3> eq(2,3,7);
+ Tensor<bool, 3> ne(2,3,7);
+ eq = (mat1 == mat2);
+ ne = (mat1 != mat2);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(eq(i,j,k), mat1(i,j,k) == mat2(i,j,k));
+ VERIFY_IS_EQUAL(ne(i,j,k), mat1(i,j,k) != mat2(i,j,k));
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_comparisons()
+{
+ CALL_SUBTEST(test_orderings());
+ CALL_SUBTEST(test_equality());
+}
diff --git a/unsupported/test/cxx11_tensor_concatenation.cpp b/unsupported/test/cxx11_tensor_concatenation.cpp
new file mode 100644
index 000000000..9fdf33c16
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_concatenation.cpp
@@ -0,0 +1,116 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+template<int DataLayout>
+static void test_dimension_failures()
+{
+ Tensor<int, 3, DataLayout> left(2, 3, 1);
+ Tensor<int, 3, DataLayout> right(3, 3, 1);
+ left.setRandom();
+ right.setRandom();
+
+ // Okay; other dimensions are equal.
+ Tensor<int, 3, DataLayout> concatenation = left.concatenate(right, 0);
+
+ // Dimension mismatches.
+ VERIFY_RAISES_ASSERT(concatenation = left.concatenate(right, 1));
+ VERIFY_RAISES_ASSERT(concatenation = left.concatenate(right, 2));
+
+ // Axis > NumDims or < 0.
+ VERIFY_RAISES_ASSERT(concatenation = left.concatenate(right, 3));
+ VERIFY_RAISES_ASSERT(concatenation = left.concatenate(right, -1));
+}
+
+template<int DataLayout>
+static void test_static_dimension_failure()
+{
+ Tensor<int, 2, DataLayout> left(2, 3);
+ Tensor<int, 3, DataLayout> right(2, 3, 1);
+
+#ifdef CXX11_TENSOR_CONCATENATION_STATIC_DIMENSION_FAILURE
+ // Technically compatible, but we static assert that the inputs have same
+ // NumDims.
+ Tensor<int, 3, DataLayout> concatenation = left.concatenate(right, 0);
+#endif
+
+ // This can be worked around in this case.
+ Tensor<int, 3, DataLayout> concatenation = left
+ .reshape(Tensor<int, 3>::Dimensions{{2, 3, 1}})
+ .concatenate(right, 0);
+ Tensor<int, 2, DataLayout> alternative = left
+ .concatenate(right.reshape(Tensor<int, 2>::Dimensions{{2, 3}}), 0);
+}
+
+template<int DataLayout>
+static void test_simple_concatenation()
+{
+ Tensor<int, 3, DataLayout> left(2, 3, 1);
+ Tensor<int, 3, DataLayout> right(2, 3, 1);
+ left.setRandom();
+ right.setRandom();
+
+ Tensor<int, 3, DataLayout> concatenation = left.concatenate(right, 0);
+ VERIFY_IS_EQUAL(concatenation.dimension(0), 4);
+ VERIFY_IS_EQUAL(concatenation.dimension(1), 3);
+ VERIFY_IS_EQUAL(concatenation.dimension(2), 1);
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < 2; ++i) {
+ VERIFY_IS_EQUAL(concatenation(i, j, 0), left(i, j, 0));
+ }
+ for (int i = 2; i < 4; ++i) {
+ VERIFY_IS_EQUAL(concatenation(i, j, 0), right(i - 2, j, 0));
+ }
+ }
+
+ concatenation = left.concatenate(right, 1);
+ VERIFY_IS_EQUAL(concatenation.dimension(0), 2);
+ VERIFY_IS_EQUAL(concatenation.dimension(1), 6);
+ VERIFY_IS_EQUAL(concatenation.dimension(2), 1);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_EQUAL(concatenation(i, j, 0), left(i, j, 0));
+ }
+ for (int j = 3; j < 6; ++j) {
+ VERIFY_IS_EQUAL(concatenation(i, j, 0), right(i, j - 3, 0));
+ }
+ }
+
+ concatenation = left.concatenate(right, 2);
+ VERIFY_IS_EQUAL(concatenation.dimension(0), 2);
+ VERIFY_IS_EQUAL(concatenation.dimension(1), 3);
+ VERIFY_IS_EQUAL(concatenation.dimension(2), 2);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_EQUAL(concatenation(i, j, 0), left(i, j, 0));
+ VERIFY_IS_EQUAL(concatenation(i, j, 1), right(i, j, 0));
+ }
+ }
+}
+
+
+// TODO(phli): Add test once we have a real vectorized implementation.
+// static void test_vectorized_concatenation() {}
+
+
+void test_cxx11_tensor_concatenation()
+{
+ CALL_SUBTEST(test_dimension_failures<ColMajor>());
+ CALL_SUBTEST(test_dimension_failures<RowMajor>());
+ CALL_SUBTEST(test_static_dimension_failure<ColMajor>());
+ CALL_SUBTEST(test_static_dimension_failure<RowMajor>());
+ CALL_SUBTEST(test_simple_concatenation<ColMajor>());
+ CALL_SUBTEST(test_simple_concatenation<RowMajor>());
+ // CALL_SUBTEST(test_vectorized_concatenation());
+}
diff --git a/unsupported/test/cxx11_tensor_const.cpp b/unsupported/test/cxx11_tensor_const.cpp
new file mode 100644
index 000000000..0ffb02afd
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_const.cpp
@@ -0,0 +1,39 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+using Eigen::Tensor;
+
+
+
+
+static void test_simple_assign()
+{
+ Tensor<int, 3> random(2,3,7);
+ random.setRandom();
+
+ TensorMap<Tensor<const int, 3> > constant(random.data(), 2, 3, 7);
+ Tensor<int, 3> result(2,3,7);
+ result = constant;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL((result(i,j,k)), random(i,j,k));
+ }
+ }
+ }
+}
+
+void test_cxx11_tensor_const()
+{
+ CALL_SUBTEST(test_simple_assign());
+}
diff --git a/unsupported/test/cxx11_tensor_contract_cuda.cpp b/unsupported/test/cxx11_tensor_contract_cuda.cpp
new file mode 100644
index 000000000..9599607c6
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_contract_cuda.cpp
@@ -0,0 +1,121 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+// Copyright (C) 2014 Navdeep Jaitly <ndjaitly@google.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#define EIGEN_TEST_NO_LONGDOUBLE
+#define EIGEN_TEST_NO_COMPLEX
+#define EIGEN_TEST_FUNC cxx11_tensor_cuda
+#define EIGEN_DEFAULT_DENSE_INDEX_TYPE int
+#define EIGEN_USE_GPU
+
+
+#include "main.h"
+#include <unsupported/Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+typedef Tensor<float, 1>::DimensionPair DimPair;
+
+template<int DataLayout>
+static void test_cuda_contraction(int m_size, int k_size, int n_size)
+{
+ cout<<"Calling with ("<<m_size<<","<<k_size<<","<<n_size<<")"<<std::endl;
+ // with these dimensions, the output has 300 * 140 elements, which is
+ // more than 30 * 1024, which is the number of threads in blocks on
+ // a 15 SM GK110 GPU
+ Tensor<float, 2, DataLayout> t_left(Eigen::array<int, 2>(m_size, k_size));
+ Tensor<float, 2, DataLayout> t_right(Eigen::array<int, 2>(k_size, n_size));
+ Tensor<float, 2, DataLayout> t_result(Eigen::array<int, 2>(m_size, n_size));
+ Tensor<float, 2, DataLayout> t_result_gpu(Eigen::array<int, 2>(m_size, n_size));
+ Eigen::array<DimPair, 1> dims(DimPair(1, 0));
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ std::size_t t_left_bytes = t_left.size() * sizeof(float);
+ std::size_t t_right_bytes = t_right.size() * sizeof(float);
+ std::size_t t_result_bytes = t_result.size() * sizeof(float);
+
+ float* d_t_left;
+ float* d_t_right;
+ float* d_t_result;
+
+ cudaMalloc((void**)(&d_t_left), t_left_bytes);
+ cudaMalloc((void**)(&d_t_right), t_right_bytes);
+ cudaMalloc((void**)(&d_t_result), t_result_bytes);
+
+ cudaMemcpy(d_t_left, t_left.data(), t_left_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_t_right, t_right.data(), t_right_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 2, DataLayout> >
+ gpu_t_left(d_t_left, Eigen::array<int, 2>(m_size, k_size));
+ Eigen::TensorMap<Eigen::Tensor<float, 2, DataLayout> >
+ gpu_t_right(d_t_right, Eigen::array<int, 2>(k_size, n_size));
+ Eigen::TensorMap<Eigen::Tensor<float, 2, DataLayout> >
+ gpu_t_result(d_t_result, Eigen::array<int, 2>(m_size, n_size));
+
+
+ gpu_t_result.device(gpu_device) = gpu_t_left.contract(gpu_t_right, dims);
+ t_result = t_left.contract(t_right, dims);
+
+ cudaMemcpy(t_result_gpu.data(), d_t_result, t_result_bytes, cudaMemcpyDeviceToHost);
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ if (fabs(t_result.data()[i] - t_result_gpu.data()[i]) >= 1e-4) {
+ cout << "mismatch detected at index " << i << ": " << t_result.data()[i]
+ << " vs " << t_result_gpu.data()[i] << endl;
+ assert(false);
+ }
+ }
+
+ cudaFree((void*)d_t_left);
+ cudaFree((void*)d_t_right);
+ cudaFree((void*)d_t_result);
+}
+
+
+void test_cxx11_tensor_cuda()
+{
+ cout<<"Calling contraction tests"<<std::endl;
+ CALL_SUBTEST(test_cuda_contraction<ColMajor>(128, 128, 128));
+ CALL_SUBTEST(test_cuda_contraction<RowMajor>(128, 128, 128));
+ for (int k = 32; k < 256; k++) {
+ CALL_SUBTEST(test_cuda_contraction<ColMajor>(128, k, 128));
+ CALL_SUBTEST(test_cuda_contraction<RowMajor>(128, k, 128));
+ }
+ for (int k = 32; k < 256; k++) {
+ CALL_SUBTEST(test_cuda_contraction<ColMajor>(128, 128, k));
+ CALL_SUBTEST(test_cuda_contraction<RowMajor>(128, 128, k));
+ }
+ for (int k = 32; k < 256; k++) {
+ CALL_SUBTEST(test_cuda_contraction<ColMajor>(k, 128, 128));
+ CALL_SUBTEST(test_cuda_contraction<RowMajor>(k, 128, 128));
+ }
+
+ int m_sizes[] = {31, 39, 63, 64, 65,
+ 127, 129, 255, 257, 511,
+ 512, 513, 1023, 1024, 1025 };
+ int n_sizes[] = {31, 39, 63, 64, 65,
+ 127, 129, 255, 257, 511,
+ 512, 513, 1023, 1024, 1025 };
+
+ int k_sizes[] = { 31, 39, 63, 64, 65,
+ 95, 96, 127, 129, 255,
+ 257, 511, 512, 513, 1023,
+ 1024, 1025};
+
+ for (int i = 0; i <15; i++)
+ for (int j = 0; j < 15; j++)
+ for (int k = 0; k < 17; k++) {
+ CALL_SUBTEST(test_cuda_contraction<ColMajor>(m_sizes[i], n_sizes[j], k_sizes[k]));
+ CALL_SUBTEST(test_cuda_contraction<RowMajor>(m_sizes[i], n_sizes[j], k_sizes[k]));
+ }
+}
diff --git a/unsupported/test/cxx11_tensor_contraction.cpp b/unsupported/test/cxx11_tensor_contraction.cpp
new file mode 100644
index 000000000..2bcae90b8
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_contraction.cpp
@@ -0,0 +1,480 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::DefaultDevice;
+using Eigen::Tensor;
+
+typedef Tensor<float, 1>::DimensionPair DimPair;
+
+template<int DataLayout>
+static void test_evals()
+{
+ Tensor<float, 2, DataLayout> mat1(2, 3);
+ Tensor<float, 2, DataLayout> mat2(2, 3);
+ Tensor<float, 2, DataLayout> mat3(3, 2);
+
+ mat1.setRandom();
+ mat2.setRandom();
+ mat3.setRandom();
+
+ Tensor<float, 2, DataLayout> mat4(3,3);
+ mat4.setZero();
+ Eigen::array<DimPair, 1> dims3({{DimPair(0, 0)}});
+ typedef TensorEvaluator<decltype(mat1.contract(mat2, dims3)), DefaultDevice> Evaluator;
+ Evaluator eval(mat1.contract(mat2, dims3), DefaultDevice());
+ eval.evalTo(mat4.data());
+ EIGEN_STATIC_ASSERT(Evaluator::NumDims==2ul, YOU_MADE_A_PROGRAMMING_MISTAKE);
+ VERIFY_IS_EQUAL(eval.dimensions()[0], 3);
+ VERIFY_IS_EQUAL(eval.dimensions()[1], 3);
+
+ VERIFY_IS_APPROX(mat4(0,0), mat1(0,0)*mat2(0,0) + mat1(1,0)*mat2(1,0));
+ VERIFY_IS_APPROX(mat4(0,1), mat1(0,0)*mat2(0,1) + mat1(1,0)*mat2(1,1));
+ VERIFY_IS_APPROX(mat4(0,2), mat1(0,0)*mat2(0,2) + mat1(1,0)*mat2(1,2));
+ VERIFY_IS_APPROX(mat4(1,0), mat1(0,1)*mat2(0,0) + mat1(1,1)*mat2(1,0));
+ VERIFY_IS_APPROX(mat4(1,1), mat1(0,1)*mat2(0,1) + mat1(1,1)*mat2(1,1));
+ VERIFY_IS_APPROX(mat4(1,2), mat1(0,1)*mat2(0,2) + mat1(1,1)*mat2(1,2));
+ VERIFY_IS_APPROX(mat4(2,0), mat1(0,2)*mat2(0,0) + mat1(1,2)*mat2(1,0));
+ VERIFY_IS_APPROX(mat4(2,1), mat1(0,2)*mat2(0,1) + mat1(1,2)*mat2(1,1));
+ VERIFY_IS_APPROX(mat4(2,2), mat1(0,2)*mat2(0,2) + mat1(1,2)*mat2(1,2));
+
+ Tensor<float, 2, DataLayout> mat5(2,2);
+ mat5.setZero();
+ Eigen::array<DimPair, 1> dims4({{DimPair(1, 1)}});
+ typedef TensorEvaluator<decltype(mat1.contract(mat2, dims4)), DefaultDevice> Evaluator2;
+ Evaluator2 eval2(mat1.contract(mat2, dims4), DefaultDevice());
+ eval2.evalTo(mat5.data());
+ EIGEN_STATIC_ASSERT(Evaluator2::NumDims==2ul, YOU_MADE_A_PROGRAMMING_MISTAKE);
+ VERIFY_IS_EQUAL(eval2.dimensions()[0], 2);
+ VERIFY_IS_EQUAL(eval2.dimensions()[1], 2);
+
+ VERIFY_IS_APPROX(mat5(0,0), mat1(0,0)*mat2(0,0) + mat1(0,1)*mat2(0,1) + mat1(0,2)*mat2(0,2));
+ VERIFY_IS_APPROX(mat5(0,1), mat1(0,0)*mat2(1,0) + mat1(0,1)*mat2(1,1) + mat1(0,2)*mat2(1,2));
+ VERIFY_IS_APPROX(mat5(1,0), mat1(1,0)*mat2(0,0) + mat1(1,1)*mat2(0,1) + mat1(1,2)*mat2(0,2));
+ VERIFY_IS_APPROX(mat5(1,1), mat1(1,0)*mat2(1,0) + mat1(1,1)*mat2(1,1) + mat1(1,2)*mat2(1,2));
+
+ Tensor<float, 2, DataLayout> mat6(2,2);
+ mat6.setZero();
+ Eigen::array<DimPair, 1> dims6({{DimPair(1, 0)}});
+ typedef TensorEvaluator<decltype(mat1.contract(mat3, dims6)), DefaultDevice> Evaluator3;
+ Evaluator3 eval3(mat1.contract(mat3, dims6), DefaultDevice());
+ eval3.evalTo(mat6.data());
+ EIGEN_STATIC_ASSERT(Evaluator3::NumDims==2ul, YOU_MADE_A_PROGRAMMING_MISTAKE);
+ VERIFY_IS_EQUAL(eval3.dimensions()[0], 2);
+ VERIFY_IS_EQUAL(eval3.dimensions()[1], 2);
+
+ VERIFY_IS_APPROX(mat6(0,0), mat1(0,0)*mat3(0,0) + mat1(0,1)*mat3(1,0) + mat1(0,2)*mat3(2,0));
+ VERIFY_IS_APPROX(mat6(0,1), mat1(0,0)*mat3(0,1) + mat1(0,1)*mat3(1,1) + mat1(0,2)*mat3(2,1));
+ VERIFY_IS_APPROX(mat6(1,0), mat1(1,0)*mat3(0,0) + mat1(1,1)*mat3(1,0) + mat1(1,2)*mat3(2,0));
+ VERIFY_IS_APPROX(mat6(1,1), mat1(1,0)*mat3(0,1) + mat1(1,1)*mat3(1,1) + mat1(1,2)*mat3(2,1));
+}
+
+template<int DataLayout>
+static void test_scalar()
+{
+ Tensor<float, 1, DataLayout> vec1({6});
+ Tensor<float, 1, DataLayout> vec2({6});
+
+ vec1.setRandom();
+ vec2.setRandom();
+
+ Tensor<float, 1, DataLayout> scalar(1);
+ scalar.setZero();
+ Eigen::array<DimPair, 1> dims({{DimPair(0, 0)}});
+ typedef TensorEvaluator<decltype(vec1.contract(vec2, dims)), DefaultDevice> Evaluator;
+ Evaluator eval(vec1.contract(vec2, dims), DefaultDevice());
+ eval.evalTo(scalar.data());
+ EIGEN_STATIC_ASSERT(Evaluator::NumDims==1ul, YOU_MADE_A_PROGRAMMING_MISTAKE);
+
+ float expected = 0.0f;
+ for (int i = 0; i < 6; ++i) {
+ expected += vec1(i) * vec2(i);
+ }
+ VERIFY_IS_APPROX(scalar(0), expected);
+}
+
+template<int DataLayout>
+static void test_multidims()
+{
+ Tensor<float, 3, DataLayout> mat1(2, 2, 2);
+ Tensor<float, 4, DataLayout> mat2(2, 2, 2, 2);
+
+ mat1.setRandom();
+ mat2.setRandom();
+
+ Tensor<float, 3, DataLayout> mat3(2, 2, 2);
+ mat3.setZero();
+ Eigen::array<DimPair, 2> dims({{DimPair(1, 2), DimPair(2, 3)}});
+ typedef TensorEvaluator<decltype(mat1.contract(mat2, dims)), DefaultDevice> Evaluator;
+ Evaluator eval(mat1.contract(mat2, dims), DefaultDevice());
+ eval.evalTo(mat3.data());
+ EIGEN_STATIC_ASSERT(Evaluator::NumDims==3ul, YOU_MADE_A_PROGRAMMING_MISTAKE);
+ VERIFY_IS_EQUAL(eval.dimensions()[0], 2);
+ VERIFY_IS_EQUAL(eval.dimensions()[1], 2);
+ VERIFY_IS_EQUAL(eval.dimensions()[2], 2);
+
+ VERIFY_IS_APPROX(mat3(0,0,0), mat1(0,0,0)*mat2(0,0,0,0) + mat1(0,1,0)*mat2(0,0,1,0) +
+ mat1(0,0,1)*mat2(0,0,0,1) + mat1(0,1,1)*mat2(0,0,1,1));
+ VERIFY_IS_APPROX(mat3(0,0,1), mat1(0,0,0)*mat2(0,1,0,0) + mat1(0,1,0)*mat2(0,1,1,0) +
+ mat1(0,0,1)*mat2(0,1,0,1) + mat1(0,1,1)*mat2(0,1,1,1));
+ VERIFY_IS_APPROX(mat3(0,1,0), mat1(0,0,0)*mat2(1,0,0,0) + mat1(0,1,0)*mat2(1,0,1,0) +
+ mat1(0,0,1)*mat2(1,0,0,1) + mat1(0,1,1)*mat2(1,0,1,1));
+ VERIFY_IS_APPROX(mat3(0,1,1), mat1(0,0,0)*mat2(1,1,0,0) + mat1(0,1,0)*mat2(1,1,1,0) +
+ mat1(0,0,1)*mat2(1,1,0,1) + mat1(0,1,1)*mat2(1,1,1,1));
+ VERIFY_IS_APPROX(mat3(1,0,0), mat1(1,0,0)*mat2(0,0,0,0) + mat1(1,1,0)*mat2(0,0,1,0) +
+ mat1(1,0,1)*mat2(0,0,0,1) + mat1(1,1,1)*mat2(0,0,1,1));
+ VERIFY_IS_APPROX(mat3(1,0,1), mat1(1,0,0)*mat2(0,1,0,0) + mat1(1,1,0)*mat2(0,1,1,0) +
+ mat1(1,0,1)*mat2(0,1,0,1) + mat1(1,1,1)*mat2(0,1,1,1));
+ VERIFY_IS_APPROX(mat3(1,1,0), mat1(1,0,0)*mat2(1,0,0,0) + mat1(1,1,0)*mat2(1,0,1,0) +
+ mat1(1,0,1)*mat2(1,0,0,1) + mat1(1,1,1)*mat2(1,0,1,1));
+ VERIFY_IS_APPROX(mat3(1,1,1), mat1(1,0,0)*mat2(1,1,0,0) + mat1(1,1,0)*mat2(1,1,1,0) +
+ mat1(1,0,1)*mat2(1,1,0,1) + mat1(1,1,1)*mat2(1,1,1,1));
+}
+
+template<int DataLayout>
+static void test_holes() {
+ Tensor<float, 4, DataLayout> t1(2, 5, 7, 3);
+ Tensor<float, 5, DataLayout> t2(2, 7, 11, 13, 3);
+ t1.setRandom();
+ t2.setRandom();
+
+ Eigen::array<DimPair, 2> dims({{DimPair(0, 0), DimPair(3, 4)}});
+ Tensor<float, 5, DataLayout> result = t1.contract(t2, dims);
+ VERIFY_IS_EQUAL(result.dimension(0), 5);
+ VERIFY_IS_EQUAL(result.dimension(1), 7);
+ VERIFY_IS_EQUAL(result.dimension(2), 7);
+ VERIFY_IS_EQUAL(result.dimension(3), 11);
+ VERIFY_IS_EQUAL(result.dimension(4), 13);
+
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 5; ++l) {
+ for (int m = 0; m < 5; ++m) {
+ VERIFY_IS_APPROX(result(i, j, k, l, m),
+ t1(0, i, j, 0) * t2(0, k, l, m, 0) +
+ t1(1, i, j, 0) * t2(1, k, l, m, 0) +
+ t1(0, i, j, 1) * t2(0, k, l, m, 1) +
+ t1(1, i, j, 1) * t2(1, k, l, m, 1) +
+ t1(0, i, j, 2) * t2(0, k, l, m, 2) +
+ t1(1, i, j, 2) * t2(1, k, l, m, 2));
+ }
+ }
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_full_redux()
+{
+ Tensor<float, 2, DataLayout> t1(2, 2);
+ Tensor<float, 3, DataLayout> t2(2, 2, 2);
+ t1.setRandom();
+ t2.setRandom();
+
+ Eigen::array<DimPair, 2> dims({{DimPair(0, 0), DimPair(1, 1)}});
+ Tensor<float, 1, DataLayout> result = t1.contract(t2, dims);
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_APPROX(result(0), t1(0, 0) * t2(0, 0, 0) + t1(1, 0) * t2(1, 0, 0)
+ + t1(0, 1) * t2(0, 1, 0) + t1(1, 1) * t2(1, 1, 0));
+ VERIFY_IS_APPROX(result(1), t1(0, 0) * t2(0, 0, 1) + t1(1, 0) * t2(1, 0, 1)
+ + t1(0, 1) * t2(0, 1, 1) + t1(1, 1) * t2(1, 1, 1));
+
+ dims[0] = DimPair(1, 0);
+ dims[1] = DimPair(2, 1);
+ result = t2.contract(t1, dims);
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_APPROX(result(0), t1(0, 0) * t2(0, 0, 0) + t1(1, 0) * t2(0, 1, 0)
+ + t1(0, 1) * t2(0, 0, 1) + t1(1, 1) * t2(0, 1, 1));
+ VERIFY_IS_APPROX(result(1), t1(0, 0) * t2(1, 0, 0) + t1(1, 0) * t2(1, 1, 0)
+ + t1(0, 1) * t2(1, 0, 1) + t1(1, 1) * t2(1, 1, 1));
+}
+
+template<int DataLayout>
+static void test_contraction_of_contraction()
+{
+ Tensor<float, 2, DataLayout> t1(2, 2);
+ Tensor<float, 2, DataLayout> t2(2, 2);
+ Tensor<float, 2, DataLayout> t3(2, 2);
+ Tensor<float, 2, DataLayout> t4(2, 2);
+ t1.setRandom();
+ t2.setRandom();
+ t3.setRandom();
+ t4.setRandom();
+
+ Eigen::array<DimPair, 1> dims({{DimPair(1, 0)}});
+ auto contract1 = t1.contract(t2, dims);
+ auto diff = t3 - contract1;
+ auto contract2 = t1.contract(t4, dims);
+ Tensor<float, 2, DataLayout> result = contract2.contract(diff, dims);
+
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_EQUAL(result.dimension(1), 2);
+
+ Eigen::Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>>
+ m1(t1.data(), 2, 2), m2(t2.data(), 2, 2), m3(t3.data(), 2, 2),
+ m4(t4.data(), 2, 2);
+ Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>
+ expected = (m1 * m4) * (m3 - m1 * m2);
+
+ VERIFY_IS_APPROX(result(0, 0), expected(0, 0));
+ VERIFY_IS_APPROX(result(0, 1), expected(0, 1));
+ VERIFY_IS_APPROX(result(1, 0), expected(1, 0));
+ VERIFY_IS_APPROX(result(1, 1), expected(1, 1));
+}
+
+template<int DataLayout>
+static void test_expr()
+{
+ Tensor<float, 2, DataLayout> mat1(2, 3);
+ Tensor<float, 2, DataLayout> mat2(3, 2);
+ mat1.setRandom();
+ mat2.setRandom();
+
+ Tensor<float, 2, DataLayout> mat3(2,2);
+
+ Eigen::array<DimPair, 1> dims({{DimPair(1, 0)}});
+ mat3 = mat1.contract(mat2, dims);
+
+ VERIFY_IS_APPROX(mat3(0,0), mat1(0,0)*mat2(0,0) + mat1(0,1)*mat2(1,0) + mat1(0,2)*mat2(2,0));
+ VERIFY_IS_APPROX(mat3(0,1), mat1(0,0)*mat2(0,1) + mat1(0,1)*mat2(1,1) + mat1(0,2)*mat2(2,1));
+ VERIFY_IS_APPROX(mat3(1,0), mat1(1,0)*mat2(0,0) + mat1(1,1)*mat2(1,0) + mat1(1,2)*mat2(2,0));
+ VERIFY_IS_APPROX(mat3(1,1), mat1(1,0)*mat2(0,1) + mat1(1,1)*mat2(1,1) + mat1(1,2)*mat2(2,1));
+}
+
+template<int DataLayout>
+static void test_out_of_order_contraction()
+{
+ Tensor<float, 3, DataLayout> mat1(2, 2, 2);
+ Tensor<float, 3, DataLayout> mat2(2, 2, 2);
+
+ mat1.setRandom();
+ mat2.setRandom();
+
+ Tensor<float, 2, DataLayout> mat3(2, 2);
+
+ Eigen::array<DimPair, 2> dims({{DimPair(2, 0), DimPair(0, 2)}});
+ mat3 = mat1.contract(mat2, dims);
+
+ VERIFY_IS_APPROX(mat3(0, 0),
+ mat1(0,0,0)*mat2(0,0,0) + mat1(1,0,0)*mat2(0,0,1) +
+ mat1(0,0,1)*mat2(1,0,0) + mat1(1,0,1)*mat2(1,0,1));
+ VERIFY_IS_APPROX(mat3(1, 0),
+ mat1(0,1,0)*mat2(0,0,0) + mat1(1,1,0)*mat2(0,0,1) +
+ mat1(0,1,1)*mat2(1,0,0) + mat1(1,1,1)*mat2(1,0,1));
+ VERIFY_IS_APPROX(mat3(0, 1),
+ mat1(0,0,0)*mat2(0,1,0) + mat1(1,0,0)*mat2(0,1,1) +
+ mat1(0,0,1)*mat2(1,1,0) + mat1(1,0,1)*mat2(1,1,1));
+ VERIFY_IS_APPROX(mat3(1, 1),
+ mat1(0,1,0)*mat2(0,1,0) + mat1(1,1,0)*mat2(0,1,1) +
+ mat1(0,1,1)*mat2(1,1,0) + mat1(1,1,1)*mat2(1,1,1));
+
+ Eigen::array<DimPair, 2> dims2({{DimPair(0, 2), DimPair(2, 0)}});
+ mat3 = mat1.contract(mat2, dims2);
+
+ VERIFY_IS_APPROX(mat3(0, 0),
+ mat1(0,0,0)*mat2(0,0,0) + mat1(1,0,0)*mat2(0,0,1) +
+ mat1(0,0,1)*mat2(1,0,0) + mat1(1,0,1)*mat2(1,0,1));
+ VERIFY_IS_APPROX(mat3(1, 0),
+ mat1(0,1,0)*mat2(0,0,0) + mat1(1,1,0)*mat2(0,0,1) +
+ mat1(0,1,1)*mat2(1,0,0) + mat1(1,1,1)*mat2(1,0,1));
+ VERIFY_IS_APPROX(mat3(0, 1),
+ mat1(0,0,0)*mat2(0,1,0) + mat1(1,0,0)*mat2(0,1,1) +
+ mat1(0,0,1)*mat2(1,1,0) + mat1(1,0,1)*mat2(1,1,1));
+ VERIFY_IS_APPROX(mat3(1, 1),
+ mat1(0,1,0)*mat2(0,1,0) + mat1(1,1,0)*mat2(0,1,1) +
+ mat1(0,1,1)*mat2(1,1,0) + mat1(1,1,1)*mat2(1,1,1));
+
+}
+
+template<int DataLayout>
+static void test_consistency()
+{
+ // this does something like testing (A*B)^T = (B^T * A^T)
+
+ Tensor<float, 3, DataLayout> mat1(4, 3, 5);
+ Tensor<float, 5, DataLayout> mat2(3, 2, 1, 5, 4);
+ mat1.setRandom();
+ mat2.setRandom();
+
+ Tensor<float, 4, DataLayout> mat3(5, 2, 1, 5);
+ Tensor<float, 4, DataLayout> mat4(2, 1, 5, 5);
+
+ // contract on dimensions of size 4 and 3
+ Eigen::array<DimPair, 2> dims1({{DimPair(0, 4), DimPair(1, 0)}});
+ Eigen::array<DimPair, 2> dims2({{DimPair(4, 0), DimPair(0, 1)}});
+
+ mat3 = mat1.contract(mat2, dims1);
+ mat4 = mat2.contract(mat1, dims2);
+
+ // check that these are equal except for ordering of dimensions
+ if (DataLayout == ColMajor) {
+ for (size_t i = 0; i < 5; i++) {
+ for (size_t j = 0; j < 10; j++) {
+ VERIFY_IS_APPROX(mat3.data()[i + 5 * j], mat4.data()[j + 10 * i]);
+ }
+ }
+ } else {
+ // Row major
+ for (size_t i = 0; i < 5; i++) {
+ for (size_t j = 0; j < 10; j++) {
+ VERIFY_IS_APPROX(mat3.data()[10 * i + j], mat4.data()[i + 5 * j]);
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_large_contraction()
+{
+ Tensor<float, 4, DataLayout> t_left(30, 50, 8, 31);
+ Tensor<float, 5, DataLayout> t_right(8, 31, 7, 20, 10);
+ Tensor<float, 5, DataLayout> t_result(30, 50, 7, 20, 10);
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ // Add a little offset so that the results won't be close to zero.
+ t_left += t_left.constant(1.0f);
+ t_right += t_right.constant(1.0f);
+
+ typedef Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>> MapXf;
+ MapXf m_left(t_left.data(), 1500, 248);
+ MapXf m_right(t_right.data(), 248, 1400);
+ Eigen::Matrix<float, Dynamic, Dynamic, DataLayout> m_result(1500, 1400);
+
+ // this contraction should be equivalent to a single matrix multiplication
+ Eigen::array<DimPair, 2> dims({{DimPair(2, 0), DimPair(3, 1)}});
+
+ // compute results by separate methods
+ t_result = t_left.contract(t_right, dims);
+ m_result = m_left * m_right;
+
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ VERIFY(&t_result.data()[i] != &m_result.data()[i]);
+ VERIFY_IS_APPROX(t_result.data()[i], m_result.data()[i]);
+ }
+}
+
+template<int DataLayout>
+static void test_matrix_vector()
+{
+ Tensor<float, 2, DataLayout> t_left(30, 50);
+ Tensor<float, 1, DataLayout> t_right(50);
+ Tensor<float, 1, DataLayout> t_result(30);
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ typedef Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>> MapXf;
+ MapXf m_left(t_left.data(), 30, 50);
+ MapXf m_right(t_right.data(), 50, 1);
+ Eigen::Matrix<float, Dynamic, Dynamic, DataLayout> m_result(30, 1);
+
+ // this contraction should be equivalent to a single matrix multiplication
+ Eigen::array<DimPair, 1> dims{{DimPair(1, 0)}};
+
+ // compute results by separate methods
+ t_result = t_left.contract(t_right, dims);
+ m_result = m_left * m_right;
+
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ VERIFY(internal::isApprox(t_result(i), m_result(i, 0), 1));
+ }
+}
+
+
+template<int DataLayout>
+static void test_tensor_vector()
+{
+ Tensor<float, 3, DataLayout> t_left(7, 13, 17);
+ Tensor<float, 2, DataLayout> t_right(1, 7);
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ typedef typename Tensor<float, 1, DataLayout>::DimensionPair DimensionPair;
+ Eigen::array<DimensionPair, 1> dim_pair01{{{0, 1}}};
+ Tensor<float, 3, DataLayout> t_result = t_left.contract(t_right, dim_pair01);
+
+ typedef Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>> MapXf;
+ MapXf m_left(t_left.data(), 7, 13*17);
+ MapXf m_right(t_right.data(), 1, 7);
+ Eigen::Matrix<float, Dynamic, Dynamic, DataLayout> m_result = m_left.transpose() * m_right.transpose();
+
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ VERIFY(internal::isApprox(t_result(i), m_result(i, 0), 1));
+ }
+}
+
+
+template<int DataLayout>
+static void test_small_blocking_factors()
+{
+ Tensor<float, 4, DataLayout> t_left(30, 5, 3, 31);
+ Tensor<float, 5, DataLayout> t_right(3, 31, 7, 20, 1);
+ t_left.setRandom();
+ t_right.setRandom();
+
+ // Add a little offset so that the results won't be close to zero.
+ t_left += t_left.constant(1.0f);
+ t_right += t_right.constant(1.0f);
+
+ // Force the cache sizes, which results in smaller blocking factors.
+ Eigen::setCpuCacheSizes(896, 1920, 2944);
+
+ // this contraction should be equivalent to a single matrix multiplication
+ Eigen::array<DimPair, 2> dims({{DimPair(2, 0), DimPair(3, 1)}});
+ Tensor<float, 5, DataLayout> t_result;
+ t_result = t_left.contract(t_right, dims);
+
+ // compute result using a simple eigen matrix product
+ Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>> m_left(t_left.data(), 150, 93);
+ Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout>> m_right(t_right.data(), 93, 140);
+ Eigen::Matrix<float, Dynamic, Dynamic, DataLayout> m_result = m_left * m_right;
+
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ VERIFY_IS_APPROX(t_result.data()[i], m_result.data()[i]);
+ }
+}
+
+
+void test_cxx11_tensor_contraction()
+{
+ CALL_SUBTEST(test_evals<ColMajor>());
+ CALL_SUBTEST(test_evals<RowMajor>());
+ CALL_SUBTEST(test_scalar<ColMajor>());
+ CALL_SUBTEST(test_scalar<RowMajor>());
+ CALL_SUBTEST(test_multidims<ColMajor>());
+ CALL_SUBTEST(test_multidims<RowMajor>());
+ CALL_SUBTEST(test_holes<ColMajor>());
+ CALL_SUBTEST(test_holes<RowMajor>());
+ CALL_SUBTEST(test_full_redux<ColMajor>());
+ CALL_SUBTEST(test_full_redux<RowMajor>());
+ CALL_SUBTEST(test_contraction_of_contraction<ColMajor>());
+ CALL_SUBTEST(test_contraction_of_contraction<RowMajor>());
+ CALL_SUBTEST(test_expr<ColMajor>());
+ CALL_SUBTEST(test_expr<RowMajor>());
+ CALL_SUBTEST(test_out_of_order_contraction<ColMajor>());
+ CALL_SUBTEST(test_out_of_order_contraction<RowMajor>());
+ CALL_SUBTEST(test_consistency<ColMajor>());
+ CALL_SUBTEST(test_consistency<RowMajor>());
+ CALL_SUBTEST(test_large_contraction<ColMajor>());
+ CALL_SUBTEST(test_large_contraction<RowMajor>());
+ CALL_SUBTEST(test_matrix_vector<ColMajor>());
+ CALL_SUBTEST(test_matrix_vector<RowMajor>());
+ CALL_SUBTEST(test_tensor_vector<ColMajor>());
+ CALL_SUBTEST(test_tensor_vector<RowMajor>());
+ CALL_SUBTEST(test_small_blocking_factors<ColMajor>());
+ CALL_SUBTEST(test_small_blocking_factors<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_convolution.cpp b/unsupported/test/cxx11_tensor_convolution.cpp
new file mode 100644
index 000000000..4672db463
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_convolution.cpp
@@ -0,0 +1,141 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::DefaultDevice;
+
+static void test_evals()
+{
+ Tensor<float, 2> input(3, 3);
+ Tensor<float, 1> kernel(2);
+
+ input.setRandom();
+ kernel.setRandom();
+
+ Tensor<float, 2> result(2,3);
+ result.setZero();
+ Eigen::array<Tensor<float, 2>::Index, 1> dims3({0});
+
+ typedef TensorEvaluator<decltype(input.convolve(kernel, dims3)), DefaultDevice> Evaluator;
+ Evaluator eval(input.convolve(kernel, dims3), DefaultDevice());
+ eval.evalTo(result.data());
+ EIGEN_STATIC_ASSERT(Evaluator::NumDims==2ul, YOU_MADE_A_PROGRAMMING_MISTAKE);
+ VERIFY_IS_EQUAL(eval.dimensions()[0], 2);
+ VERIFY_IS_EQUAL(eval.dimensions()[1], 3);
+
+ VERIFY_IS_APPROX(result(0,0), input(0,0)*kernel(0) + input(1,0)*kernel(1)); // index 0
+ VERIFY_IS_APPROX(result(0,1), input(0,1)*kernel(0) + input(1,1)*kernel(1)); // index 2
+ VERIFY_IS_APPROX(result(0,2), input(0,2)*kernel(0) + input(1,2)*kernel(1)); // index 4
+ VERIFY_IS_APPROX(result(1,0), input(1,0)*kernel(0) + input(2,0)*kernel(1)); // index 1
+ VERIFY_IS_APPROX(result(1,1), input(1,1)*kernel(0) + input(2,1)*kernel(1)); // index 3
+ VERIFY_IS_APPROX(result(1,2), input(1,2)*kernel(0) + input(2,2)*kernel(1)); // index 5
+}
+
+
+static void test_expr()
+{
+ Tensor<float, 2> input(3, 3);
+ Tensor<float, 2> kernel(2, 2);
+ input.setRandom();
+ kernel.setRandom();
+
+ Tensor<float, 2> result(2,2);
+ Eigen::array<ptrdiff_t, 2> dims({0, 1});
+ result = input.convolve(kernel, dims);
+
+ VERIFY_IS_APPROX(result(0,0), input(0,0)*kernel(0,0) + input(0,1)*kernel(0,1) +
+ input(1,0)*kernel(1,0) + input(1,1)*kernel(1,1));
+ VERIFY_IS_APPROX(result(0,1), input(0,1)*kernel(0,0) + input(0,2)*kernel(0,1) +
+ input(1,1)*kernel(1,0) + input(1,2)*kernel(1,1));
+ VERIFY_IS_APPROX(result(1,0), input(1,0)*kernel(0,0) + input(1,1)*kernel(0,1) +
+ input(2,0)*kernel(1,0) + input(2,1)*kernel(1,1));
+ VERIFY_IS_APPROX(result(1,1), input(1,1)*kernel(0,0) + input(1,2)*kernel(0,1) +
+ input(2,1)*kernel(1,0) + input(2,2)*kernel(1,1));
+}
+
+
+static void test_modes() {
+ Tensor<float, 1> input(3);
+ Tensor<float, 1> kernel(3);
+ input(0) = 1.0f;
+ input(1) = 2.0f;
+ input(2) = 3.0f;
+ kernel(0) = 0.5f;
+ kernel(1) = 1.0f;
+ kernel(2) = 0.0f;
+
+ const Eigen::array<ptrdiff_t, 1> dims{{0}};
+ Eigen::array<std::pair<ptrdiff_t, ptrdiff_t>, 1> padding;
+
+ // Emulate VALID mode (as defined in
+ // http://docs.scipy.org/doc/numpy/reference/generated/numpy.convolve.html).
+ padding[0] = std::make_pair(0, 0);
+ Tensor<float, 1> valid(1);
+ valid = input.pad(padding).convolve(kernel, dims);
+ VERIFY_IS_EQUAL(valid.dimension(0), 1);
+ VERIFY_IS_APPROX(valid(0), 2.5f);
+
+ // Emulate SAME mode (as defined in
+ // http://docs.scipy.org/doc/numpy/reference/generated/numpy.convolve.html).
+ padding[0] = std::make_pair(1, 1);
+ Tensor<float, 1> same(3);
+ same = input.pad(padding).convolve(kernel, dims);
+ VERIFY_IS_EQUAL(same.dimension(0), 3);
+ VERIFY_IS_APPROX(same(0), 1.0f);
+ VERIFY_IS_APPROX(same(1), 2.5f);
+ VERIFY_IS_APPROX(same(2), 4.0f);
+
+ // Emulate FULL mode (as defined in
+ // http://docs.scipy.org/doc/numpy/reference/generated/numpy.convolve.html).
+ padding[0] = std::make_pair(2, 2);
+ Tensor<float, 1> full(5);
+ full = input.pad(padding).convolve(kernel, dims);
+ VERIFY_IS_EQUAL(full.dimension(0), 5);
+ VERIFY_IS_APPROX(full(0), 0.0f);
+ VERIFY_IS_APPROX(full(1), 1.0f);
+ VERIFY_IS_APPROX(full(2), 2.5f);
+ VERIFY_IS_APPROX(full(3), 4.0f);
+ VERIFY_IS_APPROX(full(4), 1.5f);
+}
+
+
+static void test_strides() {
+ Tensor<float, 1> input(13);
+ Tensor<float, 1> kernel(3);
+ input.setRandom();
+ kernel.setRandom();
+
+ const Eigen::array<ptrdiff_t, 1> dims{{0}};
+ const Eigen::array<ptrdiff_t, 1> stride_of_3{{3}};
+ const Eigen::array<ptrdiff_t, 1> stride_of_2{{2}};
+
+ Tensor<float, 1> result;
+ result = input.stride(stride_of_3).convolve(kernel, dims).stride(stride_of_2);
+
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_APPROX(result(0), (input(0)*kernel(0) + input(3)*kernel(1) +
+ input(6)*kernel(2)));
+ VERIFY_IS_APPROX(result(1), (input(6)*kernel(0) + input(9)*kernel(1) +
+ input(12)*kernel(2)));
+}
+
+
+
+
+void test_cxx11_tensor_convolution()
+{
+ CALL_SUBTEST(test_evals());
+ CALL_SUBTEST(test_expr());
+ CALL_SUBTEST(test_modes());
+ CALL_SUBTEST(test_strides());
+}
diff --git a/unsupported/test/cxx11_tensor_cuda.cpp b/unsupported/test/cxx11_tensor_cuda.cpp
new file mode 100644
index 000000000..8c1ca1bf8
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_cuda.cpp
@@ -0,0 +1,514 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// TODO(mdevin): Free the cuda memory.
+
+#define EIGEN_TEST_NO_LONGDOUBLE
+#define EIGEN_TEST_NO_COMPLEX
+#define EIGEN_TEST_FUNC cxx11_tensor_cuda
+#define EIGEN_DEFAULT_DENSE_INDEX_TYPE int
+#define EIGEN_USE_GPU
+
+
+#include "main.h"
+#include <unsupported/Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+void test_cuda_elementwise_small() {
+ Tensor<float, 1> in1(Eigen::array<int, 1>(2));
+ Tensor<float, 1> in2(Eigen::array<int, 1>(2));
+ Tensor<float, 1> out(Eigen::array<int, 1>(2));
+ in1.setRandom();
+ in2.setRandom();
+
+ std::size_t in1_bytes = in1.size() * sizeof(float);
+ std::size_t in2_bytes = in2.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_in1;
+ float* d_in2;
+ float* d_out;
+ cudaMalloc((void**)(&d_in1), in1_bytes);
+ cudaMalloc((void**)(&d_in2), in2_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_in1, in1.data(), in1_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_in2, in2.data(), in2_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_in1(
+ d_in1, Eigen::array<int, 1>(2));
+ Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_in2(
+ d_in2, Eigen::array<int, 1>(2));
+ Eigen::TensorMap<Eigen::Tensor<float, 1>, Eigen::Aligned> gpu_out(
+ d_out, Eigen::array<int, 1>(2));
+
+ gpu_out.device(gpu_device) = gpu_in1 + gpu_in2;
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost,
+ gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 2; ++i) {
+ VERIFY_IS_APPROX(
+ out(Eigen::array<int, 1>(i)),
+ in1(Eigen::array<int, 1>(i)) + in2(Eigen::array<int, 1>(i)));
+ }
+}
+
+void test_cuda_elementwise()
+{
+ Tensor<float, 3> in1(Eigen::array<int, 3>(72,53,97));
+ Tensor<float, 3> in2(Eigen::array<int, 3>(72,53,97));
+ Tensor<float, 3> in3(Eigen::array<int, 3>(72,53,97));
+ Tensor<float, 3> out(Eigen::array<int, 3>(72,53,97));
+ in1.setRandom();
+ in2.setRandom();
+ in3.setRandom();
+
+ std::size_t in1_bytes = in1.size() * sizeof(float);
+ std::size_t in2_bytes = in2.size() * sizeof(float);
+ std::size_t in3_bytes = in3.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_in1;
+ float* d_in2;
+ float* d_in3;
+ float* d_out;
+ cudaMalloc((void**)(&d_in1), in1_bytes);
+ cudaMalloc((void**)(&d_in2), in2_bytes);
+ cudaMalloc((void**)(&d_in3), in3_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_in1, in1.data(), in1_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_in2, in2.data(), in2_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_in3, in3.data(), in3_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_in1(d_in1, Eigen::array<int, 3>(72,53,97));
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_in2(d_in2, Eigen::array<int, 3>(72,53,97));
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_in3(d_in3, Eigen::array<int, 3>(72,53,97));
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_out(d_out, Eigen::array<int, 3>(72,53,97));
+
+ gpu_out.device(gpu_device) = gpu_in1 + gpu_in2 * gpu_in3;
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 72; ++i) {
+ for (int j = 0; j < 53; ++j) {
+ for (int k = 0; k < 97; ++k) {
+ VERIFY_IS_APPROX(out(Eigen::array<int, 3>(i,j,k)), in1(Eigen::array<int, 3>(i,j,k)) + in2(Eigen::array<int, 3>(i,j,k)) * in3(Eigen::array<int, 3>(i,j,k)));
+ }
+ }
+ }
+}
+
+
+void test_cuda_reduction()
+{
+ Tensor<float, 4> in1(Eigen::array<int, 4>(72,53,97,113));
+ Tensor<float, 2> out(Eigen::array<int, 2>(72,97));
+ in1.setRandom();
+
+ std::size_t in1_bytes = in1.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_in1;
+ float* d_out;
+ cudaMalloc((void**)(&d_in1), in1_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_in1, in1.data(), in1_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 4> > gpu_in1(d_in1, Eigen::array<int, 4>(72,53,97,113));
+ Eigen::TensorMap<Eigen::Tensor<float, 2> > gpu_out(d_out, Eigen::array<int, 2>(72,97));
+
+ array<int, 2> reduction_axis;
+ reduction_axis[0] = 1;
+ reduction_axis[1] = 3;
+
+ gpu_out.device(gpu_device) = gpu_in1.maximum(reduction_axis);
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 72; ++i) {
+ for (int j = 0; j < 97; ++j) {
+ float expected = 0;
+ for (int k = 0; k < 53; ++k) {
+ for (int l = 0; l < 113; ++l) {
+ expected =
+ std::max<float>(expected, in1(Eigen::array<int, 4>(i, k, j, l)));
+ }
+ }
+ VERIFY_IS_APPROX(out(Eigen::array<int, 2>(i,j)), expected);
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_cuda_contraction()
+{
+ // with these dimensions, the output has 300 * 140 elements, which is
+ // more than 30 * 1024, which is the number of threads in blocks on
+ // a 15 SM GK110 GPU
+ Tensor<float, 4, DataLayout> t_left(Eigen::array<int, 4>(6, 50, 3, 31));
+ Tensor<float, 5, DataLayout> t_right(Eigen::array<int, 5>(3, 31, 7, 20, 1));
+ Tensor<float, 5, DataLayout> t_result(Eigen::array<int, 5>(6, 50, 7, 20, 1));
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ std::size_t t_left_bytes = t_left.size() * sizeof(float);
+ std::size_t t_right_bytes = t_right.size() * sizeof(float);
+ std::size_t t_result_bytes = t_result.size() * sizeof(float);
+
+ float* d_t_left;
+ float* d_t_right;
+ float* d_t_result;
+
+ cudaMalloc((void**)(&d_t_left), t_left_bytes);
+ cudaMalloc((void**)(&d_t_right), t_right_bytes);
+ cudaMalloc((void**)(&d_t_result), t_result_bytes);
+
+ cudaMemcpy(d_t_left, t_left.data(), t_left_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_t_right, t_right.data(), t_right_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 4, DataLayout> >
+ gpu_t_left(d_t_left, Eigen::array<int, 4>(6, 50, 3, 31));
+ Eigen::TensorMap<Eigen::Tensor<float, 5, DataLayout> >
+ gpu_t_right(d_t_right, Eigen::array<int, 5>(3, 31, 7, 20, 1));
+ Eigen::TensorMap<Eigen::Tensor<float, 5, DataLayout> >
+ gpu_t_result(d_t_result, Eigen::array<int, 5>(6, 50, 7, 20, 1));
+
+ typedef Eigen::Map<Eigen::Matrix<float, Dynamic, Dynamic, DataLayout> > MapXf;
+ MapXf m_left(t_left.data(), 300, 93);
+ MapXf m_right(t_right.data(), 93, 140);
+ Eigen::Matrix<float, Dynamic, Dynamic, DataLayout> m_result(300, 140);
+
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ Eigen::array<DimPair, 2> dims;
+ dims[0] = DimPair(2, 0);
+ dims[1] = DimPair(3, 1);
+
+ m_result = m_left * m_right;
+ gpu_t_result.device(gpu_device) = gpu_t_left.contract(gpu_t_right, dims);
+
+ cudaMemcpy(t_result.data(), d_t_result, t_result_bytes, cudaMemcpyDeviceToHost);
+
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ if (fabs(t_result.data()[i] - m_result.data()[i]) >= 1e-4) {
+ cout << "mismatch detected at index " << i << ": " << t_result.data()[i] << " vs " << m_result.data()[i] << endl;
+ assert(false);
+ }
+ }
+}
+
+static void test_cuda_convolution_1d()
+{
+ Tensor<float, 4> input(Eigen::array<int, 4>(74,37,11,137));
+ Tensor<float, 1> kernel(Eigen::array<int, 1>(4));
+ Tensor<float, 4> out(Eigen::array<int, 4>(74,34,11,137));
+ input = input.constant(10.0f) + input.random();
+ kernel = kernel.constant(7.0f) + kernel.random();
+
+ std::size_t input_bytes = input.size() * sizeof(float);
+ std::size_t kernel_bytes = kernel.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_input;
+ float* d_kernel;
+ float* d_out;
+ cudaMalloc((void**)(&d_input), input_bytes);
+ cudaMalloc((void**)(&d_kernel), kernel_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_input, input.data(), input_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_kernel, kernel.data(), kernel_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 4> > gpu_input(d_input, Eigen::array<int, 4>(74,37,11,137));
+ Eigen::TensorMap<Eigen::Tensor<float, 1> > gpu_kernel(d_kernel, Eigen::array<int, 1>(4));
+ Eigen::TensorMap<Eigen::Tensor<float, 4> > gpu_out(d_out, Eigen::array<int, 4>(74,34,11,137));
+
+ Eigen::array<int, 1> dims(1);
+ gpu_out.device(gpu_device) = gpu_input.convolve(gpu_kernel, dims);
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 74; ++i) {
+ for (int j = 0; j < 34; ++j) {
+ for (int k = 0; k < 11; ++k) {
+ for (int l = 0; l < 137; ++l) {
+ const float result = out(Eigen::array<int, 4>(i,j,k,l));
+ const float expected = input(Eigen::array<int, 4>(i,j+0,k,l)) * kernel(Eigen::array<int, 1>(0)) +
+ input(Eigen::array<int, 4>(i,j+1,k,l)) * kernel(Eigen::array<int, 1>(1)) +
+ input(Eigen::array<int, 4>(i,j+2,k,l)) * kernel(Eigen::array<int, 1>(2)) +
+ input(Eigen::array<int, 4>(i,j+3,k,l)) * kernel(Eigen::array<int, 1>(3));
+ VERIFY_IS_APPROX(result, expected);
+ }
+ }
+ }
+ }
+}
+
+
+static void test_cuda_convolution_2d()
+{
+ Tensor<float, 4> input(Eigen::array<int, 4>(74,37,11,137));
+ Tensor<float, 2> kernel(Eigen::array<int, 2>(3,4));
+ Tensor<float, 4> out(Eigen::array<int, 4>(74,35,8,137));
+ input = input.constant(10.0f) + input.random();
+ kernel = kernel.constant(7.0f) + kernel.random();
+
+ std::size_t input_bytes = input.size() * sizeof(float);
+ std::size_t kernel_bytes = kernel.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_input;
+ float* d_kernel;
+ float* d_out;
+ cudaMalloc((void**)(&d_input), input_bytes);
+ cudaMalloc((void**)(&d_kernel), kernel_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_input, input.data(), input_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_kernel, kernel.data(), kernel_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 4> > gpu_input(d_input, Eigen::array<int, 4>(74,37,11,137));
+ Eigen::TensorMap<Eigen::Tensor<float, 2> > gpu_kernel(d_kernel, Eigen::array<int, 2>(3,4));
+ Eigen::TensorMap<Eigen::Tensor<float, 4> > gpu_out(d_out, Eigen::array<int, 4>(74,35,8,137));
+
+ Eigen::array<int, 2> dims(1,2);
+ gpu_out.device(gpu_device) = gpu_input.convolve(gpu_kernel, dims);
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 74; ++i) {
+ for (int j = 0; j < 35; ++j) {
+ for (int k = 0; k < 8; ++k) {
+ for (int l = 0; l < 137; ++l) {
+ const float result = out(Eigen::array<int, 4>(i,j,k,l));
+ const float expected = input(Eigen::array<int, 4>(i,j+0,k+0,l)) * kernel(Eigen::array<int, 2>(0,0)) +
+ input(Eigen::array<int, 4>(i,j+1,k+0,l)) * kernel(Eigen::array<int, 2>(1,0)) +
+ input(Eigen::array<int, 4>(i,j+2,k+0,l)) * kernel(Eigen::array<int, 2>(2,0)) +
+ input(Eigen::array<int, 4>(i,j+0,k+1,l)) * kernel(Eigen::array<int, 2>(0,1)) +
+ input(Eigen::array<int, 4>(i,j+1,k+1,l)) * kernel(Eigen::array<int, 2>(1,1)) +
+ input(Eigen::array<int, 4>(i,j+2,k+1,l)) * kernel(Eigen::array<int, 2>(2,1)) +
+ input(Eigen::array<int, 4>(i,j+0,k+2,l)) * kernel(Eigen::array<int, 2>(0,2)) +
+ input(Eigen::array<int, 4>(i,j+1,k+2,l)) * kernel(Eigen::array<int, 2>(1,2)) +
+ input(Eigen::array<int, 4>(i,j+2,k+2,l)) * kernel(Eigen::array<int, 2>(2,2)) +
+ input(Eigen::array<int, 4>(i,j+0,k+3,l)) * kernel(Eigen::array<int, 2>(0,3)) +
+ input(Eigen::array<int, 4>(i,j+1,k+3,l)) * kernel(Eigen::array<int, 2>(1,3)) +
+ input(Eigen::array<int, 4>(i,j+2,k+3,l)) * kernel(Eigen::array<int, 2>(2,3));
+ VERIFY_IS_APPROX(result, expected);
+ }
+ }
+ }
+ }
+}
+
+
+static void test_cuda_convolution_3d()
+{
+ Tensor<float, 5> input(Eigen::array<int, 5>(74,37,11,137,17));
+ Tensor<float, 3> kernel(Eigen::array<int, 3>(3,4,2));
+ Tensor<float, 5> out(Eigen::array<int, 5>(74,35,8,136,17));
+ input = input.constant(10.0f) + input.random();
+ kernel = kernel.constant(7.0f) + kernel.random();
+
+ std::size_t input_bytes = input.size() * sizeof(float);
+ std::size_t kernel_bytes = kernel.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_input;
+ float* d_kernel;
+ float* d_out;
+ cudaMalloc((void**)(&d_input), input_bytes);
+ cudaMalloc((void**)(&d_kernel), kernel_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_input, input.data(), input_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_kernel, kernel.data(), kernel_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 5> > gpu_input(d_input, Eigen::array<int, 5>(74,37,11,137,17));
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_kernel(d_kernel, Eigen::array<int, 3>(3,4,2));
+ Eigen::TensorMap<Eigen::Tensor<float, 5> > gpu_out(d_out, Eigen::array<int, 5>(74,35,8,136,17));
+
+ Eigen::array<int, 3> dims(1,2,3);
+ gpu_out.device(gpu_device) = gpu_input.convolve(gpu_kernel, dims);
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 74; ++i) {
+ for (int j = 0; j < 35; ++j) {
+ for (int k = 0; k < 8; ++k) {
+ for (int l = 0; l < 136; ++l) {
+ for (int m = 0; m < 17; ++m) {
+ const float result = out(Eigen::array<int, 5>(i,j,k,l,m));
+ const float expected = input(Eigen::array<int, 5>(i,j+0,k+0,l+0,m)) * kernel(Eigen::array<int, 3>(0,0,0)) +
+ input(Eigen::array<int, 5>(i,j+1,k+0,l+0,m)) * kernel(Eigen::array<int, 3>(1,0,0)) +
+ input(Eigen::array<int, 5>(i,j+2,k+0,l+0,m)) * kernel(Eigen::array<int, 3>(2,0,0)) +
+ input(Eigen::array<int, 5>(i,j+0,k+1,l+0,m)) * kernel(Eigen::array<int, 3>(0,1,0)) +
+ input(Eigen::array<int, 5>(i,j+1,k+1,l+0,m)) * kernel(Eigen::array<int, 3>(1,1,0)) +
+ input(Eigen::array<int, 5>(i,j+2,k+1,l+0,m)) * kernel(Eigen::array<int, 3>(2,1,0)) +
+ input(Eigen::array<int, 5>(i,j+0,k+2,l+0,m)) * kernel(Eigen::array<int, 3>(0,2,0)) +
+ input(Eigen::array<int, 5>(i,j+1,k+2,l+0,m)) * kernel(Eigen::array<int, 3>(1,2,0)) +
+ input(Eigen::array<int, 5>(i,j+2,k+2,l+0,m)) * kernel(Eigen::array<int, 3>(2,2,0)) +
+ input(Eigen::array<int, 5>(i,j+0,k+3,l+0,m)) * kernel(Eigen::array<int, 3>(0,3,0)) +
+ input(Eigen::array<int, 5>(i,j+1,k+3,l+0,m)) * kernel(Eigen::array<int, 3>(1,3,0)) +
+ input(Eigen::array<int, 5>(i,j+2,k+3,l+0,m)) * kernel(Eigen::array<int, 3>(2,3,0)) +
+ input(Eigen::array<int, 5>(i,j+0,k+0,l+1,m)) * kernel(Eigen::array<int, 3>(0,0,1)) +
+ input(Eigen::array<int, 5>(i,j+1,k+0,l+1,m)) * kernel(Eigen::array<int, 3>(1,0,1)) +
+ input(Eigen::array<int, 5>(i,j+2,k+0,l+1,m)) * kernel(Eigen::array<int, 3>(2,0,1)) +
+ input(Eigen::array<int, 5>(i,j+0,k+1,l+1,m)) * kernel(Eigen::array<int, 3>(0,1,1)) +
+ input(Eigen::array<int, 5>(i,j+1,k+1,l+1,m)) * kernel(Eigen::array<int, 3>(1,1,1)) +
+ input(Eigen::array<int, 5>(i,j+2,k+1,l+1,m)) * kernel(Eigen::array<int, 3>(2,1,1)) +
+ input(Eigen::array<int, 5>(i,j+0,k+2,l+1,m)) * kernel(Eigen::array<int, 3>(0,2,1)) +
+ input(Eigen::array<int, 5>(i,j+1,k+2,l+1,m)) * kernel(Eigen::array<int, 3>(1,2,1)) +
+ input(Eigen::array<int, 5>(i,j+2,k+2,l+1,m)) * kernel(Eigen::array<int, 3>(2,2,1)) +
+ input(Eigen::array<int, 5>(i,j+0,k+3,l+1,m)) * kernel(Eigen::array<int, 3>(0,3,1)) +
+ input(Eigen::array<int, 5>(i,j+1,k+3,l+1,m)) * kernel(Eigen::array<int, 3>(1,3,1)) +
+ input(Eigen::array<int, 5>(i,j+2,k+3,l+1,m)) * kernel(Eigen::array<int, 3>(2,3,1));
+ VERIFY_IS_APPROX(result, expected);
+ }
+ }
+ }
+ }
+ }
+}
+
+static float* CudaCopyFloat(float* data, int size) {
+ const int nbytes = size * sizeof(float);
+ float* result = NULL;
+ if (cudaMalloc((void**)(&result), nbytes) != cudaSuccess) {
+ return NULL;
+ } else {
+ if (data != NULL) {
+ cudaMemcpy(result, data, nbytes, cudaMemcpyHostToDevice);
+ }
+ return result;
+ }
+}
+
+static void test_cuda_constant_broadcast()
+{
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Tensor<float, 1> t1(10);
+ for (int i = 0; i < 10; ++i) {
+ t1(i) = 10.0f * i;
+ }
+ float* t1_cuda = CudaCopyFloat(t1.data(), t1.size());
+ Eigen::TensorMap<Eigen::Tensor<float, 1> > t1_gpu(t1_cuda, 10);
+
+ Tensor<float, 1> t2(1);
+ t2 = t2.constant(20.0f);
+ float* t2_cuda = CudaCopyFloat(t2.data(), t2.size());
+ Eigen::TensorMap<Eigen::TensorFixedSize<float, Sizes<1> > > t2_gpu(t2_cuda, 1);
+
+ float* t3_cuda = CudaCopyFloat(NULL, 10);
+ Eigen::TensorMap<Eigen::Tensor<float, 1> > t3_gpu(t3_cuda, 10);
+
+ t3_gpu.device(gpu_device) =
+ t1_gpu + t2_gpu.broadcast(Eigen::array<int, 1>(10));
+
+ Eigen::Tensor<float, 1> t3(10);
+ cudaMemcpy(t3.data(), t3_gpu.data(), 10 * sizeof(float),
+ cudaMemcpyDeviceToHost);
+
+ for (int i = 0; i < 10; ++i) {
+ VERIFY_IS_APPROX(t3(i), t1(i) + t2(0));
+ }
+}
+
+
+void test_cuda_cast()
+{
+ Tensor<double, 3> in(Eigen::array<int, 3>(72,53,97));
+ Tensor<float, 3> out(Eigen::array<int, 3>(72,53,97));
+ in.setRandom();
+
+ std::size_t in_bytes = in.size() * sizeof(double);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ double* d_in;
+ float* d_out;
+ cudaMalloc((void**)(&d_in), in_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_in, in.data(), in_bytes, cudaMemcpyHostToDevice);
+
+ cudaStream_t stream;
+ assert(cudaStreamCreate(&stream) == cudaSuccess);
+ Eigen::GpuDevice gpu_device(&stream);
+
+ Eigen::TensorMap<Eigen::Tensor<double, 3> > gpu_in(d_in, Eigen::array<int, 3>(72,53,97));
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_out(d_out, Eigen::array<int, 3>(72,53,97));
+
+ gpu_out.device(gpu_device) = gpu_in.template cast<float>();
+
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, gpu_device.stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(gpu_device.stream()) == cudaSuccess);
+
+ for (int i = 0; i < 72; ++i) {
+ for (int j = 0; j < 53; ++j) {
+ for (int k = 0; k < 97; ++k) {
+ VERIFY_IS_APPROX(out(Eigen::array<int, 3>(i,j,k)), static_cast<float>(in(Eigen::array<int, 3>(i,j,k))));
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_cuda()
+{
+ CALL_SUBTEST(test_cuda_elementwise_small());
+ CALL_SUBTEST(test_cuda_elementwise());
+ CALL_SUBTEST(test_cuda_reduction());
+ CALL_SUBTEST(test_cuda_contraction<ColMajor>());
+ CALL_SUBTEST(test_cuda_contraction<RowMajor>());
+ CALL_SUBTEST(test_cuda_convolution_1d());
+ CALL_SUBTEST(test_cuda_convolution_2d());
+ CALL_SUBTEST(test_cuda_convolution_3d());
+ CALL_SUBTEST(test_cuda_constant_broadcast());
+ CALL_SUBTEST(test_cuda_cast());
+}
diff --git a/unsupported/test/cxx11_tensor_device.cpp b/unsupported/test/cxx11_tensor_device.cpp
new file mode 100644
index 000000000..f2d7e4ce6
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_device.cpp
@@ -0,0 +1,391 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#define EIGEN_TEST_NO_LONGDOUBLE
+#define EIGEN_TEST_NO_COMPLEX
+#define EIGEN_TEST_FUNC cxx11_tensor_device
+#define EIGEN_DEFAULT_DENSE_INDEX_TYPE int
+#define EIGEN_USE_GPU
+
+
+#include "main.h"
+#include <unsupported/Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+// Context for evaluation on cpu
+struct CPUContext {
+ CPUContext(const Eigen::Tensor<float, 3>& in1, Eigen::Tensor<float, 3>& in2, Eigen::Tensor<float, 3>& out) : in1_(in1), in2_(in2), out_(out), kernel_1d_(2), kernel_2d_(2,2), kernel_3d_(2,2,2) {
+ kernel_1d_(0) = 3.14f;
+ kernel_1d_(1) = 2.7f;
+
+ kernel_2d_(0,0) = 3.14f;
+ kernel_2d_(1,0) = 2.7f;
+ kernel_2d_(0,1) = 0.2f;
+ kernel_2d_(1,1) = 7.0f;
+
+ kernel_3d_(0,0,0) = 3.14f;
+ kernel_3d_(0,1,0) = 2.7f;
+ kernel_3d_(0,0,1) = 0.2f;
+ kernel_3d_(0,1,1) = 7.0f;
+ kernel_3d_(1,0,0) = -1.0f;
+ kernel_3d_(1,1,0) = -0.3f;
+ kernel_3d_(1,0,1) = -0.7f;
+ kernel_3d_(1,1,1) = -0.5f;
+ }
+
+ const Eigen::DefaultDevice& device() const { return cpu_device_; }
+
+ const Eigen::Tensor<float, 3>& in1() const { return in1_; }
+ const Eigen::Tensor<float, 3>& in2() const { return in2_; }
+ Eigen::Tensor<float, 3>& out() { return out_; }
+ const Eigen::Tensor<float, 1>& kernel1d() const { return kernel_1d_; }
+ const Eigen::Tensor<float, 2>& kernel2d() const { return kernel_2d_; }
+ const Eigen::Tensor<float, 3>& kernel3d() const { return kernel_3d_; }
+
+ private:
+ const Eigen::Tensor<float, 3>& in1_;
+ const Eigen::Tensor<float, 3>& in2_;
+ Eigen::Tensor<float, 3>& out_;
+
+ Eigen::Tensor<float, 1> kernel_1d_;
+ Eigen::Tensor<float, 2> kernel_2d_;
+ Eigen::Tensor<float, 3> kernel_3d_;
+
+ Eigen::DefaultDevice cpu_device_;
+};
+
+
+// Context for evaluation on GPU
+struct GPUContext {
+ GPUContext(const Eigen::TensorMap<Eigen::Tensor<float, 3> >& in1, Eigen::TensorMap<Eigen::Tensor<float, 3> >& in2, Eigen::TensorMap<Eigen::Tensor<float, 3> >& out) : in1_(in1), in2_(in2), out_(out), gpu_device_(&stream_) {
+ assert(cudaMalloc((void**)(&kernel_1d_), 2*sizeof(float)) == cudaSuccess);
+ float kernel_1d_val[] = {3.14f, 2.7f};
+ assert(cudaMemcpy(kernel_1d_, kernel_1d_val, 2*sizeof(float), cudaMemcpyHostToDevice) == cudaSuccess);
+
+ assert(cudaMalloc((void**)(&kernel_2d_), 4*sizeof(float)) == cudaSuccess);
+ float kernel_2d_val[] = {3.14f, 2.7f, 0.2f, 7.0f};
+ assert(cudaMemcpy(kernel_2d_, kernel_2d_val, 4*sizeof(float), cudaMemcpyHostToDevice) == cudaSuccess);
+
+ assert(cudaMalloc((void**)(&kernel_3d_), 8*sizeof(float)) == cudaSuccess);
+ float kernel_3d_val[] = {3.14f, -1.0f, 2.7f, -0.3f, 0.2f, -0.7f, 7.0f, -0.5f};
+ assert(cudaMemcpy(kernel_3d_, kernel_3d_val, 8*sizeof(float), cudaMemcpyHostToDevice) == cudaSuccess);
+
+ assert(cudaStreamCreate(&stream_) == cudaSuccess);
+ }
+ ~GPUContext() {
+ assert(cudaFree(kernel_1d_) == cudaSuccess);
+ assert(cudaFree(kernel_2d_) == cudaSuccess);
+ assert(cudaFree(kernel_3d_) == cudaSuccess);
+ assert(cudaStreamDestroy(stream_) == cudaSuccess);
+ }
+
+ const Eigen::GpuDevice& device() const { return gpu_device_; }
+
+ const Eigen::TensorMap<Eigen::Tensor<float, 3> >& in1() const { return in1_; }
+ const Eigen::TensorMap<Eigen::Tensor<float, 3> >& in2() const { return in2_; }
+ Eigen::TensorMap<Eigen::Tensor<float, 3> >& out() { return out_; }
+ Eigen::TensorMap<Eigen::Tensor<float, 1> > kernel1d() const { return Eigen::TensorMap<Eigen::Tensor<float, 1> >(kernel_1d_, 2); }
+ Eigen::TensorMap<Eigen::Tensor<float, 2> > kernel2d() const { return Eigen::TensorMap<Eigen::Tensor<float, 2> >(kernel_2d_, 2, 2); }
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > kernel3d() const { return Eigen::TensorMap<Eigen::Tensor<float, 3> >(kernel_3d_, 2, 2, 2); }
+
+ private:
+ const Eigen::TensorMap<Eigen::Tensor<float, 3> >& in1_;
+ const Eigen::TensorMap<Eigen::Tensor<float, 3> >& in2_;
+ Eigen::TensorMap<Eigen::Tensor<float, 3> >& out_;
+
+ float* kernel_1d_;
+ float* kernel_2d_;
+ float* kernel_3d_;
+
+ cudaStream_t stream_;
+ Eigen::GpuDevice gpu_device_;
+};
+
+
+// The actual expression to evaluate
+template <typename Context>
+static void test_contextual_eval(Context* context)
+{
+ context->out().device(context->device()) = context->in1() + context->in2() * 3.14f + context->in1().constant(2.718f);
+}
+
+template <typename Context>
+static void test_forced_contextual_eval(Context* context)
+{
+ context->out().device(context->device()) = (context->in1() + context->in2()).eval() * 3.14f + context->in1().constant(2.718f);
+}
+
+template <typename Context>
+static void test_compound_assignment(Context* context)
+{
+ context->out().device(context->device()) = context->in1().constant(2.718f);
+ context->out().device(context->device()) += context->in1() + context->in2() * 3.14f;
+}
+
+
+template <typename Context>
+static void test_contraction(Context* context)
+{
+ Eigen::array<std::pair<int, int>, 2> dims;
+ dims[0] = std::make_pair(1, 1);
+ dims[1] = std::make_pair(2, 2);
+
+ Eigen::array<int, 2> shape(40, 50*70);
+
+ Eigen::DSizes<int, 2> indices(0,0);
+ Eigen::DSizes<int, 2> sizes(40,40);
+
+ context->out().reshape(shape).slice(indices, sizes).device(context->device()) = context->in1().contract(context->in2(), dims);
+}
+
+
+template <typename Context>
+static void test_1d_convolution(Context* context)
+{
+ Eigen::DSizes<int, 3> indices(0,0,0);
+ Eigen::DSizes<int, 3> sizes(40,49,70);
+
+ Eigen::array<int, 1> dims(1);
+ context->out().slice(indices, sizes).device(context->device()) = context->in1().convolve(context->kernel1d(), dims);
+}
+
+template <typename Context>
+static void test_2d_convolution(Context* context)
+{
+ Eigen::DSizes<int, 3> indices(0,0,0);
+ Eigen::DSizes<int, 3> sizes(40,49,69);
+
+ Eigen::array<int, 2> dims(1,2);
+ context->out().slice(indices, sizes).device(context->device()) = context->in1().convolve(context->kernel2d(), dims);
+}
+
+template <typename Context>
+static void test_3d_convolution(Context* context)
+{
+ Eigen::DSizes<int, 3> indices(0,0,0);
+ Eigen::DSizes<int, 3> sizes(39,49,69);
+
+ Eigen::array<int, 3> dims(0,1,2);
+ context->out().slice(indices, sizes).device(context->device()) = context->in1().convolve(context->kernel3d(), dims);
+}
+
+
+static void test_cpu() {
+ Eigen::Tensor<float, 3> in1(40,50,70);
+ Eigen::Tensor<float, 3> in2(40,50,70);
+ Eigen::Tensor<float, 3> out(40,50,70);
+
+ in1 = in1.random() + in1.constant(10.0f);
+ in2 = in2.random() + in2.constant(10.0f);
+
+ CPUContext context(in1, in2, out);
+ test_contextual_eval(&context);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 50; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), in1(i,j,k) + in2(i,j,k) * 3.14f + 2.718f);
+ }
+ }
+ }
+
+ test_forced_contextual_eval(&context);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 50; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), (in1(i,j,k) + in2(i,j,k)) * 3.14f + 2.718f);
+ }
+ }
+ }
+
+ test_compound_assignment(&context);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 50; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), in1(i,j,k) + in2(i,j,k) * 3.14f + 2.718f);
+ }
+ }
+ }
+
+ test_contraction(&context);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 40; ++j) {
+ const float result = out(i,j,0);
+ float expected = 0;
+ for (int k = 0; k < 50; ++k) {
+ for (int l = 0; l < 70; ++l) {
+ expected += in1(i, k, l) * in2(j, k, l);
+ }
+ }
+ VERIFY_IS_APPROX(expected, result);
+ }
+ }
+
+ test_1d_convolution(&context);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 49; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), (in1(i,j,k) * 3.14f + in1(i,j+1,k) * 2.7f));
+ }
+ }
+ }
+
+ test_2d_convolution(&context);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 49; ++j) {
+ for (int k = 0; k < 69; ++k) {
+ const float result = out(i,j,k);
+ const float expected = (in1(i,j,k) * 3.14f + in1(i,j+1,k) * 2.7f) +
+ (in1(i,j,k+1) * 0.2f + in1(i,j+1,k+1) * 7.0f);
+ if (fabs(expected) < 1e-4 && fabs(result) < 1e-4) {
+ continue;
+ }
+ VERIFY_IS_APPROX(expected, result);
+ }
+ }
+ }
+
+ test_3d_convolution(&context);
+ for (int i = 0; i < 39; ++i) {
+ for (int j = 0; j < 49; ++j) {
+ for (int k = 0; k < 69; ++k) {
+ const float result = out(i,j,k);
+ const float expected = (in1(i,j,k) * 3.14f + in1(i,j+1,k) * 2.7f +
+ in1(i,j,k+1) * 0.2f + in1(i,j+1,k+1) * 7.0f) +
+ (in1(i+1,j,k) * -1.0f + in1(i+1,j+1,k) * -0.3f +
+ in1(i+1,j,k+1) * -0.7f + in1(i+1,j+1,k+1) * -0.5f);
+ if (fabs(expected) < 1e-4 && fabs(result) < 1e-4) {
+ continue;
+ }
+ VERIFY_IS_APPROX(expected, result);
+ }
+ }
+ }
+}
+
+static void test_gpu() {
+ Eigen::Tensor<float, 3> in1(40,50,70);
+ Eigen::Tensor<float, 3> in2(40,50,70);
+ Eigen::Tensor<float, 3> out(40,50,70);
+ in1 = in1.random() + in1.constant(10.0f);
+ in2 = in2.random() + in2.constant(10.0f);
+
+ std::size_t in1_bytes = in1.size() * sizeof(float);
+ std::size_t in2_bytes = in2.size() * sizeof(float);
+ std::size_t out_bytes = out.size() * sizeof(float);
+
+ float* d_in1;
+ float* d_in2;
+ float* d_out;
+ cudaMalloc((void**)(&d_in1), in1_bytes);
+ cudaMalloc((void**)(&d_in2), in2_bytes);
+ cudaMalloc((void**)(&d_out), out_bytes);
+
+ cudaMemcpy(d_in1, in1.data(), in1_bytes, cudaMemcpyHostToDevice);
+ cudaMemcpy(d_in2, in2.data(), in2_bytes, cudaMemcpyHostToDevice);
+
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_in1(d_in1, 40,50,70);
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_in2(d_in2, 40,50,70);
+ Eigen::TensorMap<Eigen::Tensor<float, 3> > gpu_out(d_out, 40,50,70);
+
+ GPUContext context(gpu_in1, gpu_in2, gpu_out);
+ test_contextual_eval(&context);
+ assert(cudaMemcpy(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost) == cudaSuccess);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 50; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), in1(i,j,k) + in2(i,j,k) * 3.14f + 2.718f);
+ }
+ }
+ }
+
+ test_forced_contextual_eval(&context);
+ assert(cudaMemcpy(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost) == cudaSuccess);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 50; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), (in1(i,j,k) + in2(i,j,k)) * 3.14f + 2.718f);
+ }
+ }
+ }
+
+ test_compound_assignment(&context);
+ assert(cudaMemcpy(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost) == cudaSuccess);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 50; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), in1(i,j,k) + in2(i,j,k) * 3.14f + 2.718f);
+ }
+ }
+ }
+
+ test_contraction(&context);
+ assert(cudaMemcpy(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost) == cudaSuccess);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 40; ++j) {
+ const float result = out(i,j,0);
+ float expected = 0;
+ for (int k = 0; k < 50; ++k) {
+ for (int l = 0; l < 70; ++l) {
+ expected += in1(i, k, l) * in2(j, k, l);
+ }
+ }
+ VERIFY_IS_APPROX(expected, result);
+ }
+ }
+
+ test_1d_convolution(&context);
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, context.device().stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(context.device().stream()) == cudaSuccess);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 49; ++j) {
+ for (int k = 0; k < 70; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), (in1(i,j,k) * 3.14f + in1(i,j+1,k) * 2.7f));
+ }
+ }
+ }
+
+ test_2d_convolution(&context);
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, context.device().stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(context.device().stream()) == cudaSuccess);
+ for (int i = 0; i < 40; ++i) {
+ for (int j = 0; j < 49; ++j) {
+ for (int k = 0; k < 69; ++k) {
+ const float result = out(i,j,k);
+ const float expected = (in1(i,j,k) * 3.14f + in1(i,j+1,k) * 2.7f +
+ in1(i,j,k+1) * 0.2f + in1(i,j+1,k+1) * 7.0f);
+ VERIFY_IS_APPROX(expected, result);
+ }
+ }
+ }
+
+ test_3d_convolution(&context);
+ assert(cudaMemcpyAsync(out.data(), d_out, out_bytes, cudaMemcpyDeviceToHost, context.device().stream()) == cudaSuccess);
+ assert(cudaStreamSynchronize(context.device().stream()) == cudaSuccess);
+ for (int i = 0; i < 39; ++i) {
+ for (int j = 0; j < 49; ++j) {
+ for (int k = 0; k < 69; ++k) {
+ const float result = out(i,j,k);
+ const float expected = (in1(i,j,k) * 3.14f + in1(i,j+1,k) * 2.7f +
+ in1(i,j,k+1) * 0.2f + in1(i,j+1,k+1) * 7.0f +
+ in1(i+1,j,k) * -1.0f + in1(i+1,j+1,k) * -0.3f +
+ in1(i+1,j,k+1) * -0.7f + in1(i+1,j+1,k+1) * -0.5f);
+ VERIFY_IS_APPROX(expected, result);
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_device()
+{
+ CALL_SUBTEST(test_cpu());
+ CALL_SUBTEST(test_gpu());
+}
diff --git a/unsupported/test/cxx11_tensor_dimension.cpp b/unsupported/test/cxx11_tensor_dimension.cpp
new file mode 100644
index 000000000..0cc4e86f7
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_dimension.cpp
@@ -0,0 +1,54 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+
+static void test_dynamic_size()
+{
+ Eigen::DSizes<int, 3> dimensions(2,3,7);
+
+ VERIFY_IS_EQUAL((int)Eigen::internal::array_get<0>(dimensions), 2);
+ VERIFY_IS_EQUAL((int)Eigen::internal::array_get<1>(dimensions), 3);
+ VERIFY_IS_EQUAL((int)Eigen::internal::array_get<2>(dimensions), 7);
+ VERIFY_IS_EQUAL(dimensions.TotalSize(), (size_t)2*3*7);
+ VERIFY_IS_EQUAL((int)dimensions[0], 2);
+ VERIFY_IS_EQUAL((int)dimensions[1], 3);
+ VERIFY_IS_EQUAL((int)dimensions[2], 7);
+}
+
+static void test_fixed_size()
+{
+ Eigen::Sizes<2,3,7> dimensions;
+
+ VERIFY_IS_EQUAL((int)Eigen::internal::array_get<0>(dimensions), 2);
+ VERIFY_IS_EQUAL((int)Eigen::internal::array_get<1>(dimensions), 3);
+ VERIFY_IS_EQUAL((int)Eigen::internal::array_get<2>(dimensions), 7);
+ VERIFY_IS_EQUAL(dimensions.TotalSize(), (size_t)2*3*7);
+}
+
+
+static void test_match()
+{
+ Eigen::DSizes<int, 3> dyn(2,3,7);
+ Eigen::Sizes<2,3,7> stat;
+ VERIFY_IS_EQUAL(Eigen::dimensions_match(dyn, stat), true);
+}
+
+
+void test_cxx11_tensor_dimension()
+{
+ CALL_SUBTEST(test_dynamic_size());
+ CALL_SUBTEST(test_fixed_size());
+ CALL_SUBTEST(test_match());
+}
diff --git a/unsupported/test/cxx11_tensor_expr.cpp b/unsupported/test/cxx11_tensor_expr.cpp
new file mode 100644
index 000000000..695565e9b
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_expr.cpp
@@ -0,0 +1,314 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+static void test_1d()
+{
+ Tensor<float, 1> vec1({6});
+ Tensor<float, 1, RowMajor> vec2({6});
+
+ vec1(0) = 4.0; vec2(0) = 0.0;
+ vec1(1) = 8.0; vec2(1) = 1.0;
+ vec1(2) = 15.0; vec2(2) = 2.0;
+ vec1(3) = 16.0; vec2(3) = 3.0;
+ vec1(4) = 23.0; vec2(4) = 4.0;
+ vec1(5) = 42.0; vec2(5) = 5.0;
+
+ float data3[6];
+ TensorMap<Tensor<float, 1>> vec3(data3, 6);
+ vec3 = vec1.sqrt();
+ float data4[6];
+ TensorMap<Tensor<float, 1, RowMajor>> vec4(data4, 6);
+ vec4 = vec2.square();
+ float data5[6];
+ TensorMap<Tensor<float, 1, RowMajor>> vec5(data5, 6);
+ vec5 = vec2.cube();
+
+ VERIFY_IS_APPROX(vec3(0), sqrtf(4.0));
+ VERIFY_IS_APPROX(vec3(1), sqrtf(8.0));
+ VERIFY_IS_APPROX(vec3(2), sqrtf(15.0));
+ VERIFY_IS_APPROX(vec3(3), sqrtf(16.0));
+ VERIFY_IS_APPROX(vec3(4), sqrtf(23.0));
+ VERIFY_IS_APPROX(vec3(5), sqrtf(42.0));
+
+ VERIFY_IS_APPROX(vec4(0), 0.0f);
+ VERIFY_IS_APPROX(vec4(1), 1.0f);
+ VERIFY_IS_APPROX(vec4(2), 2.0f * 2.0f);
+ VERIFY_IS_APPROX(vec4(3), 3.0f * 3.0f);
+ VERIFY_IS_APPROX(vec4(4), 4.0f * 4.0f);
+ VERIFY_IS_APPROX(vec4(5), 5.0f * 5.0f);
+
+ VERIFY_IS_APPROX(vec5(0), 0.0f);
+ VERIFY_IS_APPROX(vec5(1), 1.0f);
+ VERIFY_IS_APPROX(vec5(2), 2.0f * 2.0f * 2.0f);
+ VERIFY_IS_APPROX(vec5(3), 3.0f * 3.0f * 3.0f);
+ VERIFY_IS_APPROX(vec5(4), 4.0f * 4.0f * 4.0f);
+ VERIFY_IS_APPROX(vec5(5), 5.0f * 5.0f * 5.0f);
+
+ vec3 = vec1 + vec2;
+ VERIFY_IS_APPROX(vec3(0), 4.0f + 0.0f);
+ VERIFY_IS_APPROX(vec3(1), 8.0f + 1.0f);
+ VERIFY_IS_APPROX(vec3(2), 15.0f + 2.0f);
+ VERIFY_IS_APPROX(vec3(3), 16.0f + 3.0f);
+ VERIFY_IS_APPROX(vec3(4), 23.0f + 4.0f);
+ VERIFY_IS_APPROX(vec3(5), 42.0f + 5.0f);
+}
+
+static void test_2d()
+{
+ float data1[6];
+ TensorMap<Tensor<float, 2>> mat1(data1, 2, 3);
+ float data2[6];
+ TensorMap<Tensor<float, 2, RowMajor>> mat2(data2, 2, 3);
+
+ mat1(0,0) = 0.0;
+ mat1(0,1) = 1.0;
+ mat1(0,2) = 2.0;
+ mat1(1,0) = 3.0;
+ mat1(1,1) = 4.0;
+ mat1(1,2) = 5.0;
+
+ mat2(0,0) = -0.0;
+ mat2(0,1) = -1.0;
+ mat2(0,2) = -2.0;
+ mat2(1,0) = -3.0;
+ mat2(1,1) = -4.0;
+ mat2(1,2) = -5.0;
+
+ Tensor<float, 2> mat3(2,3);
+ Tensor<float, 2, RowMajor> mat4(2,3);
+ mat3 = mat1.abs();
+ mat4 = mat2.abs();
+
+ VERIFY_IS_APPROX(mat3(0,0), 0.0f);
+ VERIFY_IS_APPROX(mat3(0,1), 1.0f);
+ VERIFY_IS_APPROX(mat3(0,2), 2.0f);
+ VERIFY_IS_APPROX(mat3(1,0), 3.0f);
+ VERIFY_IS_APPROX(mat3(1,1), 4.0f);
+ VERIFY_IS_APPROX(mat3(1,2), 5.0f);
+
+ VERIFY_IS_APPROX(mat4(0,0), 0.0f);
+ VERIFY_IS_APPROX(mat4(0,1), 1.0f);
+ VERIFY_IS_APPROX(mat4(0,2), 2.0f);
+ VERIFY_IS_APPROX(mat4(1,0), 3.0f);
+ VERIFY_IS_APPROX(mat4(1,1), 4.0f);
+ VERIFY_IS_APPROX(mat4(1,2), 5.0f);
+}
+
+static void test_3d()
+{
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3, RowMajor> mat2(2,3,7);
+
+ float val = 1.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ mat2(i,j,k) = val;
+ val += 1.0;
+ }
+ }
+ }
+
+ Tensor<float, 3> mat3(2,3,7);
+ mat3 = mat1 + mat1;
+ Tensor<float, 3, RowMajor> mat4(2,3,7);
+ mat4 = mat2 * 3.14f;
+ Tensor<float, 3> mat5(2,3,7);
+ mat5 = mat1.inverse().log();
+ Tensor<float, 3, RowMajor> mat6(2,3,7);
+ mat6 = mat2.pow(0.5f) * 3.14f;
+ Tensor<float, 3> mat7(2,3,7);
+ mat7 = mat1.cwiseMax(mat5 * 2.0f).exp();
+ Tensor<float, 3, RowMajor> mat8(2,3,7);
+ mat8 = (-mat2).exp() * 3.14f;
+ Tensor<float, 3, RowMajor> mat9(2,3,7);
+ mat9 = mat2 + 3.14f;
+ Tensor<float, 3, RowMajor> mat10(2,3,7);
+ mat10 = mat2 - 3.14f;
+ Tensor<float, 3, RowMajor> mat11(2,3,7);
+ mat11 = mat2 / 3.14f;
+
+ val = 1.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat3(i,j,k), val + val);
+ VERIFY_IS_APPROX(mat4(i,j,k), val * 3.14f);
+ VERIFY_IS_APPROX(mat5(i,j,k), logf(1.0f/val));
+ VERIFY_IS_APPROX(mat6(i,j,k), sqrtf(val) * 3.14f);
+ VERIFY_IS_APPROX(mat7(i,j,k), expf((std::max)(val, mat5(i,j,k) * 2.0f)));
+ VERIFY_IS_APPROX(mat8(i,j,k), expf(-val) * 3.14f);
+ VERIFY_IS_APPROX(mat9(i,j,k), val + 3.14f);
+ VERIFY_IS_APPROX(mat10(i,j,k), val - 3.14f);
+ VERIFY_IS_APPROX(mat11(i,j,k), val / 3.14f);
+ val += 1.0;
+ }
+ }
+ }
+}
+
+static void test_constants()
+{
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+ Tensor<float, 3> mat3(2,3,7);
+
+ float val = 1.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ val += 1.0;
+ }
+ }
+ }
+ mat2 = mat1.constant(3.14f);
+ mat3 = mat1.cwiseMax(7.3f).exp();
+
+ val = 1.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat2(i,j,k), 3.14f);
+ VERIFY_IS_APPROX(mat3(i,j,k), expf((std::max)(val, 7.3f)));
+ val += 1.0;
+ }
+ }
+ }
+}
+
+static void test_boolean()
+{
+ Tensor<int, 1> vec(6);
+ std::copy_n(std::begin({0, 1, 2, 3, 4, 5}), 6, vec.data());
+
+ // Test ||.
+ Tensor<bool, 1> bool1 = vec < vec.constant(1) || vec > vec.constant(4);
+ VERIFY_IS_EQUAL(bool1[0], true);
+ VERIFY_IS_EQUAL(bool1[1], false);
+ VERIFY_IS_EQUAL(bool1[2], false);
+ VERIFY_IS_EQUAL(bool1[3], false);
+ VERIFY_IS_EQUAL(bool1[4], false);
+ VERIFY_IS_EQUAL(bool1[5], true);
+
+ // Test &&, including cast of operand vec.
+ Tensor<bool, 1> bool2 = vec.cast<bool>() && vec < vec.constant(4);
+ VERIFY_IS_EQUAL(bool2[0], false);
+ VERIFY_IS_EQUAL(bool2[1], true);
+ VERIFY_IS_EQUAL(bool2[2], true);
+ VERIFY_IS_EQUAL(bool2[3], true);
+ VERIFY_IS_EQUAL(bool2[4], false);
+ VERIFY_IS_EQUAL(bool2[5], false);
+
+ // Compilation tests:
+ // Test Tensor<bool> against results of cast or comparison; verifies that
+ // CoeffReturnType is set to match Op return type of bool for Unary and Binary
+ // Ops.
+ Tensor<bool, 1> bool3 = vec.cast<bool>() && bool2;
+ bool3 = vec < vec.constant(4) && bool2;
+}
+
+static void test_functors()
+{
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+ Tensor<float, 3> mat3(2,3,7);
+
+ float val = 1.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ val += 1.0;
+ }
+ }
+ }
+ mat2 = mat1.inverse().unaryExpr(&asinf);
+ mat3 = mat1.unaryExpr(&tanhf);
+
+ val = 1.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat2(i,j,k), asinf(1.0f / mat1(i,j,k)));
+ VERIFY_IS_APPROX(mat3(i,j,k), tanhf(mat1(i,j,k)));
+ val += 1.0;
+ }
+ }
+ }
+}
+
+static void test_type_casting()
+{
+ Tensor<bool, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+ Tensor<double, 3> mat3(2,3,7);
+ mat1.setRandom();
+ mat2.setRandom();
+
+ mat3 = mat1.template cast<double>();
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat3(i,j,k), mat1(i,j,k) ? 1.0 : 0.0);
+ }
+ }
+ }
+
+ mat3 = mat2.template cast<double>();
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat3(i,j,k), static_cast<double>(mat2(i,j,k)));
+ }
+ }
+ }
+}
+
+static void test_select()
+{
+ Tensor<float, 3> selector(2,3,7);
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+ Tensor<float, 3> result(2,3,7);
+
+ selector.setRandom();
+ mat1.setRandom();
+ mat2.setRandom();
+ result = (selector > selector.constant(0.5f)).select(mat1, mat2);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(result(i,j,k), (selector(i,j,k) > 0.5f) ? mat1(i,j,k) : mat2(i,j,k));
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_expr()
+{
+ CALL_SUBTEST(test_1d());
+ CALL_SUBTEST(test_2d());
+ CALL_SUBTEST(test_3d());
+ CALL_SUBTEST(test_constants());
+ CALL_SUBTEST(test_boolean());
+ CALL_SUBTEST(test_functors());
+ CALL_SUBTEST(test_type_casting());
+ CALL_SUBTEST(test_select());
+}
diff --git a/unsupported/test/cxx11_tensor_fixed_size.cpp b/unsupported/test/cxx11_tensor_fixed_size.cpp
new file mode 100644
index 000000000..8a27f5ad8
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_fixed_size.cpp
@@ -0,0 +1,198 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+
+static void test_1d()
+{
+ TensorFixedSize<float, Sizes<6> > vec1;
+ TensorFixedSize<float, Sizes<6>, RowMajor> vec2;
+
+ VERIFY_IS_EQUAL((vec1.size()), 6);
+ // VERIFY_IS_EQUAL((vec1.dimensions()[0]), 6);
+ // VERIFY_IS_EQUAL((vec1.dimension(0)), 6);
+
+ vec1(0) = 4.0; vec2(0) = 0.0;
+ vec1(1) = 8.0; vec2(1) = 1.0;
+ vec1(2) = 15.0; vec2(2) = 2.0;
+ vec1(3) = 16.0; vec2(3) = 3.0;
+ vec1(4) = 23.0; vec2(4) = 4.0;
+ vec1(5) = 42.0; vec2(5) = 5.0;
+
+ float data3[6];
+ TensorMap<TensorFixedSize<float, Sizes<6> > > vec3(data3, 6);
+ vec3 = vec1.sqrt();
+ float data4[6];
+ TensorMap<TensorFixedSize<float, Sizes<6>, RowMajor> > vec4(data4, 6);
+ vec4 = vec2.sqrt();
+
+ VERIFY_IS_EQUAL((vec3.size()), 6);
+ VERIFY_IS_EQUAL(vec3.rank(), 1);
+ // VERIFY_IS_EQUAL((vec3.dimensions()[0]), 6);
+ // VERIFY_IS_EQUAL((vec3.dimension(0)), 6);
+
+ VERIFY_IS_APPROX(vec3(0), sqrtf(4.0));
+ VERIFY_IS_APPROX(vec3(1), sqrtf(8.0));
+ VERIFY_IS_APPROX(vec3(2), sqrtf(15.0));
+ VERIFY_IS_APPROX(vec3(3), sqrtf(16.0));
+ VERIFY_IS_APPROX(vec3(4), sqrtf(23.0));
+ VERIFY_IS_APPROX(vec3(5), sqrtf(42.0));
+
+ VERIFY_IS_APPROX(vec4(0), sqrtf(0.0));
+ VERIFY_IS_APPROX(vec4(1), sqrtf(1.0));
+ VERIFY_IS_APPROX(vec4(2), sqrtf(2.0));
+ VERIFY_IS_APPROX(vec4(3), sqrtf(3.0));
+ VERIFY_IS_APPROX(vec4(4), sqrtf(4.0));
+ VERIFY_IS_APPROX(vec4(5), sqrtf(5.0));
+
+ vec3 = vec1 + vec2;
+ VERIFY_IS_APPROX(vec3(0), 4.0f + 0.0f);
+ VERIFY_IS_APPROX(vec3(1), 8.0f + 1.0f);
+ VERIFY_IS_APPROX(vec3(2), 15.0f + 2.0f);
+ VERIFY_IS_APPROX(vec3(3), 16.0f + 3.0f);
+ VERIFY_IS_APPROX(vec3(4), 23.0f + 4.0f);
+ VERIFY_IS_APPROX(vec3(5), 42.0f + 5.0f);
+}
+
+static void test_2d()
+{
+ float data1[6];
+ TensorMap<TensorFixedSize<float, Sizes<2, 3> >> mat1(data1,2,3);
+ float data2[6];
+ TensorMap<TensorFixedSize<float, Sizes<2, 3>, RowMajor>> mat2(data2,2,3);
+
+ VERIFY_IS_EQUAL((mat1.size()), 2*3);
+ VERIFY_IS_EQUAL(mat1.rank(), 2);
+ // VERIFY_IS_EQUAL((mat1.dimension(0)), 2);
+ // VERIFY_IS_EQUAL((mat1.dimension(1)), 3);
+
+ mat1(0,0) = 0.0;
+ mat1(0,1) = 1.0;
+ mat1(0,2) = 2.0;
+ mat1(1,0) = 3.0;
+ mat1(1,1) = 4.0;
+ mat1(1,2) = 5.0;
+
+ mat2(0,0) = -0.0;
+ mat2(0,1) = -1.0;
+ mat2(0,2) = -2.0;
+ mat2(1,0) = -3.0;
+ mat2(1,1) = -4.0;
+ mat2(1,2) = -5.0;
+
+ TensorFixedSize<float, Sizes<2, 3>> mat3;
+ TensorFixedSize<float, Sizes<2, 3>, RowMajor> mat4;
+ mat3 = mat1.abs();
+ mat4 = mat2.abs();
+
+ VERIFY_IS_EQUAL((mat3.size()), 2*3);
+ // VERIFY_IS_EQUAL((mat3.dimension(0)), 2);
+ // VERIFY_IS_EQUAL((mat3.dimension(1)), 3);
+
+ VERIFY_IS_APPROX(mat3(0,0), 0.0f);
+ VERIFY_IS_APPROX(mat3(0,1), 1.0f);
+ VERIFY_IS_APPROX(mat3(0,2), 2.0f);
+ VERIFY_IS_APPROX(mat3(1,0), 3.0f);
+ VERIFY_IS_APPROX(mat3(1,1), 4.0f);
+ VERIFY_IS_APPROX(mat3(1,2), 5.0f);
+
+ VERIFY_IS_APPROX(mat4(0,0), 0.0f);
+ VERIFY_IS_APPROX(mat4(0,1), 1.0f);
+ VERIFY_IS_APPROX(mat4(0,2), 2.0f);
+ VERIFY_IS_APPROX(mat4(1,0), 3.0f);
+ VERIFY_IS_APPROX(mat4(1,1), 4.0f);
+ VERIFY_IS_APPROX(mat4(1,2), 5.0f);
+}
+
+static void test_3d()
+{
+ TensorFixedSize<float, Sizes<2, 3, 7> > mat1;
+ TensorFixedSize<float, Sizes<2, 3, 7>, RowMajor> mat2;
+
+ VERIFY_IS_EQUAL((mat1.size()), 2*3*7);
+ VERIFY_IS_EQUAL(mat1.rank(), 3);
+ // VERIFY_IS_EQUAL((mat1.dimension(0)), 2);
+ // VERIFY_IS_EQUAL((mat1.dimension(1)), 3);
+ // VERIFY_IS_EQUAL((mat1.dimension(2)), 7);
+
+ float val = 0.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ mat2(i,j,k) = val;
+ val += 1.0;
+ }
+ }
+ }
+
+ TensorFixedSize<float, Sizes<2, 3, 7> > mat3;
+ mat3 = mat1.sqrt();
+ TensorFixedSize<float, Sizes<2, 3, 7>, RowMajor> mat4;
+ mat4 = mat2.sqrt();
+
+ VERIFY_IS_EQUAL((mat3.size()), 2*3*7);
+ // VERIFY_IS_EQUAL((mat3.dimension(0)), 2);
+ // VERIFY_IS_EQUAL((mat3.dimension(1)), 3);
+ // VERIFY_IS_EQUAL((mat3.dimension(2)), 7);
+
+
+ val = 0.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat3(i,j,k), sqrtf(val));
+ VERIFY_IS_APPROX(mat4(i,j,k), sqrtf(val));
+ val += 1.0;
+ }
+ }
+ }
+}
+
+
+static void test_array()
+{
+ TensorFixedSize<float, Sizes<2, 3, 7> > mat1;
+ float val = 0.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ val += 1.0;
+ }
+ }
+ }
+
+ TensorFixedSize<float, Sizes<2, 3, 7> > mat3;
+ mat3 = mat1.pow(3.5f);
+
+ val = 0.0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat3(i,j,k), powf(val, 3.5f));
+ val += 1.0;
+ }
+ }
+ }
+}
+
+void test_cxx11_tensor_fixed_size()
+{
+ CALL_SUBTEST(test_1d());
+ CALL_SUBTEST(test_2d());
+ CALL_SUBTEST(test_3d());
+ CALL_SUBTEST(test_array());
+}
diff --git a/unsupported/test/cxx11_tensor_forced_eval.cpp b/unsupported/test/cxx11_tensor_forced_eval.cpp
new file mode 100644
index 000000000..ad9de867d
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_forced_eval.cpp
@@ -0,0 +1,78 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/Core>
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::MatrixXf;
+using Eigen::Tensor;
+
+static void test_simple()
+{
+ MatrixXf m1(3,3);
+ MatrixXf m2(3,3);
+ m1.setRandom();
+ m2.setRandom();
+
+ TensorMap<Tensor<float, 2>> mat1(m1.data(), 3,3);
+ TensorMap<Tensor<float, 2>> mat2(m2.data(), 3,3);
+
+ Tensor<float, 2> mat3(3,3);
+ mat3 = mat1;
+
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ Eigen::array<DimPair, 1> dims({{DimPair(1, 0)}});
+
+ mat3 = mat3.contract(mat2, dims).eval();
+
+ VERIFY_IS_APPROX(mat3(0, 0), (m1*m2).eval()(0,0));
+ VERIFY_IS_APPROX(mat3(0, 1), (m1*m2).eval()(0,1));
+ VERIFY_IS_APPROX(mat3(0, 2), (m1*m2).eval()(0,2));
+ VERIFY_IS_APPROX(mat3(1, 0), (m1*m2).eval()(1,0));
+ VERIFY_IS_APPROX(mat3(1, 1), (m1*m2).eval()(1,1));
+ VERIFY_IS_APPROX(mat3(1, 2), (m1*m2).eval()(1,2));
+ VERIFY_IS_APPROX(mat3(2, 0), (m1*m2).eval()(2,0));
+ VERIFY_IS_APPROX(mat3(2, 1), (m1*m2).eval()(2,1));
+ VERIFY_IS_APPROX(mat3(2, 2), (m1*m2).eval()(2,2));
+}
+
+
+static void test_const()
+{
+ MatrixXf input(3,3);
+ input.setRandom();
+ MatrixXf output = input;
+ output.rowwise() -= input.colwise().maxCoeff();
+
+ Eigen::array<int, 1> depth_dim;
+ depth_dim[0] = 0;
+ Tensor<float, 2>::Dimensions dims2d;
+ dims2d[0] = 1;
+ dims2d[1] = 3;
+ Eigen::array<int, 2> bcast;
+ bcast[0] = 3;
+ bcast[1] = 1;
+ const TensorMap<Tensor<const float, 2>> input_tensor(input.data(), 3, 3);
+ Tensor<float, 2> output_tensor= (input_tensor - input_tensor.maximum(depth_dim).eval().reshape(dims2d).broadcast(bcast));
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_APPROX(output(i, j), output_tensor(i, j));
+ }
+ }
+}
+
+
+void test_cxx11_tensor_forced_eval()
+{
+ CALL_SUBTEST(test_simple());
+ CALL_SUBTEST(test_const());
+}
diff --git a/unsupported/test/cxx11_tensor_image_patch.cpp b/unsupported/test/cxx11_tensor_image_patch.cpp
new file mode 100644
index 000000000..26854f5a4
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_image_patch.cpp
@@ -0,0 +1,476 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+static void test_simple_patch()
+{
+ Tensor<float, 4> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ Tensor<float, 5> single_pixel_patch;
+ single_pixel_patch = tensor.extract_image_patches<1, 1>();
+
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(1), 1);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(2), 1);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(3), 3*5);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(4), 7);
+
+ for (int i = 0; i < tensor.size(); ++i) {
+ if (tensor.data()[i] != single_pixel_patch.data()[i]) {
+ std::cout << "Mismatch detected at index " << i << " : " << tensor.data()[i] << " vs " << single_pixel_patch.data()[i] << std::endl;
+ }
+ VERIFY_IS_EQUAL(single_pixel_patch.data()[i], tensor.data()[i]);
+ }
+
+ Tensor<float, 5> entire_image_patch;
+ entire_image_patch = tensor.extract_image_patches<3, 5>();
+
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(1), 3);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(2), 5);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(3), 3*5);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(4), 7);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ int patchId = i+3*j;
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 5; ++c) {
+ for (int d = 0; d < 2; ++d) {
+ for (int b = 0; b < 7; ++b) {
+ float expected = 0.0f;
+ if (r-1+i >= 0 && c-2+j >= 0 && r-1+i < 3 && c-2+j < 5) {
+ expected = tensor(d, r-1+i, c-2+j, b);
+ }
+ if (entire_image_patch(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(entire_image_patch(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 5> twod_patch;
+ twod_patch = tensor.extract_image_patches<2, 2>();
+
+ VERIFY_IS_EQUAL(twod_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(1), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(2), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(3), 3*5);
+ VERIFY_IS_EQUAL(twod_patch.dimension(4), 7);
+
+ // Based on the calculation described in TensorTraits.h, padding happens to be 0.
+ int row_padding = 0;
+ int col_padding = 0;
+ int stride = 1;
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ int patchId = i+3*j;
+ for (int r = 0; r < 2; ++r) {
+ for (int c = 0; c < 2; ++c) {
+ for (int d = 0; d < 2; ++d) {
+ for (int b = 0; b < 7; ++b) {
+ float expected = 0.0f;
+ int row_offset = r*stride + i - row_padding;
+ int col_offset = c*stride + j - col_padding;
+ if (row_offset >= 0 && col_offset >= 0 && row_offset < tensor.dimension(1) && col_offset < tensor.dimension(2)) {
+ expected = tensor(d, row_offset, col_offset, b);
+ }
+ if (twod_patch(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(twod_patch(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Verifies VALID padding (no padding) with incrementing values.
+static void test_patch_padding_valid()
+{
+ int input_depth = 3;
+ int input_rows = 3;
+ int input_cols = 3;
+ int input_batches = 1;
+ int ksize = 2; // Corresponds to the Rows and Cols for tensor.extract_image_patches<>.
+ int stride = 2; // Only same stride is supported.
+ Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
+ // Initializes tensor with incrementing numbers.
+ for (int i = 0; i < tensor.size(); ++i) {
+ tensor.data()[i] = i + 1;
+ }
+ Tensor<float, 5> result = tensor.extract_image_patches(ksize, ksize, stride, stride, PADDING_VALID);
+
+ VERIFY_IS_EQUAL(result.dimension(0), input_depth); // depth
+ VERIFY_IS_EQUAL(result.dimension(1), ksize); // kernel rows
+ VERIFY_IS_EQUAL(result.dimension(2), ksize); // kernel cols
+ VERIFY_IS_EQUAL(result.dimension(3), 1); // number of patches
+ VERIFY_IS_EQUAL(result.dimension(4), input_batches); // number of batches
+
+ // No padding is carried out.
+ int row_padding = 0;
+ int col_padding = 0;
+
+ for (int i = 0; (i+stride+ksize-1) < input_rows; i += stride) { // input rows
+ for (int j = 0; (j+stride+ksize-1) < input_cols; j += stride) { // input cols
+ int patchId = i+input_rows*j;
+ for (int r = 0; r < ksize; ++r) { // patch rows
+ for (int c = 0; c < ksize; ++c) { // patch cols
+ for (int d = 0; d < input_depth; ++d) { // depth
+ for (int b = 0; b < input_batches; ++b) { // batch
+ float expected = 0.0f;
+ int row_offset = r + i - row_padding;
+ int col_offset = c + j - col_padding;
+ if (row_offset >= 0 && col_offset >= 0 && row_offset < input_rows && col_offset < input_cols) {
+ expected = tensor(d, row_offset, col_offset, b);
+ }
+ if (result(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(result(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Verifies VALID padding (no padding) with the same value.
+static void test_patch_padding_valid_same_value()
+{
+ int input_depth = 1;
+ int input_rows = 5;
+ int input_cols = 5;
+ int input_batches = 2;
+ int ksize = 3; // Corresponds to the Rows and Cols for tensor.extract_image_patches<>.
+ int stride = 2; // Only same stride is supported.
+ Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
+ tensor = tensor.constant(11.0f);
+ Tensor<float, 5> result = tensor.extract_image_patches(ksize, ksize, stride, stride, PADDING_VALID);
+
+ VERIFY_IS_EQUAL(result.dimension(0), input_depth); // depth
+ VERIFY_IS_EQUAL(result.dimension(1), ksize); // kernel rows
+ VERIFY_IS_EQUAL(result.dimension(2), ksize); // kernel cols
+ VERIFY_IS_EQUAL(result.dimension(3), 4); // number of patches
+ VERIFY_IS_EQUAL(result.dimension(4), input_batches); // number of batches
+
+ // No padding is carried out.
+ int row_padding = 0;
+ int col_padding = 0;
+
+ for (int i = 0; (i+stride+ksize-1) <= input_rows; i += stride) { // input rows
+ for (int j = 0; (j+stride+ksize-1) <= input_cols; j += stride) { // input cols
+ int patchId = i+input_rows*j;
+ for (int r = 0; r < ksize; ++r) { // patch rows
+ for (int c = 0; c < ksize; ++c) { // patch cols
+ for (int d = 0; d < input_depth; ++d) { // depth
+ for (int b = 0; b < input_batches; ++b) { // batch
+ float expected = 0.0f;
+ int row_offset = r + i - row_padding;
+ int col_offset = c + j - col_padding;
+ if (row_offset >= 0 && col_offset >= 0 && row_offset < input_rows && col_offset < input_cols) {
+ expected = tensor(d, row_offset, col_offset, b);
+ }
+ if (result(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(result(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Verifies SAME padding.
+static void test_patch_padding_same()
+{
+ int input_depth = 3;
+ int input_rows = 4;
+ int input_cols = 2;
+ int input_batches = 1;
+ int ksize = 2; // Corresponds to the Rows and Cols for tensor.extract_image_patches<>.
+ int stride = 2; // Only same stride is supported.
+ Tensor<float, 4> tensor(input_depth, input_rows, input_cols, input_batches);
+ // Initializes tensor with incrementing numbers.
+ for (int i = 0; i < tensor.size(); ++i) {
+ tensor.data()[i] = i + 1;
+ }
+ Tensor<float, 5> result = tensor.extract_image_patches(ksize, ksize, stride, stride, PADDING_SAME);
+
+ VERIFY_IS_EQUAL(result.dimension(0), input_depth); // depth
+ VERIFY_IS_EQUAL(result.dimension(1), ksize); // kernel rows
+ VERIFY_IS_EQUAL(result.dimension(2), ksize); // kernel cols
+ VERIFY_IS_EQUAL(result.dimension(3), 2); // number of patches
+ VERIFY_IS_EQUAL(result.dimension(4), input_batches); // number of batches
+
+ // Based on the calculation described in TensorTraits.h, padding happens to be
+ // 0.
+ int row_padding = 0;
+ int col_padding = 0;
+
+ for (int i = 0; (i+stride+ksize-1) <= input_rows; i += stride) { // input rows
+ for (int j = 0; (j+stride+ksize-1) <= input_cols; j += stride) { // input cols
+ int patchId = i+input_rows*j;
+ for (int r = 0; r < ksize; ++r) { // patch rows
+ for (int c = 0; c < ksize; ++c) { // patch cols
+ for (int d = 0; d < input_depth; ++d) { // depth
+ for (int b = 0; b < input_batches; ++b) { // batch
+ float expected = 0.0f;
+ int row_offset = r*stride + i - row_padding;
+ int col_offset = c*stride + j - col_padding;
+ if (row_offset >= 0 && col_offset >= 0 && row_offset < input_rows && col_offset < input_cols) {
+ expected = tensor(d, row_offset, col_offset, b);
+ }
+ if (result(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(result(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void test_patch_no_extra_dim()
+{
+ Tensor<float, 3> tensor(2,3,5);
+ tensor.setRandom();
+
+ Tensor<float, 4> single_pixel_patch;
+ single_pixel_patch = tensor.extract_image_patches<1, 1>();
+
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(1), 1);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(2), 1);
+ VERIFY_IS_EQUAL(single_pixel_patch.dimension(3), 3*5);
+
+ for (int i = 0; i < tensor.size(); ++i) {
+ if (tensor.data()[i] != single_pixel_patch.data()[i]) {
+ std::cout << "Mismatch detected at index " << i << " : " << tensor.data()[i] << " vs " << single_pixel_patch.data()[i] << std::endl;
+ }
+ VERIFY_IS_EQUAL(single_pixel_patch.data()[i], tensor.data()[i]);
+ }
+
+ Tensor<float, 4> entire_image_patch;
+ entire_image_patch = tensor.extract_image_patches<3, 5>();
+
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(1), 3);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(2), 5);
+ VERIFY_IS_EQUAL(entire_image_patch.dimension(3), 3*5);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ int patchId = i+3*j;
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 5; ++c) {
+ for (int d = 0; d < 2; ++d) {
+ float expected = 0.0f;
+ if (r-1+i >= 0 && c-2+j >= 0 && r-1+i < 3 && c-2+j < 5) {
+ expected = tensor(d, r-1+i, c-2+j);
+ }
+ if (entire_image_patch(d, r, c, patchId) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << std::endl;
+ }
+ VERIFY_IS_EQUAL(entire_image_patch(d, r, c, patchId), expected);
+ }
+ }
+ }
+ }
+ }
+
+ Tensor<float, 4> twod_patch;
+ twod_patch = tensor.extract_image_patches<2, 2>();
+
+ VERIFY_IS_EQUAL(twod_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(1), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(2), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(3), 3*5);
+
+ // Based on the calculation described in TensorTraits.h, padding happens to be 0.
+ int row_padding = 0;
+ int col_padding = 0;
+ int stride = 1;
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ int patchId = i+3*j;
+ for (int r = 0; r < 2; ++r) {
+ for (int c = 0; c < 2; ++c) {
+ for (int d = 0; d < 2; ++d) {
+ float expected = 0.0f;
+ int row_offset = r*stride + i - row_padding;
+ int col_offset = c*stride + j - col_padding;
+ if (row_offset >= 0 && col_offset >= 0 && row_offset < tensor.dimension(1) && col_offset < tensor.dimension(2)) {
+ expected = tensor(d, row_offset, col_offset);
+ }
+ if (twod_patch(d, r, c, patchId) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << std::endl;
+ }
+ VERIFY_IS_EQUAL(twod_patch(d, r, c, patchId), expected);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void test_imagenet_patches()
+{
+ // Test the code on typical configurations used by the 'imagenet' benchmarks at
+ // https://github.com/soumith/convnet-benchmarks
+ Tensor<float, 4> l_in(3, 128, 128, 128);
+ l_in.setRandom();
+ Tensor<float, 5> l_out = l_in.extract_image_patches(11, 11);
+ VERIFY_IS_EQUAL(l_out.dimension(0), 3);
+ VERIFY_IS_EQUAL(l_out.dimension(1), 11);
+ VERIFY_IS_EQUAL(l_out.dimension(2), 11);
+ VERIFY_IS_EQUAL(l_out.dimension(3), 128*128);
+ VERIFY_IS_EQUAL(l_out.dimension(4), 128);
+ for (int b = 0; b < 128; ++b) {
+ for (int i = 0; i < 128; ++i) {
+ for (int j = 0; j < 128; ++j) {
+ int patchId = i+128*j;
+ for (int c = 0; c < 11; ++c) {
+ for (int r = 0; r < 11; ++r) {
+ for (int d = 0; d < 3; ++d) {
+ float expected = 0.0f;
+ if (r-5+i >= 0 && c-5+j >= 0 && r-5+i < 128 && c-5+j < 128) {
+ expected = l_in(d, r-5+i, c-5+j, b);
+ }
+ if (l_out(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ l_in.resize(64, 64, 64, 128);
+ l_in.setRandom();
+ l_out = l_in.extract_image_patches(9, 9);
+ VERIFY_IS_EQUAL(l_out.dimension(0), 64);
+ VERIFY_IS_EQUAL(l_out.dimension(1), 9);
+ VERIFY_IS_EQUAL(l_out.dimension(2), 9);
+ VERIFY_IS_EQUAL(l_out.dimension(3), 64*64);
+ VERIFY_IS_EQUAL(l_out.dimension(4), 128);
+ for (int b = 0; b < 128; ++b) {
+ for (int i = 0; i < 64; ++i) {
+ for (int j = 0; j < 64; ++j) {
+ int patchId = i+64*j;
+ for (int c = 0; c < 9; ++c) {
+ for (int r = 0; r < 9; ++r) {
+ for (int d = 0; d < 64; ++d) {
+ float expected = 0.0f;
+ if (r-4+i >= 0 && c-4+j >= 0 && r-4+i < 64 && c-4+j < 64) {
+ expected = l_in(d, r-4+i, c-4+j, b);
+ }
+ if (l_out(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ l_in.resize(128, 16, 16, 128);
+ l_in.setRandom();
+ l_out = l_in.extract_image_patches(7, 7);
+ VERIFY_IS_EQUAL(l_out.dimension(0), 128);
+ VERIFY_IS_EQUAL(l_out.dimension(1), 7);
+ VERIFY_IS_EQUAL(l_out.dimension(2), 7);
+ VERIFY_IS_EQUAL(l_out.dimension(3), 16*16);
+ VERIFY_IS_EQUAL(l_out.dimension(4), 128);
+ for (int b = 0; b < 128; ++b) {
+ for (int i = 0; i < 16; ++i) {
+ for (int j = 0; j < 16; ++j) {
+ int patchId = i+16*j;
+ for (int c = 0; c < 7; ++c) {
+ for (int r = 0; r < 7; ++r) {
+ for (int d = 0; d < 128; ++d) {
+ float expected = 0.0f;
+ if (r-3+i >= 0 && c-3+j >= 0 && r-3+i < 16 && c-3+j < 16) {
+ expected = l_in(d, r-3+i, c-3+j, b);
+ }
+ if (l_out(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ l_in.resize(384, 13, 13, 128);
+ l_in.setRandom();
+ l_out = l_in.extract_image_patches(3, 3);
+ VERIFY_IS_EQUAL(l_out.dimension(0), 384);
+ VERIFY_IS_EQUAL(l_out.dimension(1), 3);
+ VERIFY_IS_EQUAL(l_out.dimension(2), 3);
+ VERIFY_IS_EQUAL(l_out.dimension(3), 13*13);
+ VERIFY_IS_EQUAL(l_out.dimension(4), 128);
+ for (int b = 0; b < 128; ++b) {
+ for (int i = 0; i < 13; ++i) {
+ for (int j = 0; j < 13; ++j) {
+ int patchId = i+13*j;
+ for (int c = 0; c < 3; ++c) {
+ for (int r = 0; r < 3; ++r) {
+ for (int d = 0; d < 384; ++d) {
+ float expected = 0.0f;
+ if (r-1+i >= 0 && c-1+j >= 0 && r-1+i < 13 && c-1+j < 13) {
+ expected = l_in(d, r-1+i, c-1+j, b);
+ }
+ if (l_out(d, r, c, patchId, b) != expected) {
+ std::cout << "Mismatch detected at index i=" << i << " j=" << j << " r=" << r << " c=" << c << " d=" << d << " b=" << b << std::endl;
+ }
+ VERIFY_IS_EQUAL(l_out(d, r, c, patchId, b), expected);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void test_cxx11_tensor_image_patch()
+{
+ CALL_SUBTEST(test_simple_patch());
+ CALL_SUBTEST(test_patch_no_extra_dim());
+ CALL_SUBTEST(test_patch_padding_valid());
+ CALL_SUBTEST(test_patch_padding_valid_same_value());
+ CALL_SUBTEST(test_patch_padding_same());
+ CALL_SUBTEST(test_imagenet_patches());
+}
diff --git a/unsupported/test/cxx11_tensor_index_list.cpp b/unsupported/test/cxx11_tensor_index_list.cpp
new file mode 100644
index 000000000..c4d4f244f
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_index_list.cpp
@@ -0,0 +1,268 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+#ifdef EIGEN_HAS_CONSTEXPR
+
+static void test_static_index_list()
+{
+ Tensor<float, 4> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ constexpr auto reduction_axis = make_index_list(0, 1, 2);
+ VERIFY_IS_EQUAL(internal::array_get<0>(reduction_axis), 0);
+ VERIFY_IS_EQUAL(internal::array_get<1>(reduction_axis), 1);
+ VERIFY_IS_EQUAL(internal::array_get<2>(reduction_axis), 2);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[0]), 0);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[1]), 1);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[2]), 2);
+
+ EIGEN_STATIC_ASSERT((internal::array_get<0>(reduction_axis) == 0), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::array_get<1>(reduction_axis) == 1), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::array_get<2>(reduction_axis) == 2), YOU_MADE_A_PROGRAMMING_MISTAKE);
+
+ Tensor<float, 1> result = tensor.sum(reduction_axis);
+ for (int i = 0; i < result.size(); ++i) {
+ float expected = 0.0f;
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 5; ++l) {
+ expected += tensor(j,k,l,i);
+ }
+ }
+ }
+ VERIFY_IS_APPROX(result(i), expected);
+ }
+}
+
+
+static void test_type2index_list()
+{
+ Tensor<float, 5> tensor(2,3,5,7,11);
+ tensor.setRandom();
+ tensor += tensor.constant(10.0f);
+
+ typedef Eigen::IndexList<Eigen::type2index<0>> Dims0;
+ typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>> Dims1;
+ typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>, Eigen::type2index<2>> Dims2;
+ typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>, Eigen::type2index<2>, Eigen::type2index<3>> Dims3;
+ typedef Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1>, Eigen::type2index<2>, Eigen::type2index<3>, Eigen::type2index<4>> Dims4;
+
+#if 0
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims0>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims1>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims2>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims3>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<Dims4>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+#endif
+
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims0, 1, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims1, 2, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims2, 3, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims3, 4, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims4, 5, ColMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims0, 1, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims1, 2, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims2, 3, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims3, 4, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::are_inner_most_dims<Dims4, 5, RowMajor>::value == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+
+ const Dims0 reduction_axis0;
+ Tensor<float, 4> result0 = tensor.sum(reduction_axis0);
+ for (int m = 0; m < 11; ++m) {
+ for (int l = 0; l < 7; ++l) {
+ for (int k = 0; k < 5; ++k) {
+ for (int j = 0; j < 3; ++j) {
+ float expected = 0.0f;
+ for (int i = 0; i < 2; ++i) {
+ expected += tensor(i,j,k,l,m);
+ }
+ VERIFY_IS_APPROX(result0(j,k,l,m), expected);
+ }
+ }
+ }
+ }
+
+ const Dims1 reduction_axis1;
+ Tensor<float, 3> result1 = tensor.sum(reduction_axis1);
+ for (int m = 0; m < 11; ++m) {
+ for (int l = 0; l < 7; ++l) {
+ for (int k = 0; k < 5; ++k) {
+ float expected = 0.0f;
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < 2; ++i) {
+ expected += tensor(i,j,k,l,m);
+ }
+ }
+ VERIFY_IS_APPROX(result1(k,l,m), expected);
+ }
+ }
+ }
+
+ const Dims2 reduction_axis2;
+ Tensor<float, 2> result2 = tensor.sum(reduction_axis2);
+ for (int m = 0; m < 11; ++m) {
+ for (int l = 0; l < 7; ++l) {
+ float expected = 0.0f;
+ for (int k = 0; k < 5; ++k) {
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < 2; ++i) {
+ expected += tensor(i,j,k,l,m);
+ }
+ }
+ }
+ VERIFY_IS_APPROX(result2(l,m), expected);
+ }
+ }
+
+ const Dims3 reduction_axis3;
+ Tensor<float, 1> result3 = tensor.sum(reduction_axis3);
+ for (int m = 0; m < 11; ++m) {
+ float expected = 0.0f;
+ for (int l = 0; l < 7; ++l) {
+ for (int k = 0; k < 5; ++k) {
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < 2; ++i) {
+ expected += tensor(i,j,k,l,m);
+ }
+ }
+ }
+ }
+ VERIFY_IS_APPROX(result3(m), expected);
+ }
+
+ const Dims4 reduction_axis4;
+ Tensor<float, 1> result4 = tensor.sum(reduction_axis4);
+ float expected = 0.0f;
+ for (int m = 0; m < 11; ++m) {
+ for (int l = 0; l < 7; ++l) {
+ for (int k = 0; k < 5; ++k) {
+ for (int j = 0; j < 3; ++j) {
+ for (int i = 0; i < 2; ++i) {
+ expected += tensor(i,j,k,l,m);
+ }
+ }
+ }
+ }
+ }
+ VERIFY_IS_APPROX(result4(0), expected);
+}
+
+
+static void test_dynamic_index_list()
+{
+ Tensor<float, 4> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ int dim1 = 2;
+ int dim2 = 1;
+ int dim3 = 0;
+
+ auto reduction_axis = make_index_list(dim1, dim2, dim3);
+
+ VERIFY_IS_EQUAL(internal::array_get<0>(reduction_axis), 2);
+ VERIFY_IS_EQUAL(internal::array_get<1>(reduction_axis), 1);
+ VERIFY_IS_EQUAL(internal::array_get<2>(reduction_axis), 0);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[0]), 2);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[1]), 1);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[2]), 0);
+
+ Tensor<float, 1> result = tensor.sum(reduction_axis);
+ for (int i = 0; i < result.size(); ++i) {
+ float expected = 0.0f;
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 5; ++l) {
+ expected += tensor(j,k,l,i);
+ }
+ }
+ }
+ VERIFY_IS_APPROX(result(i), expected);
+ }
+}
+
+static void test_mixed_index_list()
+{
+ Tensor<float, 4> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ int dim2 = 1;
+ int dim4 = 3;
+
+ auto reduction_axis = make_index_list(0, dim2, 2, dim4);
+
+ VERIFY_IS_EQUAL(internal::array_get<0>(reduction_axis), 0);
+ VERIFY_IS_EQUAL(internal::array_get<1>(reduction_axis), 1);
+ VERIFY_IS_EQUAL(internal::array_get<2>(reduction_axis), 2);
+ VERIFY_IS_EQUAL(internal::array_get<3>(reduction_axis), 3);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[0]), 0);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[1]), 1);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[2]), 2);
+ VERIFY_IS_EQUAL(static_cast<DenseIndex>(reduction_axis[3]), 3);
+
+ typedef IndexList<type2index<0>, int, type2index<2>, int> ReductionIndices;
+ ReductionIndices reduction_indices;
+ reduction_indices.set(1, 1);
+ reduction_indices.set(3, 3);
+ EIGEN_STATIC_ASSERT((internal::array_get<0>(reduction_indices) == 0), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::array_get<2>(reduction_indices) == 2), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_known_statically<ReductionIndices>()(0) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_known_statically<ReductionIndices>()(2) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionIndices>()(0, 0) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionIndices>()(2, 2) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+#if 0
+ EIGEN_STATIC_ASSERT((internal::all_indices_known_statically<ReductionIndices>()() == false), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<ReductionIndices>()() == false), YOU_MADE_A_PROGRAMMING_MISTAKE);
+#endif
+
+ typedef IndexList<type2index<0>, type2index<1>, type2index<2>, type2index<3>> ReductionList;
+ ReductionList reduction_list;
+ EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(0, 0) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(1, 1) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(2, 2) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::index_statically_eq<ReductionList>()(3, 3) == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+#if 0
+ EIGEN_STATIC_ASSERT((internal::all_indices_known_statically<ReductionList>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+ EIGEN_STATIC_ASSERT((internal::indices_statically_known_to_increase<ReductionList>()() == true), YOU_MADE_A_PROGRAMMING_MISTAKE);
+#endif
+
+ Tensor<float, 1> result1 = tensor.sum(reduction_axis);
+ Tensor<float, 1> result2 = tensor.sum(reduction_indices);
+ Tensor<float, 1> result3 = tensor.sum(reduction_list);
+
+ float expected = 0.0f;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ expected += tensor(i,j,k,l);
+ }
+ }
+ }
+ }
+ VERIFY_IS_APPROX(result1(0), expected);
+ VERIFY_IS_APPROX(result2(0), expected);
+ VERIFY_IS_APPROX(result3(0), expected);
+}
+
+#endif
+
+void test_cxx11_tensor_index_list()
+{
+#ifdef EIGEN_HAS_CONSTEXPR
+ CALL_SUBTEST(test_static_index_list());
+ CALL_SUBTEST(test_type2index_list());
+ CALL_SUBTEST(test_dynamic_index_list());
+ CALL_SUBTEST(test_mixed_index_list());
+#endif
+}
diff --git a/unsupported/test/cxx11_tensor_intdiv.cpp b/unsupported/test/cxx11_tensor_intdiv.cpp
new file mode 100644
index 000000000..a510dc695
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_intdiv.cpp
@@ -0,0 +1,77 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+
+static void test_signed_32bit()
+{
+ for (int32_t i = 1; i < 25000; ++i) {
+ const Eigen::internal::TensorIntDivisor<int32_t> div(i);
+
+ for (int32_t j = 0; j < 25000; ++j) {
+ const int32_t fast_div = j / div;
+ const int32_t slow_div = j / i;
+ VERIFY_IS_EQUAL(fast_div, slow_div);
+ }
+ }
+}
+
+
+static void test_unsigned_32bit()
+{
+ for (uint32_t i = 1; i < 25000; ++i) {
+ const Eigen::internal::TensorIntDivisor<uint32_t> div(i);
+
+ for (uint32_t j = 0; j < 25000; ++j) {
+ const uint32_t fast_div = j / div;
+ const uint32_t slow_div = j / i;
+ VERIFY_IS_EQUAL(fast_div, slow_div);
+ }
+ }
+}
+
+
+static void test_signed_64bit()
+{
+ for (int64_t i = 2; i < 25000; ++i) {
+ const Eigen::internal::TensorIntDivisor<int64_t> div(i);
+
+ for (int64_t j = 0; j < 25000; ++j) {
+ const int64_t fast_div = j / div;
+ const int64_t slow_div = j / i;
+ VERIFY_IS_EQUAL(fast_div, slow_div);
+ }
+ }
+}
+
+
+static void test_unsigned_64bit()
+{
+ for (uint64_t i = 2; i < 25000; ++i) {
+ const Eigen::internal::TensorIntDivisor<uint64_t> div(i);
+
+ for (uint64_t j = 0; j < 25000; ++j) {
+ const uint64_t fast_div = j / div;
+ const uint64_t slow_div = j / i;
+ VERIFY_IS_EQUAL(fast_div, slow_div);
+ }
+ }
+}
+
+
+void test_cxx11_tensor_intdiv()
+{
+ CALL_SUBTEST(test_signed_32bit());
+ CALL_SUBTEST(test_unsigned_32bit());
+ CALL_SUBTEST(test_signed_64bit());
+ CALL_SUBTEST(test_unsigned_64bit());
+}
diff --git a/unsupported/test/cxx11_tensor_io.cpp b/unsupported/test/cxx11_tensor_io.cpp
new file mode 100644
index 000000000..8bbcf7089
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_io.cpp
@@ -0,0 +1,114 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+#include <sstream>
+#include <string>
+#include <Eigen/CXX11/Tensor>
+
+
+template<int DataLayout>
+static void test_output_1d()
+{
+ Tensor<int, 1, DataLayout> tensor(5);
+ for (int i = 0; i < 5; ++i) {
+ tensor(i) = i;
+ }
+
+ std::stringstream os;
+ os << tensor;
+
+ std::string expected("0\n1\n2\n3\n4");
+ VERIFY_IS_EQUAL(std::string(os.str()), expected);
+}
+
+
+template<int DataLayout>
+static void test_output_2d()
+{
+ Tensor<int, 2, DataLayout> tensor(5, 3);
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ tensor(i, j) = i*j;
+ }
+ }
+
+ std::stringstream os;
+ os << tensor;
+
+ std::string expected("0 0 0\n0 1 2\n0 2 4\n0 3 6\n0 4 8");
+ VERIFY_IS_EQUAL(std::string(os.str()), expected);
+}
+
+
+template<int DataLayout>
+static void test_output_expr()
+{
+ Tensor<int, 1, DataLayout> tensor1(5);
+ Tensor<int, 1, DataLayout> tensor2(5);
+ for (int i = 0; i < 5; ++i) {
+ tensor1(i) = i;
+ tensor2(i) = 7;
+ }
+
+ std::stringstream os;
+ os << tensor1 + tensor2;
+
+ std::string expected(" 7\n 8\n 9\n10\n11");
+ VERIFY_IS_EQUAL(std::string(os.str()), expected);
+}
+
+
+template<int DataLayout>
+static void test_output_string()
+{
+ Tensor<std::string, 2, DataLayout> tensor(5, 3);
+ tensor.setConstant(std::string("foo"));
+
+ std::cout << tensor << std::endl;
+
+ std::stringstream os;
+ os << tensor;
+
+ std::string expected("foo foo foo\nfoo foo foo\nfoo foo foo\nfoo foo foo\nfoo foo foo");
+ VERIFY_IS_EQUAL(std::string(os.str()), expected);
+}
+
+
+template<int DataLayout>
+static void test_output_const()
+{
+ Tensor<int, 1, DataLayout> tensor(5);
+ for (int i = 0; i < 5; ++i) {
+ tensor(i) = i;
+ }
+
+ TensorMap<Tensor<const int, 1, DataLayout> > tensor_map(tensor.data(), 5);
+
+ std::stringstream os;
+ os << tensor_map;
+
+ std::string expected("0\n1\n2\n3\n4");
+ VERIFY_IS_EQUAL(std::string(os.str()), expected);
+}
+
+
+void test_cxx11_tensor_io()
+{
+ CALL_SUBTEST(test_output_1d<ColMajor>());
+ CALL_SUBTEST(test_output_1d<RowMajor>());
+ CALL_SUBTEST(test_output_2d<ColMajor>());
+ CALL_SUBTEST(test_output_2d<RowMajor>());
+ CALL_SUBTEST(test_output_expr<ColMajor>());
+ CALL_SUBTEST(test_output_expr<RowMajor>());
+ CALL_SUBTEST(test_output_string<ColMajor>());
+ CALL_SUBTEST(test_output_string<RowMajor>());
+ CALL_SUBTEST(test_output_const<ColMajor>());
+ CALL_SUBTEST(test_output_const<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_layout_swap.cpp b/unsupported/test/cxx11_tensor_layout_swap.cpp
new file mode 100644
index 000000000..ae297a9da
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_layout_swap.cpp
@@ -0,0 +1,61 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+static void test_simple_swap()
+{
+ Tensor<float, 3, ColMajor> tensor(2,3,7);
+ tensor.setRandom();
+
+ Tensor<float, 3, RowMajor> tensor2 = tensor.swap_layout();
+ VERIFY_IS_EQUAL(tensor.dimension(0), tensor2.dimension(2));
+ VERIFY_IS_EQUAL(tensor.dimension(1), tensor2.dimension(1));
+ VERIFY_IS_EQUAL(tensor.dimension(2), tensor2.dimension(0));
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(tensor(i,j,k), tensor2(k,j,i));
+ }
+ }
+ }
+}
+
+
+static void test_swap_as_lvalue()
+{
+ Tensor<float, 3, ColMajor> tensor(2,3,7);
+ tensor.setRandom();
+
+ Tensor<float, 3, RowMajor> tensor2(7,3,2);
+ tensor2.swap_layout() = tensor;
+ VERIFY_IS_EQUAL(tensor.dimension(0), tensor2.dimension(2));
+ VERIFY_IS_EQUAL(tensor.dimension(1), tensor2.dimension(1));
+ VERIFY_IS_EQUAL(tensor.dimension(2), tensor2.dimension(0));
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(tensor(i,j,k), tensor2(k,j,i));
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_layout_swap()
+{
+ CALL_SUBTEST(test_simple_swap());
+ CALL_SUBTEST(test_swap_as_lvalue());
+}
diff --git a/unsupported/test/cxx11_tensor_lvalue.cpp b/unsupported/test/cxx11_tensor_lvalue.cpp
new file mode 100644
index 000000000..071f5b406
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_lvalue.cpp
@@ -0,0 +1,42 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+
+static void test_compound_assignment()
+{
+ Tensor<float, 3> mat1(2,3,7);
+ Tensor<float, 3> mat2(2,3,7);
+ Tensor<float, 3> mat3(2,3,7);
+
+ mat1.setRandom();
+ mat2.setRandom();
+ mat3 = mat1;
+ mat3 += mat2;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(mat3(i,j,k), mat1(i,j,k) + mat2(i,j,k));
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_lvalue()
+{
+ CALL_SUBTEST(test_compound_assignment());
+}
diff --git a/unsupported/test/cxx11_tensor_map.cpp b/unsupported/test/cxx11_tensor_map.cpp
new file mode 100644
index 000000000..9cf2eb150
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_map.cpp
@@ -0,0 +1,147 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+static void test_1d()
+{
+ Tensor<int, 1> vec1(6);
+ Tensor<int, 1, RowMajor> vec2(6);
+
+ TensorMap<Tensor<const int, 1>> vec3(vec1.data(), 6);
+ TensorMap<Tensor<const int, 1, RowMajor>> vec4(vec2.data(), 6);
+
+ vec1(0) = 4; vec2(0) = 0;
+ vec1(1) = 8; vec2(1) = 1;
+ vec1(2) = 15; vec2(2) = 2;
+ vec1(3) = 16; vec2(3) = 3;
+ vec1(4) = 23; vec2(4) = 4;
+ vec1(5) = 42; vec2(5) = 5;
+
+ VERIFY_IS_EQUAL(vec1.rank(), 1);
+ VERIFY_IS_EQUAL(vec1.size(), 6);
+ VERIFY_IS_EQUAL(vec1.dimension(0), 6);
+
+ VERIFY_IS_EQUAL(vec3(0), 4);
+ VERIFY_IS_EQUAL(vec3(1), 8);
+ VERIFY_IS_EQUAL(vec3(2), 15);
+ VERIFY_IS_EQUAL(vec3(3), 16);
+ VERIFY_IS_EQUAL(vec3(4), 23);
+ VERIFY_IS_EQUAL(vec3(5), 42);
+
+ VERIFY_IS_EQUAL(vec4(0), 0);
+ VERIFY_IS_EQUAL(vec4(1), 1);
+ VERIFY_IS_EQUAL(vec4(2), 2);
+ VERIFY_IS_EQUAL(vec4(3), 3);
+ VERIFY_IS_EQUAL(vec4(4), 4);
+ VERIFY_IS_EQUAL(vec4(5), 5);
+}
+
+static void test_2d()
+{
+ Tensor<int, 2> mat1(2,3);
+ Tensor<int, 2, RowMajor> mat2(2,3);
+
+ mat1(0,0) = 0;
+ mat1(0,1) = 1;
+ mat1(0,2) = 2;
+ mat1(1,0) = 3;
+ mat1(1,1) = 4;
+ mat1(1,2) = 5;
+
+ mat2(0,0) = 0;
+ mat2(0,1) = 1;
+ mat2(0,2) = 2;
+ mat2(1,0) = 3;
+ mat2(1,1) = 4;
+ mat2(1,2) = 5;
+
+ TensorMap<Tensor<const int, 2>> mat3(mat1.data(), 2, 3);
+ TensorMap<Tensor<const int, 2, RowMajor>> mat4(mat2.data(), 2, 3);
+
+ VERIFY_IS_EQUAL(mat3.rank(), 2);
+ VERIFY_IS_EQUAL(mat3.size(), 6);
+ VERIFY_IS_EQUAL(mat3.dimension(0), 2);
+ VERIFY_IS_EQUAL(mat3.dimension(1), 3);
+
+ VERIFY_IS_EQUAL(mat4.rank(), 2);
+ VERIFY_IS_EQUAL(mat4.size(), 6);
+ VERIFY_IS_EQUAL(mat4.dimension(0), 2);
+ VERIFY_IS_EQUAL(mat4.dimension(1), 3);
+
+ VERIFY_IS_EQUAL(mat3(0,0), 0);
+ VERIFY_IS_EQUAL(mat3(0,1), 1);
+ VERIFY_IS_EQUAL(mat3(0,2), 2);
+ VERIFY_IS_EQUAL(mat3(1,0), 3);
+ VERIFY_IS_EQUAL(mat3(1,1), 4);
+ VERIFY_IS_EQUAL(mat3(1,2), 5);
+
+ VERIFY_IS_EQUAL(mat4(0,0), 0);
+ VERIFY_IS_EQUAL(mat4(0,1), 1);
+ VERIFY_IS_EQUAL(mat4(0,2), 2);
+ VERIFY_IS_EQUAL(mat4(1,0), 3);
+ VERIFY_IS_EQUAL(mat4(1,1), 4);
+ VERIFY_IS_EQUAL(mat4(1,2), 5);
+}
+
+static void test_3d()
+{
+ Tensor<int, 3> mat1(2,3,7);
+ Tensor<int, 3, RowMajor> mat2(2,3,7);
+
+ int val = 0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ mat1(i,j,k) = val;
+ mat2(i,j,k) = val;
+ val++;
+ }
+ }
+ }
+
+ TensorMap<Tensor<const int, 3>> mat3(mat1.data(), 2, 3, 7);
+ TensorMap<Tensor<const int, 3, RowMajor>> mat4(mat2.data(), array<DenseIndex, 3>{{2, 3, 7}});
+
+ VERIFY_IS_EQUAL(mat3.rank(), 3);
+ VERIFY_IS_EQUAL(mat3.size(), 2*3*7);
+ VERIFY_IS_EQUAL(mat3.dimension(0), 2);
+ VERIFY_IS_EQUAL(mat3.dimension(1), 3);
+ VERIFY_IS_EQUAL(mat3.dimension(2), 7);
+
+ VERIFY_IS_EQUAL(mat4.rank(), 3);
+ VERIFY_IS_EQUAL(mat4.size(), 2*3*7);
+ VERIFY_IS_EQUAL(mat4.dimension(0), 2);
+ VERIFY_IS_EQUAL(mat4.dimension(1), 3);
+ VERIFY_IS_EQUAL(mat4.dimension(2), 7);
+
+ val = 0;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(mat3(i,j,k), val);
+ VERIFY_IS_EQUAL(mat4(i,j,k), val);
+ val++;
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_map()
+{
+ CALL_SUBTEST(test_1d());
+ CALL_SUBTEST(test_2d());
+ CALL_SUBTEST(test_3d());
+}
diff --git a/unsupported/test/cxx11_tensor_morphing.cpp b/unsupported/test/cxx11_tensor_morphing.cpp
new file mode 100644
index 000000000..7fd7a283a
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_morphing.cpp
@@ -0,0 +1,342 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+static void test_simple_reshape()
+{
+ Tensor<float, 5> tensor1(2,3,1,7,1);
+ tensor1.setRandom();
+
+ Tensor<float, 3> tensor2(2,3,7);
+ Tensor<float, 2> tensor3(6,7);
+ Tensor<float, 2> tensor4(2,21);
+
+ Tensor<float, 3>::Dimensions dim1{{2,3,7}};
+ tensor2 = tensor1.reshape(dim1);
+ Tensor<float, 2>::Dimensions dim2{{6,7}};
+ tensor3 = tensor1.reshape(dim2);
+ Tensor<float, 2>::Dimensions dim3{{2,21}};
+ tensor4 = tensor1.reshape(dim1).reshape(dim3);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(tensor1(i,j,0,k,0), tensor2(i,j,k));
+ VERIFY_IS_EQUAL(tensor1(i,j,0,k,0), tensor3(i+2*j,k));
+ VERIFY_IS_EQUAL(tensor1(i,j,0,k,0), tensor4(i,j+3*k));
+ }
+ }
+ }
+}
+
+
+static void test_reshape_in_expr() {
+ MatrixXf m1(2,3*5*7*11);
+ MatrixXf m2(3*5*7*11,13);
+ m1.setRandom();
+ m2.setRandom();
+ MatrixXf m3 = m1 * m2;
+
+ TensorMap<Tensor<float, 5>> tensor1(m1.data(), 2,3,5,7,11);
+ TensorMap<Tensor<float, 5>> tensor2(m2.data(), 3,5,7,11,13);
+ Tensor<float, 2>::Dimensions newDims1{{2,3*5*7*11}};
+ Tensor<float, 2>::Dimensions newDims2{{3*5*7*11,13}};
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ array<DimPair, 1> contract_along{{DimPair(1, 0)}};
+ Tensor<float, 2> tensor3(2,13);
+ tensor3 = tensor1.reshape(newDims1).contract(tensor2.reshape(newDims2), contract_along);
+
+ Map<MatrixXf> res(tensor3.data(), 2, 13);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 13; ++j) {
+ VERIFY_IS_APPROX(res(i,j), m3(i,j));
+ }
+ }
+}
+
+
+static void test_reshape_as_lvalue()
+{
+ Tensor<float, 3> tensor(2,3,7);
+ tensor.setRandom();
+
+ Tensor<float, 2> tensor2d(6,7);
+ Tensor<float, 3>::Dimensions dim{{2,3,7}};
+ tensor2d.reshape(dim) = tensor;
+
+ float scratch[2*3*1*7*1];
+ TensorMap<Tensor<float, 5>> tensor5d(scratch, 2,3,1,7,1);
+ tensor5d.reshape(dim).device(Eigen::DefaultDevice()) = tensor;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(tensor2d(i+2*j,k), tensor(i,j,k));
+ VERIFY_IS_EQUAL(tensor5d(i,j,0,k,0), tensor(i,j,k));
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_simple_slice()
+{
+ Tensor<float, 5, DataLayout> tensor(2,3,5,7,11);
+ tensor.setRandom();
+
+ Tensor<float, 5, DataLayout> slice1(1,1,1,1,1);
+ Eigen::DSizes<ptrdiff_t, 5> indices(1,2,3,4,5);
+ Eigen::DSizes<ptrdiff_t, 5> sizes(1,1,1,1,1);
+ slice1 = tensor.slice(indices, sizes);
+ VERIFY_IS_EQUAL(slice1(0,0,0,0,0), tensor(1,2,3,4,5));
+
+ Tensor<float, 5, DataLayout> slice2(1,1,2,2,3);
+ Eigen::DSizes<ptrdiff_t, 5> indices2(1,1,3,4,5);
+ Eigen::DSizes<ptrdiff_t, 5> sizes2(1,1,2,2,3);
+ slice2 = tensor.slice(indices2, sizes2);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ VERIFY_IS_EQUAL(slice2(0,0,i,j,k), tensor(1,1,3+i,4+j,5+k));
+ }
+ }
+ }
+}
+
+// TODO(andydavis) Add RowMajor support when TensorContract supports RowMajor.
+static void test_slice_in_expr() {
+ MatrixXf m1(7,7);
+ MatrixXf m2(3,3);
+ m1.setRandom();
+ m2.setRandom();
+
+ MatrixXf m3 = m1.block(1, 2, 3, 3) * m2.block(0, 2, 3, 1);
+
+ TensorMap<Tensor<float, 2>> tensor1(m1.data(), 7, 7);
+ TensorMap<Tensor<float, 2>> tensor2(m2.data(), 3, 3);
+ Tensor<float, 2> tensor3(3,1);
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ array<DimPair, 1> contract_along{{DimPair(1, 0)}};
+
+ Eigen::DSizes<ptrdiff_t, 2> indices1(1,2);
+ Eigen::DSizes<ptrdiff_t, 2> sizes1(3,3);
+ Eigen::DSizes<ptrdiff_t, 2> indices2(0,2);
+ Eigen::DSizes<ptrdiff_t, 2> sizes2(3,1);
+ tensor3 = tensor1.slice(indices1, sizes1).contract(tensor2.slice(indices2, sizes2), contract_along);
+
+ Map<MatrixXf> res(tensor3.data(), 3, 1);
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 1; ++j) {
+ VERIFY_IS_APPROX(res(i,j), m3(i,j));
+ }
+ }
+
+ // Take an arbitrary slice of an arbitrarily sized tensor.
+ TensorMap<Tensor<const float, 2>> tensor4(m1.data(), 7, 7);
+ Tensor<float, 1> tensor6 = tensor4.reshape(DSizes<ptrdiff_t, 1>(7*7)).exp().slice(DSizes<ptrdiff_t, 1>(0), DSizes<ptrdiff_t, 1>(35));
+ for (int i = 0; i < 35; ++i) {
+ VERIFY_IS_APPROX(tensor6(i), expf(tensor4.data()[i]));
+ }
+}
+
+template<int DataLayout>
+static void test_slice_as_lvalue()
+{
+ Tensor<float, 3, DataLayout> tensor1(2,2,7);
+ tensor1.setRandom();
+ Tensor<float, 3, DataLayout> tensor2(2,2,7);
+ tensor2.setRandom();
+ Tensor<float, 3, DataLayout> tensor3(4,3,5);
+ tensor3.setRandom();
+ Tensor<float, 3, DataLayout> tensor4(4,3,2);
+ tensor4.setRandom();
+ Tensor<float, 3, DataLayout> tensor5(10,13,12);
+ tensor5.setRandom();
+
+ Tensor<float, 3, DataLayout> result(4,5,7);
+ Eigen::DSizes<ptrdiff_t, 3> sizes12(2,2,7);
+ Eigen::DSizes<ptrdiff_t, 3> first_slice(0,0,0);
+ result.slice(first_slice, sizes12) = tensor1;
+ Eigen::DSizes<ptrdiff_t, 3> second_slice(2,0,0);
+ result.slice(second_slice, sizes12).device(Eigen::DefaultDevice()) = tensor2;
+
+ Eigen::DSizes<ptrdiff_t, 3> sizes3(4,3,5);
+ Eigen::DSizes<ptrdiff_t, 3> third_slice(0,2,0);
+ result.slice(third_slice, sizes3) = tensor3;
+
+ Eigen::DSizes<ptrdiff_t, 3> sizes4(4,3,2);
+ Eigen::DSizes<ptrdiff_t, 3> fourth_slice(0,2,5);
+ result.slice(fourth_slice, sizes4) = tensor4;
+
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int i = 0; i < 2; ++i) {
+ VERIFY_IS_EQUAL(result(i,j,k), tensor1(i,j,k));
+ VERIFY_IS_EQUAL(result(i+2,j,k), tensor2(i,j,k));
+ }
+ }
+ }
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 2; j < 5; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ VERIFY_IS_EQUAL(result(i,j,k), tensor3(i,j-2,k));
+ }
+ for (int k = 5; k < 7; ++k) {
+ VERIFY_IS_EQUAL(result(i,j,k), tensor4(i,j-2,k-5));
+ }
+ }
+ }
+
+ Eigen::DSizes<ptrdiff_t, 3> sizes5(4,5,7);
+ Eigen::DSizes<ptrdiff_t, 3> fifth_slice(0,0,0);
+ result.slice(fifth_slice, sizes5) = tensor5.slice(fifth_slice, sizes5);
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 2; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(result(i,j,k), tensor5(i,j,k));
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_slice_raw_data()
+{
+ Tensor<float, 4, DataLayout> tensor(3,5,7,11);
+ tensor.setRandom();
+
+ Eigen::DSizes<ptrdiff_t, 4> offsets(1,2,3,4);
+ Eigen::DSizes<ptrdiff_t, 4> extents(1,1,1,1);
+ typedef TensorEvaluator<decltype(tensor.slice(offsets, extents)), DefaultDevice> SliceEvaluator;
+ auto slice1 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice1.dimensions().TotalSize(), 1ul);
+ VERIFY_IS_EQUAL(slice1.data()[0], tensor(1,2,3,4));
+
+ if (DataLayout == ColMajor) {
+ extents = Eigen::DSizes<ptrdiff_t, 4>(2,1,1,1);
+ auto slice2 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice2.dimensions().TotalSize(), 2ul);
+ VERIFY_IS_EQUAL(slice2.data()[0], tensor(1,2,3,4));
+ VERIFY_IS_EQUAL(slice2.data()[1], tensor(2,2,3,4));
+ } else {
+ extents = Eigen::DSizes<ptrdiff_t, 4>(1,1,1,2);
+ auto slice2 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice2.dimensions().TotalSize(), 2ul);
+ VERIFY_IS_EQUAL(slice2.data()[0], tensor(1,2,3,4));
+ VERIFY_IS_EQUAL(slice2.data()[1], tensor(1,2,3,5));
+ }
+
+ extents = Eigen::DSizes<ptrdiff_t, 4>(1,2,1,1);
+ auto slice3 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice3.dimensions().TotalSize(), 2ul);
+ VERIFY_IS_EQUAL(slice3.data(), static_cast<float*>(0));
+
+ if (DataLayout == ColMajor) {
+ offsets = Eigen::DSizes<ptrdiff_t, 4>(0,2,3,4);
+ extents = Eigen::DSizes<ptrdiff_t, 4>(3,2,1,1);
+ auto slice4 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice4.dimensions().TotalSize(), 6ul);
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ VERIFY_IS_EQUAL(slice4.data()[i+3*j], tensor(i,2+j,3,4));
+ }
+ }
+ } else {
+ offsets = Eigen::DSizes<ptrdiff_t, 4>(1,2,3,0);
+ extents = Eigen::DSizes<ptrdiff_t, 4>(1,1,2,11);
+ auto slice4 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice4.dimensions().TotalSize(), 22ul);
+ for (int l = 0; l < 11; ++l) {
+ for (int k = 0; k < 2; ++k) {
+ VERIFY_IS_EQUAL(slice4.data()[l+11*k], tensor(1,2,3+k,l));
+ }
+ }
+ }
+
+ if (DataLayout == ColMajor) {
+ offsets = Eigen::DSizes<ptrdiff_t, 4>(0,0,0,4);
+ extents = Eigen::DSizes<ptrdiff_t, 4>(3,5,7,2);
+ auto slice5 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice5.dimensions().TotalSize(), 210ul);
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ for (int l = 0; l < 2; ++l) {
+ int slice_index = i + 3 * (j + 5 * (k + 7 * l));
+ VERIFY_IS_EQUAL(slice5.data()[slice_index], tensor(i,j,k,l+4));
+ }
+ }
+ }
+ }
+ } else {
+ offsets = Eigen::DSizes<ptrdiff_t, 4>(1,0,0,0);
+ extents = Eigen::DSizes<ptrdiff_t, 4>(2,5,7,11);
+ auto slice5 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice5.dimensions().TotalSize(), 770ul);
+ for (int l = 0; l < 11; ++l) {
+ for (int k = 0; k < 7; ++k) {
+ for (int j = 0; j < 5; ++j) {
+ for (int i = 0; i < 2; ++i) {
+ int slice_index = l + 11 * (k + 7 * (j + 5 * i));
+ VERIFY_IS_EQUAL(slice5.data()[slice_index], tensor(i+1,j,k,l));
+ }
+ }
+ }
+ }
+
+ }
+
+ offsets = Eigen::DSizes<ptrdiff_t, 4>(0,0,0,0);
+ extents = Eigen::DSizes<ptrdiff_t, 4>(3,5,7,11);
+ auto slice6 = SliceEvaluator(tensor.slice(offsets, extents), DefaultDevice());
+ VERIFY_IS_EQUAL(slice6.dimensions().TotalSize(), 3ul*5*7*11);
+ VERIFY_IS_EQUAL(slice6.data(), tensor.data());
+}
+
+
+static void test_composition()
+{
+ Eigen::Tensor<float, 2> matrix(7, 11);
+ matrix.setRandom();
+
+ const DSizes<ptrdiff_t, 3> newDims{{1, 1, 11}};
+ Eigen::Tensor<float, 3> tensor =
+ matrix.slice(DSizes<ptrdiff_t, 2>(2, 0), DSizes<ptrdiff_t, 2>(1, 11)).reshape(newDims);
+
+ VERIFY_IS_EQUAL(tensor.dimensions().TotalSize(), 11ul);
+ VERIFY_IS_EQUAL(tensor.dimension(0), 1);
+ VERIFY_IS_EQUAL(tensor.dimension(1), 1);
+ VERIFY_IS_EQUAL(tensor.dimension(2), 11);
+ for (int i = 0; i < 11; ++i) {
+ VERIFY_IS_EQUAL(tensor(0,0,i), matrix(2,i));
+ }
+}
+
+
+void test_cxx11_tensor_morphing()
+{
+ CALL_SUBTEST(test_simple_reshape());
+ CALL_SUBTEST(test_reshape_in_expr());
+ CALL_SUBTEST(test_reshape_as_lvalue());
+
+ CALL_SUBTEST(test_simple_slice<ColMajor>());
+ CALL_SUBTEST(test_simple_slice<RowMajor>());
+ CALL_SUBTEST(test_slice_in_expr());
+ CALL_SUBTEST(test_slice_as_lvalue<ColMajor>());
+ CALL_SUBTEST(test_slice_as_lvalue<RowMajor>());
+ CALL_SUBTEST(test_slice_raw_data<ColMajor>());
+ CALL_SUBTEST(test_slice_raw_data<RowMajor>());
+
+ CALL_SUBTEST(test_composition());
+}
diff --git a/unsupported/test/cxx11_tensor_of_complex.cpp b/unsupported/test/cxx11_tensor_of_complex.cpp
new file mode 100644
index 000000000..24b2bcb58
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_of_complex.cpp
@@ -0,0 +1,81 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::TensorMap;
+
+
+
+static void test_additions()
+{
+ Tensor<std::complex<float>, 1> data1(3);
+ Tensor<std::complex<float>, 1> data2(3);
+ for (int i = 0; i < 3; ++i) {
+ data1(i) = std::complex<float>(i, -i);
+ data2(i) = std::complex<float>(i, 7 * i);
+ }
+
+ Tensor<std::complex<float>, 1> sum = data1 + data2;
+ for (int i = 0; i < 3; ++i) {
+ VERIFY_IS_EQUAL(sum(i), std::complex<float>(2*i, 6*i));
+ }
+}
+
+
+static void test_abs()
+{
+ Tensor<std::complex<float>, 1> data1(3);
+ Tensor<std::complex<double>, 1> data2(3);
+ data1.setRandom();
+ data2.setRandom();
+
+ Tensor<float, 1> abs1 = data1.abs();
+ Tensor<double, 1> abs2 = data2.abs();
+ for (int i = 0; i < 3; ++i) {
+ VERIFY_IS_APPROX(abs1(i), std::abs(data1(i)));
+ VERIFY_IS_APPROX(abs2(i), std::abs(data2(i)));
+ }
+}
+
+
+static void test_contractions()
+{
+ Tensor<std::complex<float>, 4> t_left(30, 50, 8, 31);
+ Tensor<std::complex<float>, 5> t_right(8, 31, 7, 20, 10);
+ Tensor<std::complex<float>, 5> t_result(30, 50, 7, 20, 10);
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ typedef Map<Matrix<std::complex<float>, Dynamic, Dynamic>> MapXcf;
+ MapXcf m_left(t_left.data(), 1500, 248);
+ MapXcf m_right(t_right.data(), 248, 1400);
+ Matrix<std::complex<float>, Dynamic, Dynamic> m_result(1500, 1400);
+
+ // This contraction should be equivalent to a regular matrix multiplication
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ Eigen::array<DimPair, 2> dims({{DimPair(2, 0), DimPair(3, 1)}});
+ t_result = t_left.contract(t_right, dims);
+ m_result = m_left * m_right;
+ for (size_t i = 0; i < t_result.dimensions().TotalSize(); i++) {
+ VERIFY_IS_APPROX(t_result.data()[i], m_result.data()[i]);
+ }
+}
+
+
+void test_cxx11_tensor_of_complex()
+{
+ CALL_SUBTEST(test_additions());
+ CALL_SUBTEST(test_abs());
+ CALL_SUBTEST(test_contractions());
+}
diff --git a/unsupported/test/cxx11_tensor_of_const_values.cpp b/unsupported/test/cxx11_tensor_of_const_values.cpp
new file mode 100644
index 000000000..f179a0c21
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_of_const_values.cpp
@@ -0,0 +1,105 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+static void test_assign()
+{
+ float data1[6];
+ TensorMap<Tensor<const float, 2>> mat1(data1, 2, 3);
+ float data2[6];
+ const TensorMap<Tensor<float, 2>> mat2(data2, 2, 3);
+
+ for (int i = 0; i < 6; ++i) {
+ data1[i] = i;
+ data2[i] = -i;
+ }
+
+ Tensor<float, 2> rslt1;
+ rslt1 = mat1;
+ Tensor<float, 2> rslt2;
+ rslt2 = mat2;
+
+ Tensor<float, 2> rslt3 = mat1;
+ Tensor<float, 2> rslt4 = mat2;
+
+ Tensor<float, 2> rslt5(mat1);
+ Tensor<float, 2> rslt6(mat2);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_APPROX(rslt1(i,j), static_cast<float>(i + 2*j));
+ VERIFY_IS_APPROX(rslt2(i,j), static_cast<float>(-i - 2*j));
+ VERIFY_IS_APPROX(rslt3(i,j), static_cast<float>(i + 2*j));
+ VERIFY_IS_APPROX(rslt4(i,j), static_cast<float>(-i - 2*j));
+ VERIFY_IS_APPROX(rslt5(i,j), static_cast<float>(i + 2*j));
+ VERIFY_IS_APPROX(rslt6(i,j), static_cast<float>(-i - 2*j));
+ }
+ }
+}
+
+
+static void test_plus()
+{
+ float data1[6];
+ TensorMap<Tensor<const float, 2>> mat1(data1, 2, 3);
+ float data2[6];
+ TensorMap<Tensor<float, 2>> mat2(data2, 2, 3);
+
+ for (int i = 0; i < 6; ++i) {
+ data1[i] = i;
+ data2[i] = -i;
+ }
+
+ Tensor<float, 2> sum1;
+ sum1 = mat1 + mat2;
+ Tensor<float, 2> sum2;
+ sum2 = mat2 + mat1;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_APPROX(sum1(i,j), 0.0f);
+ VERIFY_IS_APPROX(sum2(i,j), 0.0f);
+ }
+ }
+}
+
+
+static void test_plus_equal()
+{
+ float data1[6];
+ TensorMap<Tensor<const float, 2>> mat1(data1, 2, 3);
+ float data2[6];
+ TensorMap<Tensor<float, 2>> mat2(data2, 2, 3);
+
+ for (int i = 0; i < 6; ++i) {
+ data1[i] = i;
+ data2[i] = -i;
+ }
+ mat2 += mat1;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_APPROX(mat2(i,j), 0.0f);
+ }
+ }
+}
+
+
+void test_cxx11_tensor_of_const_values()
+{
+ CALL_SUBTEST(test_assign());
+ CALL_SUBTEST(test_plus());
+ CALL_SUBTEST(test_plus_equal());
+}
diff --git a/unsupported/test/cxx11_tensor_of_strings.cpp b/unsupported/test/cxx11_tensor_of_strings.cpp
new file mode 100644
index 000000000..8d05d154e
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_of_strings.cpp
@@ -0,0 +1,152 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::TensorMap;
+
+static void test_assign()
+{
+ std::string data1[6];
+ TensorMap<Tensor<std::string, 2>> mat1(data1, 2, 3);
+ std::string data2[6];
+ const TensorMap<Tensor<const std::string, 2>> mat2(data2, 2, 3);
+
+ for (int i = 0; i < 6; ++i) {
+ std::ostringstream s1;
+ s1 << "abc" << i*3;
+ data1[i] = s1.str();
+ std::ostringstream s2;
+ s2 << "def" << i*5;
+ data2[i] = s2.str();
+ }
+
+ Tensor<std::string, 2> rslt1;
+ rslt1 = mat1;
+ Tensor<std::string, 2> rslt2;
+ rslt2 = mat2;
+
+ Tensor<std::string, 2> rslt3 = mat1;
+ Tensor<std::string, 2> rslt4 = mat2;
+
+ Tensor<std::string, 2> rslt5(mat1);
+ Tensor<std::string, 2> rslt6(mat2);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_EQUAL(rslt1(i,j), data1[i+2*j]);
+ VERIFY_IS_EQUAL(rslt2(i,j), data2[i+2*j]);
+ VERIFY_IS_EQUAL(rslt3(i,j), data1[i+2*j]);
+ VERIFY_IS_EQUAL(rslt4(i,j), data2[i+2*j]);
+ VERIFY_IS_EQUAL(rslt5(i,j), data1[i+2*j]);
+ VERIFY_IS_EQUAL(rslt6(i,j), data2[i+2*j]);
+ }
+ }
+}
+
+
+static void test_concat()
+{
+ Tensor<std::string, 2> t1(2, 3);
+ Tensor<std::string, 2> t2(2, 3);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ std::ostringstream s1;
+ s1 << "abc" << i + j*2;
+ t1(i, j) = s1.str();
+ std::ostringstream s2;
+ s2 << "def" << i*5 + j*32;
+ t2(i, j) = s2.str();
+ }
+ }
+
+ Tensor<std::string, 2> result = t1.concatenate(t2, 1);
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_EQUAL(result.dimension(1), 6);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_EQUAL(result(i, j), t1(i, j));
+ VERIFY_IS_EQUAL(result(i, j+3), t2(i, j));
+ }
+ }
+}
+
+
+static void test_slices()
+{
+ Tensor<std::string, 2> data(2, 6);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ std::ostringstream s1;
+ s1 << "abc" << i + j*2;
+ data(i, j) = s1.str();
+ }
+ }
+
+ const Eigen::DSizes<ptrdiff_t, 2> half_size{{2, 3}};
+ const Eigen::DSizes<ptrdiff_t, 2> first_half{{0, 0}};
+ const Eigen::DSizes<ptrdiff_t, 2> second_half{{0, 3}};
+
+ Tensor<std::string, 2> t1 = data.slice(first_half, half_size);
+ Tensor<std::string, 2> t2 = data.slice(second_half, half_size);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ VERIFY_IS_EQUAL(data(i, j), t1(i, j));
+ VERIFY_IS_EQUAL(data(i, j+3), t2(i, j));
+ }
+ }
+}
+
+
+static void test_additions()
+{
+ Tensor<std::string, 1> data1(3);
+ Tensor<std::string, 1> data2(3);
+ for (int i = 0; i < 3; ++i) {
+ data1(i) = "abc";
+ std::ostringstream s1;
+ s1 << i;
+ data2(i) = s1.str();
+ }
+
+ Tensor<std::string, 1> sum = data1 + data2;
+ for (int i = 0; i < 3; ++i) {
+ std::ostringstream concat;
+ concat << "abc" << i;
+ std::string expected = concat.str();
+ VERIFY_IS_EQUAL(sum(i), expected);
+ }
+}
+
+
+static void test_initialization()
+{
+ Tensor<std::string, 2> a(2, 3);
+ a.setConstant(std::string("foo"));
+ for (int i = 0; i < 2*3; ++i) {
+ VERIFY_IS_EQUAL(a(i), std::string("foo"));
+ }
+}
+
+
+void test_cxx11_tensor_of_strings()
+{
+ // Beware: none of this is likely to ever work on a GPU.
+ CALL_SUBTEST(test_assign());
+ CALL_SUBTEST(test_concat());
+ CALL_SUBTEST(test_slices());
+ CALL_SUBTEST(test_additions());
+ CALL_SUBTEST(test_initialization());
+}
diff --git a/unsupported/test/cxx11_tensor_padding.cpp b/unsupported/test/cxx11_tensor_padding.cpp
new file mode 100644
index 000000000..ffa19896e
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_padding.cpp
@@ -0,0 +1,93 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+template<int DataLayout>
+static void test_simple_padding()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ array<std::pair<ptrdiff_t, ptrdiff_t>, 4> paddings;
+ paddings[0] = std::make_pair(0, 0);
+ paddings[1] = std::make_pair(2, 1);
+ paddings[2] = std::make_pair(3, 4);
+ paddings[3] = std::make_pair(0, 0);
+
+ Tensor<float, 4, DataLayout> padded;
+ padded = tensor.pad(paddings);
+
+ VERIFY_IS_EQUAL(padded.dimension(0), 2+0);
+ VERIFY_IS_EQUAL(padded.dimension(1), 3+3);
+ VERIFY_IS_EQUAL(padded.dimension(2), 5+7);
+ VERIFY_IS_EQUAL(padded.dimension(3), 7+0);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 6; ++j) {
+ for (int k = 0; k < 12; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ if (j >= 2 && j < 5 && k >= 3 && k < 8) {
+ VERIFY_IS_EQUAL(padded(i,j,k,l), tensor(i,j-2,k-3,l));
+ } else {
+ VERIFY_IS_EQUAL(padded(i,j,k,l), 0.0f);
+ }
+ }
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_padded_expr()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ array<std::pair<ptrdiff_t, ptrdiff_t>, 4> paddings;
+ paddings[0] = std::make_pair(0, 0);
+ paddings[1] = std::make_pair(2, 1);
+ paddings[2] = std::make_pair(3, 4);
+ paddings[3] = std::make_pair(0, 0);
+
+ Eigen::DSizes<ptrdiff_t, 2> reshape_dims;
+ reshape_dims[0] = 12;
+ reshape_dims[1] = 84;
+
+ Tensor<float, 2, DataLayout> result;
+ result = tensor.pad(paddings).reshape(reshape_dims);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 6; ++j) {
+ for (int k = 0; k < 12; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ const float result_value = DataLayout == ColMajor ?
+ result(i+2*j,k+12*l) : result(j+6*i,l+7*k);
+ if (j >= 2 && j < 5 && k >= 3 && k < 8) {
+ VERIFY_IS_EQUAL(result_value, tensor(i,j-2,k-3,l));
+ } else {
+ VERIFY_IS_EQUAL(result_value, 0.0f);
+ }
+ }
+ }
+ }
+ }
+}
+
+void test_cxx11_tensor_padding()
+{
+ CALL_SUBTEST(test_simple_padding<ColMajor>());
+ CALL_SUBTEST(test_simple_padding<RowMajor>());
+ CALL_SUBTEST(test_padded_expr<ColMajor>());
+ CALL_SUBTEST(test_padded_expr<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_patch.cpp b/unsupported/test/cxx11_tensor_patch.cpp
new file mode 100644
index 000000000..0ee7b46d4
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_patch.cpp
@@ -0,0 +1,120 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+static void test_simple_patch()
+{
+ Tensor<float, 4> tensor(2,3,5,7);
+ tensor.setRandom();
+ array<ptrdiff_t, 4> patch_dims;
+ patch_dims[0] = 1;
+ patch_dims[1] = 1;
+ patch_dims[2] = 1;
+ patch_dims[3] = 1;
+
+ Tensor<float, 5> no_patch;
+ no_patch = tensor.extract_patches(patch_dims);
+
+ VERIFY_IS_EQUAL(no_patch.dimension(0), 1);
+ VERIFY_IS_EQUAL(no_patch.dimension(1), 1);
+ VERIFY_IS_EQUAL(no_patch.dimension(2), 1);
+ VERIFY_IS_EQUAL(no_patch.dimension(3), 1);
+ VERIFY_IS_EQUAL(no_patch.dimension(4), tensor.size());
+
+ for (int i = 0; i < tensor.size(); ++i) {
+ VERIFY_IS_EQUAL(tensor.data()[i], no_patch.data()[i]);
+ }
+
+ patch_dims[0] = 2;
+ patch_dims[1] = 3;
+ patch_dims[2] = 5;
+ patch_dims[3] = 7;
+ Tensor<float, 5> single_patch;
+ single_patch = tensor.extract_patches(patch_dims);
+
+ VERIFY_IS_EQUAL(single_patch.dimension(0), 2);
+ VERIFY_IS_EQUAL(single_patch.dimension(1), 3);
+ VERIFY_IS_EQUAL(single_patch.dimension(2), 5);
+ VERIFY_IS_EQUAL(single_patch.dimension(3), 7);
+ VERIFY_IS_EQUAL(single_patch.dimension(4), 1);
+
+ for (int i = 0; i < tensor.size(); ++i) {
+ VERIFY_IS_EQUAL(tensor.data()[i], single_patch.data()[i]);
+ }
+
+ patch_dims[0] = 1;
+ patch_dims[1] = 2;
+ patch_dims[2] = 2;
+ patch_dims[3] = 1;
+ Tensor<float, 5> twod_patch;
+ twod_patch = tensor.extract_patches(patch_dims);
+
+ VERIFY_IS_EQUAL(twod_patch.dimension(0), 1);
+ VERIFY_IS_EQUAL(twod_patch.dimension(1), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(2), 2);
+ VERIFY_IS_EQUAL(twod_patch.dimension(3), 1);
+ VERIFY_IS_EQUAL(twod_patch.dimension(4), 2*2*4*7);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 4; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ int patch_loc = i + 2 * (j + 2 * (k + 4 * l));
+ for (int x = 0; x < 2; ++x) {
+ for (int y = 0; y < 2; ++y) {
+ VERIFY_IS_EQUAL(tensor(i,j+x,k+y,l), twod_patch(0,x,y,0,patch_loc));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ patch_dims[0] = 1;
+ patch_dims[1] = 2;
+ patch_dims[2] = 3;
+ patch_dims[3] = 5;
+ Tensor<float, 5> threed_patch;
+ threed_patch = tensor.extract_patches(patch_dims);
+
+ VERIFY_IS_EQUAL(threed_patch.dimension(0), 1);
+ VERIFY_IS_EQUAL(threed_patch.dimension(1), 2);
+ VERIFY_IS_EQUAL(threed_patch.dimension(2), 3);
+ VERIFY_IS_EQUAL(threed_patch.dimension(3), 5);
+ VERIFY_IS_EQUAL(threed_patch.dimension(4), 2*2*3*3);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 3; ++l) {
+ int patch_loc = i + 2 * (j + 2 * (k + 3 * l));
+ for (int x = 0; x < 2; ++x) {
+ for (int y = 0; y < 3; ++y) {
+ for (int z = 0; z < 5; ++z) {
+ VERIFY_IS_EQUAL(tensor(i,j+x,k+y,l+z), threed_patch(0,x,y,z,patch_loc));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_patch()
+{
+ CALL_SUBTEST(test_simple_patch());
+ // CALL_SUBTEST(test_expr_shuffling());
+}
diff --git a/unsupported/test/cxx11_tensor_random.cpp b/unsupported/test/cxx11_tensor_random.cpp
new file mode 100644
index 000000000..8276ae822
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_random.cpp
@@ -0,0 +1,78 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+static void test_default()
+{
+ Tensor<float, 1> vec(6);
+ vec.setRandom();
+
+ // Fixme: we should check that the generated numbers follow a uniform
+ // distribution instead.
+ for (int i = 1; i < 6; ++i) {
+ VERIFY_IS_NOT_EQUAL(vec(i), vec(i-1));
+ }
+}
+
+static void test_normal()
+{
+ Tensor<float, 1> vec(6);
+ vec.setRandom<Eigen::internal::NormalRandomGenerator<float>>();
+
+ // Fixme: we should check that the generated numbers follow a gaussian
+ // distribution instead.
+ for (int i = 1; i < 6; ++i) {
+ VERIFY_IS_NOT_EQUAL(vec(i), vec(i-1));
+ }
+}
+
+
+struct MyGenerator {
+ MyGenerator() { }
+ MyGenerator(const MyGenerator&) { }
+
+ // Return a random value to be used. "element_location" is the
+ // location of the entry to set in the tensor, it can typically
+ // be ignored.
+ int operator()(Eigen::DenseIndex element_location, Eigen::DenseIndex /*unused*/ = 0) const {
+ return 3 * element_location;
+ }
+
+ // Same as above but generates several numbers at a time.
+ typename internal::packet_traits<int>::type packetOp(
+ Eigen::DenseIndex packet_location, Eigen::DenseIndex /*unused*/ = 0) const {
+ const int packetSize = internal::packet_traits<int>::size;
+ EIGEN_ALIGN_DEFAULT int values[packetSize];
+ for (int i = 0; i < packetSize; ++i) {
+ values[i] = 3 * (packet_location + i);
+ }
+ return internal::pload<typename internal::packet_traits<int>::type>(values);
+ }
+};
+
+
+static void test_custom()
+{
+ Tensor<int, 1> vec(6);
+ vec.setRandom<MyGenerator>();
+
+ for (int i = 0; i < 6; ++i) {
+ VERIFY_IS_EQUAL(vec(i), 3*i);
+ }
+}
+
+void test_cxx11_tensor_random()
+{
+ CALL_SUBTEST(test_default());
+ CALL_SUBTEST(test_normal());
+ CALL_SUBTEST(test_custom());
+}
diff --git a/unsupported/test/cxx11_tensor_reduction.cpp b/unsupported/test/cxx11_tensor_reduction.cpp
new file mode 100644
index 000000000..0269853a9
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_reduction.cpp
@@ -0,0 +1,420 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+#include <limits>
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+template <int DataLayout>
+static void test_simple_reductions() {
+ Tensor<float, 4, DataLayout> tensor(2, 3, 5, 7);
+ tensor.setRandom();
+ array<ptrdiff_t, 2> reduction_axis;
+ reduction_axis[0] = 1;
+ reduction_axis[1] = 3;
+
+ Tensor<float, 2, DataLayout> result = tensor.sum(reduction_axis);
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_EQUAL(result.dimension(1), 5);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ float sum = 0.0f;
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ sum += tensor(i, k, j, l);
+ }
+ }
+ VERIFY_IS_APPROX(result(i, j), sum);
+ }
+ }
+
+ {
+ Tensor<float, 1, DataLayout> sum1 = tensor.sum();
+ VERIFY_IS_EQUAL(sum1.dimension(0), 1);
+
+ array<ptrdiff_t, 4> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ reduction_axis[2] = 2;
+ reduction_axis[3] = 3;
+ Tensor<float, 1, DataLayout> sum2 = tensor.sum(reduction_axis);
+ VERIFY_IS_EQUAL(sum2.dimension(0), 1);
+
+ VERIFY_IS_APPROX(sum1(0), sum2(0));
+ }
+
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 2;
+ result = tensor.prod(reduction_axis);
+ VERIFY_IS_EQUAL(result.dimension(0), 3);
+ VERIFY_IS_EQUAL(result.dimension(1), 7);
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ float prod = 1.0f;
+ for (int k = 0; k < 2; ++k) {
+ for (int l = 0; l < 5; ++l) {
+ prod *= tensor(k, i, l, j);
+ }
+ }
+ VERIFY_IS_APPROX(result(i, j), prod);
+ }
+ }
+
+ {
+ Tensor<float, 1, DataLayout> prod1 = tensor.prod();
+ VERIFY_IS_EQUAL(prod1.dimension(0), 1);
+
+ array<ptrdiff_t, 4> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ reduction_axis[2] = 2;
+ reduction_axis[3] = 3;
+ Tensor<float, 1, DataLayout> prod2 = tensor.prod(reduction_axis);
+ VERIFY_IS_EQUAL(prod2.dimension(0), 1);
+
+ VERIFY_IS_APPROX(prod1(0), prod2(0));
+ }
+
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 2;
+ result = tensor.maximum(reduction_axis);
+ VERIFY_IS_EQUAL(result.dimension(0), 3);
+ VERIFY_IS_EQUAL(result.dimension(1), 7);
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ float max_val = std::numeric_limits<float>::lowest();
+ for (int k = 0; k < 2; ++k) {
+ for (int l = 0; l < 5; ++l) {
+ max_val = (std::max)(max_val, tensor(k, i, l, j));
+ }
+ }
+ VERIFY_IS_APPROX(result(i, j), max_val);
+ }
+ }
+
+ {
+ Tensor<float, 1, DataLayout> max1 = tensor.maximum();
+ VERIFY_IS_EQUAL(max1.dimension(0), 1);
+
+ array<ptrdiff_t, 4> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ reduction_axis[2] = 2;
+ reduction_axis[3] = 3;
+ Tensor<float, 1, DataLayout> max2 = tensor.maximum(reduction_axis);
+ VERIFY_IS_EQUAL(max2.dimension(0), 1);
+
+ VERIFY_IS_APPROX(max1(0), max2(0));
+ }
+
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ result = tensor.minimum(reduction_axis);
+ VERIFY_IS_EQUAL(result.dimension(0), 5);
+ VERIFY_IS_EQUAL(result.dimension(1), 7);
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ float min_val = (std::numeric_limits<float>::max)();
+ for (int k = 0; k < 2; ++k) {
+ for (int l = 0; l < 3; ++l) {
+ min_val = (std::min)(min_val, tensor(k, l, i, j));
+ }
+ }
+ VERIFY_IS_APPROX(result(i, j), min_val);
+ }
+ }
+
+ {
+ Tensor<float, 1, DataLayout> min1 = tensor.minimum();
+ VERIFY_IS_EQUAL(min1.dimension(0), 1);
+
+ array<ptrdiff_t, 4> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ reduction_axis[2] = 2;
+ reduction_axis[3] = 3;
+ Tensor<float, 1, DataLayout> min2 = tensor.minimum(reduction_axis);
+ VERIFY_IS_EQUAL(min2.dimension(0), 1);
+
+ VERIFY_IS_APPROX(min1(0), min2(0));
+ }
+
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ result = tensor.mean(reduction_axis);
+ VERIFY_IS_EQUAL(result.dimension(0), 5);
+ VERIFY_IS_EQUAL(result.dimension(1), 7);
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ float sum = 0.0f;
+ int count = 0;
+ for (int k = 0; k < 2; ++k) {
+ for (int l = 0; l < 3; ++l) {
+ sum += tensor(k, l, i, j);
+ ++count;
+ }
+ }
+ VERIFY_IS_APPROX(result(i, j), sum / count);
+ }
+ }
+
+ {
+ Tensor<float, 1, DataLayout> mean1 = tensor.mean();
+ VERIFY_IS_EQUAL(mean1.dimension(0), 1);
+
+ array<ptrdiff_t, 4> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+ reduction_axis[2] = 2;
+ reduction_axis[3] = 3;
+ Tensor<float, 1, DataLayout> mean2 = tensor.mean(reduction_axis);
+ VERIFY_IS_EQUAL(mean2.dimension(0), 1);
+
+ VERIFY_IS_APPROX(mean1(0), mean2(0));
+ }
+}
+
+template <int DataLayout>
+static void test_full_reductions() {
+ Tensor<float, 2, DataLayout> tensor(2, 3);
+ tensor.setRandom();
+ array<ptrdiff_t, 2> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+
+ Tensor<float, 1, DataLayout> result = tensor.sum(reduction_axis);
+ VERIFY_IS_EQUAL(result.dimension(0), 1);
+
+ float sum = 0.0f;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ sum += tensor(i, j);
+ }
+ }
+ VERIFY_IS_APPROX(result(0), sum);
+
+ result = tensor.square().sum(reduction_axis).sqrt();
+ VERIFY_IS_EQUAL(result.dimension(0), 1);
+
+ sum = 0.0f;
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ sum += tensor(i, j) * tensor(i, j);
+ }
+ }
+ VERIFY_IS_APPROX(result(0), sqrtf(sum));
+}
+
+struct UserReducer {
+ static const bool PacketAccess = false;
+ UserReducer(float offset) : offset_(offset) {}
+ void reduce(const float val, float* accum) { *accum += val * val; }
+ float initialize() const { return 0; }
+ float finalize(const float accum) const { return 1.0f / (accum + offset_); }
+
+ private:
+ const float offset_;
+};
+
+template <int DataLayout>
+static void test_user_defined_reductions() {
+ Tensor<float, 2, DataLayout> tensor(5, 7);
+ tensor.setRandom();
+ array<ptrdiff_t, 1> reduction_axis;
+ reduction_axis[0] = 1;
+
+ UserReducer reducer(10.0f);
+ Tensor<float, 1, DataLayout> result = tensor.reduce(reduction_axis, reducer);
+ VERIFY_IS_EQUAL(result.dimension(0), 5);
+ for (int i = 0; i < 5; ++i) {
+ float expected = 10.0f;
+ for (int j = 0; j < 7; ++j) {
+ expected += tensor(i, j) * tensor(i, j);
+ }
+ expected = 1.0f / expected;
+ VERIFY_IS_APPROX(result(i), expected);
+ }
+}
+
+template <int DataLayout>
+static void test_tensor_maps() {
+ int inputs[2 * 3 * 5 * 7];
+ TensorMap<Tensor<int, 4, DataLayout> > tensor_map(inputs, 2, 3, 5, 7);
+ TensorMap<Tensor<const int, 4, DataLayout> > tensor_map_const(inputs, 2, 3, 5,
+ 7);
+ const TensorMap<Tensor<const int, 4, DataLayout> > tensor_map_const_const(
+ inputs, 2, 3, 5, 7);
+
+ tensor_map.setRandom();
+ array<ptrdiff_t, 2> reduction_axis;
+ reduction_axis[0] = 1;
+ reduction_axis[1] = 3;
+
+ Tensor<int, 2, DataLayout> result = tensor_map.sum(reduction_axis);
+ Tensor<int, 2, DataLayout> result2 = tensor_map_const.sum(reduction_axis);
+ Tensor<int, 2, DataLayout> result3 =
+ tensor_map_const_const.sum(reduction_axis);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ int sum = 0;
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ sum += tensor_map(i, k, j, l);
+ }
+ }
+ VERIFY_IS_EQUAL(result(i, j), sum);
+ VERIFY_IS_EQUAL(result2(i, j), sum);
+ VERIFY_IS_EQUAL(result3(i, j), sum);
+ }
+ }
+}
+
+template <int DataLayout>
+static void test_static_dims() {
+ Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
+ Tensor<float, 2, DataLayout> out(72, 97);
+ in.setRandom();
+
+#ifndef EIGEN_HAS_CONSTEXPR
+ array<int, 2> reduction_axis;
+ reduction_axis[0] = 1;
+ reduction_axis[1] = 3;
+#else
+ Eigen::IndexList<Eigen::type2index<1>, Eigen::type2index<3> > reduction_axis;
+#endif
+
+ out = in.maximum(reduction_axis);
+
+ for (int i = 0; i < 72; ++i) {
+ for (int j = 0; j < 97; ++j) {
+ float expected = -1e10f;
+ for (int k = 0; k < 53; ++k) {
+ for (int l = 0; l < 113; ++l) {
+ expected = (std::max)(expected, in(i, k, j, l));
+ }
+ }
+ VERIFY_IS_APPROX(out(i, j), expected);
+ }
+ }
+}
+
+template <int DataLayout>
+static void test_innermost_last_dims() {
+ Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
+ Tensor<float, 2, DataLayout> out(97, 113);
+ in.setRandom();
+
+// Reduce on the innermost dimensions.
+#ifndef EIGEN_HAS_CONSTEXPR
+ array<int, 2> reduction_axis;
+ reduction_axis[0] = 0;
+ reduction_axis[1] = 1;
+#else
+ // This triggers the use of packets for ColMajor.
+ Eigen::IndexList<Eigen::type2index<0>, Eigen::type2index<1> > reduction_axis;
+#endif
+
+ out = in.maximum(reduction_axis);
+
+ for (int i = 0; i < 97; ++i) {
+ for (int j = 0; j < 113; ++j) {
+ float expected = -1e10f;
+ for (int k = 0; k < 53; ++k) {
+ for (int l = 0; l < 72; ++l) {
+ expected = (std::max)(expected, in(l, k, i, j));
+ }
+ }
+ VERIFY_IS_APPROX(out(i, j), expected);
+ }
+ }
+}
+
+template <int DataLayout>
+static void test_innermost_first_dims() {
+ Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
+ Tensor<float, 2, DataLayout> out(72, 53);
+ in.setRandom();
+
+// Reduce on the innermost dimensions.
+#ifndef EIGEN_HAS_CONSTEXPR
+ array<int, 2> reduction_axis;
+ reduction_axis[0] = 2;
+ reduction_axis[1] = 3;
+#else
+ // This triggers the use of packets for RowMajor.
+ Eigen::IndexList<Eigen::type2index<2>, Eigen::type2index<3>> reduction_axis;
+#endif
+
+ out = in.maximum(reduction_axis);
+
+ for (int i = 0; i < 72; ++i) {
+ for (int j = 0; j < 53; ++j) {
+ float expected = -1e10f;
+ for (int k = 0; k < 97; ++k) {
+ for (int l = 0; l < 113; ++l) {
+ expected = (std::max)(expected, in(i, j, k, l));
+ }
+ }
+ VERIFY_IS_APPROX(out(i, j), expected);
+ }
+ }
+}
+
+template <int DataLayout>
+static void test_reduce_middle_dims() {
+ Tensor<float, 4, DataLayout> in(72, 53, 97, 113);
+ Tensor<float, 2, DataLayout> out(72, 53);
+ in.setRandom();
+
+// Reduce on the innermost dimensions.
+#ifndef EIGEN_HAS_CONSTEXPR
+ array<int, 2> reduction_axis;
+ reduction_axis[0] = 1;
+ reduction_axis[1] = 2;
+#else
+ // This triggers the use of packets for RowMajor.
+ Eigen::IndexList<Eigen::type2index<1>, Eigen::type2index<2>> reduction_axis;
+#endif
+
+ out = in.maximum(reduction_axis);
+
+ for (int i = 0; i < 72; ++i) {
+ for (int j = 0; j < 113; ++j) {
+ float expected = -1e10f;
+ for (int k = 0; k < 53; ++k) {
+ for (int l = 0; l < 97; ++l) {
+ expected = (std::max)(expected, in(i, k, l, j));
+ }
+ }
+ VERIFY_IS_APPROX(out(i, j), expected);
+ }
+ }
+}
+
+void test_cxx11_tensor_reduction() {
+ CALL_SUBTEST(test_simple_reductions<ColMajor>());
+ CALL_SUBTEST(test_simple_reductions<RowMajor>());
+ CALL_SUBTEST(test_full_reductions<ColMajor>());
+ CALL_SUBTEST(test_full_reductions<RowMajor>());
+ CALL_SUBTEST(test_user_defined_reductions<ColMajor>());
+ CALL_SUBTEST(test_user_defined_reductions<RowMajor>());
+ CALL_SUBTEST(test_tensor_maps<ColMajor>());
+ CALL_SUBTEST(test_tensor_maps<RowMajor>());
+ CALL_SUBTEST(test_static_dims<ColMajor>());
+ CALL_SUBTEST(test_static_dims<RowMajor>());
+ CALL_SUBTEST(test_innermost_last_dims<ColMajor>());
+ CALL_SUBTEST(test_innermost_last_dims<RowMajor>());
+ CALL_SUBTEST(test_innermost_first_dims<ColMajor>());
+ CALL_SUBTEST(test_innermost_first_dims<RowMajor>());
+ CALL_SUBTEST(test_reduce_middle_dims<ColMajor>());
+ CALL_SUBTEST(test_reduce_middle_dims<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_ref.cpp b/unsupported/test/cxx11_tensor_ref.cpp
new file mode 100644
index 000000000..aa369f278
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_ref.cpp
@@ -0,0 +1,208 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::RowMajor;
+
+static void test_simple_lvalue_ref()
+{
+ Tensor<int, 1> input(6);
+ input.setRandom();
+
+ TensorRef<Tensor<int, 1>> ref3(input);
+ TensorRef<Tensor<int, 1>> ref4 = input;
+
+ VERIFY_IS_EQUAL(ref3.data(), input.data());
+ VERIFY_IS_EQUAL(ref4.data(), input.data());
+
+ for (int i = 0; i < 6; ++i) {
+ VERIFY_IS_EQUAL(ref3(i), input(i));
+ VERIFY_IS_EQUAL(ref4(i), input(i));
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ ref3.coeffRef(i) = i;
+ }
+ for (int i = 0; i < 6; ++i) {
+ VERIFY_IS_EQUAL(input(i), i);
+ }
+ for (int i = 0; i < 6; ++i) {
+ ref4.coeffRef(i) = -i * 2;
+ }
+ for (int i = 0; i < 6; ++i) {
+ VERIFY_IS_EQUAL(input(i), -i*2);
+ }
+}
+
+
+static void test_simple_rvalue_ref()
+{
+ Tensor<int, 1> input1(6);
+ input1.setRandom();
+ Tensor<int, 1> input2(6);
+ input2.setRandom();
+
+ TensorRef<Tensor<int, 1>> ref3(input1 + input2);
+ TensorRef<Tensor<int, 1>> ref4 = input1 + input2;
+
+ VERIFY_IS_NOT_EQUAL(ref3.data(), input1.data());
+ VERIFY_IS_NOT_EQUAL(ref4.data(), input1.data());
+ VERIFY_IS_NOT_EQUAL(ref3.data(), input2.data());
+ VERIFY_IS_NOT_EQUAL(ref4.data(), input2.data());
+
+ for (int i = 0; i < 6; ++i) {
+ VERIFY_IS_EQUAL(ref3(i), input1(i) + input2(i));
+ VERIFY_IS_EQUAL(ref4(i), input1(i) + input2(i));
+ }
+}
+
+
+static void test_multiple_dims()
+{
+ Tensor<float, 3> input(3,5,7);
+ input.setRandom();
+
+ TensorRef<Tensor<float, 3>> ref(input);
+ VERIFY_IS_EQUAL(ref.data(), input.data());
+ VERIFY_IS_EQUAL(ref.dimension(0), 3);
+ VERIFY_IS_EQUAL(ref.dimension(1), 5);
+ VERIFY_IS_EQUAL(ref.dimension(2), 7);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(ref(i,j,k), input(i,j,k));
+ }
+ }
+ }
+}
+
+
+static void test_slice()
+{
+ Tensor<float, 5> tensor(2,3,5,7,11);
+ tensor.setRandom();
+
+ Eigen::DSizes<ptrdiff_t, 5> indices(1,2,3,4,5);
+ Eigen::DSizes<ptrdiff_t, 5> sizes(1,1,1,1,1);
+ TensorRef<Tensor<float, 5>> slice = tensor.slice(indices, sizes);
+ VERIFY_IS_EQUAL(slice(0,0,0,0,0), tensor(1,2,3,4,5));
+
+ Eigen::DSizes<ptrdiff_t, 5> indices2(1,1,3,4,5);
+ Eigen::DSizes<ptrdiff_t, 5> sizes2(1,1,2,2,3);
+ slice = tensor.slice(indices2, sizes2);
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ VERIFY_IS_EQUAL(slice(0,0,i,j,k), tensor(1,1,3+i,4+j,5+k));
+ }
+ }
+ }
+
+ Eigen::DSizes<ptrdiff_t, 5> indices3(0,0,0,0,0);
+ Eigen::DSizes<ptrdiff_t, 5> sizes3(2,3,1,1,1);
+ slice = tensor.slice(indices3, sizes3);
+ VERIFY_IS_EQUAL(slice.data(), tensor.data());
+}
+
+
+static void test_ref_of_ref()
+{
+ Tensor<float, 3> input(3,5,7);
+ input.setRandom();
+
+ TensorRef<Tensor<float, 3>> ref(input);
+ TensorRef<Tensor<float, 3>> ref_of_ref(ref);
+ TensorRef<Tensor<float, 3>> ref_of_ref2;
+ ref_of_ref2 = ref;
+
+ VERIFY_IS_EQUAL(ref_of_ref.data(), input.data());
+ VERIFY_IS_EQUAL(ref_of_ref.dimension(0), 3);
+ VERIFY_IS_EQUAL(ref_of_ref.dimension(1), 5);
+ VERIFY_IS_EQUAL(ref_of_ref.dimension(2), 7);
+
+ VERIFY_IS_EQUAL(ref_of_ref2.data(), input.data());
+ VERIFY_IS_EQUAL(ref_of_ref2.dimension(0), 3);
+ VERIFY_IS_EQUAL(ref_of_ref2.dimension(1), 5);
+ VERIFY_IS_EQUAL(ref_of_ref2.dimension(2), 7);
+
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(ref_of_ref(i,j,k), input(i,j,k));
+ VERIFY_IS_EQUAL(ref_of_ref2(i,j,k), input(i,j,k));
+ }
+ }
+ }
+}
+
+
+static void test_ref_in_expr()
+{
+ Tensor<float, 3> input(3,5,7);
+ input.setRandom();
+ TensorRef<Tensor<float, 3>> input_ref(input);
+
+ Tensor<float, 3> result(3,5,7);
+ result.setRandom();
+ TensorRef<Tensor<float, 3>> result_ref(result);
+
+ Tensor<float, 3> bias(3,5,7);
+ bias.setRandom();
+
+ result_ref = input_ref + bias;
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(result_ref(i,j,k), input(i,j,k) + bias(i,j,k));
+ VERIFY_IS_NOT_EQUAL(result(i,j,k), input(i,j,k) + bias(i,j,k));
+ }
+ }
+ }
+
+ result = result_ref;
+ for (int i = 0; i < 3; ++i) {
+ for (int j = 0; j < 5; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_EQUAL(result(i,j,k), input(i,j,k) + bias(i,j,k));
+ }
+ }
+ }
+}
+
+
+static void test_coeff_ref()
+{
+ Tensor<float, 5> tensor(2,3,5,7,11);
+ tensor.setRandom();
+ Tensor<float, 5> original = tensor;
+
+ TensorRef<Tensor<float, 4>> slice = tensor.chip(7, 4);
+ slice.coeffRef(0, 0, 0, 0) = 1.0f;
+ slice.coeffRef(1, 0, 0, 0) += 2.0f;
+
+ VERIFY_IS_EQUAL(tensor(0,0,0,0,7), 1.0f);
+ VERIFY_IS_EQUAL(tensor(1,0,0,0,7), original(1,0,0,0,7) + 2.0f);
+}
+
+
+void test_cxx11_tensor_ref()
+{
+ CALL_SUBTEST(test_simple_lvalue_ref());
+ CALL_SUBTEST(test_simple_rvalue_ref());
+ CALL_SUBTEST(test_multiple_dims());
+ CALL_SUBTEST(test_slice());
+ CALL_SUBTEST(test_ref_of_ref());
+ CALL_SUBTEST(test_ref_in_expr());
+ CALL_SUBTEST(test_coeff_ref());
+}
diff --git a/unsupported/test/cxx11_tensor_reverse.cpp b/unsupported/test/cxx11_tensor_reverse.cpp
new file mode 100644
index 000000000..4c0be35da
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_reverse.cpp
@@ -0,0 +1,167 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Navdeep Jaitly <ndjaitly@google.com and
+// Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::array;
+
+template <int DataLayout>
+static void test_simple_reverse()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ array<bool, 4> dim_rev;
+ dim_rev[0] = false;
+ dim_rev[1] = true;
+ dim_rev[2] = true;
+ dim_rev[3] = false;
+
+ Tensor<float, 4, DataLayout> reversed_tensor;
+ reversed_tensor = tensor.reverse(dim_rev);
+
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(0), 2);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(1), 3);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(2), 5);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(3), 7);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), reversed_tensor(i,2-j,4-k,l));
+ }
+ }
+ }
+ }
+
+ dim_rev[0] = true;
+ dim_rev[1] = false;
+ dim_rev[2] = false;
+ dim_rev[3] = false;
+
+ reversed_tensor = tensor.reverse(dim_rev);
+
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(0), 2);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(1), 3);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(2), 5);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(3), 7);
+
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), reversed_tensor(1-i,j,k,l));
+ }
+ }
+ }
+ }
+
+ dim_rev[0] = true;
+ dim_rev[1] = false;
+ dim_rev[2] = false;
+ dim_rev[3] = true;
+
+ reversed_tensor = tensor.reverse(dim_rev);
+
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(0), 2);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(1), 3);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(2), 5);
+ VERIFY_IS_EQUAL(reversed_tensor.dimension(3), 7);
+
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), reversed_tensor(1-i,j,k,6-l));
+ }
+ }
+ }
+ }
+}
+
+
+template <int DataLayout>
+static void test_expr_reverse()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ array<bool, 4> dim_rev;
+ dim_rev[0] = false;
+ dim_rev[1] = true;
+ dim_rev[2] = false;
+ dim_rev[3] = true;
+
+
+ Tensor<float, 4, DataLayout> expected;
+ expected = tensor.reverse(dim_rev);
+
+ Tensor<float, 4, DataLayout> result(2,3,5,7);
+
+ array<ptrdiff_t, 4> src_slice_dim{{2,3,1,7}};
+ array<ptrdiff_t, 4> src_slice_start{{0,0,0,0}};
+ array<ptrdiff_t, 4> dst_slice_dim{{2,3,1,7}};
+ array<ptrdiff_t, 4> dst_slice_start{{0,0,0,0}};
+
+ for (int i = 0; i < 5; ++i) {
+ result.slice(dst_slice_start, dst_slice_dim) =
+ tensor.slice(src_slice_start, src_slice_dim).reverse(dim_rev);
+ src_slice_start[2] += 1;
+ dst_slice_start[2] += 1;
+ }
+
+ VERIFY_IS_EQUAL(result.dimension(0), 2);
+ VERIFY_IS_EQUAL(result.dimension(1), 3);
+ VERIFY_IS_EQUAL(result.dimension(2), 5);
+ VERIFY_IS_EQUAL(result.dimension(3), 7);
+
+ for (int i = 0; i < expected.dimension(0); ++i) {
+ for (int j = 0; j < expected.dimension(1); ++j) {
+ for (int k = 0; k < expected.dimension(2); ++k) {
+ for (int l = 0; l < expected.dimension(3); ++l) {
+ VERIFY_IS_EQUAL(result(i,j,k,l), expected(i,j,k,l));
+ }
+ }
+ }
+ }
+
+ dst_slice_start[2] = 0;
+ result.setRandom();
+ for (int i = 0; i < 5; ++i) {
+ result.slice(dst_slice_start, dst_slice_dim) =
+ tensor.reverse(dim_rev).slice(dst_slice_start, dst_slice_dim);
+ dst_slice_start[2] += 1;
+ }
+
+ for (int i = 0; i < expected.dimension(0); ++i) {
+ for (int j = 0; j < expected.dimension(1); ++j) {
+ for (int k = 0; k < expected.dimension(2); ++k) {
+ for (int l = 0; l < expected.dimension(3); ++l) {
+ VERIFY_IS_EQUAL(result(i,j,k,l), expected(i,j,k,l));
+ }
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_reverse()
+{
+ CALL_SUBTEST(test_simple_reverse<ColMajor>());
+ CALL_SUBTEST(test_simple_reverse<RowMajor>());
+ CALL_SUBTEST(test_expr_reverse<ColMajor>());
+ CALL_SUBTEST(test_expr_reverse<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_shuffling.cpp b/unsupported/test/cxx11_tensor_shuffling.cpp
new file mode 100644
index 000000000..2f7fd9e50
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_shuffling.cpp
@@ -0,0 +1,187 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+using Eigen::array;
+
+template <int DataLayout>
+static void test_simple_shuffling()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+ array<ptrdiff_t, 4> shuffles;
+ shuffles[0] = 0;
+ shuffles[1] = 1;
+ shuffles[2] = 2;
+ shuffles[3] = 3;
+
+ Tensor<float, 4, DataLayout> no_shuffle;
+ no_shuffle = tensor.shuffle(shuffles);
+
+ VERIFY_IS_EQUAL(no_shuffle.dimension(0), 2);
+ VERIFY_IS_EQUAL(no_shuffle.dimension(1), 3);
+ VERIFY_IS_EQUAL(no_shuffle.dimension(2), 5);
+ VERIFY_IS_EQUAL(no_shuffle.dimension(3), 7);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), no_shuffle(i,j,k,l));
+ }
+ }
+ }
+ }
+
+ shuffles[0] = 2;
+ shuffles[1] = 3;
+ shuffles[2] = 1;
+ shuffles[3] = 0;
+ Tensor<float, 4, DataLayout> shuffle;
+ shuffle = tensor.shuffle(shuffles);
+
+ VERIFY_IS_EQUAL(shuffle.dimension(0), 5);
+ VERIFY_IS_EQUAL(shuffle.dimension(1), 7);
+ VERIFY_IS_EQUAL(shuffle.dimension(2), 3);
+ VERIFY_IS_EQUAL(shuffle.dimension(3), 2);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), shuffle(k,l,j,i));
+ }
+ }
+ }
+ }
+}
+
+
+template <int DataLayout>
+static void test_expr_shuffling()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+
+ array<ptrdiff_t, 4> shuffles;
+ shuffles[0] = 2;
+ shuffles[1] = 3;
+ shuffles[2] = 1;
+ shuffles[3] = 0;
+ Tensor<float, 4, DataLayout> expected;
+ expected = tensor.shuffle(shuffles);
+
+ Tensor<float, 4, DataLayout> result(5,7,3,2);
+
+ array<int, 4> src_slice_dim{{2,3,1,7}};
+ array<int, 4> src_slice_start{{0,0,0,0}};
+ array<int, 4> dst_slice_dim{{1,7,3,2}};
+ array<int, 4> dst_slice_start{{0,0,0,0}};
+
+ for (int i = 0; i < 5; ++i) {
+ result.slice(dst_slice_start, dst_slice_dim) =
+ tensor.slice(src_slice_start, src_slice_dim).shuffle(shuffles);
+ src_slice_start[2] += 1;
+ dst_slice_start[0] += 1;
+ }
+
+ VERIFY_IS_EQUAL(result.dimension(0), 5);
+ VERIFY_IS_EQUAL(result.dimension(1), 7);
+ VERIFY_IS_EQUAL(result.dimension(2), 3);
+ VERIFY_IS_EQUAL(result.dimension(3), 2);
+
+ for (int i = 0; i < expected.dimension(0); ++i) {
+ for (int j = 0; j < expected.dimension(1); ++j) {
+ for (int k = 0; k < expected.dimension(2); ++k) {
+ for (int l = 0; l < expected.dimension(3); ++l) {
+ VERIFY_IS_EQUAL(result(i,j,k,l), expected(i,j,k,l));
+ }
+ }
+ }
+ }
+
+ dst_slice_start[0] = 0;
+ result.setRandom();
+ for (int i = 0; i < 5; ++i) {
+ result.slice(dst_slice_start, dst_slice_dim) =
+ tensor.shuffle(shuffles).slice(dst_slice_start, dst_slice_dim);
+ dst_slice_start[0] += 1;
+ }
+
+ for (int i = 0; i < expected.dimension(0); ++i) {
+ for (int j = 0; j < expected.dimension(1); ++j) {
+ for (int k = 0; k < expected.dimension(2); ++k) {
+ for (int l = 0; l < expected.dimension(3); ++l) {
+ VERIFY_IS_EQUAL(result(i,j,k,l), expected(i,j,k,l));
+ }
+ }
+ }
+ }
+}
+
+
+template <int DataLayout>
+static void test_shuffling_as_value()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+ array<ptrdiff_t, 4> shuffles;
+ shuffles[2] = 0;
+ shuffles[3] = 1;
+ shuffles[1] = 2;
+ shuffles[0] = 3;
+ Tensor<float, 4, DataLayout> shuffle(5,7,3,2);
+ shuffle.shuffle(shuffles) = tensor;
+
+ VERIFY_IS_EQUAL(shuffle.dimension(0), 5);
+ VERIFY_IS_EQUAL(shuffle.dimension(1), 7);
+ VERIFY_IS_EQUAL(shuffle.dimension(2), 3);
+ VERIFY_IS_EQUAL(shuffle.dimension(3), 2);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), shuffle(k,l,j,i));
+ }
+ }
+ }
+ }
+
+ array<ptrdiff_t, 4> no_shuffle;
+ no_shuffle[0] = 0;
+ no_shuffle[1] = 1;
+ no_shuffle[2] = 2;
+ no_shuffle[3] = 3;
+ Tensor<float, 4, DataLayout> shuffle2(5,7,3,2);
+ shuffle2.shuffle(shuffles) = tensor.shuffle(no_shuffle);
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 2; ++l) {
+ VERIFY_IS_EQUAL(shuffle2(i,j,k,l), shuffle(i,j,k,l));
+ }
+ }
+ }
+ }
+}
+
+void test_cxx11_tensor_shuffling()
+{
+ CALL_SUBTEST(test_simple_shuffling<ColMajor>());
+ CALL_SUBTEST(test_simple_shuffling<RowMajor>());
+ CALL_SUBTEST(test_expr_shuffling<ColMajor>());
+ CALL_SUBTEST(test_expr_shuffling<RowMajor>());
+ CALL_SUBTEST(test_shuffling_as_value<ColMajor>());
+ CALL_SUBTEST(test_shuffling_as_value<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_simple.cpp b/unsupported/test/cxx11_tensor_simple.cpp
index ea512c9cc..23855fca0 100644
--- a/unsupported/test/cxx11_tensor_simple.cpp
+++ b/unsupported/test/cxx11_tensor_simple.cpp
@@ -32,6 +32,7 @@ static void test_1d()
vec1(5) = 42; vec2(5) = 5; vec3(5) = 0;
vec4.setZero();
+ VERIFY_IS_EQUAL((vec1.rank()), 1);
VERIFY_IS_EQUAL((vec1.size()), 6);
VERIFY_IS_EQUAL((vec1.dimensions()[0]), 6);
@@ -99,10 +100,12 @@ static void test_2d()
mat2(1,1) = 4;
mat2(1,2) = 5;
+ VERIFY_IS_EQUAL((mat1.rank()), 2);
VERIFY_IS_EQUAL((mat1.size()), 6);
VERIFY_IS_EQUAL((mat1.dimensions()[0]), 2);
VERIFY_IS_EQUAL((mat1.dimensions()[1]), 3);
+ VERIFY_IS_EQUAL((mat2.rank()), 2);
VERIFY_IS_EQUAL((mat2.size()), 6);
VERIFY_IS_EQUAL((mat2.dimensions()[0]), 2);
VERIFY_IS_EQUAL((mat2.dimensions()[1]), 3);
@@ -163,7 +166,7 @@ static void test_3d()
VERIFY_IS_EQUAL((epsilon(0,2,1)), -1);
VERIFY_IS_EQUAL((epsilon(1,0,2)), -1);
- std::array<Eigen::DenseIndex, 3> dims{{2,3,4}};
+ array<Eigen::DenseIndex, 3> dims{{2,3,4}};
Tensor<int, 3> t1(dims);
Tensor<int, 3, RowMajor> t2(dims);
@@ -244,7 +247,7 @@ static void test_simple_assign()
epsilon(0,1,2) = epsilon(2,0,1) = epsilon(1,2,0) = 1;
epsilon(2,1,0) = epsilon(0,2,1) = epsilon(1,0,2) = -1;
- Tensor<int, 3> e2(2,3,1);
+ Tensor<int, 3> e2(3,3,3);
e2.setZero();
VERIFY_IS_EQUAL((e2(1,2,0)), 0);
@@ -257,12 +260,38 @@ static void test_simple_assign()
VERIFY_IS_EQUAL((e2(1,0,2)), -1);
}
+static void test_resize()
+{
+ Tensor<int, 3> epsilon;
+ epsilon.resize(2,3,7);
+ VERIFY_IS_EQUAL(epsilon.dimension(0), 2);
+ VERIFY_IS_EQUAL(epsilon.dimension(1), 3);
+ VERIFY_IS_EQUAL(epsilon.dimension(2), 7);
+ VERIFY_IS_EQUAL(epsilon.dimensions().TotalSize(), 2ul*3*7);
+
+ const int* old_data = epsilon.data();
+ epsilon.resize(3,2,7);
+ VERIFY_IS_EQUAL(epsilon.dimension(0), 3);
+ VERIFY_IS_EQUAL(epsilon.dimension(1), 2);
+ VERIFY_IS_EQUAL(epsilon.dimension(2), 7);
+ VERIFY_IS_EQUAL(epsilon.dimensions().TotalSize(), 2ul*3*7);
+ VERIFY_IS_EQUAL(epsilon.data(), old_data);
+
+ epsilon.resize(3,5,7);
+ VERIFY_IS_EQUAL(epsilon.dimension(0), 3);
+ VERIFY_IS_EQUAL(epsilon.dimension(1), 5);
+ VERIFY_IS_EQUAL(epsilon.dimension(2), 7);
+ VERIFY_IS_EQUAL(epsilon.dimensions().TotalSize(), 3ul*5*7);
+ VERIFY_IS_NOT_EQUAL(epsilon.data(), old_data);
+}
+
void test_cxx11_tensor_simple()
{
CALL_SUBTEST(test_1d());
CALL_SUBTEST(test_2d());
CALL_SUBTEST(test_3d());
CALL_SUBTEST(test_simple_assign());
+ CALL_SUBTEST(test_resize());
}
/*
diff --git a/unsupported/test/cxx11_tensor_striding.cpp b/unsupported/test/cxx11_tensor_striding.cpp
new file mode 100644
index 000000000..935b908cc
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_striding.cpp
@@ -0,0 +1,119 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include "main.h"
+
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+template<int DataLayout>
+static void test_simple_striding()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+ array<ptrdiff_t, 4> strides;
+ strides[0] = 1;
+ strides[1] = 1;
+ strides[2] = 1;
+ strides[3] = 1;
+
+ Tensor<float, 4, DataLayout> no_stride;
+ no_stride = tensor.stride(strides);
+
+ VERIFY_IS_EQUAL(no_stride.dimension(0), 2);
+ VERIFY_IS_EQUAL(no_stride.dimension(1), 3);
+ VERIFY_IS_EQUAL(no_stride.dimension(2), 5);
+ VERIFY_IS_EQUAL(no_stride.dimension(3), 7);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), no_stride(i,j,k,l));
+ }
+ }
+ }
+ }
+
+ strides[0] = 2;
+ strides[1] = 4;
+ strides[2] = 2;
+ strides[3] = 3;
+ Tensor<float, 4, DataLayout> stride;
+ stride = tensor.stride(strides);
+
+ VERIFY_IS_EQUAL(stride.dimension(0), 1);
+ VERIFY_IS_EQUAL(stride.dimension(1), 1);
+ VERIFY_IS_EQUAL(stride.dimension(2), 3);
+ VERIFY_IS_EQUAL(stride.dimension(3), 3);
+
+ for (int i = 0; i < 1; ++i) {
+ for (int j = 0; j < 1; ++j) {
+ for (int k = 0; k < 3; ++k) {
+ for (int l = 0; l < 3; ++l) {
+ VERIFY_IS_EQUAL(tensor(2*i,4*j,2*k,3*l), stride(i,j,k,l));
+ }
+ }
+ }
+ }
+}
+
+
+template<int DataLayout>
+static void test_striding_as_lvalue()
+{
+ Tensor<float, 4, DataLayout> tensor(2,3,5,7);
+ tensor.setRandom();
+ array<ptrdiff_t, 4> strides;
+ strides[0] = 2;
+ strides[1] = 4;
+ strides[2] = 2;
+ strides[3] = 3;
+
+ Tensor<float, 4, DataLayout> result(3, 12, 10, 21);
+ result.stride(strides) = tensor;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), result(2*i,4*j,2*k,3*l));
+ }
+ }
+ }
+ }
+
+ array<ptrdiff_t, 4> no_strides;
+ no_strides[0] = 1;
+ no_strides[1] = 1;
+ no_strides[2] = 1;
+ no_strides[3] = 1;
+ Tensor<float, 4, DataLayout> result2(3, 12, 10, 21);
+ result2.stride(strides) = tensor.stride(no_strides);
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 5; ++k) {
+ for (int l = 0; l < 7; ++l) {
+ VERIFY_IS_EQUAL(tensor(i,j,k,l), result2(2*i,4*j,2*k,3*l));
+ }
+ }
+ }
+ }
+}
+
+
+void test_cxx11_tensor_striding()
+{
+ CALL_SUBTEST(test_simple_striding<ColMajor>());
+ CALL_SUBTEST(test_simple_striding<RowMajor>());
+ CALL_SUBTEST(test_striding_as_lvalue<ColMajor>());
+ CALL_SUBTEST(test_striding_as_lvalue<RowMajor>());
+}
diff --git a/unsupported/test/cxx11_tensor_thread_pool.cpp b/unsupported/test/cxx11_tensor_thread_pool.cpp
new file mode 100644
index 000000000..6fe65c7f9
--- /dev/null
+++ b/unsupported/test/cxx11_tensor_thread_pool.cpp
@@ -0,0 +1,270 @@
+// This file is part of Eigen, a lightweight C++ template library
+// for linear algebra.
+//
+// Copyright (C) 2014 Benoit Steiner <benoit.steiner.goog@gmail.com>
+//
+// This Source Code Form is subject to the terms of the Mozilla
+// Public License v. 2.0. If a copy of the MPL was not distributed
+// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#define EIGEN_USE_THREADS
+
+
+#include "main.h"
+#include <iostream>
+#include <Eigen/CXX11/Tensor>
+
+using Eigen::Tensor;
+
+
+static void test_multithread_elementwise()
+{
+ Tensor<float, 3> in1(2,3,7);
+ Tensor<float, 3> in2(2,3,7);
+ Tensor<float, 3> out(2,3,7);
+
+ in1.setRandom();
+ in2.setRandom();
+
+ Eigen::ThreadPoolDevice thread_pool_device(internal::random<int>(3, 11));
+ out.device(thread_pool_device) = in1 + in2 * 3.14f;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), in1(i,j,k) + in2(i,j,k) * 3.14f);
+ }
+ }
+ }
+}
+
+
+static void test_multithread_compound_assignment()
+{
+ Tensor<float, 3> in1(2,3,7);
+ Tensor<float, 3> in2(2,3,7);
+ Tensor<float, 3> out(2,3,7);
+
+ in1.setRandom();
+ in2.setRandom();
+
+ Eigen::ThreadPoolDevice thread_pool_device(internal::random<int>(3, 11));
+ out.device(thread_pool_device) = in1;
+ out.device(thread_pool_device) += in2 * 3.14f;
+
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 3; ++j) {
+ for (int k = 0; k < 7; ++k) {
+ VERIFY_IS_APPROX(out(i,j,k), in1(i,j,k) + in2(i,j,k) * 3.14f);
+ }
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_multithread_contraction()
+{
+ Tensor<float, 4, DataLayout> t_left(30, 50, 37, 31);
+ Tensor<float, 5, DataLayout> t_right(37, 31, 70, 2, 10);
+ Tensor<float, 5, DataLayout> t_result(30, 50, 70, 2, 10);
+
+ t_left.setRandom();
+ t_right.setRandom();
+
+ // this contraction should be equivalent to a single matrix multiplication
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ Eigen::array<DimPair, 2> dims({{DimPair(2, 0), DimPair(3, 1)}});
+
+ typedef Map<Matrix<float, Dynamic, Dynamic, DataLayout>> MapXf;
+ MapXf m_left(t_left.data(), 1500, 1147);
+ MapXf m_right(t_right.data(), 1147, 1400);
+ Matrix<float, Dynamic, Dynamic, DataLayout> m_result(1500, 1400);
+
+ Eigen::ThreadPoolDevice thread_pool_device(4);
+
+ // compute results by separate methods
+ t_result.device(thread_pool_device) = t_left.contract(t_right, dims);
+ m_result = m_left * m_right;
+
+ for (ptrdiff_t i = 0; i < t_result.size(); i++) {
+ VERIFY(&t_result.data()[i] != &m_result.data()[i]);
+ if (fabs(t_result.data()[i] - m_result.data()[i]) >= 1e-4) {
+ std::cout << "mismatch detected: " << t_result.data()[i] << " vs " << m_result.data()[i] << std::endl;
+ assert(false);
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_contraction_corner_cases()
+{
+ Tensor<float, 2, DataLayout> t_left(32, 500);
+ Tensor<float, 2, DataLayout> t_right(32, 28*28);
+ Tensor<float, 2, DataLayout> t_result(500, 28*28);
+
+ t_left = (t_left.constant(-0.5f) + t_left.random()) * 2.0f;
+ t_right = (t_right.constant(-0.6f) + t_right.random()) * 2.0f;
+ t_result = t_result.constant(NAN);
+
+ // this contraction should be equivalent to a single matrix multiplication
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ Eigen::array<DimPair, 1> dims{{DimPair(0, 0)}};
+
+ typedef Map<Matrix<float, Dynamic, Dynamic, DataLayout>> MapXf;
+ MapXf m_left(t_left.data(), 32, 500);
+ MapXf m_right(t_right.data(), 32, 28*28);
+ Matrix<float, Dynamic, Dynamic, DataLayout> m_result(500, 28*28);
+
+ Eigen::ThreadPoolDevice thread_pool_device(12);
+
+ // compute results by separate methods
+ t_result.device(thread_pool_device) = t_left.contract(t_right, dims);
+ m_result = m_left.transpose() * m_right;
+
+ for (ptrdiff_t i = 0; i < t_result.size(); i++) {
+ assert(!std::isnan(t_result.data()[i]));
+ if (fabs(t_result.data()[i] - m_result.data()[i]) >= 1e-4) {
+ std::cout << "mismatch detected at index " << i << " : " << t_result.data()[i] << " vs " << m_result.data()[i] << std::endl;
+ assert(false);
+ }
+ }
+
+ t_left.resize(32, 1);
+ t_left = (t_left.constant(-0.5f) + t_left.random()) * 2.0f;
+ t_result.resize (1, 28*28);
+ t_result = t_result.constant(NAN);
+ t_result.device(thread_pool_device) = t_left.contract(t_right, dims);
+ new(&m_left) MapXf(t_left.data(), 32, 1);
+ m_result = m_left.transpose() * m_right;
+ for (ptrdiff_t i = 0; i < t_result.size(); i++) {
+ assert(!std::isnan(t_result.data()[i]));
+ if (fabs(t_result.data()[i] - m_result.data()[i]) >= 1e-4) {
+ std::cout << "mismatch detected: " << t_result.data()[i] << " vs " << m_result.data()[i] << std::endl;
+ assert(false);
+ }
+ }
+
+ t_left.resize(32, 500);
+ t_right.resize(32, 4);
+ t_left = (t_left.constant(-0.5f) + t_left.random()) * 2.0f;
+ t_right = (t_right.constant(-0.6f) + t_right.random()) * 2.0f;
+ t_result.resize (500, 4);
+ t_result = t_result.constant(NAN);
+ t_result.device(thread_pool_device) = t_left.contract(t_right, dims);
+ new(&m_left) MapXf(t_left.data(), 32, 500);
+ new(&m_right) MapXf(t_right.data(), 32, 4);
+ m_result = m_left.transpose() * m_right;
+ for (ptrdiff_t i = 0; i < t_result.size(); i++) {
+ assert(!std::isnan(t_result.data()[i]));
+ if (fabs(t_result.data()[i] - m_result.data()[i]) >= 1e-4) {
+ std::cout << "mismatch detected: " << t_result.data()[i] << " vs " << m_result.data()[i] << std::endl;
+ assert(false);
+ }
+ }
+
+ t_left.resize(32, 1);
+ t_right.resize(32, 4);
+ t_left = (t_left.constant(-0.5f) + t_left.random()) * 2.0f;
+ t_right = (t_right.constant(-0.6f) + t_right.random()) * 2.0f;
+ t_result.resize (1, 4);
+ t_result = t_result.constant(NAN);
+ t_result.device(thread_pool_device) = t_left.contract(t_right, dims);
+ new(&m_left) MapXf(t_left.data(), 32, 1);
+ new(&m_right) MapXf(t_right.data(), 32, 4);
+ m_result = m_left.transpose() * m_right;
+ for (ptrdiff_t i = 0; i < t_result.size(); i++) {
+ assert(!std::isnan(t_result.data()[i]));
+ if (fabs(t_result.data()[i] - m_result.data()[i]) >= 1e-4) {
+ std::cout << "mismatch detected: " << t_result.data()[i] << " vs " << m_result.data()[i] << std::endl;
+ assert(false);
+ }
+ }
+}
+
+template<int DataLayout>
+static void test_multithread_contraction_agrees_with_singlethread() {
+ int contract_size = internal::random<int>(1, 5000);
+
+ Tensor<float, 3, DataLayout> left(internal::random<int>(1, 80),
+ contract_size,
+ internal::random<int>(1, 100));
+
+ Tensor<float, 4, DataLayout> right(internal::random<int>(1, 25),
+ internal::random<int>(1, 37),
+ contract_size,
+ internal::random<int>(1, 51));
+
+ left.setRandom();
+ right.setRandom();
+
+ // add constants to shift values away from 0 for more precision
+ left += left.constant(1.5f);
+ right += right.constant(1.5f);
+
+ typedef Tensor<float, 1>::DimensionPair DimPair;
+ Eigen::array<DimPair, 1> dims({{DimPair(1, 2)}});
+
+ Eigen::ThreadPoolDevice thread_pool_device(internal::random<int>(2, 11));
+
+ Tensor<float, 5, DataLayout> st_result;
+ st_result = left.contract(right, dims);
+
+ Tensor<float, 5, DataLayout> tp_result(st_result.dimensions());
+ tp_result.device(thread_pool_device) = left.contract(right, dims);
+
+ VERIFY(dimensions_match(st_result.dimensions(), tp_result.dimensions()));
+ for (ptrdiff_t i = 0; i < st_result.size(); i++) {
+ // if both of the values are very small, then do nothing (because the test will fail
+ // due to numerical precision issues when values are small)
+ if (fabs(st_result.data()[i] - tp_result.data()[i]) >= 1e-4) {
+ VERIFY_IS_APPROX(st_result.data()[i], tp_result.data()[i]);
+ }
+ }
+}
+
+
+static void test_memcpy() {
+
+ for (int i = 0; i < 5; ++i) {
+ const int num_threads = internal::random<int>(3, 11);
+ Eigen::ThreadPoolDevice thread_pool_device(num_threads);
+
+ const int size = internal::random<int>(13, 7632);
+ Tensor<float, 1> t1(size);
+ t1.setRandom();
+ std::vector<float> result(size);
+ thread_pool_device.memcpy(&result[0], t1.data(), size*sizeof(float));
+ for (int i = 0; i < size; i++) {
+ VERIFY_IS_EQUAL(t1(i), result[i]);
+ }
+ }
+}
+
+
+static void test_multithread_random()
+{
+ Eigen::ThreadPoolDevice device(2);
+ Tensor<float, 1> t(1 << 20);
+ t.device(device) = t.random<Eigen::internal::NormalRandomGenerator<float>>();
+}
+
+
+void test_cxx11_tensor_thread_pool()
+{
+ CALL_SUBTEST(test_multithread_elementwise());
+ CALL_SUBTEST(test_multithread_compound_assignment());
+
+ CALL_SUBTEST(test_multithread_contraction<ColMajor>());
+ CALL_SUBTEST(test_multithread_contraction<RowMajor>());
+
+ CALL_SUBTEST(test_multithread_contraction_agrees_with_singlethread<ColMajor>());
+ CALL_SUBTEST(test_multithread_contraction_agrees_with_singlethread<RowMajor>());
+
+ // Exercise various cases that have been problematic in the past.
+ CALL_SUBTEST(test_contraction_corner_cases<ColMajor>());
+ CALL_SUBTEST(test_contraction_corner_cases<RowMajor>());
+
+ CALL_SUBTEST(test_memcpy());
+
+ CALL_SUBTEST(test_multithread_random());
+}